cleanup and fixed initial capital / commision / splippage

This commit is contained in:
Boki 2025-07-03 19:39:19 -04:00
parent 70da2c68e5
commit d14380d740
19 changed files with 1344 additions and 33 deletions

Binary file not shown.

View file

@ -0,0 +1,149 @@
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);
});

View file

@ -101,7 +101,7 @@ export class PerformanceAnalyzer {
addEquityPoint(date: Date, value: number): void { addEquityPoint(date: Date, value: number): void {
this.equityCurve.push({ date, value }); this.equityCurve.push({ date, value });
this.calculateDailyReturns(); // Don't calculate daily returns on every point - wait until analyze() is called
} }
addTrade(trade: Trade): void { addTrade(trade: Trade): void {
@ -117,6 +117,9 @@ export class PerformanceAnalyzer {
return this.getEmptyMetrics(); return this.getEmptyMetrics();
} }
// Calculate daily returns before analysis
this.calculateDailyReturns();
// Calculate returns // Calculate returns
const totalReturn = this.calculateTotalReturn(); const totalReturn = this.calculateTotalReturn();
const annualizedReturn = this.calculateAnnualizedReturn(); const annualizedReturn = this.calculateAnnualizedReturn();
@ -131,7 +134,7 @@ export class PerformanceAnalyzer {
// Risk-adjusted returns // Risk-adjusted returns
const sharpeRatio = this.calculateSharpeRatio(annualizedReturn, volatility); const sharpeRatio = this.calculateSharpeRatio(annualizedReturn, volatility);
const sortinoRatio = this.calculateSortinoRatio(annualizedReturn, downVolatility); const sortinoRatio = this.calculateSortinoRatio(annualizedReturn, downVolatility);
const calmarRatio = annualizedReturn / Math.abs(drawdownAnalysis.maxDrawdown); const calmarRatio = drawdownAnalysis.maxDrawdown > 0 ? annualizedReturn / (drawdownAnalysis.maxDrawdown * 100) : 0;
const informationRatio = this.calculateInformationRatio(); const informationRatio = this.calculateInformationRatio();
// Trade statistics // Trade statistics
@ -148,8 +151,8 @@ export class PerformanceAnalyzer {
totalReturn, totalReturn,
annualizedReturn, annualizedReturn,
cagr, cagr,
volatility, volatility: volatility * 100, // Convert to percentage for display
downVolatility, downVolatility: downVolatility * 100, // Convert to percentage for display
maxDrawdown: drawdownAnalysis.maxDrawdown, maxDrawdown: drawdownAnalysis.maxDrawdown,
maxDrawdownDuration: drawdownAnalysis.maxDrawdownDuration, maxDrawdownDuration: drawdownAnalysis.maxDrawdownDuration,
var95, var95,
@ -289,10 +292,26 @@ export class PerformanceAnalyzer {
private calculateDailyReturns(): void { private calculateDailyReturns(): void {
this.dailyReturns = []; this.dailyReturns = [];
for (let i = 1; i < this.equityCurve.length; i++) {
const prevValue = this.equityCurve[i - 1].value; // First, aggregate equity curve by day (taking last value of each day)
const currValue = this.equityCurve[i].value; const dailyEquity = new Map<string, { date: Date; value: number }>();
this.dailyReturns.push((currValue - prevValue) / prevValue);
for (const point of this.equityCurve) {
const dateKey = point.date.toISOString().split('T')[0];
dailyEquity.set(dateKey, point); // This will keep the last value of each day
}
// Convert to sorted array
const dailyPoints = Array.from(dailyEquity.values())
.sort((a, b) => a.date.getTime() - b.date.getTime());
// Calculate returns from daily points
for (let i = 1; i < dailyPoints.length; i++) {
const prevValue = dailyPoints[i - 1].value;
const currValue = dailyPoints[i].value;
if (prevValue > 0) {
this.dailyReturns.push((currValue - prevValue) / prevValue);
}
} }
} }
@ -315,13 +334,15 @@ export class PerformanceAnalyzer {
private calculateVolatility(): number { private calculateVolatility(): number {
if (this.dailyReturns.length === 0) return 0; if (this.dailyReturns.length === 0) return 0;
return stats.standardDeviation(this.dailyReturns) * Math.sqrt(252) * 100; // Return volatility as a decimal percentage (not multiplied by 100)
return stats.standardDeviation(this.dailyReturns) * Math.sqrt(252);
} }
private calculateDownsideVolatility(): number { private calculateDownsideVolatility(): number {
const negativeReturns = this.dailyReturns.filter(r => r < 0); const negativeReturns = this.dailyReturns.filter(r => r < 0);
if (negativeReturns.length === 0) return 0; if (negativeReturns.length === 0) return 0;
return stats.standardDeviation(negativeReturns) * Math.sqrt(252) * 100; // Return volatility as a decimal percentage (not multiplied by 100)
return stats.standardDeviation(negativeReturns) * Math.sqrt(252);
} }
private calculateVaR(): { var95: number; cvar95: number } { private calculateVaR(): { var95: number; cvar95: number } {
@ -330,20 +351,30 @@ export class PerformanceAnalyzer {
const sortedReturns = [...this.dailyReturns].sort((a, b) => a - b); const sortedReturns = [...this.dailyReturns].sort((a, b) => a - b);
const index95 = Math.floor(sortedReturns.length * 0.05); const index95 = Math.floor(sortedReturns.length * 0.05);
// Handle edge case where we don't have enough data points
if (index95 === 0 || sortedReturns.length < 20) {
return { var95: 0, cvar95: 0 };
}
const var95 = Math.abs(sortedReturns[index95]) * 100; const var95 = Math.abs(sortedReturns[index95]) * 100;
const cvar95 = Math.abs(stats.mean(sortedReturns.slice(0, index95))) * 100; const tailReturns = sortedReturns.slice(0, index95);
const cvar95 = tailReturns.length > 0 ? Math.abs(stats.mean(tailReturns)) * 100 : 0;
return { var95, cvar95 }; return { var95, cvar95 };
} }
private calculateSharpeRatio(annualReturn: number, volatility: number, riskFree: number = 2): number { private calculateSharpeRatio(annualReturn: number, volatility: number, riskFree: number = 2): number {
if (volatility === 0) return 0; if (volatility === 0) return 0;
return (annualReturn - riskFree) / volatility; // Both annualReturn and volatility should be in percentage form
const volatilityPct = volatility * 100;
return (annualReturn - riskFree) / volatilityPct;
} }
private calculateSortinoRatio(annualReturn: number, downVolatility: number, riskFree: number = 2): number { private calculateSortinoRatio(annualReturn: number, downVolatility: number, riskFree: number = 2): number {
if (downVolatility === 0) return 0; if (downVolatility === 0) return 0;
return (annualReturn - riskFree) / downVolatility; // Both annualReturn and downVolatility should be in percentage form
const downVolatilityPct = downVolatility * 100;
return (annualReturn - riskFree) / downVolatilityPct;
} }
private calculateInformationRatio(): number { private calculateInformationRatio(): number {
@ -380,8 +411,13 @@ export class PerformanceAnalyzer {
}; };
} }
const wins = this.trades.filter(t => t.pnl > 0); // Filter out trades with 0 or undefined P&L
const losses = this.trades.filter(t => t.pnl < 0); const validTrades = this.trades.filter(t => t.pnl !== undefined && t.pnl !== null);
const wins = validTrades.filter(t => t.pnl > 0);
const losses = validTrades.filter(t => t.pnl < 0);
// Log trade analysis for debugging
logger.debug(`Trade Analysis: Total=${this.trades.length}, Valid=${validTrades.length}, Wins=${wins.length}, Losses=${losses.length}`);
const totalWins = wins.reduce((sum, t) => sum + t.pnl, 0); const totalWins = wins.reduce((sum, t) => sum + t.pnl, 0);
const totalLosses = Math.abs(losses.reduce((sum, t) => sum + t.pnl, 0)); const totalLosses = Math.abs(losses.reduce((sum, t) => sum + t.pnl, 0));
@ -389,7 +425,7 @@ export class PerformanceAnalyzer {
const avgWin = wins.length > 0 ? totalWins / wins.length : 0; const avgWin = wins.length > 0 ? totalWins / wins.length : 0;
const avgLoss = losses.length > 0 ? totalLosses / losses.length : 0; const avgLoss = losses.length > 0 ? totalLosses / losses.length : 0;
const winRate = (wins.length / this.trades.length) * 100; const winRate = validTrades.length > 0 ? (wins.length / validTrades.length) * 100 : 0;
const profitFactor = totalLosses > 0 ? totalWins / totalLosses : totalWins > 0 ? Infinity : 0; const profitFactor = totalLosses > 0 ? totalWins / totalLosses : totalWins > 0 ? Infinity : 0;
const expectancy = (winRate / 100 * avgWin) - ((100 - winRate) / 100 * avgLoss); const expectancy = (winRate / 100 * avgWin) - ((100 - winRate) / 100 * avgLoss);
const payoffRatio = avgLoss > 0 ? avgWin / avgLoss : 0; const payoffRatio = avgLoss > 0 ? avgWin / avgLoss : 0;

View file

@ -99,6 +99,8 @@ export class BacktestEngine extends EventEmitter {
private microstructures: Map<string, MarketMicrostructure> = new Map(); private microstructures: Map<string, MarketMicrostructure> = new Map();
private container: IServiceContainer; private container: IServiceContainer;
private initialCapital: number = 100000; private initialCapital: number = 100000;
private commission: number = 0.001; // Default 0.1%
private slippage: number = 0.0001; // Default 0.01%
private pendingOrders: Map<string, any> = new Map(); private pendingOrders: Map<string, any> = new Map();
private ordersListenerSetup = false; private ordersListenerSetup = false;
@ -116,7 +118,8 @@ export class BacktestEngine extends EventEmitter {
includeDarkPools: true, includeDarkPools: true,
latencyMs: 1 latencyMs: 1
}); });
this.performanceAnalyzer = new PerformanceAnalyzer(); // Don't create performance analyzer here - wait for runBacktest
// this.performanceAnalyzer = new PerformanceAnalyzer(this.initialCapital);
// Set up order listener immediately // Set up order listener immediately
this.setupOrderListener(); this.setupOrderListener();
@ -132,6 +135,11 @@ export class BacktestEngine extends EventEmitter {
this.reset(); this.reset();
this.isRunning = true; this.isRunning = true;
this.initialCapital = validatedConfig.initialCapital; this.initialCapital = validatedConfig.initialCapital;
this.commission = validatedConfig.commission || 0.001;
this.slippage = validatedConfig.slippage || 0.0001;
// Recreate performance analyzer with correct initial capital
this.performanceAnalyzer = new PerformanceAnalyzer(this.initialCapital);
// Initialize equity curve with starting capital // Initialize equity curve with starting capital
this.equityCurve.push({ this.equityCurve.push({
@ -145,6 +153,8 @@ export class BacktestEngine extends EventEmitter {
this.initialCapital this.initialCapital
); );
this.container.logger.info(`[BacktestEngine] Initial capital set to ${this.initialCapital}, equity curve initialized with ${this.equityCurve.length} points`);
// Generate backtest ID // Generate backtest ID
const backtestId = `backtest_${Date.now()}`; const backtestId = `backtest_${Date.now()}`;
@ -668,12 +678,16 @@ export class BacktestEngine extends EventEmitter {
private async updateEquityCurve(): Promise<void> { private async updateEquityCurve(): Promise<void> {
const totalEquity = await this.getPortfolioValue(); const totalEquity = await this.getPortfolioValue();
this.container.logger.debug(`[updateEquityCurve] currentTime=${this.currentTime}, totalEquity=${totalEquity}, initialCapital=${this.initialCapital}`);
// Don't add duplicate points at the same timestamp // Don't add duplicate points at the same timestamp
const lastPoint = this.equityCurve[this.equityCurve.length - 1]; const lastPoint = this.equityCurve[this.equityCurve.length - 1];
if (lastPoint && lastPoint.timestamp === this.currentTime) { if (lastPoint && lastPoint.timestamp === this.currentTime) {
// Update the value instead of adding a duplicate // Update the value instead of adding a duplicate
this.container.logger.debug(`[updateEquityCurve] Updating existing point at ${this.currentTime}: ${lastPoint.value} -> ${totalEquity}`);
lastPoint.value = totalEquity; lastPoint.value = totalEquity;
} else { } else {
this.container.logger.debug(`[updateEquityCurve] Adding new point at ${this.currentTime}: ${totalEquity}`);
this.equityCurve.push({ this.equityCurve.push({
timestamp: this.currentTime, timestamp: this.currentTime,
value: totalEquity value: totalEquity
@ -684,6 +698,7 @@ export class BacktestEngine extends EventEmitter {
private async getPortfolioValue(): Promise<number> { private async getPortfolioValue(): Promise<number> {
const tradingEngine = this.strategyManager.getTradingEngine(); const tradingEngine = this.strategyManager.getTradingEngine();
if (!tradingEngine) { if (!tradingEngine) {
this.container.logger.debug(`[getPortfolioValue] No trading engine, returning initialCapital: ${this.initialCapital}`);
return this.initialCapital; return this.initialCapital;
} }
@ -692,9 +707,12 @@ export class BacktestEngine extends EventEmitter {
const [realized, unrealized] = tradingEngine.getTotalPnl(); const [realized, unrealized] = tradingEngine.getTotalPnl();
const totalValue = this.initialCapital + realized + unrealized; const totalValue = this.initialCapital + realized + unrealized;
this.container.logger.debug(`[getPortfolioValue] P&L: realized=${realized}, unrealized=${unrealized}, initialCapital=${this.initialCapital}, totalValue=${totalValue}`);
// Ensure we never return 0 or negative values that would cause spikes // Ensure we never return 0 or negative values that would cause spikes
// This handles the case where getTotalPnl might not be initialized yet // This handles the case where getTotalPnl might not be initialized yet
if (totalValue <= 0 || isNaN(totalValue)) { if (totalValue <= 0 || isNaN(totalValue)) {
this.container.logger.debug(`[getPortfolioValue] Invalid totalValue, returning initialCapital: ${this.initialCapital}`);
return this.initialCapital; return this.initialCapital;
} }
@ -941,13 +959,6 @@ export class BacktestEngine extends EventEmitter {
return profile.map(v => v / sum); return profile.map(v => v / sum);
} }
private getPortfolioValue(): number {
const tradingEngine = this.strategyManager.getTradingEngine();
if (!tradingEngine) {return 100000;} // Default initial capital
const [realized, unrealized] = tradingEngine.getTotalPnl();
return 100000 + realized + unrealized;
}
async stopBacktest(): Promise<void> { async stopBacktest(): Promise<void> {
this.isRunning = false; this.isRunning = false;
@ -1160,8 +1171,8 @@ export class BacktestEngine extends EventEmitter {
if (!orderEvent) continue; if (!orderEvent) continue;
const fillPrice = orderEvent.order.side === 'buy' ? const fillPrice = orderEvent.order.side === 'buy' ?
currentPrice * (1 + (this.marketSimulator.config.slippage || 0.0001)) : currentPrice * (1 + this.slippage) :
currentPrice * (1 - (this.marketSimulator.config.slippage || 0.0001)); currentPrice * (1 - this.slippage);
const fill = { const fill = {
orderId, orderId,
@ -1170,7 +1181,7 @@ export class BacktestEngine extends EventEmitter {
quantity: orderEvent.order.quantity, quantity: orderEvent.order.quantity,
price: fillPrice, price: fillPrice,
timestamp: this.currentTime, timestamp: this.currentTime,
commission: orderEvent.order.quantity * fillPrice * 0.001, // 0.1% commission commission: orderEvent.order.quantity * fillPrice * this.commission,
strategyId: orderEvent.strategyId strategyId: orderEvent.strategyId
}; };

View file

@ -64,7 +64,8 @@ export class ModeManager extends EventEmitter {
return { return {
startTime: new Date(config.startDate).getTime(), startTime: new Date(config.startDate).getTime(),
endTime: new Date(config.endDate).getTime(), endTime: new Date(config.endDate).getTime(),
speedMultiplier: this.getSpeedMultiplier(config.speed) speedMultiplier: this.getSpeedMultiplier(config.speed),
initialCapital: config.initialCapital
}; };
case 'paper': case 'paper':
return { return {

View file

@ -202,8 +202,9 @@ export abstract class BaseStrategy extends EventEmitter {
} }
protected getEquity(): number { protected getEquity(): number {
// Simplified - in reality would calculate based on positions and market values // Get initial capital from config parameters or default
return 100000 + this.performance.totalPnl; // Assuming 100k starting capital const initialCapital = this.config.parameters?.initialCapital || 100000;
return initialCapital + this.performance.totalPnl;
} }
protected async signalToOrder(signal: Signal): Promise<OrderRequest | null> { protected async signalToOrder(signal: Signal): Promise<OrderRequest | null> {

View file

@ -272,13 +272,14 @@ export class SimpleMovingAverageCrossover extends BaseStrategy {
} }
// Try to get account balance from trading engine // Try to get account balance from trading engine
let accountBalance = 100000; // Default // Use initial capital from config parameters if available
let accountBalance = this.config.parameters?.initialCapital || 100000;
try { try {
if (tradingEngine.getAccountBalance) { if (tradingEngine.getAccountBalance) {
accountBalance = tradingEngine.getAccountBalance(); accountBalance = tradingEngine.getAccountBalance();
} else if (tradingEngine.getTotalPnl) { } else if (tradingEngine.getTotalPnl) {
const [realized, unrealized] = tradingEngine.getTotalPnl(); const [realized, unrealized] = tradingEngine.getTotalPnl();
accountBalance = 100000 + realized + unrealized; // Assuming 100k initial accountBalance = (this.config.parameters?.initialCapital || 100000) + realized + unrealized;
} }
} catch (error) { } catch (error) {
logger.warn('Could not get account balance:', error); logger.warn('Could not get account balance:', error);

View file

@ -0,0 +1,69 @@
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);
});

View file

@ -0,0 +1,120 @@
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);
});

View file

@ -0,0 +1,123 @@
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);
});

View file

@ -0,0 +1,41 @@
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)}%`);

View file

@ -0,0 +1,73 @@
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);
});

View file

@ -0,0 +1,130 @@
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);
});

View file

@ -0,0 +1,122 @@
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);
});

View file

@ -0,0 +1,87 @@
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);
});

View file

@ -0,0 +1,104 @@
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);
});

View file

@ -0,0 +1,134 @@
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);
});

View file

@ -0,0 +1,109 @@
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);
});

View file

@ -91,7 +91,7 @@ export function BacktestResults({ status, results, currentTime }: BacktestResult
/> />
<MetricsCard <MetricsCard
title="Max Drawdown" title="Max Drawdown"
value={`${results.metrics.maxDrawdown.toFixed(2)}%`} value={`${(results.metrics.maxDrawdown * 100).toFixed(2)}%`}
trend="down" trend="down"
/> />
<MetricsCard <MetricsCard