work on new engine
This commit is contained in:
parent
44476da13f
commit
a1e5a21847
126 changed files with 3425 additions and 6695 deletions
|
|
@ -1,149 +0,0 @@
|
|||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
import { ModeManager } from './src/core/ModeManager';
|
||||
import { MarketDataService } from './src/services/MarketDataService';
|
||||
import { ExecutionService } from './src/services/ExecutionService';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
|
||||
async function debugPerformanceValues() {
|
||||
console.log('Debugging Performance Calculation Values...\n');
|
||||
|
||||
// Create minimal service container with more logging
|
||||
const container: IServiceContainer = {
|
||||
logger: {
|
||||
info: (msg: string, ...args: any[]) => {
|
||||
// Log everything related to P&L and portfolio
|
||||
if (msg.includes('P&L') || msg.includes('portfolio') || msg.includes('Portfolio') ||
|
||||
msg.includes('equity') || msg.includes('Total') || msg.includes('pnl')) {
|
||||
console.log('[INFO]', msg, ...args);
|
||||
}
|
||||
},
|
||||
error: (msg: string, ...args: any[]) => console.error('[ERROR]', msg, ...args),
|
||||
warn: (msg: string, ...args: any[]) => console.warn('[WARN]', msg, ...args),
|
||||
debug: (msg: string, ...args: any[]) => {
|
||||
if (msg.includes('P&L') || msg.includes('portfolio') || msg.includes('pnl')) {
|
||||
console.log('[DEBUG]', msg, ...args);
|
||||
}
|
||||
},
|
||||
} as any,
|
||||
custom: {}
|
||||
};
|
||||
|
||||
// Initialize services
|
||||
const storageService = new StorageService();
|
||||
const marketDataService = new MarketDataService(container);
|
||||
const executionService = new ExecutionService(container);
|
||||
const modeManager = new ModeManager(container, marketDataService, executionService, storageService);
|
||||
const strategyManager = new StrategyManager(container);
|
||||
|
||||
// Set services in container
|
||||
container.custom = {
|
||||
MarketDataService: marketDataService,
|
||||
ExecutionService: executionService,
|
||||
ModeManager: modeManager,
|
||||
StorageService: storageService
|
||||
};
|
||||
|
||||
// Initialize backtest mode
|
||||
await modeManager.initializeMode({
|
||||
mode: 'backtest',
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-01-15T00:00:00Z', // Just 15 days
|
||||
speed: 'max',
|
||||
symbols: ['TEST'],
|
||||
initialCapital: 100000,
|
||||
dataFrequency: '1d',
|
||||
strategy: 'sma-crossover'
|
||||
});
|
||||
|
||||
// Create backtest engine
|
||||
const backtestEngine = new BacktestEngine(container, storageService, strategyManager);
|
||||
|
||||
// Run backtest
|
||||
const config = {
|
||||
mode: 'backtest',
|
||||
name: 'Debug Performance Values',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['TEST'],
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-01-15T00:00:00Z',
|
||||
initialCapital: 100000,
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
dataFrequency: '1d',
|
||||
speed: 'max'
|
||||
};
|
||||
|
||||
console.log('Running backtest...');
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
// Debug values
|
||||
console.log('\n=== RAW VALUES DEBUG ===');
|
||||
console.log(`Initial Capital: $${config.initialCapital}`);
|
||||
console.log(`Reported Metrics:`);
|
||||
console.log(` - Total Return: ${result.metrics.totalReturn}%`);
|
||||
console.log(` - Sharpe Ratio: ${result.metrics.sharpeRatio}`);
|
||||
console.log(` - Max Drawdown: ${result.metrics.maxDrawdown}%`);
|
||||
console.log(` - Win Rate: ${result.metrics.winRate}%`);
|
||||
console.log(` - Total Trades: ${result.metrics.totalTrades}`);
|
||||
|
||||
console.log('\n=== EQUITY CURVE VALUES ===');
|
||||
console.log(`Equity Points: ${result.equity.length}`);
|
||||
if (result.equity.length > 0) {
|
||||
const first = result.equity[0];
|
||||
const last = result.equity[result.equity.length - 1];
|
||||
console.log(`First: ${first.date} => $${first.value}`);
|
||||
console.log(`Last: ${last.date} => $${last.value}`);
|
||||
|
||||
// Manual calculation
|
||||
const manualReturn = ((last.value - first.value) / first.value) * 100;
|
||||
console.log(`\nManual Total Return: ${manualReturn.toFixed(2)}%`);
|
||||
console.log(`Difference from reported: ${Math.abs(manualReturn - result.metrics.totalReturn).toFixed(2)}%`);
|
||||
}
|
||||
|
||||
console.log('\n=== TRADE ANALYSIS ===');
|
||||
console.log(`Closed Trades: ${result.trades.length}`);
|
||||
if (result.trades.length > 0) {
|
||||
const wins = result.trades.filter(t => t.pnl > 0);
|
||||
const losses = result.trades.filter(t => t.pnl < 0);
|
||||
const manualWinRate = (wins.length / result.trades.length) * 100;
|
||||
|
||||
console.log(`Wins: ${wins.length}`);
|
||||
console.log(`Losses: ${losses.length}`);
|
||||
console.log(`Manual Win Rate: ${manualWinRate.toFixed(2)}%`);
|
||||
|
||||
// Show P&L values
|
||||
const totalPnL = result.trades.reduce((sum, t) => sum + t.pnl, 0);
|
||||
console.log(`\nTotal P&L from trades: $${totalPnL.toFixed(2)}`);
|
||||
|
||||
// Show first few trades
|
||||
console.log('\nFirst 3 trades:');
|
||||
result.trades.slice(0, 3).forEach((t, i) => {
|
||||
console.log(` ${i+1}. ${t.side} ${t.quantity} @ ${t.exitPrice} | P&L: $${t.pnl.toFixed(2)}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Check trading engine P&L
|
||||
const tradingEngine = strategyManager.getTradingEngine();
|
||||
if (tradingEngine) {
|
||||
try {
|
||||
const [realized, unrealized] = tradingEngine.getTotalPnl();
|
||||
console.log('\n=== TRADING ENGINE P&L ===');
|
||||
console.log(`Realized P&L: $${realized.toFixed(2)}`);
|
||||
console.log(`Unrealized P&L: $${unrealized.toFixed(2)}`);
|
||||
console.log(`Total P&L: $${(realized + unrealized).toFixed(2)}`);
|
||||
console.log(`Portfolio Value: $${(config.initialCapital + realized + unrealized).toFixed(2)}`);
|
||||
} catch (e) {
|
||||
console.error('Failed to get P&L from trading engine:', e);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n=== TEST COMPLETE ===');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
debugPerformanceValues().catch(error => {
|
||||
console.error('Test failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -6,7 +6,7 @@ The stock-bot orchestrator includes a high-performance Technical Analysis (TA) l
|
|||
|
||||
The TA library consists of:
|
||||
1. **Rust Core**: High-performance indicator calculations in `apps/stock/core/src/indicators/`
|
||||
2. **NAPI Bindings**: TypeScript interfaces exposed through `@stock-bot/core`
|
||||
2. **NAPI Bindings**: TypeScript interfaces exposed through `@stock-bot/engine`
|
||||
3. **TypeScript Wrapper**: Convenient API in `orchestrator/src/indicators/TechnicalAnalysis.ts`
|
||||
|
||||
## Available Indicators
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
* Demonstrates orderbook analytics, portfolio risk, and bet sizing
|
||||
*/
|
||||
|
||||
import { TradingEngine, RiskAnalyzer, OrderbookAnalyzer } from '@stock-bot/core';
|
||||
import { OrderbookAnalyzer, RiskAnalyzer, TradingEngine } from '@stock-bot/engine';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
|
||||
const logger = getLogger('AdvancedRiskExample');
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { TradingEngine } from '@stock-bot/core';
|
||||
import { TradingEngine } from '@stock-bot/engine';
|
||||
|
||||
async function debugRustShortTrades() {
|
||||
// Create engine config for backtest mode
|
||||
|
|
|
|||
|
|
@ -2,8 +2,7 @@
|
|||
* Examples of using the Rust-based Technical Analysis library
|
||||
*/
|
||||
|
||||
import { TechnicalIndicators, IncrementalSMA, IncrementalEMA, IncrementalRSI } from '@stock-bot/core';
|
||||
import { TechnicalAnalysis, IncrementalIndicators, SignalGenerator } from '../src/indicators/TechnicalAnalysis';
|
||||
import { IncrementalIndicators, SignalGenerator, TechnicalAnalysis } from '../src/indicators/TechnicalAnalysis';
|
||||
|
||||
// Example 1: Basic indicator calculations
|
||||
async function basicIndicatorExample() {
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
import { EventEmitter } from 'events';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
import { BacktestEngine as RustEngine } from '@stock-bot/core';
|
||||
import { BacktestConfig, BacktestResult } from '../types';
|
||||
import { BacktestEngine as RustEngine } from '@stock-bot/engine';
|
||||
import { EventEmitter } from 'events';
|
||||
import { StorageService } from '../services/StorageService';
|
||||
import { StrategyExecutor, SMACrossoverStrategy } from '../strategies/StrategyExecutor';
|
||||
import { SMACrossoverStrategy, StrategyExecutor } from '../strategies/StrategyExecutor';
|
||||
import { BacktestConfig, BacktestResult } from '../types';
|
||||
|
||||
/**
|
||||
* Adapter that bridges the orchestrator with the Rust backtest engine
|
||||
|
|
@ -208,7 +208,7 @@ export class RustBacktestAdapter extends EventEmitter {
|
|||
}
|
||||
|
||||
private registerStrategy(strategyName: string, parameters: any): void {
|
||||
if (!this.currentEngine) return;
|
||||
if (!this.currentEngine) {return;}
|
||||
|
||||
this.container.logger.info('Registering strategy', {
|
||||
strategyName,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { BacktestEngine as RustEngine } from '@stock-bot/core';
|
||||
import { RustStrategy } from '../strategies/RustStrategy';
|
||||
import { MarketData, BacktestConfig } from '../types';
|
||||
import { StorageService } from '../services/StorageService';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
import { BacktestEngine as RustEngine } from '@stock-bot/engine';
|
||||
import { StorageService } from '../services/StorageService';
|
||||
import { RustStrategy } from '../strategies/RustStrategy';
|
||||
import { BacktestConfig } from '../types';
|
||||
|
||||
export interface RustBacktestConfig {
|
||||
name: string;
|
||||
|
|
|
|||
|
|
@ -1,10 +1,10 @@
|
|||
import { TradingEngine } from '@stock-bot/core';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
import { TradingMode, ModeConfig, BacktestConfigSchema, PaperConfigSchema, LiveConfigSchema } from '../types';
|
||||
import { MarketDataService } from '../services/MarketDataService';
|
||||
import { ExecutionService } from '../services/ExecutionService';
|
||||
import { StorageService } from '../services/StorageService';
|
||||
import { TradingEngine } from '@stock-bot/engine';
|
||||
import { EventEmitter } from 'events';
|
||||
import { ExecutionService } from '../services/ExecutionService';
|
||||
import { MarketDataService } from '../services/MarketDataService';
|
||||
import { StorageService } from '../services/StorageService';
|
||||
import { BacktestConfigSchema, LiveConfigSchema, ModeConfig, PaperConfigSchema, TradingMode } from '../types';
|
||||
|
||||
export class ModeManager extends EventEmitter {
|
||||
private mode: TradingMode = 'paper';
|
||||
|
|
@ -148,7 +148,7 @@ export class ModeManager extends EventEmitter {
|
|||
}
|
||||
|
||||
async shutdown(): Promise<void> {
|
||||
if (!this.isInitialized) return;
|
||||
if (!this.isInitialized) {return;}
|
||||
|
||||
this.container.logger.info(`Shutting down ${this.mode} mode...`);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { TechnicalIndicators, IncrementalSMA, IncrementalEMA, IncrementalRSI, MacdResult, BollingerBandsResult, StochasticResult } from '@stock-bot/core';
|
||||
import { BollingerBandsResult, IncrementalEMA, IncrementalRSI, IncrementalSMA, MacdResult, StochasticResult, TechnicalIndicators } from '@stock-bot/engine';
|
||||
|
||||
/**
|
||||
* Wrapper class for the Rust TA library with TypeScript-friendly interfaces
|
||||
|
|
@ -57,7 +57,7 @@ export class TechnicalAnalysis {
|
|||
|
||||
// Helper to check for crossovers
|
||||
static crossover(series1: number[], series2: number[]): boolean {
|
||||
if (series1.length < 2 || series2.length < 2) return false;
|
||||
if (series1.length < 2 || series2.length < 2) {return false;}
|
||||
const prev1 = series1[series1.length - 2];
|
||||
const curr1 = series1[series1.length - 1];
|
||||
const prev2 = series2[series2.length - 2];
|
||||
|
|
@ -66,7 +66,7 @@ export class TechnicalAnalysis {
|
|||
}
|
||||
|
||||
static crossunder(series1: number[], series2: number[]): boolean {
|
||||
if (series1.length < 2 || series2.length < 2) return false;
|
||||
if (series1.length < 2 || series2.length < 2) {return false;}
|
||||
const prev1 = series1[series1.length - 2];
|
||||
const curr1 = series1[series1.length - 1];
|
||||
const prev2 = series2[series2.length - 2];
|
||||
|
|
|
|||
|
|
@ -1,9 +1,8 @@
|
|||
import { IServiceContainer } from '@stock-bot/di';
|
||||
import { TradingEngine } from '@stock-bot/engine';
|
||||
import { EventEmitter } from 'events';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
import { ModeConfig, OrderRequest, OrderRequestSchema } from '../types';
|
||||
import { TradingEngine } from '@stock-bot/core';
|
||||
import axios from 'axios';
|
||||
import { StorageService } from './StorageService';
|
||||
|
||||
interface ExecutionReport {
|
||||
|
|
@ -233,7 +232,7 @@ export class ExecutionService extends EventEmitter {
|
|||
}
|
||||
|
||||
private async processFills(executionReport: ExecutionReport): Promise<void> {
|
||||
if (!this.tradingEngine) return;
|
||||
if (!this.tradingEngine) {return;}
|
||||
|
||||
for (const fill of executionReport.fills) {
|
||||
// Update position in engine
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { BacktestEngine } from '@stock-bot/core';
|
||||
import { BacktestEngine } from '@stock-bot/engine';
|
||||
import { MarketData } from '../types';
|
||||
|
||||
export interface Signal {
|
||||
|
|
|
|||
|
|
@ -1,8 +1,8 @@
|
|||
import { EventEmitter } from 'events';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
import { MarketData, StrategyConfig, OrderRequest } from '../types';
|
||||
import { TradingEngine } from '@stock-bot/engine';
|
||||
import { EventEmitter } from 'events';
|
||||
import { MarketData, OrderRequest, StrategyConfig } from '../types';
|
||||
import { BaseStrategy } from './BaseStrategy';
|
||||
import { TradingEngine } from '@stock-bot/core';
|
||||
|
||||
export class StrategyManager extends EventEmitter {
|
||||
private strategies = new Map<string, BaseStrategy>();
|
||||
|
|
|
|||
|
|
@ -1,69 +0,0 @@
|
|||
import { createContainer } from './src/simple-container';
|
||||
import fetch from 'node-fetch';
|
||||
|
||||
async function testApiResponse() {
|
||||
console.log('Testing API Response Values...\n');
|
||||
|
||||
// First, create the container and start the server
|
||||
const container = await createContainer({
|
||||
database: {
|
||||
mongodb: { enabled: false, uri: '' },
|
||||
postgres: { enabled: false, uri: '' },
|
||||
questdb: { enabled: false, host: '', port: 0 },
|
||||
dragonfly: { enabled: false }
|
||||
}
|
||||
});
|
||||
|
||||
// Run a simple backtest via API
|
||||
const backtestRequest = {
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'],
|
||||
startDate: '2023-01-01',
|
||||
endDate: '2023-02-01',
|
||||
initialCapital: 100000,
|
||||
config: {
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
dataFrequency: '1d'
|
||||
}
|
||||
};
|
||||
|
||||
try {
|
||||
console.log('Sending backtest request...');
|
||||
const response = await fetch('http://localhost:2003/api/backtest/run', {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
body: JSON.stringify(backtestRequest)
|
||||
});
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(`HTTP error! status: ${response.status}`);
|
||||
}
|
||||
|
||||
const result = await response.json();
|
||||
console.log('\n=== API RESPONSE ===');
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
|
||||
if (result.metrics) {
|
||||
console.log('\n=== METRICS VALUES ===');
|
||||
console.log(`Total Return: ${result.metrics.totalReturn}`);
|
||||
console.log(`Sharpe Ratio: ${result.metrics.sharpeRatio}`);
|
||||
console.log(`Max Drawdown: ${result.metrics.maxDrawdown}`);
|
||||
console.log(`Win Rate: ${result.metrics.winRate}`);
|
||||
console.log(`Total Trades: ${result.metrics.totalTrades}`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('API call failed:', error);
|
||||
}
|
||||
|
||||
console.log('\n=== TEST COMPLETE ===');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
testApiResponse().catch(error => {
|
||||
console.error('Test failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,70 +0,0 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
/**
|
||||
* Simple backtest test without full container
|
||||
*/
|
||||
|
||||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
|
||||
async function runSimpleBacktest() {
|
||||
console.log('Running simple backtest test...\n');
|
||||
|
||||
// Create minimal container
|
||||
const logger = getLogger('test');
|
||||
const container = {
|
||||
logger,
|
||||
custom: {}
|
||||
};
|
||||
|
||||
// Create services
|
||||
const storageService = new StorageService(container as any);
|
||||
const strategyManager = new StrategyManager(container as any);
|
||||
|
||||
// Initialize strategy
|
||||
await strategyManager.initializeStrategies([{
|
||||
id: 'test-sma',
|
||||
name: 'sma-crossover',
|
||||
enabled: true,
|
||||
symbols: ['AAPL'],
|
||||
allocation: 1.0
|
||||
}]);
|
||||
|
||||
// Create backtest engine
|
||||
const backtestEngine = new BacktestEngine(container as any, storageService, strategyManager);
|
||||
|
||||
const config = {
|
||||
mode: 'backtest',
|
||||
name: 'Simple SMA Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'],
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-03-01T00:00:00Z', // Just 2 months
|
||||
initialCapital: 100000,
|
||||
dataFrequency: '1d',
|
||||
commission: 0.001,
|
||||
slippage: 0.0001
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
console.log('\nBacktest Results:');
|
||||
console.log(`Total Return: ${result.metrics.totalReturn.toFixed(2)}%`);
|
||||
console.log(`Total Trades: ${result.metrics.totalTrades}`);
|
||||
console.log(`Trades in history: ${result.trades.length}`);
|
||||
console.log(`Win Rate: ${result.metrics.winRate.toFixed(2)}%`);
|
||||
|
||||
console.log('\nTrade Details:');
|
||||
result.trades.forEach((trade, i) => {
|
||||
console.log(`Trade ${i + 1}: ${trade.side} ${trade.quantity} @ $${trade.entryPrice.toFixed(2)} -> $${trade.exitPrice.toFixed(2)} (P&L: $${trade.pnl.toFixed(2)})`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Backtest failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
runSimpleBacktest().catch(console.error);
|
||||
|
|
@ -1,99 +0,0 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
import { createContainer } from './src/simple-container';
|
||||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { SimpleMovingAverageCrossover } from './src/strategies/examples/SimpleMovingAverageCrossover';
|
||||
|
||||
async function runBacktest() {
|
||||
console.log('Starting backtest test...');
|
||||
|
||||
// Create container with minimal config
|
||||
const config = {
|
||||
port: 2004,
|
||||
mode: 'paper',
|
||||
enableWebSocket: false,
|
||||
database: {
|
||||
mongodb: { enabled: false },
|
||||
postgres: { enabled: false },
|
||||
questdb: { enabled: false }
|
||||
},
|
||||
redis: {
|
||||
url: 'redis://localhost:6379'
|
||||
},
|
||||
backtesting: {
|
||||
maxConcurrent: 1,
|
||||
defaultSpeed: 'max',
|
||||
dataResolutions: ['1d']
|
||||
},
|
||||
strategies: {
|
||||
maxActive: 10,
|
||||
defaultTimeout: 30000
|
||||
}
|
||||
};
|
||||
const container = await createContainer(config);
|
||||
|
||||
// Initialize strategy manager
|
||||
const strategyManager = new StrategyManager(container.executionService, container.modeManager);
|
||||
|
||||
// Create and add strategy
|
||||
const strategyConfig = {
|
||||
id: 'sma-test',
|
||||
name: 'SMA Test',
|
||||
type: 'sma-crossover',
|
||||
symbols: ['AA'],
|
||||
active: true,
|
||||
allocation: 1.0,
|
||||
riskLimit: 0.02,
|
||||
maxPositions: 10
|
||||
};
|
||||
|
||||
const strategy = new SimpleMovingAverageCrossover(strategyConfig, container.modeManager, container.executionService);
|
||||
await strategyManager.addStrategy(strategy);
|
||||
|
||||
// Create backtest engine
|
||||
const backtestEngine = new BacktestEngine(container, strategyManager);
|
||||
|
||||
// Run backtest
|
||||
const backtestConfig = {
|
||||
symbols: ['AA'],
|
||||
strategy: 'sma-crossover',
|
||||
startDate: '2024-01-01',
|
||||
endDate: '2024-12-31',
|
||||
initialCapital: 100000,
|
||||
commission: 0.001,
|
||||
slippage: 0.0005,
|
||||
dataFrequency: '1d'
|
||||
};
|
||||
|
||||
try {
|
||||
console.log('Running backtest...');
|
||||
const result = await backtestEngine.runBacktest(backtestConfig);
|
||||
|
||||
console.log('\n=== Backtest Results ===');
|
||||
console.log(`Total trades: ${result.metrics.totalTrades}`);
|
||||
console.log(`Win rate: ${result.metrics.winRate.toFixed(2)}%`);
|
||||
console.log(`Total return: ${result.metrics.totalReturn.toFixed(2)}%`);
|
||||
console.log(`Sharpe ratio: ${result.metrics.sharpeRatio.toFixed(2)}`);
|
||||
console.log(`Max drawdown: ${result.metrics.maxDrawdown.toFixed(2)}%`);
|
||||
|
||||
console.log('\n=== Trade History ===');
|
||||
result.trades.forEach((trade, i) => {
|
||||
console.log(`Trade ${i + 1}: ${trade.side} ${trade.quantity} ${trade.symbol} @ ${trade.entryPrice.toFixed(2)}`);
|
||||
if (trade.exitDate) {
|
||||
console.log(` Exit: ${trade.exitPrice.toFixed(2)}, P&L: ${trade.pnl.toFixed(2)} (${trade.pnlPercent.toFixed(2)}%)`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log(`\nTotal trades in result: ${result.trades.length}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Backtest failed:', error);
|
||||
} finally {
|
||||
// Cleanup
|
||||
await container.shutdownManager.shutdown();
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
runBacktest().catch(console.error);
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
/**
|
||||
* Test with very clear crossover patterns
|
||||
*/
|
||||
|
||||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import { ModeManager } from './src/core/ModeManager';
|
||||
import { MarketDataService } from './src/services/MarketDataService';
|
||||
import { ExecutionService } from './src/services/ExecutionService';
|
||||
import { DataManager } from './src/data/DataManager';
|
||||
|
||||
async function testClearCrossovers() {
|
||||
console.log('=== Test with Clear Crossovers ===\n');
|
||||
|
||||
const logger = getLogger('test');
|
||||
const container = {
|
||||
logger,
|
||||
custom: {}
|
||||
};
|
||||
|
||||
const storageService = new StorageService(container as any);
|
||||
const marketDataService = new MarketDataService(container as any);
|
||||
const executionService = new ExecutionService(container as any);
|
||||
const modeManager = new ModeManager(container as any, marketDataService, executionService, storageService);
|
||||
|
||||
container.custom = {
|
||||
ModeManager: modeManager,
|
||||
MarketDataService: marketDataService,
|
||||
ExecutionService: executionService
|
||||
};
|
||||
|
||||
const strategyManager = new StrategyManager(container as any);
|
||||
const backtestEngine = new BacktestEngine(container as any, storageService, strategyManager);
|
||||
|
||||
// Override data loading to provide clear patterns
|
||||
const dataManager = new DataManager(container as any, storageService);
|
||||
(backtestEngine as any).dataManager = dataManager;
|
||||
|
||||
(dataManager as any).loadHistoricalData = async (symbols: string[], startDate: Date, endDate: Date) => {
|
||||
const data = new Map();
|
||||
const bars = [];
|
||||
|
||||
console.log('Generating clear crossover patterns...');
|
||||
|
||||
// Generate 100 days of data with 3 clear crossovers
|
||||
// Pattern: Start high, go low (death cross), go high (golden cross), go low (death cross)
|
||||
|
||||
for (let i = 0; i < 100; i++) {
|
||||
let price;
|
||||
|
||||
if (i < 20) {
|
||||
// Start at 100, slight upward trend
|
||||
price = 100 + i * 0.5;
|
||||
} else if (i < 40) {
|
||||
// Sharp downtrend from 110 to 70 (death cross around day 30)
|
||||
price = 110 - (i - 20) * 2;
|
||||
} else if (i < 60) {
|
||||
// Sharp uptrend from 70 to 110 (golden cross around day 50)
|
||||
price = 70 + (i - 40) * 2;
|
||||
} else if (i < 80) {
|
||||
// Sharp downtrend from 110 to 70 (death cross around day 70)
|
||||
price = 110 - (i - 60) * 2;
|
||||
} else {
|
||||
// Stabilize around 70
|
||||
price = 70 + Math.sin((i - 80) * 0.3) * 2;
|
||||
}
|
||||
|
||||
const timestamp = startDate.getTime() + i * 86400000;
|
||||
|
||||
bars.push({
|
||||
type: 'bar',
|
||||
data: {
|
||||
symbol: 'AAPL',
|
||||
open: price - 0.5,
|
||||
high: price + 1,
|
||||
low: price - 1,
|
||||
close: price,
|
||||
volume: 1000000,
|
||||
timestamp
|
||||
}
|
||||
});
|
||||
|
||||
if (i % 10 === 0) {
|
||||
console.log(`Day ${i + 1}: Price = $${price.toFixed(2)}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\nExpected crossovers:');
|
||||
console.log('- Death cross around day 30');
|
||||
console.log('- Golden cross around day 50');
|
||||
console.log('- Death cross around day 70\n');
|
||||
|
||||
data.set('AAPL', bars);
|
||||
return data;
|
||||
};
|
||||
|
||||
const config = {
|
||||
mode: 'backtest' as const,
|
||||
name: 'Clear Crossovers Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'],
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-04-10T00:00:00Z', // 100 days
|
||||
initialCapital: 100000,
|
||||
dataFrequency: '1d',
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
speed: 'max' as const
|
||||
};
|
||||
|
||||
await modeManager.initializeMode(config);
|
||||
|
||||
try {
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
console.log('\n=== Backtest Results ===');
|
||||
console.log(`Total Return: ${result.metrics.totalReturn.toFixed(2)}%`);
|
||||
console.log(`Total Trades: ${result.metrics.totalTrades}`);
|
||||
console.log(`Trades in history: ${result.trades.length}`);
|
||||
console.log(`Win Rate: ${result.metrics.winRate.toFixed(2)}%`);
|
||||
|
||||
console.log('\nTrade Details:');
|
||||
result.trades.forEach((trade, i) => {
|
||||
const entry = new Date(trade.entryDate).toLocaleDateString();
|
||||
const exit = trade.exitDate ? new Date(trade.exitDate).toLocaleDateString() : 'OPEN';
|
||||
console.log(`Trade ${i + 1}: ${trade.side} ${trade.quantity} @ $${trade.entryPrice.toFixed(2)} (${entry}) -> ${exit === 'OPEN' ? 'OPEN' : `$${trade.exitPrice.toFixed(2)} (${exit})`} | P&L: ${trade.pnl.toFixed(2)}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Backtest failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
testClearCrossovers().catch(console.error);
|
||||
|
|
@ -1,120 +0,0 @@
|
|||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
import { ModeManager } from './src/core/ModeManager';
|
||||
import { MarketDataService } from './src/services/MarketDataService';
|
||||
import { ExecutionService } from './src/services/ExecutionService';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
|
||||
async function testCommissionDetailed() {
|
||||
// Create service container with minimal logging
|
||||
const container: IServiceContainer = {
|
||||
logger: {
|
||||
info: () => {},
|
||||
error: (msg: string, ...args: any[]) => console.error('[ERROR]', msg, ...args),
|
||||
warn: () => {},
|
||||
debug: () => {},
|
||||
} as any,
|
||||
custom: {}
|
||||
};
|
||||
|
||||
// Initialize services
|
||||
const storageService = new StorageService();
|
||||
const marketDataService = new MarketDataService(container);
|
||||
const executionService = new ExecutionService(container);
|
||||
const modeManager = new ModeManager(container, marketDataService, executionService, storageService);
|
||||
const strategyManager = new StrategyManager(container);
|
||||
|
||||
// Set services in container
|
||||
container.custom = {
|
||||
MarketDataService: marketDataService,
|
||||
ExecutionService: executionService,
|
||||
ModeManager: modeManager,
|
||||
StorageService: storageService
|
||||
};
|
||||
|
||||
// Test with moderate commission and slippage
|
||||
const config = {
|
||||
mode: 'backtest',
|
||||
name: 'Commission Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'],
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-02-28T00:00:00Z', // 2 months
|
||||
initialCapital: 10000,
|
||||
commission: 0.002, // 0.2% commission
|
||||
slippage: 0.001, // 0.1% slippage
|
||||
dataFrequency: '1d',
|
||||
speed: 'max'
|
||||
};
|
||||
|
||||
await modeManager.initializeMode(config);
|
||||
const backtestEngine = new BacktestEngine(container, storageService, strategyManager);
|
||||
|
||||
console.log('\n=== COMMISSION & SLIPPAGE TEST ===');
|
||||
console.log(`Initial Capital: $${config.initialCapital}`);
|
||||
console.log(`Commission: ${(config.commission * 100).toFixed(2)}%`);
|
||||
console.log(`Slippage: ${(config.slippage * 100).toFixed(2)}%`);
|
||||
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
// Get P&L from Rust
|
||||
const tradingEngine = strategyManager.getTradingEngine();
|
||||
const [realized, unrealized] = tradingEngine.getTotalPnl();
|
||||
|
||||
console.log('\n=== RESULTS ===');
|
||||
console.log(`Total Trades: ${result.trades.length}`);
|
||||
console.log(`Final Portfolio Value: $${result.equity[result.equity.length - 1].value.toFixed(2)}`);
|
||||
console.log(`Realized P&L: $${realized.toFixed(2)}`);
|
||||
console.log(`Unrealized P&L: $${unrealized.toFixed(2)}`);
|
||||
|
||||
if (result.trades.length > 0) {
|
||||
console.log('\n=== TRADE DETAILS ===');
|
||||
let totalCommission = 0;
|
||||
let totalSlippageCost = 0;
|
||||
|
||||
result.trades.slice(0, 5).forEach((trade, idx) => {
|
||||
const entryValue = trade.quantity * trade.entryPrice;
|
||||
const exitValue = trade.quantity * trade.exitPrice;
|
||||
|
||||
// Calculate expected slippage
|
||||
// For a buy entry, we pay more (positive slippage)
|
||||
// For a sell entry (short), we receive less (negative slippage)
|
||||
const entrySlippage = trade.side === 'buy' ?
|
||||
entryValue * config.slippage :
|
||||
-entryValue * config.slippage;
|
||||
|
||||
// For exit, it's opposite
|
||||
const exitSlippage = trade.side === 'buy' ?
|
||||
-exitValue * config.slippage :
|
||||
exitValue * config.slippage;
|
||||
|
||||
const expectedCommission = (entryValue + exitValue) * config.commission;
|
||||
const slippageCost = entrySlippage + exitSlippage;
|
||||
|
||||
totalCommission += trade.commission;
|
||||
totalSlippageCost += slippageCost;
|
||||
|
||||
console.log(`\nTrade ${idx + 1}: ${trade.symbol} ${trade.side}`);
|
||||
console.log(` Quantity: ${trade.quantity} shares`);
|
||||
console.log(` Entry: $${trade.entryPrice.toFixed(2)} (value: $${entryValue.toFixed(2)})`);
|
||||
console.log(` Exit: $${trade.exitPrice.toFixed(2)} (value: $${exitValue.toFixed(2)})`);
|
||||
console.log(` Commission: $${trade.commission.toFixed(2)} (expected: $${expectedCommission.toFixed(2)})`);
|
||||
console.log(` Slippage Cost: $${slippageCost.toFixed(2)}`);
|
||||
console.log(` Gross P&L: $${(exitValue - entryValue).toFixed(2)}`);
|
||||
console.log(` Net P&L: $${trade.pnl.toFixed(2)}`);
|
||||
});
|
||||
|
||||
console.log(`\n=== TOTALS ===`);
|
||||
console.log(`Total Commission Paid: $${totalCommission.toFixed(2)}`);
|
||||
console.log(`Total Slippage Cost: $${totalSlippageCost.toFixed(2)}`);
|
||||
console.log(`Total Trading Costs: $${(totalCommission + totalSlippageCost).toFixed(2)}`);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
testCommissionDetailed().catch(error => {
|
||||
console.error('Test failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,123 +0,0 @@
|
|||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
import { ModeManager } from './src/core/ModeManager';
|
||||
import { MarketDataService } from './src/services/MarketDataService';
|
||||
import { ExecutionService } from './src/services/ExecutionService';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
|
||||
async function testCommissionSlippage() {
|
||||
console.log('Testing Commission and Slippage...\n');
|
||||
|
||||
// Create service container
|
||||
const container: IServiceContainer = {
|
||||
logger: {
|
||||
info: (msg: string, ...args: any[]) => console.log('[INFO]', msg, ...args),
|
||||
error: (msg: string, ...args: any[]) => console.error('[ERROR]', msg, ...args),
|
||||
warn: (msg: string, ...args: any[]) => console.warn('[WARN]', msg, ...args),
|
||||
debug: (msg: string, ...args: any[]) => console.log('[DEBUG]', msg, ...args),
|
||||
} as any,
|
||||
custom: {}
|
||||
};
|
||||
|
||||
// Initialize services
|
||||
const storageService = new StorageService();
|
||||
const marketDataService = new MarketDataService(container);
|
||||
const executionService = new ExecutionService(container);
|
||||
const modeManager = new ModeManager(container, marketDataService, executionService, storageService);
|
||||
const strategyManager = new StrategyManager(container);
|
||||
|
||||
// Set services in container
|
||||
container.custom = {
|
||||
MarketDataService: marketDataService,
|
||||
ExecutionService: executionService,
|
||||
ModeManager: modeManager,
|
||||
StorageService: storageService
|
||||
};
|
||||
|
||||
// Test with high commission and slippage
|
||||
const config = {
|
||||
mode: 'backtest',
|
||||
name: 'Commission Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'],
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-01-31T00:00:00Z',
|
||||
initialCapital: 10000,
|
||||
commission: 0.01, // 1% commission (very high for testing)
|
||||
slippage: 0.005, // 0.5% slippage (very high for testing)
|
||||
dataFrequency: '1d',
|
||||
speed: 'max'
|
||||
};
|
||||
|
||||
// Initialize backtest mode
|
||||
await modeManager.initializeMode(config);
|
||||
|
||||
// Create backtest engine
|
||||
const backtestEngine = new BacktestEngine(container, storageService, strategyManager);
|
||||
|
||||
console.log('Running backtest with:');
|
||||
console.log(` Initial Capital: $${config.initialCapital}`);
|
||||
console.log(` Commission: ${(config.commission * 100).toFixed(1)}%`);
|
||||
console.log(` Slippage: ${(config.slippage * 100).toFixed(1)}%`);
|
||||
|
||||
try {
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
console.log('\n=== RESULTS ===');
|
||||
console.log(`Initial Capital: $${config.initialCapital}`);
|
||||
console.log(`Final Value: $${result.equity[result.equity.length - 1].value.toFixed(2)}`);
|
||||
console.log(`Total Return: ${result.metrics.totalReturn.toFixed(2)}%`);
|
||||
console.log(`Total Trades: ${result.metrics.totalTrades}`);
|
||||
|
||||
// Check trades for commission
|
||||
if (result.trades.length > 0) {
|
||||
console.log('\nFirst 3 trades with commission:');
|
||||
result.trades.slice(0, 3).forEach((t, idx) => {
|
||||
const tradeValue = t.quantity * t.entryPrice;
|
||||
const expectedCommission = tradeValue * config.commission;
|
||||
const totalCost = tradeValue + expectedCommission;
|
||||
|
||||
console.log(`\n${idx + 1}. ${t.symbol} ${t.side} ${t.quantity} shares`);
|
||||
console.log(` Entry Price: $${t.entryPrice.toFixed(2)}`);
|
||||
console.log(` Exit Price: $${t.exitPrice.toFixed(2)}`);
|
||||
console.log(` Trade Value: $${tradeValue.toFixed(2)}`);
|
||||
console.log(` Commission: $${t.commission.toFixed(2)} (expected: $${expectedCommission.toFixed(2)})`);
|
||||
console.log(` P&L: $${t.pnl.toFixed(2)}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Compare with zero commission/slippage
|
||||
const zeroConfig = {
|
||||
...config,
|
||||
commission: 0,
|
||||
slippage: 0
|
||||
};
|
||||
|
||||
await modeManager.initializeMode(zeroConfig);
|
||||
const zeroResult = await backtestEngine.runBacktest(zeroConfig);
|
||||
|
||||
console.log('\n=== COMPARISON ===');
|
||||
console.log('With commission/slippage:');
|
||||
console.log(` Final Value: $${result.equity[result.equity.length - 1].value.toFixed(2)}`);
|
||||
console.log(` Total Return: ${result.metrics.totalReturn.toFixed(2)}%`);
|
||||
|
||||
console.log('\nWithout commission/slippage:');
|
||||
console.log(` Final Value: $${zeroResult.equity[zeroResult.equity.length - 1].value.toFixed(2)}`);
|
||||
console.log(` Total Return: ${zeroResult.metrics.totalReturn.toFixed(2)}%`);
|
||||
|
||||
const difference = zeroResult.equity[zeroResult.equity.length - 1].value - result.equity[result.equity.length - 1].value;
|
||||
console.log(`\nCost of commission/slippage: $${difference.toFixed(2)}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Backtest failed:', error);
|
||||
}
|
||||
|
||||
console.log('\n=== TEST COMPLETE ===');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
testCommissionSlippage().catch(error => {
|
||||
console.error('Test failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,41 +0,0 @@
|
|||
import { PerformanceAnalyzer } from './src/analytics/PerformanceAnalyzer';
|
||||
|
||||
// Test with simple data
|
||||
const analyzer = new PerformanceAnalyzer(100000);
|
||||
|
||||
// Add some equity points over 30 days
|
||||
const startDate = new Date('2023-01-01');
|
||||
const values = [
|
||||
100000, 101000, 100500, 102000, 101500,
|
||||
103000, 102500, 104000, 103500, 105000,
|
||||
104500, 106000, 105500, 107000, 106500,
|
||||
108000, 107500, 109000, 108500, 110000
|
||||
];
|
||||
|
||||
for (let i = 0; i < values.length; i++) {
|
||||
const date = new Date(startDate);
|
||||
date.setDate(date.getDate() + i);
|
||||
analyzer.addEquityPoint(date, values[i]);
|
||||
console.log(`Day ${i + 1}: ${date.toISOString().split('T')[0]} => $${values[i]}`);
|
||||
}
|
||||
|
||||
console.log('\n=== Analyzing Performance ===');
|
||||
const metrics = analyzer.analyze();
|
||||
|
||||
console.log(`\nTotal Return: ${metrics.totalReturn.toFixed(2)}%`);
|
||||
console.log(`Annualized Return: ${metrics.annualizedReturn.toFixed(2)}%`);
|
||||
console.log(`Volatility: ${metrics.volatility.toFixed(2)}%`);
|
||||
console.log(`Sharpe Ratio: ${metrics.sharpeRatio.toFixed(3)}`);
|
||||
|
||||
// Debug calculations
|
||||
const finalValue = 110000;
|
||||
const initialValue = 100000;
|
||||
const totalReturn = ((finalValue - initialValue) / initialValue) * 100;
|
||||
const days = 19;
|
||||
const years = days / 365;
|
||||
const annualizedReturn = (Math.pow(1 + totalReturn/100, 1/years) - 1) * 100;
|
||||
|
||||
console.log('\n=== Manual Calculations ===');
|
||||
console.log(`Total Return: ${totalReturn.toFixed(2)}%`);
|
||||
console.log(`Years: ${years.toFixed(4)}`);
|
||||
console.log(`Annualized Return: ${annualizedReturn.toFixed(2)}%`);
|
||||
|
|
@ -1,73 +0,0 @@
|
|||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
import { ModeManager } from './src/core/ModeManager';
|
||||
import { MarketDataService } from './src/services/MarketDataService';
|
||||
import { ExecutionService } from './src/services/ExecutionService';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
|
||||
async function debugEquityCurve() {
|
||||
// Create service container
|
||||
const container: IServiceContainer = {
|
||||
logger: {
|
||||
info: (msg: string, ...args: any[]) => console.log('[INFO]', msg, ...args),
|
||||
error: (msg: string, ...args: any[]) => console.error('[ERROR]', msg, ...args),
|
||||
warn: (msg: string, ...args: any[]) => console.warn('[WARN]', msg, ...args),
|
||||
debug: (msg: string, ...args: any[]) => console.log('[DEBUG]', msg, ...args),
|
||||
} as any,
|
||||
custom: {}
|
||||
};
|
||||
|
||||
// Initialize services
|
||||
const storageService = new StorageService();
|
||||
const marketDataService = new MarketDataService(container);
|
||||
const executionService = new ExecutionService(container);
|
||||
const modeManager = new ModeManager(container, marketDataService, executionService, storageService);
|
||||
const strategyManager = new StrategyManager(container);
|
||||
|
||||
// Set services in container
|
||||
container.custom = {
|
||||
MarketDataService: marketDataService,
|
||||
ExecutionService: executionService,
|
||||
ModeManager: modeManager,
|
||||
StorageService: storageService
|
||||
};
|
||||
|
||||
// Test with 5000 initial capital
|
||||
const config = {
|
||||
mode: 'backtest',
|
||||
name: 'Debug',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'],
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-01-05T00:00:00Z', // Just 5 days
|
||||
initialCapital: 5000,
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
dataFrequency: '1d',
|
||||
speed: 'max'
|
||||
};
|
||||
|
||||
await modeManager.initializeMode(config);
|
||||
const backtestEngine = new BacktestEngine(container, storageService, strategyManager);
|
||||
|
||||
console.log('Before runBacktest - checking backtestEngine state...');
|
||||
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
console.log('\n=== EQUITY CURVE DEBUG ===');
|
||||
console.log(`Config Initial Capital: $${config.initialCapital}`);
|
||||
console.log(`Number of equity points: ${result.equity.length}`);
|
||||
|
||||
// Show all equity points
|
||||
result.equity.forEach((point, idx) => {
|
||||
console.log(` ${idx}: ${point.date} -> $${point.value.toFixed(2)}`);
|
||||
});
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
debugEquityCurve().catch(error => {
|
||||
console.error('Test failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,95 +0,0 @@
|
|||
import { createContainer } from './src/simple-container';
|
||||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
|
||||
async function testEquityCurveSpikeFixture() {
|
||||
console.log('Testing equity curve spike fix...\n');
|
||||
|
||||
// Create minimal container
|
||||
const container = await createContainer({
|
||||
database: {
|
||||
mongodb: { enabled: false, uri: '' },
|
||||
postgres: { enabled: false, uri: '' },
|
||||
questdb: { enabled: false, host: '', port: 0 }
|
||||
}
|
||||
});
|
||||
|
||||
// Create services
|
||||
const storageService = new StorageService(container);
|
||||
const strategyManager = new StrategyManager(container);
|
||||
|
||||
// Create backtest engine
|
||||
const backtestEngine = new BacktestEngine(container, storageService, strategyManager);
|
||||
|
||||
// Run a quick backtest
|
||||
const config = {
|
||||
mode: 'backtest',
|
||||
name: 'Equity Curve Spike Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'],
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-01-31T00:00:00Z', // Just one month
|
||||
initialCapital: 100000,
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
dataFrequency: '1d',
|
||||
speed: 'max'
|
||||
};
|
||||
|
||||
console.log('Running backtest...');
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
// Check the equity curve
|
||||
console.log('\n=== Equity Curve Analysis ===');
|
||||
console.log(`Initial Capital: $${config.initialCapital}`);
|
||||
console.log(`Total equity points: ${result.equity.length}`);
|
||||
|
||||
if (result.equity.length > 0) {
|
||||
// Check first few points
|
||||
console.log('\nFirst 5 equity curve points:');
|
||||
result.equity.slice(0, 5).forEach((point, index) => {
|
||||
console.log(`${index + 1}. ${point.date}: $${point.value.toFixed(2)}`);
|
||||
});
|
||||
|
||||
// Check for spike
|
||||
const firstValue = result.equity[0].value;
|
||||
const secondValue = result.equity.length > 1 ? result.equity[1].value : firstValue;
|
||||
|
||||
console.log(`\nFirst equity value: $${firstValue.toFixed(2)}`);
|
||||
console.log(`Second equity value: $${secondValue.toFixed(2)}`);
|
||||
|
||||
// Check if there's a spike from 0 to initial capital
|
||||
if (firstValue === 0 || firstValue < config.initialCapital * 0.5) {
|
||||
console.log('\n❌ SPIKE DETECTED: First equity value is too low!');
|
||||
} else if (Math.abs(firstValue - config.initialCapital) < 1) {
|
||||
console.log('\n✅ NO SPIKE: First equity value correctly starts at initial capital!');
|
||||
} else {
|
||||
console.log(`\n⚠️ First equity value differs from initial capital by $${Math.abs(firstValue - config.initialCapital).toFixed(2)}`);
|
||||
}
|
||||
|
||||
// Check for duplicate timestamps
|
||||
const timestamps = new Set();
|
||||
let duplicates = 0;
|
||||
result.equity.forEach(point => {
|
||||
if (timestamps.has(point.date)) {
|
||||
duplicates++;
|
||||
}
|
||||
timestamps.add(point.date);
|
||||
});
|
||||
|
||||
if (duplicates > 0) {
|
||||
console.log(`\n⚠️ Found ${duplicates} duplicate timestamps in equity curve`);
|
||||
} else {
|
||||
console.log('\n✅ No duplicate timestamps found');
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n=== Test Complete ===');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
testEquityCurveSpikeFixture().catch(error => {
|
||||
console.error('Test failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,105 +0,0 @@
|
|||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
import { ModeManager } from './src/core/ModeManager';
|
||||
import { MarketDataService } from './src/services/MarketDataService';
|
||||
import { ExecutionService } from './src/services/ExecutionService';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
|
||||
async function testEquitySpike() {
|
||||
console.log('Testing equity curve spike fix...\n');
|
||||
|
||||
// Create minimal service container
|
||||
const container: IServiceContainer = {
|
||||
logger: {
|
||||
info: (msg: string, ...args: any[]) => console.log('[INFO]', msg, ...args),
|
||||
error: (msg: string, ...args: any[]) => console.error('[ERROR]', msg, ...args),
|
||||
warn: (msg: string, ...args: any[]) => console.warn('[WARN]', msg, ...args),
|
||||
debug: (msg: string, ...args: any[]) => console.log('[DEBUG]', msg, ...args),
|
||||
} as any,
|
||||
custom: {}
|
||||
};
|
||||
|
||||
// Initialize services
|
||||
const storageService = new StorageService();
|
||||
const marketDataService = new MarketDataService(container);
|
||||
const executionService = new ExecutionService(container);
|
||||
const modeManager = new ModeManager(container, marketDataService, executionService, storageService);
|
||||
const strategyManager = new StrategyManager(container);
|
||||
|
||||
// Set services in container
|
||||
container.custom = {
|
||||
MarketDataService: marketDataService,
|
||||
ExecutionService: executionService,
|
||||
ModeManager: modeManager,
|
||||
StorageService: storageService
|
||||
};
|
||||
|
||||
// Initialize backtest mode
|
||||
await modeManager.initializeMode({
|
||||
mode: 'backtest',
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-01-10T00:00:00Z', // Just 10 days
|
||||
speed: 'max',
|
||||
symbols: ['TEST'],
|
||||
initialCapital: 100000,
|
||||
dataFrequency: '1d',
|
||||
strategy: 'sma-crossover'
|
||||
});
|
||||
|
||||
// Create backtest engine
|
||||
const backtestEngine = new BacktestEngine(container, storageService, strategyManager);
|
||||
|
||||
// Run backtest
|
||||
const config = {
|
||||
mode: 'backtest',
|
||||
name: 'Equity Spike Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['TEST'],
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-01-10T00:00:00Z',
|
||||
initialCapital: 100000,
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
dataFrequency: '1d',
|
||||
speed: 'max'
|
||||
};
|
||||
|
||||
console.log('Running backtest...');
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
// Analyze equity curve
|
||||
console.log('\n=== EQUITY CURVE ANALYSIS ===');
|
||||
console.log(`Initial Capital: $${config.initialCapital.toLocaleString()}`);
|
||||
console.log(`Equity points: ${result.equity.length}`);
|
||||
|
||||
if (result.equity.length > 0) {
|
||||
console.log('\nFirst 3 equity points:');
|
||||
result.equity.slice(0, 3).forEach((point, i) => {
|
||||
console.log(` ${i + 1}. ${point.date} => $${point.value.toFixed(2)}`);
|
||||
});
|
||||
|
||||
const firstValue = result.equity[0].value;
|
||||
const hasSpike = firstValue === 0 || firstValue < config.initialCapital * 0.9;
|
||||
|
||||
console.log(`\nFirst value: $${firstValue.toFixed(2)}`);
|
||||
console.log(`Expected: $${config.initialCapital.toFixed(2)}`);
|
||||
console.log(`Difference: $${Math.abs(firstValue - config.initialCapital).toFixed(2)}`);
|
||||
|
||||
if (hasSpike) {
|
||||
console.log('\n❌ SPIKE DETECTED! Portfolio value starts at 0 or too low.');
|
||||
} else if (Math.abs(firstValue - config.initialCapital) < 1) {
|
||||
console.log('\n✅ SUCCESS! Portfolio value correctly starts at initial capital.');
|
||||
} else {
|
||||
console.log('\n⚠️ WARNING: Small difference detected, but no major spike.');
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n=== TEST COMPLETE ===');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
testEquitySpike().catch(error => {
|
||||
console.error('Test failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,130 +0,0 @@
|
|||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
import { ModeManager } from './src/core/ModeManager';
|
||||
import { MarketDataService } from './src/services/MarketDataService';
|
||||
import { ExecutionService } from './src/services/ExecutionService';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
|
||||
async function testFullBacktest() {
|
||||
console.log('Testing Full Backtest with Real Data...\n');
|
||||
|
||||
// Create service container
|
||||
const container: IServiceContainer = {
|
||||
logger: {
|
||||
info: (msg: string, ...args: any[]) => console.log('[INFO]', msg, ...args),
|
||||
error: (msg: string, ...args: any[]) => console.error('[ERROR]', msg, ...args),
|
||||
warn: (msg: string, ...args: any[]) => console.warn('[WARN]', msg, ...args),
|
||||
debug: (msg: string, ...args: any[]) => console.log('[DEBUG]', msg, ...args),
|
||||
} as any,
|
||||
custom: {}
|
||||
};
|
||||
|
||||
// Initialize services
|
||||
const storageService = new StorageService();
|
||||
const marketDataService = new MarketDataService(container);
|
||||
const executionService = new ExecutionService(container);
|
||||
const modeManager = new ModeManager(container, marketDataService, executionService, storageService);
|
||||
const strategyManager = new StrategyManager(container);
|
||||
|
||||
// Set services in container
|
||||
container.custom = {
|
||||
MarketDataService: marketDataService,
|
||||
ExecutionService: executionService,
|
||||
ModeManager: modeManager,
|
||||
StorageService: storageService
|
||||
};
|
||||
|
||||
// Test configuration matching what frontend might send
|
||||
const config = {
|
||||
mode: 'backtest',
|
||||
name: 'Full Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL', 'MSFT', 'GOOGL'], // Multiple symbols
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-12-31T00:00:00Z', // Full year
|
||||
initialCapital: 100000,
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
dataFrequency: '1d',
|
||||
speed: 'max'
|
||||
};
|
||||
|
||||
// Initialize backtest mode
|
||||
await modeManager.initializeMode(config);
|
||||
|
||||
// Create backtest engine
|
||||
const backtestEngine = new BacktestEngine(container, storageService, strategyManager);
|
||||
|
||||
console.log('Running full year backtest with multiple symbols...');
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
const duration = (Date.now() - startTime) / 1000;
|
||||
console.log(`\nBacktest completed in ${duration.toFixed(2)} seconds`);
|
||||
|
||||
// Check for data anomalies
|
||||
console.log('\n=== RESULT ANALYSIS ===');
|
||||
console.log(`Total Return: ${result.metrics.totalReturn.toFixed(2)}%`);
|
||||
console.log(`Sharpe Ratio: ${result.metrics.sharpeRatio.toFixed(3)}`);
|
||||
console.log(`Max Drawdown: ${(result.metrics.maxDrawdown * 100).toFixed(2)}%`);
|
||||
console.log(`Win Rate: ${result.metrics.winRate.toFixed(1)}%`);
|
||||
console.log(`Total Trades: ${result.metrics.totalTrades}`);
|
||||
console.log(`Trades Array Length: ${result.trades.length}`);
|
||||
|
||||
// Check equity curve
|
||||
console.log('\n=== EQUITY CURVE CHECK ===');
|
||||
console.log(`Equity Points: ${result.equity.length}`);
|
||||
if (result.equity.length > 0) {
|
||||
const first = result.equity[0];
|
||||
const last = result.equity[result.equity.length - 1];
|
||||
const max = Math.max(...result.equity.map(e => e.value));
|
||||
const min = Math.min(...result.equity.map(e => e.value));
|
||||
|
||||
console.log(`First: $${first.value.toFixed(2)}`);
|
||||
console.log(`Last: $${last.value.toFixed(2)}`);
|
||||
console.log(`Max: $${max.toFixed(2)}`);
|
||||
console.log(`Min: $${min.toFixed(2)}`);
|
||||
|
||||
// Check for exponential growth
|
||||
const growthRatio = last.value / first.value;
|
||||
if (growthRatio > 10) {
|
||||
console.log('\n⚠️ WARNING: Detected possible exponential growth!');
|
||||
console.log(`Growth ratio: ${growthRatio.toFixed(2)}x`);
|
||||
}
|
||||
}
|
||||
|
||||
// Sample some trades
|
||||
console.log('\n=== TRADE SAMPLES ===');
|
||||
if (result.trades.length > 0) {
|
||||
console.log('First 3 trades:');
|
||||
result.trades.slice(0, 3).forEach((t, i) => {
|
||||
console.log(` ${i+1}. ${t.symbol} ${t.side} ${t.quantity} @ ${t.exitPrice.toFixed(2)} | P&L: $${t.pnl.toFixed(2)} (${t.pnlPercent.toFixed(2)}%)`);
|
||||
});
|
||||
|
||||
if (result.trades.length > 3) {
|
||||
console.log('\nLast 3 trades:');
|
||||
result.trades.slice(-3).forEach((t, i) => {
|
||||
console.log(` ${i+1}. ${t.symbol} ${t.side} ${t.quantity} @ ${t.exitPrice.toFixed(2)} | P&L: $${t.pnl.toFixed(2)} (${t.pnlPercent.toFixed(2)}%)`);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Check raw metrics object
|
||||
console.log('\n=== RAW METRICS ===');
|
||||
console.log(JSON.stringify(result.metrics, null, 2));
|
||||
|
||||
} catch (error) {
|
||||
console.error('Backtest failed:', error);
|
||||
}
|
||||
|
||||
console.log('\n=== TEST COMPLETE ===');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
testFullBacktest().catch(error => {
|
||||
console.error('Test failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,93 +0,0 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
/**
|
||||
* Test the full flow from market data to trade execution
|
||||
*/
|
||||
|
||||
import { SimpleMovingAverageCrossover } from './src/strategies/examples/SimpleMovingAverageCrossover';
|
||||
import { BaseStrategy } from './src/strategies/BaseStrategy';
|
||||
import { MarketData } from './src/types';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
|
||||
const logger = getLogger('test-flow');
|
||||
|
||||
async function testFullFlow() {
|
||||
console.log('Testing full flow from market data to orders...\n');
|
||||
|
||||
// Create strategy
|
||||
const config = {
|
||||
id: 'test-sma',
|
||||
name: 'Test SMA',
|
||||
enabled: true,
|
||||
symbols: ['AAPL'],
|
||||
allocation: 1.0
|
||||
};
|
||||
|
||||
const strategy = new SimpleMovingAverageCrossover(config, null, null);
|
||||
|
||||
let signalCount = 0;
|
||||
let orderCount = 0;
|
||||
|
||||
// Listen for signals
|
||||
strategy.on('signal', (signal) => {
|
||||
signalCount++;
|
||||
logger.info(`Signal #${signalCount}:`, signal);
|
||||
});
|
||||
|
||||
// Listen for orders
|
||||
strategy.on('order', (order) => {
|
||||
orderCount++;
|
||||
logger.info(`Order #${orderCount}:`, order);
|
||||
});
|
||||
|
||||
await strategy.start();
|
||||
|
||||
// Generate 50 days of data with a clear uptrend after day 20
|
||||
logger.info('Generating market data with uptrend...');
|
||||
|
||||
for (let day = 0; day < 50; day++) {
|
||||
const basePrice = 100;
|
||||
let price = basePrice;
|
||||
|
||||
// Create a clear uptrend after day 20
|
||||
if (day > 20) {
|
||||
price = basePrice + (day - 20) * 0.5; // 50 cents per day uptrend
|
||||
} else {
|
||||
price = basePrice + Math.sin(day * 0.3) * 2; // Sideways
|
||||
}
|
||||
|
||||
const marketData: MarketData = {
|
||||
type: 'bar',
|
||||
data: {
|
||||
symbol: 'AAPL',
|
||||
open: price - 0.5,
|
||||
high: price + 0.5,
|
||||
low: price - 1,
|
||||
close: price,
|
||||
volume: 1000000,
|
||||
timestamp: Date.now() + day * 86400000
|
||||
}
|
||||
};
|
||||
|
||||
// Process the data
|
||||
await strategy.onMarketData(marketData);
|
||||
|
||||
if (day === 19) {
|
||||
logger.info(`Day 19: Last day before uptrend, price = ${price}`);
|
||||
}
|
||||
if (day === 25) {
|
||||
logger.info(`Day 25: Should see golden cross soon, price = ${price}`);
|
||||
}
|
||||
}
|
||||
|
||||
await strategy.stop();
|
||||
|
||||
console.log('\n=== Test Results ===');
|
||||
console.log(`Total signals generated: ${signalCount}`);
|
||||
console.log(`Total orders generated: ${orderCount}`);
|
||||
|
||||
const perf = strategy.getPerformance();
|
||||
console.log('Strategy performance:', perf);
|
||||
}
|
||||
|
||||
testFullFlow().catch(console.error);
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
import { ModeManager } from './src/core/ModeManager';
|
||||
import { MarketDataService } from './src/services/MarketDataService';
|
||||
import { ExecutionService } from './src/services/ExecutionService';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
|
||||
async function testInitialCapitalDebug() {
|
||||
console.log('Debugging Initial Capital Issue...\n');
|
||||
|
||||
// Create service container
|
||||
const container: IServiceContainer = {
|
||||
logger: {
|
||||
info: (msg: string, ...args: any[]) => console.log('[INFO]', msg, ...args),
|
||||
error: (msg: string, ...args: any[]) => console.error('[ERROR]', msg, ...args),
|
||||
warn: (msg: string, ...args: any[]) => console.warn('[WARN]', msg, ...args),
|
||||
debug: (msg: string, ...args: any[]) => console.log('[DEBUG]', msg, ...args),
|
||||
} as any,
|
||||
custom: {}
|
||||
};
|
||||
|
||||
// Initialize services
|
||||
const storageService = new StorageService();
|
||||
const marketDataService = new MarketDataService(container);
|
||||
const executionService = new ExecutionService(container);
|
||||
const modeManager = new ModeManager(container, marketDataService, executionService, storageService);
|
||||
const strategyManager = new StrategyManager(container);
|
||||
|
||||
// Set services in container
|
||||
container.custom = {
|
||||
MarketDataService: marketDataService,
|
||||
ExecutionService: executionService,
|
||||
ModeManager: modeManager,
|
||||
StorageService: storageService
|
||||
};
|
||||
|
||||
// Test with LOW initial capital
|
||||
const config = {
|
||||
mode: 'backtest',
|
||||
name: 'Initial Capital Debug',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'],
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-01-31T00:00:00Z', // Just one month
|
||||
initialCapital: 1000, // LOW CAPITAL
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
dataFrequency: '1d',
|
||||
speed: 'max'
|
||||
};
|
||||
|
||||
// Initialize backtest mode
|
||||
await modeManager.initializeMode(config);
|
||||
|
||||
// Create backtest engine
|
||||
const backtestEngine = new BacktestEngine(container, storageService, strategyManager);
|
||||
|
||||
console.log('Running backtest with initial capital: $' + config.initialCapital);
|
||||
|
||||
try {
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
console.log('\n=== DEBUG INFO ===');
|
||||
console.log(`Initial Capital: $${config.initialCapital}`);
|
||||
|
||||
// Check equity curve
|
||||
console.log('\nEquity Curve:');
|
||||
result.equity.forEach((point, idx) => {
|
||||
if (idx < 3 || idx >= result.equity.length - 3) {
|
||||
console.log(` ${new Date(point.timestamp).toISOString()}: $${point.value.toFixed(2)}`);
|
||||
}
|
||||
if (idx === 3 && result.equity.length > 6) {
|
||||
console.log(' ...');
|
||||
}
|
||||
});
|
||||
|
||||
// Get trading engine to check P&L
|
||||
const tradingEngine = strategyManager.getTradingEngine();
|
||||
if (tradingEngine) {
|
||||
const [realized, unrealized] = tradingEngine.getTotalPnl();
|
||||
console.log(`\nRust Core P&L:`);
|
||||
console.log(` Realized: $${realized.toFixed(2)}`);
|
||||
console.log(` Unrealized: $${unrealized.toFixed(2)}`);
|
||||
console.log(` Total P&L: $${(realized + unrealized).toFixed(2)}`);
|
||||
console.log(` Expected Final Value: $${(config.initialCapital + realized + unrealized).toFixed(2)}`);
|
||||
}
|
||||
|
||||
// Check trades
|
||||
console.log('\nTrades:');
|
||||
result.trades.forEach((t, idx) => {
|
||||
console.log(` ${idx + 1}. ${t.symbol} ${t.side} ${t.quantity} @ $${t.entryPrice} -> $${t.exitPrice}`);
|
||||
console.log(` P&L: $${t.pnl.toFixed(2)} (${t.pnlPercent.toFixed(2)}%)`);
|
||||
});
|
||||
|
||||
// The issue calculation
|
||||
const finalValue = result.equity[result.equity.length - 1].value;
|
||||
console.log('\n=== ISSUE ANALYSIS ===');
|
||||
console.log(`Final portfolio value: $${finalValue.toFixed(2)}`);
|
||||
console.log(`Initial capital: $${config.initialCapital}`);
|
||||
console.log(`Difference: $${(finalValue - config.initialCapital).toFixed(2)}`);
|
||||
console.log(`Return %: ${((finalValue - config.initialCapital) / config.initialCapital * 100).toFixed(2)}%`);
|
||||
|
||||
// Check if it's using 100k
|
||||
const assumedInitial = 100000;
|
||||
const assumedReturn = (finalValue - assumedInitial) / assumedInitial * 100;
|
||||
console.log(`\nIf initial was $100k:`);
|
||||
console.log(` Return would be: ${assumedReturn.toFixed(2)}%`);
|
||||
console.log(` P&L would be: $${(finalValue - assumedInitial).toFixed(2)}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Backtest failed:', error);
|
||||
}
|
||||
|
||||
console.log('\n=== TEST COMPLETE ===');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
testInitialCapitalDebug().catch(error => {
|
||||
console.error('Test failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,87 +0,0 @@
|
|||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
import { ModeManager } from './src/core/ModeManager';
|
||||
import { MarketDataService } from './src/services/MarketDataService';
|
||||
import { ExecutionService } from './src/services/ExecutionService';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
|
||||
async function testSimple() {
|
||||
// Create service container
|
||||
const container: IServiceContainer = {
|
||||
logger: {
|
||||
info: (msg: string, ...args: any[]) => {},
|
||||
error: (msg: string, ...args: any[]) => {},
|
||||
warn: (msg: string, ...args: any[]) => {},
|
||||
debug: (msg: string, ...args: any[]) => {},
|
||||
} as any,
|
||||
custom: {}
|
||||
};
|
||||
|
||||
// Initialize services
|
||||
const storageService = new StorageService();
|
||||
const marketDataService = new MarketDataService(container);
|
||||
const executionService = new ExecutionService(container);
|
||||
const modeManager = new ModeManager(container, marketDataService, executionService, storageService);
|
||||
const strategyManager = new StrategyManager(container);
|
||||
|
||||
// Set services in container
|
||||
container.custom = {
|
||||
MarketDataService: marketDataService,
|
||||
ExecutionService: executionService,
|
||||
ModeManager: modeManager,
|
||||
StorageService: storageService
|
||||
};
|
||||
|
||||
// Test with 1000 initial capital
|
||||
const config = {
|
||||
mode: 'backtest',
|
||||
name: 'Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'],
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-01-31T00:00:00Z',
|
||||
initialCapital: 1000,
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
dataFrequency: '1d',
|
||||
speed: 'max'
|
||||
};
|
||||
|
||||
await modeManager.initializeMode(config);
|
||||
const backtestEngine = new BacktestEngine(container, storageService, strategyManager);
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
// Get P&L from Rust
|
||||
const tradingEngine = strategyManager.getTradingEngine();
|
||||
const [realized, unrealized] = tradingEngine.getTotalPnl();
|
||||
|
||||
console.log('\n=== RESULTS ===');
|
||||
console.log(`Config Initial Capital: $${config.initialCapital}`);
|
||||
console.log(`First Equity Value: $${result.equity[0].value.toFixed(2)}`);
|
||||
console.log(`Final Equity Value: $${result.equity[result.equity.length - 1].value.toFixed(2)}`);
|
||||
console.log(`\nRust Core:`);
|
||||
console.log(` Realized P&L: $${realized.toFixed(2)}`);
|
||||
console.log(` Unrealized P&L: $${unrealized.toFixed(2)}`);
|
||||
console.log(` Total P&L: $${(realized + unrealized).toFixed(2)}`);
|
||||
console.log(`\nCalculated Final Value:`);
|
||||
console.log(` $${config.initialCapital} + $${(realized + unrealized).toFixed(2)} = $${(config.initialCapital + realized + unrealized).toFixed(2)}`);
|
||||
console.log(`\nActual vs Expected:`);
|
||||
const actualFinal = result.equity[result.equity.length - 1].value;
|
||||
const expectedFinal = config.initialCapital + realized + unrealized;
|
||||
console.log(` Actual: $${actualFinal.toFixed(2)}`);
|
||||
console.log(` Expected: $${expectedFinal.toFixed(2)}`);
|
||||
console.log(` Difference: $${(actualFinal - expectedFinal).toFixed(2)}`);
|
||||
|
||||
// Check if it's using 100k base
|
||||
if (Math.abs(actualFinal - (100000 + realized + unrealized)) < 1) {
|
||||
console.log('\n⚠️ BUG CONFIRMED: Using 100000 as base instead of ' + config.initialCapital);
|
||||
}
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
testSimple().catch(error => {
|
||||
console.error('Test failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,104 +0,0 @@
|
|||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
import { ModeManager } from './src/core/ModeManager';
|
||||
import { MarketDataService } from './src/services/MarketDataService';
|
||||
import { ExecutionService } from './src/services/ExecutionService';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
|
||||
async function testInitialCapital() {
|
||||
console.log('Testing Initial Capital Issue...\n');
|
||||
|
||||
// Create service container
|
||||
const container: IServiceContainer = {
|
||||
logger: {
|
||||
info: (msg: string, ...args: any[]) => console.log('[INFO]', msg, ...args),
|
||||
error: (msg: string, ...args: any[]) => console.error('[ERROR]', msg, ...args),
|
||||
warn: (msg: string, ...args: any[]) => console.warn('[WARN]', msg, ...args),
|
||||
debug: (msg: string, ...args: any[]) => console.log('[DEBUG]', msg, ...args),
|
||||
} as any,
|
||||
custom: {}
|
||||
};
|
||||
|
||||
// Initialize services
|
||||
const storageService = new StorageService();
|
||||
const marketDataService = new MarketDataService(container);
|
||||
const executionService = new ExecutionService(container);
|
||||
const modeManager = new ModeManager(container, marketDataService, executionService, storageService);
|
||||
const strategyManager = new StrategyManager(container);
|
||||
|
||||
// Set services in container
|
||||
container.custom = {
|
||||
MarketDataService: marketDataService,
|
||||
ExecutionService: executionService,
|
||||
ModeManager: modeManager,
|
||||
StorageService: storageService
|
||||
};
|
||||
|
||||
// Test with LOW initial capital
|
||||
const config = {
|
||||
mode: 'backtest',
|
||||
name: 'Initial Capital Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'],
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-01-31T00:00:00Z', // Just one month
|
||||
initialCapital: 1000, // LOW CAPITAL
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
dataFrequency: '1d',
|
||||
speed: 'max'
|
||||
};
|
||||
|
||||
// Initialize backtest mode
|
||||
await modeManager.initializeMode(config);
|
||||
|
||||
// Create backtest engine
|
||||
const backtestEngine = new BacktestEngine(container, storageService, strategyManager);
|
||||
|
||||
console.log('Running backtest with initial capital: $' + config.initialCapital);
|
||||
|
||||
try {
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
console.log('\n=== RESULTS ===');
|
||||
console.log(`Initial Capital: $${config.initialCapital}`);
|
||||
console.log(`Final Value: $${result.equity[result.equity.length - 1].value.toFixed(2)}`);
|
||||
console.log(`Total Return: ${result.metrics.totalReturn.toFixed(2)}%`);
|
||||
console.log(`Total Trades: ${result.metrics.totalTrades}`);
|
||||
|
||||
// Check if portfolio grew from 1000
|
||||
const finalValue = result.equity[result.equity.length - 1].value;
|
||||
const expectedRange = config.initialCapital * 0.5; // Within 50% of initial
|
||||
|
||||
if (Math.abs(finalValue - config.initialCapital) > expectedRange &&
|
||||
Math.abs(finalValue - 100000) < expectedRange) {
|
||||
console.log('\n⚠️ WARNING: Portfolio appears to be using 100000 as initial capital!');
|
||||
} else {
|
||||
console.log('\n✅ Portfolio appears to be using correct initial capital');
|
||||
}
|
||||
|
||||
// Check first few trades
|
||||
if (result.trades.length > 0) {
|
||||
console.log('\nFirst trade:');
|
||||
const t = result.trades[0];
|
||||
console.log(` ${t.symbol} ${t.side} ${t.quantity} shares @ $${t.entryPrice}`);
|
||||
console.log(` Trade value: $${(t.quantity * t.entryPrice).toFixed(2)}`);
|
||||
|
||||
if (t.quantity * t.entryPrice > config.initialCapital) {
|
||||
console.log(' ⚠️ WARNING: Trade size exceeds initial capital!');
|
||||
}
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Backtest failed:', error);
|
||||
}
|
||||
|
||||
console.log('\n=== TEST COMPLETE ===');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
testInitialCapital().catch(error => {
|
||||
console.error('Test failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
import { RustBacktestAdapter } from './src/backtest/RustBacktestAdapter';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
import { BacktestConfig } from './src/types';
|
||||
|
||||
// Mock container
|
||||
const mockContainer: IServiceContainer = {
|
||||
logger: {
|
||||
info: console.log,
|
||||
error: console.error,
|
||||
warn: console.warn,
|
||||
debug: console.log,
|
||||
},
|
||||
mongodb: {} as any,
|
||||
postgres: {} as any,
|
||||
redis: {} as any,
|
||||
custom: {},
|
||||
} as IServiceContainer;
|
||||
|
||||
// Mock storage service that returns test data
|
||||
class MockStorageService {
|
||||
async getHistoricalBars(symbol: string, startDate: Date, endDate: Date, frequency: string) {
|
||||
console.log(`MockStorageService: Getting bars for ${symbol} from ${startDate} to ${endDate}`);
|
||||
|
||||
// Generate test data with mean reverting behavior
|
||||
const bars = [];
|
||||
const startTime = startDate.getTime();
|
||||
const endTime = endDate.getTime();
|
||||
const dayMs = 24 * 60 * 60 * 1000;
|
||||
|
||||
let time = startTime;
|
||||
let dayIndex = 0;
|
||||
|
||||
// Base prices for different symbols
|
||||
const basePrices = {
|
||||
'AAPL': 150,
|
||||
'GOOGL': 2800,
|
||||
'MSFT': 400,
|
||||
};
|
||||
|
||||
const basePrice = basePrices[symbol as keyof typeof basePrices] || 100;
|
||||
|
||||
while (time <= endTime) {
|
||||
// Create mean reverting price movement
|
||||
// Price oscillates around the base price with increasing then decreasing deviations
|
||||
const cycleLength = 40; // 40 day cycle
|
||||
const positionInCycle = dayIndex % cycleLength;
|
||||
const halfCycle = cycleLength / 2;
|
||||
|
||||
let deviation;
|
||||
if (positionInCycle < halfCycle) {
|
||||
// First half: price moves away from mean
|
||||
deviation = (positionInCycle / halfCycle) * 0.1; // Up to 10% deviation
|
||||
} else {
|
||||
// Second half: price reverts to mean
|
||||
deviation = ((cycleLength - positionInCycle) / halfCycle) * 0.1;
|
||||
}
|
||||
|
||||
// Alternate between above and below mean
|
||||
const cycleNumber = Math.floor(dayIndex / cycleLength);
|
||||
const multiplier = cycleNumber % 2 === 0 ? 1 : -1;
|
||||
|
||||
const price = basePrice * (1 + multiplier * deviation);
|
||||
|
||||
// Add some noise
|
||||
const noise = (Math.random() - 0.5) * 0.02 * basePrice;
|
||||
const finalPrice = price + noise;
|
||||
|
||||
bars.push({
|
||||
timestamp: new Date(time),
|
||||
open: finalPrice * 0.99,
|
||||
high: finalPrice * 1.01,
|
||||
low: finalPrice * 0.98,
|
||||
close: finalPrice,
|
||||
volume: 1000000,
|
||||
vwap: finalPrice,
|
||||
});
|
||||
|
||||
time += dayMs;
|
||||
dayIndex++;
|
||||
}
|
||||
|
||||
console.log(`Generated ${bars.length} bars for ${symbol}, first close: ${bars[0].close.toFixed(2)}, last close: ${bars[bars.length - 1].close.toFixed(2)}`);
|
||||
return bars;
|
||||
}
|
||||
}
|
||||
|
||||
// Test the backtest
|
||||
async function testMeanReversionBacktest() {
|
||||
console.log('=== Testing Mean Reversion Backtest ===\n');
|
||||
|
||||
// Create adapter with mock storage
|
||||
const adapter = new RustBacktestAdapter(mockContainer);
|
||||
(adapter as any).storageService = new MockStorageService();
|
||||
|
||||
const config: BacktestConfig = {
|
||||
name: 'Mean Reversion Test',
|
||||
strategy: 'mean_reversion',
|
||||
symbols: ['AAPL', 'GOOGL', 'MSFT'],
|
||||
startDate: '2024-01-01T00:00:00Z',
|
||||
endDate: '2024-06-01T00:00:00Z',
|
||||
initialCapital: 100000,
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
dataFrequency: '1d',
|
||||
config: {
|
||||
lookbackPeriod: 20,
|
||||
entryThreshold: 2.0,
|
||||
positionSize: 100,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
console.log('Starting backtest...\n');
|
||||
const result = await adapter.runBacktest(config);
|
||||
|
||||
console.log('\n=== Backtest Results ===');
|
||||
console.log(`Status: ${result.status}`);
|
||||
console.log(`Total Trades: ${result.metrics.totalTrades}`);
|
||||
console.log(`Profitable Trades: ${result.metrics.profitableTrades}`);
|
||||
console.log(`Win Rate: ${result.metrics.winRate.toFixed(2)}%`);
|
||||
console.log(`Total Return: ${result.metrics.totalReturn.toFixed(2)}%`);
|
||||
console.log(`Sharpe Ratio: ${result.metrics.sharpeRatio.toFixed(2)}`);
|
||||
console.log(`Max Drawdown: ${result.metrics.maxDrawdown.toFixed(2)}%`);
|
||||
|
||||
console.log('\n=== Trade Analysis ===');
|
||||
console.log(`Number of completed trades: ${result.trades.length}`);
|
||||
|
||||
// Analyze trades by symbol
|
||||
const tradesBySymbol: Record<string, any[]> = {};
|
||||
result.trades.forEach(trade => {
|
||||
if (!tradesBySymbol[trade.symbol]) {
|
||||
tradesBySymbol[trade.symbol] = [];
|
||||
}
|
||||
tradesBySymbol[trade.symbol].push(trade);
|
||||
});
|
||||
|
||||
Object.entries(tradesBySymbol).forEach(([symbol, trades]) => {
|
||||
console.log(`\n${symbol}: ${trades.length} trades`);
|
||||
const longTrades = trades.filter(t => t.side === 'long');
|
||||
const shortTrades = trades.filter(t => t.side === 'short');
|
||||
console.log(` - Long trades: ${longTrades.length}`);
|
||||
console.log(` - Short trades: ${shortTrades.length}`);
|
||||
|
||||
// Count buy/sell pairs
|
||||
const buyTrades = trades.filter(t => t.side === 'buy');
|
||||
const sellTrades = trades.filter(t => t.side === 'sell');
|
||||
console.log(` - Buy trades: ${buyTrades.length}`);
|
||||
console.log(` - Sell trades: ${sellTrades.length}`);
|
||||
|
||||
// Show first few trades
|
||||
console.log(` - First 3 trades:`);
|
||||
trades.slice(0, 3).forEach((trade, idx) => {
|
||||
console.log(` ${idx + 1}. Trade:`, JSON.stringify(trade, null, 2));
|
||||
});
|
||||
});
|
||||
|
||||
// Analyze trade pairing
|
||||
console.log('\n=== Trade Pairing Analysis ===');
|
||||
console.log(`Total fills: ${result.trades.length}`);
|
||||
console.log(`Expected pairs: ${result.trades.length / 2}`);
|
||||
|
||||
// Look for patterns that show instant buy/sell
|
||||
let instantPairs = 0;
|
||||
for (let i = 1; i < result.trades.length; i++) {
|
||||
const prev = result.trades[i-1];
|
||||
const curr = result.trades[i];
|
||||
if (prev.symbol === curr.symbol &&
|
||||
prev.side === 'buy' && curr.side === 'sell' &&
|
||||
new Date(curr.timestamp).getTime() - new Date(prev.timestamp).getTime() < 86400000) {
|
||||
instantPairs++;
|
||||
}
|
||||
}
|
||||
console.log(`Instant buy/sell pairs (< 1 day): ${instantPairs}`);
|
||||
|
||||
// Final positions
|
||||
console.log('\n=== Final Positions ===');
|
||||
Object.entries(result.finalPositions).forEach(([symbol, position]) => {
|
||||
console.log(`${symbol}: ${position}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Backtest failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the test
|
||||
testMeanReversionBacktest().catch(console.error);
|
||||
|
|
@ -1,80 +0,0 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
/**
|
||||
* Minimal test to debug order flow
|
||||
*/
|
||||
|
||||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import { TradingEngine } from '@stock-bot/core';
|
||||
import { ModeManager } from './src/core/ModeManager';
|
||||
import { MarketDataService } from './src/services/MarketDataService';
|
||||
import { ExecutionService } from './src/services/ExecutionService';
|
||||
|
||||
async function testMinimalBacktest() {
|
||||
console.log('=== Minimal Backtest Test ===\n');
|
||||
|
||||
const logger = getLogger('test');
|
||||
const container = {
|
||||
logger,
|
||||
custom: {}
|
||||
};
|
||||
|
||||
const storageService = new StorageService(container as any);
|
||||
const marketDataService = new MarketDataService(container as any);
|
||||
const executionService = new ExecutionService(container as any);
|
||||
const modeManager = new ModeManager(container as any, marketDataService, executionService, storageService);
|
||||
|
||||
// Add services to container
|
||||
container.custom = {
|
||||
ModeManager: modeManager,
|
||||
MarketDataService: marketDataService,
|
||||
ExecutionService: executionService
|
||||
};
|
||||
|
||||
const strategyManager = new StrategyManager(container as any);
|
||||
|
||||
// Add debug logging to strategy manager
|
||||
const origHandleMarketData = (strategyManager as any).handleMarketData;
|
||||
(strategyManager as any).handleMarketData = async function(data: any) {
|
||||
console.log(`>>> StrategyManager.handleMarketData called for ${data.data.symbol} @ ${data.data.close}`);
|
||||
console.log(` Active strategies: ${this.activeStrategies.size}`);
|
||||
console.log(` Strategy IDs: ${Array.from(this.activeStrategies).join(', ')}`);
|
||||
return origHandleMarketData.call(this, data);
|
||||
};
|
||||
|
||||
const backtestEngine = new BacktestEngine(container as any, storageService, strategyManager);
|
||||
|
||||
const config = {
|
||||
mode: 'backtest' as const,
|
||||
name: 'Minimal Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'],
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-02-15T00:00:00Z', // 45 days to ensure we have enough data
|
||||
initialCapital: 100000,
|
||||
dataFrequency: '1d',
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
speed: 'max' as const
|
||||
};
|
||||
|
||||
// Initialize mode manager with backtest config
|
||||
await modeManager.initializeMode(config);
|
||||
|
||||
try {
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
console.log('\nResults:');
|
||||
console.log(`Total Return: ${result.metrics.totalReturn.toFixed(2)}%`);
|
||||
console.log(`Total Trades: ${result.metrics.totalTrades}`);
|
||||
console.log(`Trades in history: ${result.trades.length}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Backtest failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
testMinimalBacktest().catch(console.error);
|
||||
|
|
@ -1,122 +0,0 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
/**
|
||||
* Test order flow through the full backtest system
|
||||
*/
|
||||
|
||||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
import { SimpleMovingAverageCrossover } from './src/strategies/examples/SimpleMovingAverageCrossover';
|
||||
|
||||
async function testOrderFlow() {
|
||||
console.log('Testing order flow in backtest...\n');
|
||||
|
||||
// Create minimal container
|
||||
const logger = getLogger('test');
|
||||
const container = {
|
||||
logger,
|
||||
custom: {}
|
||||
};
|
||||
|
||||
// Create services
|
||||
const storageService = new StorageService(container as any);
|
||||
const strategyManager = new StrategyManager(container as any);
|
||||
|
||||
// First test the strategy directly
|
||||
console.log('=== Testing Strategy Directly ===');
|
||||
const testStrategy = new SimpleMovingAverageCrossover({
|
||||
id: 'test-direct',
|
||||
name: 'Direct Test',
|
||||
enabled: true,
|
||||
symbols: ['AAPL'],
|
||||
allocation: 1.0
|
||||
}, null, null);
|
||||
|
||||
let directSignals = 0;
|
||||
let directOrders = 0;
|
||||
|
||||
testStrategy.on('signal', (signal) => {
|
||||
directSignals++;
|
||||
console.log(`Direct signal #${directSignals}:`, signal);
|
||||
});
|
||||
|
||||
testStrategy.on('order', (order) => {
|
||||
directOrders++;
|
||||
console.log(`Direct order #${directOrders}:`, order);
|
||||
});
|
||||
|
||||
await testStrategy.start();
|
||||
|
||||
// Generate test data with clear crossover
|
||||
for (let i = 0; i < 30; i++) {
|
||||
const basePrice = 100;
|
||||
const price = i < 15 ? basePrice - i * 0.5 : basePrice + (i - 15) * 0.5;
|
||||
|
||||
await testStrategy.onMarketData({
|
||||
type: 'bar',
|
||||
data: {
|
||||
symbol: 'AAPL',
|
||||
open: price,
|
||||
high: price + 1,
|
||||
low: price - 1,
|
||||
close: price,
|
||||
volume: 1000000,
|
||||
timestamp: Date.now() + i * 86400000
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await testStrategy.stop();
|
||||
console.log(`\nDirect test: ${directSignals} signals, ${directOrders} orders\n`);
|
||||
|
||||
// Now test through backtest engine
|
||||
console.log('=== Testing Through Backtest Engine ===');
|
||||
|
||||
// Create backtest engine (it will initialize strategies)
|
||||
const backtestEngine = new BacktestEngine(container as any, storageService, strategyManager);
|
||||
|
||||
// Hook into backtest engine to see what's happening
|
||||
const origProcessMarketData = (backtestEngine as any).processMarketData;
|
||||
(backtestEngine as any).processMarketData = async function(data: any) {
|
||||
console.log(`Processing market data: ${data.data.symbol} @ ${data.data.close}`);
|
||||
return origProcessMarketData.call(this, data);
|
||||
};
|
||||
|
||||
const origCheckAndFillOrders = (backtestEngine as any).checkAndFillOrders;
|
||||
(backtestEngine as any).checkAndFillOrders = async function(data: any) {
|
||||
const pendingCount = this.pendingOrders.size;
|
||||
if (pendingCount > 0) {
|
||||
console.log(`Checking ${pendingCount} pending orders...`);
|
||||
}
|
||||
return origCheckAndFillOrders.call(this, data);
|
||||
};
|
||||
|
||||
const config = {
|
||||
mode: 'backtest',
|
||||
name: 'Order Flow Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'],
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-02-01T00:00:00Z', // Just 1 month
|
||||
initialCapital: 100000,
|
||||
dataFrequency: '1d',
|
||||
commission: 0.001,
|
||||
slippage: 0.0001
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
console.log('\nBacktest Results:');
|
||||
console.log(`Total Return: ${result.metrics.totalReturn.toFixed(2)}%`);
|
||||
console.log(`Total Trades: ${result.metrics.totalTrades}`);
|
||||
console.log(`Trades in history: ${result.trades.length}`);
|
||||
|
||||
} catch (error) {
|
||||
console.error('Backtest failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
testOrderFlow().catch(console.error);
|
||||
|
|
@ -1,134 +0,0 @@
|
|||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
import { ModeManager } from './src/core/ModeManager';
|
||||
import { MarketDataService } from './src/services/MarketDataService';
|
||||
import { ExecutionService } from './src/services/ExecutionService';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
|
||||
async function testPerformanceMetrics() {
|
||||
console.log('Testing Performance Metrics Calculation...\n');
|
||||
|
||||
// Create minimal service container
|
||||
const container: IServiceContainer = {
|
||||
logger: {
|
||||
info: (msg: string, ...args: any[]) => console.log('[INFO]', msg, ...args),
|
||||
error: (msg: string, ...args: any[]) => console.error('[ERROR]', msg, ...args),
|
||||
warn: (msg: string, ...args: any[]) => console.warn('[WARN]', msg, ...args),
|
||||
debug: (msg: string, ...args: any[]) => console.log('[DEBUG]', msg, ...args),
|
||||
} as any,
|
||||
custom: {}
|
||||
};
|
||||
|
||||
// Initialize services
|
||||
const storageService = new StorageService();
|
||||
const marketDataService = new MarketDataService(container);
|
||||
const executionService = new ExecutionService(container);
|
||||
const modeManager = new ModeManager(container, marketDataService, executionService, storageService);
|
||||
const strategyManager = new StrategyManager(container);
|
||||
|
||||
// Set services in container
|
||||
container.custom = {
|
||||
MarketDataService: marketDataService,
|
||||
ExecutionService: executionService,
|
||||
ModeManager: modeManager,
|
||||
StorageService: storageService
|
||||
};
|
||||
|
||||
// Initialize backtest mode
|
||||
await modeManager.initializeMode({
|
||||
mode: 'backtest',
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-03-31T00:00:00Z', // 3 months for more trades
|
||||
speed: 'max',
|
||||
symbols: ['AAPL'],
|
||||
initialCapital: 100000,
|
||||
dataFrequency: '1d',
|
||||
strategy: 'sma-crossover'
|
||||
});
|
||||
|
||||
// Create backtest engine
|
||||
const backtestEngine = new BacktestEngine(container, storageService, strategyManager);
|
||||
|
||||
// Run backtest
|
||||
const config = {
|
||||
mode: 'backtest',
|
||||
name: 'Performance Metrics Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'],
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-03-31T00:00:00Z',
|
||||
initialCapital: 100000,
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
dataFrequency: '1d',
|
||||
speed: 'max'
|
||||
};
|
||||
|
||||
console.log('Running backtest...');
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
// Analyze metrics
|
||||
console.log('\n=== PERFORMANCE METRICS ANALYSIS ===');
|
||||
console.log('\n📊 Returns:');
|
||||
console.log(` Total Return: ${result.metrics.totalReturn.toFixed(2)}%`);
|
||||
console.log(` Sharpe Ratio: ${result.metrics.sharpeRatio.toFixed(3)}`);
|
||||
console.log(` Max Drawdown: ${(result.metrics.maxDrawdown * 100).toFixed(2)}%`);
|
||||
|
||||
console.log('\n📈 Trading Statistics:');
|
||||
console.log(` Total Trades: ${result.metrics.totalTrades}`);
|
||||
console.log(` Win Rate: ${result.metrics.winRate.toFixed(2)}%`);
|
||||
console.log(` Profit Factor: ${result.metrics.profitFactor.toFixed(2)}`);
|
||||
console.log(` Average Win: $${result.metrics.avgWin.toFixed(2)}`);
|
||||
console.log(` Average Loss: $${result.metrics.avgLoss.toFixed(2)}`);
|
||||
|
||||
console.log('\n🎯 Trade Details:');
|
||||
console.log(` Closed Trades: ${result.trades.length}`);
|
||||
if (result.trades.length > 0) {
|
||||
const wins = result.trades.filter(t => t.pnl > 0);
|
||||
const losses = result.trades.filter(t => t.pnl < 0);
|
||||
console.log(` Winning Trades: ${wins.length}`);
|
||||
console.log(` Losing Trades: ${losses.length}`);
|
||||
console.log(` Calculated Win Rate: ${(wins.length / result.trades.length * 100).toFixed(2)}%`);
|
||||
|
||||
// Show first few trades
|
||||
console.log('\n First 3 trades:');
|
||||
result.trades.slice(0, 3).forEach((trade, i) => {
|
||||
console.log(` ${i + 1}. ${trade.side} ${trade.quantity} @ ${trade.exitPrice.toFixed(2)} | P&L: $${trade.pnl.toFixed(2)} (${trade.pnlPercent.toFixed(2)}%)`);
|
||||
});
|
||||
}
|
||||
|
||||
console.log('\n💰 Equity Curve:');
|
||||
console.log(` Starting Capital: $${config.initialCapital.toLocaleString()}`);
|
||||
console.log(` Final Value: $${result.equity[result.equity.length - 1].value.toFixed(2)}`);
|
||||
console.log(` Equity Points: ${result.equity.length}`);
|
||||
|
||||
// Show first and last few equity points
|
||||
console.log('\n First 3 equity points:');
|
||||
result.equity.slice(0, 3).forEach((point, i) => {
|
||||
console.log(` ${i + 1}. ${point.date} => $${point.value.toFixed(2)}`);
|
||||
});
|
||||
|
||||
console.log('\n Last 3 equity points:');
|
||||
result.equity.slice(-3).forEach((point, i) => {
|
||||
console.log(` ${i + 1}. ${point.date} => $${point.value.toFixed(2)}`);
|
||||
});
|
||||
|
||||
// Diagnostic checks
|
||||
console.log('\n🔍 Diagnostics:');
|
||||
const sharpeOK = result.metrics.sharpeRatio !== 0;
|
||||
const drawdownOK = result.metrics.maxDrawdown > 0 && result.metrics.maxDrawdown < 0.99;
|
||||
const winRateOK = result.metrics.winRate > 0 && result.metrics.winRate < 100;
|
||||
|
||||
console.log(` ✅ Sharpe Ratio calculated: ${sharpeOK ? 'YES' : 'NO'}`);
|
||||
console.log(` ✅ Max Drawdown reasonable: ${drawdownOK ? 'YES' : 'NO (suspicious value)'}`);
|
||||
console.log(` ✅ Win Rate reasonable: ${winRateOK ? 'YES' : 'NO (check trade P&L)'}`);
|
||||
|
||||
console.log('\n=== TEST COMPLETE ===');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
testPerformanceMetrics().catch(error => {
|
||||
console.error('Test failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,109 +0,0 @@
|
|||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
import { StrategyManager } from './src/strategies/StrategyManager';
|
||||
import { StorageService } from './src/services/StorageService';
|
||||
import { ModeManager } from './src/core/ModeManager';
|
||||
import { MarketDataService } from './src/services/MarketDataService';
|
||||
import { ExecutionService } from './src/services/ExecutionService';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
|
||||
async function testPortfolioTracking() {
|
||||
console.log('Testing Portfolio Value Tracking...\n');
|
||||
|
||||
// Create minimal service container
|
||||
const container: IServiceContainer = {
|
||||
logger: {
|
||||
info: (msg: string, ...args: any[]) => {
|
||||
// Filter for portfolio value messages
|
||||
if (msg.includes('portfolio') || msg.includes('Portfolio') || msg.includes('equity')) {
|
||||
console.log('[INFO]', msg, ...args);
|
||||
}
|
||||
},
|
||||
error: (msg: string, ...args: any[]) => console.error('[ERROR]', msg, ...args),
|
||||
warn: (msg: string, ...args: any[]) => console.warn('[WARN]', msg, ...args),
|
||||
debug: (msg: string, ...args: any[]) => {},
|
||||
} as any,
|
||||
custom: {}
|
||||
};
|
||||
|
||||
// Initialize services
|
||||
const storageService = new StorageService();
|
||||
const marketDataService = new MarketDataService(container);
|
||||
const executionService = new ExecutionService(container);
|
||||
const modeManager = new ModeManager(container, marketDataService, executionService, storageService);
|
||||
const strategyManager = new StrategyManager(container);
|
||||
|
||||
// Set services in container
|
||||
container.custom = {
|
||||
MarketDataService: marketDataService,
|
||||
ExecutionService: executionService,
|
||||
ModeManager: modeManager,
|
||||
StorageService: storageService
|
||||
};
|
||||
|
||||
// Initialize backtest mode
|
||||
await modeManager.initializeMode({
|
||||
mode: 'backtest',
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-01-10T00:00:00Z', // Just 10 days
|
||||
speed: 'max',
|
||||
symbols: ['TEST'],
|
||||
initialCapital: 100000,
|
||||
dataFrequency: '1d',
|
||||
strategy: 'sma-crossover'
|
||||
});
|
||||
|
||||
// Create backtest engine
|
||||
const backtestEngine = new BacktestEngine(container, storageService, strategyManager);
|
||||
|
||||
// Run backtest
|
||||
const config = {
|
||||
mode: 'backtest',
|
||||
name: 'Portfolio Tracking Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['TEST'],
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-01-10T00:00:00Z',
|
||||
initialCapital: 100000,
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
dataFrequency: '1d',
|
||||
speed: 'max'
|
||||
};
|
||||
|
||||
console.log('Running backtest...');
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
// Analyze equity curve
|
||||
console.log('\n=== EQUITY CURVE ANALYSIS ===');
|
||||
console.log(`Initial Capital: $${config.initialCapital.toLocaleString()}`);
|
||||
console.log(`Equity Points: ${result.equity.length}`);
|
||||
|
||||
// Show all equity points
|
||||
console.log('\nAll equity points:');
|
||||
result.equity.forEach((point, i) => {
|
||||
console.log(` ${i + 1}. ${point.date} => $${point.value.toFixed(2)}`);
|
||||
});
|
||||
|
||||
// Check for anomalies
|
||||
const firstValue = result.equity[0].value;
|
||||
const lastValue = result.equity[result.equity.length - 1].value;
|
||||
const totalReturn = ((lastValue - firstValue) / firstValue) * 100;
|
||||
|
||||
console.log(`\nFirst Value: $${firstValue.toFixed(2)}`);
|
||||
console.log(`Last Value: $${lastValue.toFixed(2)}`);
|
||||
console.log(`Calculated Total Return: ${totalReturn.toFixed(2)}%`);
|
||||
console.log(`Reported Total Return: ${result.metrics.totalReturn.toFixed(2)}%`);
|
||||
|
||||
// Check if values match
|
||||
if (Math.abs(totalReturn - result.metrics.totalReturn) > 0.01) {
|
||||
console.log('\n❌ WARNING: Calculated return does not match reported return!');
|
||||
}
|
||||
|
||||
console.log('\n=== TEST COMPLETE ===');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
testPortfolioTracking().catch(error => {
|
||||
console.error('Test failed:', error);
|
||||
process.exit(1);
|
||||
});
|
||||
|
|
@ -1,187 +0,0 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
/**
|
||||
* Test with predictable data to ensure trades are generated
|
||||
*/
|
||||
|
||||
import { TradingEngine } from '@stock-bot/core';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
|
||||
async function testPredictableBacktest() {
|
||||
console.log('=== Predictable Backtest Test ===\n');
|
||||
|
||||
const logger = getLogger('test');
|
||||
|
||||
// Create trading engine directly
|
||||
const tradingEngine = new TradingEngine('backtest', {
|
||||
startTime: new Date('2023-01-01').getTime(),
|
||||
endTime: new Date('2023-03-01').getTime(),
|
||||
speedMultiplier: 0
|
||||
});
|
||||
|
||||
// Set initial capital
|
||||
await tradingEngine.setCapital(100000);
|
||||
|
||||
// Generate predictable price data that will cause crossovers
|
||||
console.log('Generating predictable market data...');
|
||||
|
||||
// Phase 1: Downtrend (days 1-25) - prices fall from 100 to 75
|
||||
for (let i = 0; i < 25; i++) {
|
||||
const price = 100 - i;
|
||||
const timestamp = new Date('2023-01-01').getTime() + i * 86400000;
|
||||
|
||||
await tradingEngine.advanceTime(timestamp);
|
||||
await tradingEngine.updateQuote('AAPL', price - 0.01, price + 0.01, 10000, 10000);
|
||||
|
||||
if (i % 5 === 0) {
|
||||
console.log(`Day ${i + 1}: Price = $${price}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 2: Uptrend (days 26-50) - prices rise from 76 to 100
|
||||
for (let i = 25; i < 50; i++) {
|
||||
const price = 76 + (i - 25);
|
||||
const timestamp = new Date('2023-01-01').getTime() + i * 86400000;
|
||||
|
||||
await tradingEngine.advanceTime(timestamp);
|
||||
await tradingEngine.updateQuote('AAPL', price - 0.01, price + 0.01, 10000, 10000);
|
||||
|
||||
if (i % 5 === 0) {
|
||||
console.log(`Day ${i + 1}: Price = $${price}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 3: Another downtrend (days 51-60) - prices fall from 99 to 90
|
||||
for (let i = 50; i < 60; i++) {
|
||||
const price = 100 - (i - 50);
|
||||
const timestamp = new Date('2023-01-01').getTime() + i * 86400000;
|
||||
|
||||
await tradingEngine.advanceTime(timestamp);
|
||||
await tradingEngine.updateQuote('AAPL', price - 0.01, price + 0.01, 10000, 10000);
|
||||
|
||||
if (i % 5 === 0) {
|
||||
console.log(`Day ${i + 1}: Price = $${price}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Test moving averages manually
|
||||
console.log('\n=== Expected Crossovers ===');
|
||||
console.log('Around day 35-40: Golden cross (10 SMA crosses above 20 SMA)');
|
||||
console.log('Around day 55-60: Death cross (10 SMA crosses below 20 SMA)');
|
||||
|
||||
// Get results
|
||||
const closedTrades = tradingEngine.getClosedTrades ? JSON.parse(tradingEngine.getClosedTrades()) : [];
|
||||
const tradeCount = tradingEngine.getTradeCount ? tradingEngine.getTradeCount() : 0;
|
||||
const [realizedPnl, unrealizedPnl] = tradingEngine.getTotalPnl();
|
||||
|
||||
console.log('\n=== Results ===');
|
||||
console.log(`Total trades: ${tradeCount}`);
|
||||
console.log(`Closed trades: ${closedTrades.length}`);
|
||||
console.log(`Realized P&L: $${realizedPnl.toFixed(2)}`);
|
||||
console.log(`Unrealized P&L: $${unrealizedPnl.toFixed(2)}`);
|
||||
|
||||
// Now let's test the full backtest with this data pattern
|
||||
console.log('\n=== Running Full Backtest with SMA Strategy ===');
|
||||
|
||||
const { BacktestEngine } = await import('./src/backtest/BacktestEngine');
|
||||
const { StrategyManager } = await import('./src/strategies/StrategyManager');
|
||||
const { StorageService } = await import('./src/services/StorageService');
|
||||
const { ModeManager } = await import('./src/core/ModeManager');
|
||||
const { MarketDataService } = await import('./src/services/MarketDataService');
|
||||
const { ExecutionService } = await import('./src/services/ExecutionService');
|
||||
const { DataManager } = await import('./src/data/DataManager');
|
||||
|
||||
const container = {
|
||||
logger,
|
||||
custom: {}
|
||||
};
|
||||
|
||||
const storageService = new StorageService(container as any);
|
||||
const marketDataService = new MarketDataService(container as any);
|
||||
const executionService = new ExecutionService(container as any);
|
||||
const modeManager = new ModeManager(container as any, marketDataService, executionService, storageService);
|
||||
|
||||
container.custom = {
|
||||
ModeManager: modeManager,
|
||||
MarketDataService: marketDataService,
|
||||
ExecutionService: executionService
|
||||
};
|
||||
|
||||
const strategyManager = new StrategyManager(container as any);
|
||||
const backtestEngine = new BacktestEngine(container as any, storageService, strategyManager);
|
||||
|
||||
// Override the data manager to return our predictable data
|
||||
const dataManager = new DataManager(container as any, storageService);
|
||||
(backtestEngine as any).dataManager = dataManager;
|
||||
|
||||
// Mock the loadHistoricalData to return our pattern
|
||||
(dataManager as any).loadHistoricalData = async (symbols: string[], startDate: Date, endDate: Date) => {
|
||||
const data = new Map();
|
||||
const bars = [];
|
||||
|
||||
// Generate the same pattern as above
|
||||
for (let i = 0; i < 60; i++) {
|
||||
let price;
|
||||
if (i < 25) {
|
||||
price = 100 - i;
|
||||
} else if (i < 50) {
|
||||
price = 76 + (i - 25);
|
||||
} else {
|
||||
price = 100 - (i - 50);
|
||||
}
|
||||
|
||||
const timestamp = startDate.getTime() + i * 86400000;
|
||||
bars.push({
|
||||
type: 'bar',
|
||||
data: {
|
||||
symbol: 'AAPL',
|
||||
open: price - 0.5,
|
||||
high: price + 0.5,
|
||||
low: price - 0.5,
|
||||
close: price,
|
||||
volume: 1000000,
|
||||
timestamp
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
data.set('AAPL', bars);
|
||||
return data;
|
||||
};
|
||||
|
||||
const config = {
|
||||
mode: 'backtest' as const,
|
||||
name: 'Predictable Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'],
|
||||
startDate: '2023-01-01',
|
||||
endDate: '2023-03-01',
|
||||
initialCapital: 100000,
|
||||
dataFrequency: '1d',
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
speed: 'max' as const
|
||||
};
|
||||
|
||||
await modeManager.initializeMode(config);
|
||||
|
||||
try {
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
console.log('\nBacktest Results:');
|
||||
console.log(`Total Return: ${result.metrics.totalReturn.toFixed(2)}%`);
|
||||
console.log(`Total Trades: ${result.metrics.totalTrades}`);
|
||||
console.log(`Trades in history: ${result.trades.length}`);
|
||||
console.log(`Win Rate: ${result.metrics.winRate.toFixed(2)}%`);
|
||||
|
||||
console.log('\nTrade Details:');
|
||||
result.trades.forEach((trade, i) => {
|
||||
console.log(`Trade ${i + 1}: ${trade.side} ${trade.quantity} @ $${trade.entryPrice.toFixed(2)} -> $${trade.exitPrice?.toFixed(2) || 'OPEN'}`);
|
||||
});
|
||||
|
||||
} catch (error) {
|
||||
console.error('Backtest failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
testPredictableBacktest().catch(console.error);
|
||||
|
|
@ -1,54 +0,0 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
/**
|
||||
* Quick test of backtest with fixed order execution
|
||||
*/
|
||||
|
||||
import { createContainer } from './src/simple-container';
|
||||
import { BacktestEngine } from './src/backtest/BacktestEngine';
|
||||
|
||||
async function runQuickBacktest() {
|
||||
const container = await createContainer();
|
||||
|
||||
const backtestEngine = container.resolve('backtestEngine') as BacktestEngine;
|
||||
|
||||
const config = {
|
||||
name: 'Quick SMA Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'],
|
||||
startDate: '2020-01-01',
|
||||
endDate: '2021-01-01', // Just 1 year for quick test
|
||||
initialCapital: 100000,
|
||||
dataFrequency: '1d',
|
||||
commission: 0.001,
|
||||
slippage: 0.0001
|
||||
};
|
||||
|
||||
console.log('Running quick backtest...\n');
|
||||
|
||||
try {
|
||||
const result = await backtestEngine.runBacktest(config);
|
||||
|
||||
console.log('Backtest Results:');
|
||||
console.log(`Total Return: ${result.metrics.totalReturn.toFixed(2)}%`);
|
||||
console.log(`Total Trades: ${result.metrics.totalTrades}`);
|
||||
console.log(`Win Rate: ${result.metrics.winRate.toFixed(2)}%`);
|
||||
console.log(`Sharpe Ratio: ${result.metrics.sharpeRatio.toFixed(2)}`);
|
||||
console.log(`Max Drawdown: ${result.metrics.maxDrawdown.toFixed(2)}%`);
|
||||
console.log('\nTrade History:');
|
||||
console.log(`Trades in history: ${result.trades.length}`);
|
||||
|
||||
result.trades.slice(0, 5).forEach(trade => {
|
||||
console.log(`- ${trade.side} ${trade.quantity} @ $${trade.entryPrice} -> $${trade.exitPrice} (${trade.pnlPercent.toFixed(2)}%)`);
|
||||
});
|
||||
|
||||
if (result.trades.length > 5) {
|
||||
console.log(`... and ${result.trades.length - 5} more trades`);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Backtest failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
runQuickBacktest().catch(console.error);
|
||||
|
|
@ -1,145 +0,0 @@
|
|||
import { createRustBacktest } from './src/backtest/RustBacktestEngine';
|
||||
import { SimpleMovingAverageCrossoverRust } from './src/strategies/rust/SimpleMovingAverageCrossoverRust';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
|
||||
// Mock StorageService
|
||||
class MockStorageService {
|
||||
async getHistoricalBars(symbol: string, startDate: Date, endDate: Date, frequency: string) {
|
||||
// Generate mock data
|
||||
const bars = [];
|
||||
const msPerDay = 24 * 60 * 60 * 1000;
|
||||
let currentDate = new Date(startDate);
|
||||
let price = 100 + Math.random() * 50; // Start between 100-150
|
||||
|
||||
while (currentDate <= endDate) {
|
||||
// Random walk
|
||||
const change = (Math.random() - 0.5) * 2; // +/- 1%
|
||||
price *= (1 + change / 100);
|
||||
|
||||
bars.push({
|
||||
symbol,
|
||||
timestamp: new Date(currentDate),
|
||||
open: price * (1 + (Math.random() - 0.5) * 0.01),
|
||||
high: price * (1 + Math.random() * 0.02),
|
||||
low: price * (1 - Math.random() * 0.02),
|
||||
close: price,
|
||||
volume: 1000000 + Math.random() * 500000,
|
||||
});
|
||||
|
||||
currentDate = new Date(currentDate.getTime() + msPerDay);
|
||||
}
|
||||
|
||||
return bars;
|
||||
}
|
||||
}
|
||||
|
||||
async function testRustBacktest() {
|
||||
console.log('🚀 Testing Rust Backtest Engine with TypeScript Strategy\n');
|
||||
|
||||
// Create minimal container
|
||||
const container: IServiceContainer = {
|
||||
logger: {
|
||||
info: (msg: string, ...args: any[]) => console.log('[INFO]', msg, ...args),
|
||||
error: (msg: string, ...args: any[]) => console.error('[ERROR]', msg, ...args),
|
||||
warn: (msg: string, ...args: any[]) => console.warn('[WARN]', msg, ...args),
|
||||
debug: (msg: string, ...args: any[]) => console.log('[DEBUG]', msg, ...args),
|
||||
} as any,
|
||||
custom: {
|
||||
StorageService: new MockStorageService(),
|
||||
}
|
||||
};
|
||||
|
||||
// Backtest configuration
|
||||
const config = {
|
||||
mode: 'backtest' as const,
|
||||
name: 'Rust Engine Test',
|
||||
strategy: 'sma-crossover',
|
||||
symbols: ['AAPL'], // Just one symbol for testing
|
||||
startDate: '2023-01-01T00:00:00Z',
|
||||
endDate: '2023-01-31T00:00:00Z', // Just one month for testing
|
||||
initialCapital: 100000,
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
dataFrequency: '1d',
|
||||
speed: 'max' as const,
|
||||
};
|
||||
|
||||
// Create strategy
|
||||
const strategy = new SimpleMovingAverageCrossoverRust({
|
||||
fastPeriod: 10,
|
||||
slowPeriod: 30,
|
||||
minHoldingBars: 5,
|
||||
});
|
||||
|
||||
console.log('Configuration:');
|
||||
console.log(` Symbols: ${config.symbols.join(', ')}`);
|
||||
console.log(` Period: ${config.startDate} to ${config.endDate}`);
|
||||
console.log(` Initial Capital: $${config.initialCapital.toLocaleString()}`);
|
||||
console.log(` Strategy: ${strategy.constructor.name}`);
|
||||
console.log('');
|
||||
|
||||
try {
|
||||
console.log('Running backtest in Rust engine...\n');
|
||||
const startTime = Date.now();
|
||||
|
||||
try {
|
||||
const result = await createRustBacktest(container, config, [strategy]);
|
||||
console.log('Raw result:', result);
|
||||
|
||||
const duration = (Date.now() - startTime) / 1000;
|
||||
|
||||
console.log(`\n✅ Backtest completed in ${duration.toFixed(2)} seconds`);
|
||||
|
||||
if (!result || !result.metrics) {
|
||||
console.error('Invalid result structure:', result);
|
||||
return;
|
||||
}
|
||||
|
||||
console.log('\n=== PERFORMANCE METRICS ===');
|
||||
console.log(`Total Return: ${result.metrics.totalReturn?.toFixed(2) || 'N/A'}%`);
|
||||
console.log(`Sharpe Ratio: ${result.metrics.sharpeRatio?.toFixed(2) || 'N/A'}`);
|
||||
console.log(`Max Drawdown: ${result.metrics.maxDrawdown ? (result.metrics.maxDrawdown * 100).toFixed(2) : 'N/A'}%`);
|
||||
console.log(`Win Rate: ${result.metrics.winRate?.toFixed(1) || 'N/A'}%`);
|
||||
console.log(`Total Trades: ${result.metrics.totalTrades || 0}`);
|
||||
console.log(`Profit Factor: ${result.metrics.profitFactor?.toFixed(2) || 'N/A'}`);
|
||||
|
||||
console.log('\n=== TRADE STATISTICS ===');
|
||||
console.log(`Profitable Trades: ${result.metrics.profitableTrades || 0}`);
|
||||
console.log(`Average Win: $${result.metrics.avgWin?.toFixed(2) || '0.00'}`);
|
||||
console.log(`Average Loss: $${result.metrics.avgLoss?.toFixed(2) || '0.00'}`);
|
||||
console.log(`Total P&L: $${result.metrics.totalPnl?.toFixed(2) || '0.00'}`);
|
||||
|
||||
console.log('\n=== EQUITY CURVE ===');
|
||||
if (result.equityCurve.length > 0) {
|
||||
const firstValue = result.equityCurve[0].value;
|
||||
const lastValue = result.equityCurve[result.equityCurve.length - 1].value;
|
||||
console.log(`Starting Value: $${firstValue.toLocaleString()}`);
|
||||
console.log(`Ending Value: $${lastValue.toLocaleString()}`);
|
||||
console.log(`Growth: ${((lastValue / firstValue - 1) * 100).toFixed(2)}%`);
|
||||
}
|
||||
|
||||
console.log('\n=== FINAL POSITIONS ===');
|
||||
const positions = Object.entries(result.finalPositions);
|
||||
if (positions.length > 0) {
|
||||
for (const [symbol, position] of positions) {
|
||||
console.log(`${symbol}: ${position.quantity} shares @ $${position.averagePrice}`);
|
||||
}
|
||||
} else {
|
||||
console.log('No open positions');
|
||||
}
|
||||
|
||||
// Compare with TypeScript engine performance
|
||||
console.log('\n=== PERFORMANCE COMPARISON ===');
|
||||
console.log('TypeScript Engine: ~5-10 seconds for 1 year backtest');
|
||||
console.log(`Rust Engine: ${duration.toFixed(2)} seconds`);
|
||||
console.log(`Speed Improvement: ${(10 / duration).toFixed(1)}x faster`);
|
||||
} catch (innerError) {
|
||||
console.error('Result processing error:', innerError);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('❌ Backtest failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
// Run the test
|
||||
testRustBacktest().catch(console.error);
|
||||
|
|
@ -1,151 +0,0 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
/**
|
||||
* Test SMA strategy directly to debug trading
|
||||
*/
|
||||
|
||||
import { SimpleMovingAverageCrossover } from './src/strategies/examples/SimpleMovingAverageCrossover';
|
||||
import { getLogger } from '@stock-bot/logger';
|
||||
|
||||
async function testSMAStrategy() {
|
||||
console.log('=== Testing SMA Strategy Trading ===\n');
|
||||
|
||||
const logger = getLogger('test');
|
||||
|
||||
const config = {
|
||||
id: 'test-sma',
|
||||
name: 'Test SMA',
|
||||
enabled: true,
|
||||
symbols: ['AAPL'],
|
||||
allocation: 1.0
|
||||
};
|
||||
|
||||
const strategy = new SimpleMovingAverageCrossover(config, null, null);
|
||||
|
||||
let signalCount = 0;
|
||||
let orderCount = 0;
|
||||
const orders: any[] = [];
|
||||
|
||||
strategy.on('signal', (signal) => {
|
||||
signalCount++;
|
||||
console.log(`\n📊 Signal #${signalCount}:`, {
|
||||
type: signal.type,
|
||||
symbol: signal.symbol,
|
||||
strength: signal.strength,
|
||||
reason: signal.reason
|
||||
});
|
||||
});
|
||||
|
||||
strategy.on('order', (order) => {
|
||||
orderCount++;
|
||||
orders.push(order);
|
||||
console.log(`\n📈 Order #${orderCount}:`, order);
|
||||
});
|
||||
|
||||
await strategy.start();
|
||||
|
||||
// Generate clear pattern: downtrend then uptrend
|
||||
console.log('Generating market data with clear trend changes...\n');
|
||||
|
||||
// Phase 1: Stable around 100 for first 10 days
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const price = 100 + Math.sin(i * 0.5) * 2;
|
||||
await strategy.onMarketData({
|
||||
type: 'bar',
|
||||
data: {
|
||||
symbol: 'AAPL',
|
||||
open: price,
|
||||
high: price + 1,
|
||||
low: price - 1,
|
||||
close: price,
|
||||
volume: 1000000,
|
||||
timestamp: Date.now() + i * 86400000
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Phase 2: Clear downtrend from 100 to 80 (days 11-30)
|
||||
for (let i = 10; i < 30; i++) {
|
||||
const price = 100 - (i - 10); // Falls by $1 per day
|
||||
await strategy.onMarketData({
|
||||
type: 'bar',
|
||||
data: {
|
||||
symbol: 'AAPL',
|
||||
open: price,
|
||||
high: price + 0.5,
|
||||
low: price - 0.5,
|
||||
close: price,
|
||||
volume: 1000000,
|
||||
timestamp: Date.now() + i * 86400000
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Phase 3: Clear uptrend from 80 to 110 (days 31-60)
|
||||
for (let i = 30; i < 60; i++) {
|
||||
const price = 80 + (i - 30); // Rises by $1 per day
|
||||
await strategy.onMarketData({
|
||||
type: 'bar',
|
||||
data: {
|
||||
symbol: 'AAPL',
|
||||
open: price,
|
||||
high: price + 0.5,
|
||||
low: price - 0.5,
|
||||
close: price,
|
||||
volume: 1000000,
|
||||
timestamp: Date.now() + i * 86400000
|
||||
}
|
||||
});
|
||||
|
||||
// Simulate position update after first buy
|
||||
if (i === 45 && orders.length > 0) {
|
||||
console.log('\n🔄 Simulating position update after buy order...');
|
||||
await strategy.onOrderUpdate({
|
||||
orderId: 'test-order-1',
|
||||
symbol: 'AAPL',
|
||||
side: 'buy',
|
||||
status: 'filled',
|
||||
fills: [{
|
||||
quantity: orders[0].quantity,
|
||||
price: 95
|
||||
}]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Phase 4: Another downtrend from 110 to 90 (days 61-80)
|
||||
for (let i = 60; i < 80; i++) {
|
||||
const price = 110 - (i - 60); // Falls by $1 per day
|
||||
|
||||
if (i % 5 === 0) {
|
||||
console.log(`\nDay ${i + 1}: Price = $${price}`);
|
||||
}
|
||||
|
||||
await strategy.onMarketData({
|
||||
type: 'bar',
|
||||
data: {
|
||||
symbol: 'AAPL',
|
||||
open: price,
|
||||
high: price + 0.5,
|
||||
low: price - 0.5,
|
||||
close: price,
|
||||
volume: 1000000,
|
||||
timestamp: Date.now() + i * 86400000
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
await strategy.stop();
|
||||
|
||||
console.log('\n=== Test Results ===');
|
||||
console.log(`Total signals generated: ${signalCount}`);
|
||||
console.log(`Total orders generated: ${orderCount}`);
|
||||
console.log(`\nExpected behavior:`);
|
||||
console.log(`- Golden cross around day 40-45 (when 10 SMA crosses above 20 SMA)`);
|
||||
console.log(`- Death cross around day 70-75 (when 10 SMA crosses below 20 SMA)`);
|
||||
|
||||
const perf = strategy.getPerformance();
|
||||
console.log('\nStrategy performance:', perf);
|
||||
}
|
||||
|
||||
testSMAStrategy().catch(console.error);
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
/**
|
||||
* Test strategy signal generation
|
||||
*/
|
||||
|
||||
import { SimpleMovingAverageCrossover } from './src/strategies/examples/SimpleMovingAverageCrossover';
|
||||
import { MarketData } from './src/types';
|
||||
|
||||
async function testStrategySignals() {
|
||||
console.log('Testing strategy signal generation...\n');
|
||||
|
||||
// Create strategy with mock config
|
||||
const config = {
|
||||
id: 'test-sma',
|
||||
name: 'Test SMA Strategy',
|
||||
enabled: true,
|
||||
symbols: ['AAPL'],
|
||||
allocation: 1.0
|
||||
};
|
||||
|
||||
const strategy = new SimpleMovingAverageCrossover(config, null, null);
|
||||
await strategy.start();
|
||||
|
||||
// Generate 100 days of mock data with a clear trend
|
||||
const basePrice = 100;
|
||||
let price = basePrice;
|
||||
let signalCount = 0;
|
||||
|
||||
console.log('Feeding market data to strategy...\n');
|
||||
|
||||
for (let day = 0; day < 100; day++) {
|
||||
// Create uptrend for days 30-50, downtrend for days 60-80
|
||||
if (day >= 30 && day < 50) {
|
||||
price += 0.5; // Uptrend
|
||||
} else if (day >= 60 && day < 80) {
|
||||
price -= 0.5; // Downtrend
|
||||
} else {
|
||||
price += (Math.random() - 0.5) * 0.2; // Small random movement
|
||||
}
|
||||
|
||||
const marketData: MarketData = {
|
||||
type: 'bar',
|
||||
data: {
|
||||
symbol: 'AAPL',
|
||||
open: price - 0.1,
|
||||
high: price + 0.2,
|
||||
low: price - 0.2,
|
||||
close: price,
|
||||
volume: 1000000,
|
||||
timestamp: Date.now() + day * 86400000
|
||||
}
|
||||
};
|
||||
|
||||
// Listen for signals
|
||||
strategy.once('signal', (signal) => {
|
||||
signalCount++;
|
||||
console.log(`Day ${day}: Signal generated!`);
|
||||
console.log(` Type: ${signal.type}`);
|
||||
console.log(` Strength: ${signal.strength}`);
|
||||
console.log(` Reason: ${signal.reason}`);
|
||||
console.log(` Metadata:`, signal.metadata);
|
||||
});
|
||||
|
||||
// Listen for orders
|
||||
strategy.once('order', (order) => {
|
||||
console.log(`Day ${day}: Order generated!`);
|
||||
console.log(` Side: ${order.side}`);
|
||||
console.log(` Quantity: ${order.quantity}`);
|
||||
console.log(` Type: ${order.orderType}`);
|
||||
});
|
||||
|
||||
// Process the market data
|
||||
await strategy.onMarketData(marketData);
|
||||
|
||||
if (day % 10 === 0) {
|
||||
console.log(`Day ${day}: Price = ${price.toFixed(2)}, Total signals = ${signalCount}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`\n✅ Test completed. Total signals generated: ${signalCount}`);
|
||||
|
||||
const perf = strategy.getPerformance();
|
||||
console.log('\nStrategy Performance:', perf);
|
||||
|
||||
await strategy.stop();
|
||||
}
|
||||
|
||||
testStrategySignals().catch(console.error);
|
||||
|
|
@ -1,101 +0,0 @@
|
|||
import { RustBacktestAdapter } from './src/backtest/RustBacktestAdapter';
|
||||
import { IServiceContainer } from '@stock-bot/di';
|
||||
import { BacktestConfig } from './src/types';
|
||||
|
||||
// Mock container
|
||||
const mockContainer: IServiceContainer = {
|
||||
logger: {
|
||||
info: console.log,
|
||||
error: console.error,
|
||||
warn: console.warn,
|
||||
debug: console.log,
|
||||
},
|
||||
mongodb: {} as any,
|
||||
postgres: {} as any,
|
||||
redis: {} as any,
|
||||
custom: {},
|
||||
} as IServiceContainer;
|
||||
|
||||
// Mock storage service
|
||||
class MockStorageService {
|
||||
async getHistoricalBars(symbol: string, startDate: Date, endDate: Date, frequency: string) {
|
||||
const bars = [];
|
||||
const startTime = startDate.getTime();
|
||||
const endTime = endDate.getTime();
|
||||
const dayMs = 24 * 60 * 60 * 1000;
|
||||
|
||||
let time = startTime;
|
||||
let dayIndex = 0;
|
||||
|
||||
// Simple oscillating price for testing
|
||||
while (time <= endTime) {
|
||||
const price = 100 + 10 * Math.sin(dayIndex * 0.2);
|
||||
|
||||
bars.push({
|
||||
timestamp: new Date(time),
|
||||
open: price * 0.99,
|
||||
high: price * 1.01,
|
||||
low: price * 0.98,
|
||||
close: price,
|
||||
volume: 1000000,
|
||||
vwap: price,
|
||||
});
|
||||
|
||||
time += dayMs;
|
||||
dayIndex++;
|
||||
}
|
||||
|
||||
return bars;
|
||||
}
|
||||
}
|
||||
|
||||
// Test the backtest
|
||||
async function testTradeFormat() {
|
||||
console.log('=== Testing Trade Format ===\n');
|
||||
|
||||
const adapter = new RustBacktestAdapter(mockContainer);
|
||||
(adapter as any).storageService = new MockStorageService();
|
||||
|
||||
const config: BacktestConfig = {
|
||||
name: 'Trade Format Test',
|
||||
strategy: 'Simple Moving Average Crossover',
|
||||
symbols: ['TEST'],
|
||||
startDate: '2024-01-01T00:00:00Z',
|
||||
endDate: '2024-03-01T00:00:00Z',
|
||||
initialCapital: 100000,
|
||||
commission: 0.001,
|
||||
slippage: 0.0001,
|
||||
dataFrequency: '1d',
|
||||
config: {
|
||||
fastPeriod: 5,
|
||||
slowPeriod: 15,
|
||||
},
|
||||
};
|
||||
|
||||
try {
|
||||
const result = await adapter.runBacktest(config);
|
||||
|
||||
console.log('\n=== Trade Format ===');
|
||||
console.log('Number of trades:', result.trades.length);
|
||||
console.log('\nFirst 3 trades:');
|
||||
result.trades.slice(0, 3).forEach((trade, idx) => {
|
||||
console.log(`\nTrade ${idx + 1}:`, JSON.stringify(trade, null, 2));
|
||||
});
|
||||
|
||||
// Check what format the trades are in
|
||||
if (result.trades.length > 0) {
|
||||
const firstTrade = result.trades[0];
|
||||
console.log('\n=== Trade Structure Analysis ===');
|
||||
console.log('Keys:', Object.keys(firstTrade));
|
||||
console.log('Has entryDate/exitDate?', 'entryDate' in firstTrade && 'exitDate' in firstTrade);
|
||||
console.log('Has timestamp?', 'timestamp' in firstTrade);
|
||||
console.log('Has side field?', 'side' in firstTrade);
|
||||
console.log('Side value:', firstTrade.side);
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('Test failed:', error);
|
||||
}
|
||||
}
|
||||
|
||||
testTradeFormat().catch(console.error);
|
||||
|
|
@ -1,107 +0,0 @@
|
|||
#!/usr/bin/env bun
|
||||
|
||||
/**
|
||||
* Test the new trade history functionality
|
||||
*/
|
||||
|
||||
import { TradingEngine } from '@stock-bot/core';
|
||||
|
||||
async function testTradeHistory() {
|
||||
console.log('Testing trade history functionality...\n');
|
||||
|
||||
// Create a trading engine in backtest mode
|
||||
const config = {
|
||||
startTime: Date.now() - 86400000, // 24 hours ago
|
||||
endTime: Date.now(),
|
||||
speedMultiplier: 1.0
|
||||
};
|
||||
|
||||
const engine = new TradingEngine('backtest', config as any);
|
||||
|
||||
console.log('Trading engine created in', engine.getMode(), 'mode');
|
||||
console.log('Initial trade count:', engine.getTradeCount());
|
||||
console.log('Initial closed trades:', engine.getClosedTradeCount());
|
||||
|
||||
// Simulate some trades
|
||||
console.log('\n--- Simulating trades ---');
|
||||
|
||||
// Buy 100 shares at $50
|
||||
console.log('Processing BUY: 100 shares @ $50');
|
||||
engine.processFillWithMetadata(
|
||||
'AAPL',
|
||||
50.0,
|
||||
100,
|
||||
'buy',
|
||||
1.0,
|
||||
'ORDER001',
|
||||
'STRATEGY001'
|
||||
);
|
||||
|
||||
console.log('Trade count after buy:', engine.getTradeCount());
|
||||
console.log('Closed trades after buy:', engine.getClosedTradeCount());
|
||||
|
||||
// Sell 50 shares at $55
|
||||
console.log('\nProcessing SELL: 50 shares @ $55');
|
||||
engine.processFillWithMetadata(
|
||||
'AAPL',
|
||||
55.0,
|
||||
50,
|
||||
'sell',
|
||||
1.0,
|
||||
'ORDER002',
|
||||
'STRATEGY001'
|
||||
);
|
||||
|
||||
console.log('Trade count after partial sell:', engine.getTradeCount());
|
||||
console.log('Closed trades after partial sell:', engine.getClosedTradeCount());
|
||||
|
||||
// Get closed trades
|
||||
const closedTradesJson = engine.getClosedTrades();
|
||||
const closedTrades = JSON.parse(closedTradesJson);
|
||||
|
||||
console.log('\n--- Closed Trades ---');
|
||||
closedTrades.forEach((trade: any) => {
|
||||
console.log(`Trade ${trade.id}:`);
|
||||
console.log(` Symbol: ${trade.symbol}`);
|
||||
console.log(` Entry: ${trade.entry_price} @ ${new Date(trade.entry_time).toISOString()}`);
|
||||
console.log(` Exit: ${trade.exit_price} @ ${new Date(trade.exit_time).toISOString()}`);
|
||||
console.log(` Quantity: ${trade.quantity}`);
|
||||
console.log(` P&L: $${trade.pnl.toFixed(2)} (${trade.pnl_percent.toFixed(2)}%)`);
|
||||
console.log(` Duration: ${trade.duration_ms}ms`);
|
||||
});
|
||||
|
||||
// Sell remaining 50 shares at $52
|
||||
console.log('\nProcessing SELL: 50 shares @ $52');
|
||||
engine.processFillWithMetadata(
|
||||
'AAPL',
|
||||
52.0,
|
||||
50,
|
||||
'sell',
|
||||
1.0,
|
||||
'ORDER003',
|
||||
'STRATEGY001'
|
||||
);
|
||||
|
||||
console.log('Trade count after full close:', engine.getTradeCount());
|
||||
console.log('Closed trades after full close:', engine.getClosedTradeCount());
|
||||
|
||||
// Get all trade history
|
||||
const allTradesJson = engine.getTradeHistory();
|
||||
const allTrades = JSON.parse(allTradesJson);
|
||||
|
||||
console.log('\n--- All Trade History ---');
|
||||
console.log(`Total trades executed: ${allTrades.length}`);
|
||||
allTrades.forEach((trade: any) => {
|
||||
console.log(`${trade.id}: ${trade.side} ${trade.quantity} ${trade.symbol} @ ${trade.price}`);
|
||||
});
|
||||
|
||||
// Get final P&L
|
||||
const [realizedPnl, unrealizedPnl] = engine.getTotalPnl();
|
||||
console.log('\n--- Final P&L ---');
|
||||
console.log(`Realized P&L: $${realizedPnl.toFixed(2)}`);
|
||||
console.log(`Unrealized P&L: $${unrealizedPnl.toFixed(2)}`);
|
||||
|
||||
console.log('\n✅ Trade history test completed successfully!');
|
||||
}
|
||||
|
||||
testTradeHistory().catch(console.error);
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
import { TechnicalIndicators, IncrementalSMA, IncrementalEMA, IncrementalRSI } from '@stock-bot/core';
|
||||
import { TechnicalAnalysis, IncrementalIndicators, SignalGenerator } from '../src/indicators/TechnicalAnalysis';
|
||||
import { IncrementalSMA, TechnicalIndicators } from '@stock-bot/engine';
|
||||
import { IncrementalIndicators, SignalGenerator, TechnicalAnalysis } from '../src/indicators/TechnicalAnalysis';
|
||||
|
||||
describe('Technical Analysis Library', () => {
|
||||
let ta: TechnicalAnalysis;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue