initial backtests

This commit is contained in:
Boki 2025-07-03 09:07:45 -04:00
parent fa70ada2bb
commit 5a3a23a2ba
6 changed files with 400 additions and 129 deletions

View file

@ -32,40 +32,10 @@ export function BacktestPage() {
// Current time is not available in the new API, so we'll estimate it based on progress
const currentTime = null;
// Adapt the results when they come in
// No adaptation needed - results are already in the correct format
useEffect(() => {
if (results && config) {
setAdaptedResults({
id: backtest?.id || '',
config,
metrics: {
totalReturn: results.metrics.totalReturn,
sharpeRatio: results.metrics.sharpeRatio,
maxDrawdown: results.metrics.maxDrawdown,
winRate: results.metrics.winRate,
totalTrades: results.metrics.totalTrades,
profitableTrades: Math.round(results.metrics.totalTrades * results.metrics.winRate / 100),
},
positions: [], // Not provided by current API
trades: results.trades?.map(t => ({
id: `${t.symbol}-${t.entryDate}`,
timestamp: t.exitDate,
symbol: t.symbol,
side: t.pnl > 0 ? 'buy' : 'sell',
quantity: t.quantity,
price: t.exitPrice,
commission: 0,
pnl: t.pnl,
})) || [],
performanceData: results.equity.map(e => ({
timestamp: e.date,
portfolioValue: e.value,
pnl: 0, // Would need to calculate from equity curve
drawdown: 0, // Would need to calculate
})),
});
}
}, [results, config, backtest]);
setAdaptedResults(results);
}, [results]);
const handleConfigSubmit = useCallback(async (newConfig: BacktestConfig) => {
setConfig(newConfig);

View file

@ -13,11 +13,6 @@ interface BacktestResultsProps {
}
export function BacktestResults({ status, results, currentTime }: BacktestResultsProps) {
// Debug logging
console.log('BacktestResults - results:', results);
console.log('BacktestResults - ohlcData keys:', results?.ohlcData ? Object.keys(results.ohlcData) : 'No ohlcData');
console.log('BacktestResults - first symbol data:', results?.ohlcData && Object.keys(results.ohlcData).length > 0 ? results.ohlcData[Object.keys(results.ohlcData)[0]] : 'No data');
console.log('BacktestResults - equity data:', results?.equity);
if (status === 'idle') {
return (
<div className="bg-surface-secondary p-8 rounded-lg border border-border h-full flex items-center justify-center">
@ -125,14 +120,9 @@ export function BacktestResults({ status, results, currentTime }: BacktestResult
const hasOhlcData = results.ohlcData && Object.keys(results.ohlcData).length > 0;
const hasEquityData = results.equity && results.equity.length > 0;
console.log('Chart section - hasOhlcData:', hasOhlcData);
console.log('Chart section - hasEquityData:', hasEquityData);
if (hasOhlcData) {
const firstSymbol = Object.keys(results.ohlcData)[0];
const ohlcData = results.ohlcData[firstSymbol];
console.log('Chart section - using OHLC data for symbol:', firstSymbol);
console.log('Chart section - OHLC data points:', ohlcData?.length);
return (
<Chart
@ -156,7 +146,6 @@ export function BacktestResults({ status, results, currentTime }: BacktestResult
/>
);
} else if (hasEquityData) {
console.log('Chart section - using equity data only');
return (
<Chart
data={results.equity.map(point => ({
@ -171,7 +160,6 @@ export function BacktestResults({ status, results, currentTime }: BacktestResult
/>
);
} else {
console.log('Chart section - showing no data message');
return (
<div className="h-96 bg-background rounded border border-border flex items-center justify-center">
<p className="text-sm text-text-muted">
@ -190,13 +178,13 @@ export function BacktestResults({ status, results, currentTime }: BacktestResult
Trade History
</h3>
<TradeLog trades={results.trades.map(trade => ({
id: crypto.randomUUID(),
timestamp: trade.entryDate,
id: trade.id,
timestamp: trade.exitDate || trade.entryDate,
symbol: trade.symbol,
side: 'buy' as const,
side: trade.side as 'buy' | 'sell',
quantity: trade.quantity,
price: trade.entryPrice,
commission: 0,
price: trade.exitPrice,
commission: trade.commission,
pnl: trade.pnl
}))} />
</div>

View file

@ -24,26 +24,43 @@ export interface BacktestJob {
}
export interface BacktestResult {
// Identification
backtestId: string;
status: 'completed' | 'failed' | 'cancelled';
completedAt: string;
// Configuration
config: {
name: string;
strategy: string;
symbols: string[];
startDate: string;
endDate: string;
initialCapital: number;
commission: number;
slippage: number;
dataFrequency: string;
};
// Performance metrics
metrics: {
totalReturn: number;
sharpeRatio: number;
maxDrawdown: number;
winRate: number;
totalTrades: number;
profitFactor?: number;
profitFactor: number;
profitableTrades: number;
avgWin: number;
avgLoss: number;
expectancy: number;
calmarRatio: number;
sortinoRatio: number;
};
// Chart data
equity: Array<{ date: string; value: number }>;
trades?: Array<{
symbol: string;
entryDate: string;
exitDate: string;
entryPrice: number;
exitPrice: number;
quantity: number;
pnl: number;
}>;
ohlcData?: Record<string, Array<{
ohlcData: Record<string, Array<{
time: number;
open: number;
high: number;
@ -51,6 +68,41 @@ export interface BacktestResult {
close: number;
volume?: number;
}>>;
// Trade history
trades: Array<{
id: string;
symbol: string;
entryDate: string;
exitDate: string | null;
entryPrice: number;
exitPrice: number;
quantity: number;
side: string;
pnl: number;
pnlPercent: number;
commission: number;
duration: number;
}>;
// Positions
positions: Array<{
symbol: string;
quantity: number;
averagePrice: number;
currentPrice: number;
unrealizedPnl: number;
realizedPnl: number;
}>;
// Analytics
analytics: {
drawdownSeries: Array<{ timestamp: number; value: number }>;
dailyReturns: number[];
monthlyReturns: Record<string, number>;
exposureTime: number;
riskMetrics: Record<string, number>;
};
}
export const backtestApi = {