486 lines
15 KiB
JavaScript
486 lines
15 KiB
JavaScript
#!/usr/bin/env node
|
|
/**
|
|
* Neural-Trader CLI
|
|
*
|
|
* Command-line interface for running trading strategies
|
|
*
|
|
* Usage:
|
|
* npx neural-trader run --strategy=hybrid --symbol=AAPL
|
|
* npx neural-trader backtest --data=./data.json --days=252
|
|
* npx neural-trader paper --capital=100000
|
|
*/
|
|
|
|
import { createTradingPipeline } from './system/trading-pipeline.js';
|
|
import { BacktestEngine, PerformanceMetrics } from './system/backtesting.js';
|
|
import { DataManager } from './system/data-connectors.js';
|
|
import { RiskManager } from './system/risk-management.js';
|
|
|
|
// CLI Configuration
|
|
const CLI_VERSION = '1.0.0';
|
|
|
|
// Parse command line arguments
|
|
function parseArgs(args) {
|
|
const parsed = {
|
|
command: args[0] || 'help',
|
|
options: {}
|
|
};
|
|
|
|
for (let i = 1; i < args.length; i++) {
|
|
const arg = args[i];
|
|
if (arg.startsWith('--')) {
|
|
const [key, value] = arg.slice(2).split('=');
|
|
parsed.options[key] = value || true;
|
|
} else if (arg.startsWith('-')) {
|
|
parsed.options[arg.slice(1)] = args[++i] || true;
|
|
}
|
|
}
|
|
|
|
return parsed;
|
|
}
|
|
|
|
// Generate synthetic data for demo
|
|
function generateSyntheticData(days = 252, startPrice = 100) {
|
|
const data = [];
|
|
let price = startPrice;
|
|
|
|
for (let i = 0; i < days; i++) {
|
|
const trend = Math.sin(i / 50) * 0.001;
|
|
const noise = (Math.random() - 0.5) * 0.02;
|
|
price *= (1 + trend + noise);
|
|
|
|
data.push({
|
|
date: new Date(Date.now() - (days - i) * 24 * 60 * 60 * 1000),
|
|
open: price * (1 - Math.random() * 0.005),
|
|
high: price * (1 + Math.random() * 0.01),
|
|
low: price * (1 - Math.random() * 0.01),
|
|
close: price,
|
|
volume: 1000000 * (0.5 + Math.random())
|
|
});
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
// Commands
|
|
const commands = {
|
|
help: () => {
|
|
console.log(`
|
|
Neural-Trader CLI v${CLI_VERSION}
|
|
|
|
USAGE:
|
|
neural-trader <command> [options]
|
|
|
|
COMMANDS:
|
|
run Execute trading strategy in real-time mode
|
|
backtest Run historical backtest simulation
|
|
paper Start paper trading session
|
|
analyze Analyze market data and generate signals
|
|
benchmark Run performance benchmarks
|
|
help Show this help message
|
|
|
|
OPTIONS:
|
|
--strategy=<name> Strategy: hybrid, lstm, drl, sentiment (default: hybrid)
|
|
--symbol=<ticker> Stock/crypto symbol (default: AAPL)
|
|
--capital=<amount> Initial capital (default: 100000)
|
|
--days=<n> Number of trading days (default: 252)
|
|
--data=<path> Path to historical data file
|
|
--output=<path> Path for output results
|
|
--verbose Enable verbose output
|
|
--json Output in JSON format
|
|
|
|
EXAMPLES:
|
|
neural-trader run --strategy=hybrid --symbol=AAPL
|
|
neural-trader backtest --days=500 --capital=50000
|
|
neural-trader paper --capital=100000 --strategy=drl
|
|
neural-trader analyze --symbol=TSLA --verbose
|
|
`);
|
|
},
|
|
|
|
run: async (options) => {
|
|
console.log('═'.repeat(70));
|
|
console.log('NEURAL-TRADER: REAL-TIME MODE');
|
|
console.log('═'.repeat(70));
|
|
console.log();
|
|
|
|
const strategy = options.strategy || 'hybrid';
|
|
const symbol = options.symbol || 'AAPL';
|
|
const capital = parseFloat(options.capital) || 100000;
|
|
|
|
console.log(`Strategy: ${strategy}`);
|
|
console.log(`Symbol: ${symbol}`);
|
|
console.log(`Capital: $${capital.toLocaleString()}`);
|
|
console.log();
|
|
|
|
const pipeline = createTradingPipeline();
|
|
const riskManager = new RiskManager();
|
|
riskManager.startDay(capital);
|
|
|
|
// Generate sample data for demo
|
|
const marketData = generateSyntheticData(100);
|
|
const currentPrice = marketData[marketData.length - 1].close;
|
|
|
|
const context = {
|
|
marketData,
|
|
newsData: [
|
|
{ symbol, text: 'Market showing positive momentum today', source: 'news' },
|
|
{ symbol, text: 'Analysts maintain buy rating', source: 'analyst' }
|
|
],
|
|
symbols: [symbol],
|
|
portfolio: {
|
|
equity: capital,
|
|
cash: capital,
|
|
positions: {},
|
|
assets: [symbol]
|
|
},
|
|
prices: { [symbol]: currentPrice },
|
|
riskManager
|
|
};
|
|
|
|
console.log('Executing pipeline...');
|
|
const result = await pipeline.execute(context);
|
|
|
|
console.log();
|
|
console.log('RESULTS:');
|
|
console.log('─'.repeat(70));
|
|
|
|
if (result.signals) {
|
|
for (const [sym, signal] of Object.entries(result.signals)) {
|
|
console.log(`${sym}: ${signal.direction.toUpperCase()}`);
|
|
console.log(` Strength: ${(signal.strength * 100).toFixed(1)}%`);
|
|
console.log(` Confidence: ${(signal.confidence * 100).toFixed(1)}%`);
|
|
}
|
|
}
|
|
|
|
if (result.orders && result.orders.length > 0) {
|
|
console.log();
|
|
console.log('ORDERS:');
|
|
for (const order of result.orders) {
|
|
console.log(` ${order.side.toUpperCase()} ${order.quantity} ${order.symbol} @ $${order.price.toFixed(2)}`);
|
|
}
|
|
} else {
|
|
console.log();
|
|
console.log('No orders generated');
|
|
}
|
|
|
|
console.log();
|
|
console.log(`Pipeline latency: ${result.metrics.totalLatency.toFixed(2)}ms`);
|
|
|
|
if (options.json) {
|
|
console.log();
|
|
console.log('JSON OUTPUT:');
|
|
console.log(JSON.stringify(result, null, 2));
|
|
}
|
|
},
|
|
|
|
backtest: async (options) => {
|
|
console.log('═'.repeat(70));
|
|
console.log('NEURAL-TRADER: BACKTEST MODE');
|
|
console.log('═'.repeat(70));
|
|
console.log();
|
|
|
|
const days = parseInt(options.days) || 252;
|
|
const capital = parseFloat(options.capital) || 100000;
|
|
const symbol = options.symbol || 'TEST';
|
|
|
|
console.log(`Period: ${days} trading days`);
|
|
console.log(`Initial Capital: $${capital.toLocaleString()}`);
|
|
console.log();
|
|
|
|
const engine = new BacktestEngine({
|
|
simulation: { initialCapital: capital, warmupPeriod: 50 }
|
|
});
|
|
|
|
const historicalData = generateSyntheticData(days);
|
|
|
|
console.log('Running backtest...');
|
|
const results = await engine.run(historicalData, {
|
|
symbols: [symbol],
|
|
newsData: [
|
|
{ symbol, text: 'Positive market sentiment', source: 'news' }
|
|
]
|
|
});
|
|
|
|
console.log(engine.generateReport(results));
|
|
|
|
if (options.output) {
|
|
const fs = await import('fs');
|
|
fs.writeFileSync(options.output, JSON.stringify(results, null, 2));
|
|
console.log(`Results saved to ${options.output}`);
|
|
}
|
|
},
|
|
|
|
paper: async (options) => {
|
|
console.log('═'.repeat(70));
|
|
console.log('NEURAL-TRADER: PAPER TRADING MODE');
|
|
console.log('═'.repeat(70));
|
|
console.log();
|
|
|
|
const capital = parseFloat(options.capital) || 100000;
|
|
const symbol = options.symbol || 'AAPL';
|
|
const interval = parseInt(options.interval) || 5000; // 5 seconds default
|
|
|
|
console.log(`Starting paper trading session...`);
|
|
console.log(`Capital: $${capital.toLocaleString()}`);
|
|
console.log(`Symbol: ${symbol}`);
|
|
console.log(`Update interval: ${interval}ms`);
|
|
console.log();
|
|
console.log('Press Ctrl+C to stop');
|
|
console.log();
|
|
|
|
const pipeline = createTradingPipeline();
|
|
const riskManager = new RiskManager();
|
|
riskManager.startDay(capital);
|
|
|
|
let portfolio = {
|
|
equity: capital,
|
|
cash: capital,
|
|
positions: {},
|
|
assets: [symbol]
|
|
};
|
|
|
|
let priceHistory = generateSyntheticData(100);
|
|
let iteration = 0;
|
|
|
|
const tick = async () => {
|
|
iteration++;
|
|
|
|
// Simulate price movement
|
|
const lastPrice = priceHistory[priceHistory.length - 1].close;
|
|
const newPrice = lastPrice * (1 + (Math.random() - 0.48) * 0.01);
|
|
|
|
priceHistory.push({
|
|
date: new Date(),
|
|
open: lastPrice,
|
|
high: Math.max(lastPrice, newPrice) * 1.002,
|
|
low: Math.min(lastPrice, newPrice) * 0.998,
|
|
close: newPrice,
|
|
volume: 1000000
|
|
});
|
|
|
|
if (priceHistory.length > 200) {
|
|
priceHistory = priceHistory.slice(-200);
|
|
}
|
|
|
|
const context = {
|
|
marketData: priceHistory,
|
|
newsData: [],
|
|
symbols: [symbol],
|
|
portfolio,
|
|
prices: { [symbol]: newPrice },
|
|
riskManager
|
|
};
|
|
|
|
try {
|
|
const result = await pipeline.execute(context);
|
|
|
|
// Update portfolio based on positions
|
|
portfolio.equity = portfolio.cash;
|
|
for (const [sym, qty] of Object.entries(portfolio.positions)) {
|
|
portfolio.equity += qty * newPrice;
|
|
}
|
|
|
|
const pnl = portfolio.equity - capital;
|
|
const pnlPercent = (pnl / capital) * 100;
|
|
|
|
console.log(`[${new Date().toLocaleTimeString()}] Tick #${iteration}`);
|
|
console.log(` Price: $${newPrice.toFixed(2)} | Equity: $${portfolio.equity.toFixed(2)} | P&L: ${pnl >= 0 ? '+' : ''}$${pnl.toFixed(2)} (${pnlPercent.toFixed(2)}%)`);
|
|
|
|
if (result.signals?.[symbol]) {
|
|
const signal = result.signals[symbol];
|
|
if (signal.direction !== 'neutral') {
|
|
console.log(` Signal: ${signal.direction.toUpperCase()} (${(signal.strength * 100).toFixed(0)}%)`);
|
|
}
|
|
}
|
|
|
|
console.log();
|
|
} catch (error) {
|
|
console.error(` Error: ${error.message}`);
|
|
}
|
|
};
|
|
|
|
// Run paper trading loop
|
|
const intervalId = setInterval(tick, interval);
|
|
|
|
// Initial tick
|
|
await tick();
|
|
|
|
// Handle graceful shutdown
|
|
process.on('SIGINT', () => {
|
|
clearInterval(intervalId);
|
|
console.log();
|
|
console.log('─'.repeat(70));
|
|
console.log('Paper trading session ended');
|
|
console.log(`Final equity: $${portfolio.equity.toFixed(2)}`);
|
|
console.log(`Total P&L: $${(portfolio.equity - capital).toFixed(2)}`);
|
|
process.exit(0);
|
|
});
|
|
},
|
|
|
|
analyze: async (options) => {
|
|
console.log('═'.repeat(70));
|
|
console.log('NEURAL-TRADER: ANALYSIS MODE');
|
|
console.log('═'.repeat(70));
|
|
console.log();
|
|
|
|
const symbol = options.symbol || 'AAPL';
|
|
|
|
console.log(`Analyzing ${symbol}...`);
|
|
console.log();
|
|
|
|
// Import modules
|
|
const { LexiconAnalyzer, EmbeddingAnalyzer } = await import('./production/sentiment-alpha.js');
|
|
const { FeatureExtractor, HybridLSTMTransformer } = await import('./production/hybrid-lstm-transformer.js');
|
|
|
|
const lexicon = new LexiconAnalyzer();
|
|
const embedding = new EmbeddingAnalyzer();
|
|
const featureExtractor = new FeatureExtractor();
|
|
const lstm = new HybridLSTMTransformer();
|
|
|
|
// Generate sample data
|
|
const marketData = generateSyntheticData(100);
|
|
const features = featureExtractor.extract(marketData);
|
|
|
|
console.log('TECHNICAL ANALYSIS:');
|
|
console.log('─'.repeat(70));
|
|
|
|
const prediction = lstm.predict(features);
|
|
console.log(`LSTM Prediction: ${prediction.signal}`);
|
|
console.log(`Direction: ${prediction.direction}`);
|
|
console.log(`Confidence: ${(prediction.confidence * 100).toFixed(1)}%`);
|
|
|
|
console.log();
|
|
console.log('SENTIMENT ANALYSIS:');
|
|
console.log('─'.repeat(70));
|
|
|
|
const sampleNews = [
|
|
'Strong earnings beat analyst expectations with revenue growth',
|
|
'Company faces regulatory headwinds',
|
|
'Quarterly results in line with market estimates'
|
|
];
|
|
|
|
for (const text of sampleNews) {
|
|
const result = lexicon.analyze(text);
|
|
const sentiment = result.score > 0.2 ? 'Positive' : result.score < -0.2 ? 'Negative' : 'Neutral';
|
|
console.log(`"${text.slice(0, 50)}..."`);
|
|
console.log(` → ${sentiment} (score: ${result.score.toFixed(2)})`);
|
|
}
|
|
|
|
console.log();
|
|
console.log('RISK METRICS:');
|
|
console.log('─'.repeat(70));
|
|
|
|
const metrics = new PerformanceMetrics();
|
|
const equityCurve = marketData.map(d => d.close * 1000);
|
|
const perf = metrics.calculate(equityCurve);
|
|
|
|
console.log(`Volatility (Ann.): ${(perf.annualizedVolatility * 100).toFixed(2)}%`);
|
|
console.log(`Max Drawdown: ${(perf.maxDrawdown * 100).toFixed(2)}%`);
|
|
console.log(`Sharpe Ratio: ${perf.sharpeRatio.toFixed(2)}`);
|
|
},
|
|
|
|
benchmark: async (options) => {
|
|
console.log('═'.repeat(70));
|
|
console.log('NEURAL-TRADER: BENCHMARK MODE');
|
|
console.log('═'.repeat(70));
|
|
console.log();
|
|
|
|
const iterations = parseInt(options.iterations) || 100;
|
|
|
|
console.log(`Running ${iterations} iterations...`);
|
|
console.log();
|
|
|
|
// Import all modules
|
|
const { KellyCriterion } = await import('./production/fractional-kelly.js');
|
|
const { LSTMCell, HybridLSTMTransformer } = await import('./production/hybrid-lstm-transformer.js');
|
|
const { NeuralNetwork, ReplayBuffer } = await import('./production/drl-portfolio-manager.js');
|
|
const { LexiconAnalyzer } = await import('./production/sentiment-alpha.js');
|
|
const { PerformanceMetrics } = await import('./system/backtesting.js');
|
|
|
|
const results = {};
|
|
|
|
// Benchmark Kelly
|
|
const kelly = new KellyCriterion();
|
|
let start = performance.now();
|
|
for (let i = 0; i < iterations; i++) {
|
|
kelly.calculateFractionalKelly(0.55 + Math.random() * 0.1, 2.0);
|
|
}
|
|
results.kelly = (performance.now() - start) / iterations;
|
|
|
|
// Benchmark LSTM Cell
|
|
const cell = new LSTMCell(10, 64);
|
|
const x = new Array(10).fill(0.1);
|
|
const h = new Array(64).fill(0);
|
|
const c = new Array(64).fill(0);
|
|
start = performance.now();
|
|
for (let i = 0; i < iterations; i++) {
|
|
cell.forward(x, h, c);
|
|
}
|
|
results.lstmCell = (performance.now() - start) / iterations;
|
|
|
|
// Benchmark Neural Network
|
|
const net = new NeuralNetwork([62, 128, 10]);
|
|
const state = new Array(62).fill(0.5);
|
|
start = performance.now();
|
|
for (let i = 0; i < iterations; i++) {
|
|
net.forward(state);
|
|
}
|
|
results.neuralNet = (performance.now() - start) / iterations;
|
|
|
|
// Benchmark Lexicon
|
|
const lexicon = new LexiconAnalyzer();
|
|
const text = 'Strong earnings growth beat analyst expectations with positive revenue outlook';
|
|
start = performance.now();
|
|
for (let i = 0; i < iterations; i++) {
|
|
lexicon.analyze(text);
|
|
}
|
|
results.lexicon = (performance.now() - start) / iterations;
|
|
|
|
// Benchmark Metrics
|
|
const metrics = new PerformanceMetrics();
|
|
const equityCurve = new Array(252).fill(100000).map((v, i) => v * (1 + (Math.random() - 0.5) * 0.02 * i / 252));
|
|
start = performance.now();
|
|
for (let i = 0; i < iterations; i++) {
|
|
metrics.calculate(equityCurve);
|
|
}
|
|
results.metrics = (performance.now() - start) / iterations;
|
|
|
|
console.log('BENCHMARK RESULTS:');
|
|
console.log('─'.repeat(70));
|
|
console.log(`Kelly Criterion: ${results.kelly.toFixed(3)}ms (${(1000 / results.kelly).toFixed(0)}/s)`);
|
|
console.log(`LSTM Cell: ${results.lstmCell.toFixed(3)}ms (${(1000 / results.lstmCell).toFixed(0)}/s)`);
|
|
console.log(`Neural Network: ${results.neuralNet.toFixed(3)}ms (${(1000 / results.neuralNet).toFixed(0)}/s)`);
|
|
console.log(`Lexicon Analyzer: ${results.lexicon.toFixed(3)}ms (${(1000 / results.lexicon).toFixed(0)}/s)`);
|
|
console.log(`Metrics Calculator: ${results.metrics.toFixed(3)}ms (${(1000 / results.metrics).toFixed(0)}/s)`);
|
|
}
|
|
};
|
|
|
|
// Main entry point
|
|
async function main() {
|
|
const args = process.argv.slice(2);
|
|
const { command, options } = parseArgs(args);
|
|
|
|
if (commands[command]) {
|
|
try {
|
|
await commands[command](options);
|
|
} catch (error) {
|
|
console.error(`Error: ${error.message}`);
|
|
if (options.verbose) {
|
|
console.error(error.stack);
|
|
}
|
|
process.exit(1);
|
|
}
|
|
} else {
|
|
console.error(`Unknown command: ${command}`);
|
|
commands.help();
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
// Run if executed directly
|
|
const isMainModule = import.meta.url === `file://${process.argv[1]}`;
|
|
if (isMainModule) {
|
|
main();
|
|
}
|
|
|
|
export { commands, parseArgs };
|