work on backtest

This commit is contained in:
Boki 2025-07-03 09:55:13 -04:00
parent 5a3a23a2ba
commit 143e2e1678
9 changed files with 613 additions and 46 deletions

View file

@ -13,7 +13,7 @@ export function BacktestConfiguration({ onSubmit, disabled }: BacktestConfigurat
endDate: new Date(),
initialCapital: 100000,
symbols: [],
strategy: 'momentum',
strategy: 'sma-crossover',
speedMultiplier: 1,
commission: 0.001, // 0.1%
slippage: 0.0005, // 0.05%
@ -185,11 +185,19 @@ export function BacktestConfiguration({ onSubmit, disabled }: BacktestConfigurat
className="w-full px-3 py-2 bg-background border border-border rounded-md text-sm text-text-primary focus:outline-none focus:ring-1 focus:ring-primary-500"
disabled={disabled}
>
<option value="momentum">Momentum</option>
<option value="sma-crossover">SMA Crossover</option>
<option value="mean-reversion">Mean Reversion</option>
<option value="pairs-trading">Pairs Trading</option>
<option value="custom">Custom</option>
<option value="ml-enhanced">ML Enhanced (Advanced)</option>
<option value="momentum">Momentum (Coming Soon)</option>
<option value="pairs-trading">Pairs Trading (Coming Soon)</option>
</select>
<p className="text-xs text-text-muted mt-1">
{formData.strategy === 'sma-crossover' && 'Trades on 10/20 day moving average crossovers'}
{formData.strategy === 'mean-reversion' && 'Trades when price deviates from mean by 2+ std devs'}
{formData.strategy === 'ml-enhanced' && 'Uses machine learning for signal generation'}
{formData.strategy === 'momentum' && 'Follows strong price trends'}
{formData.strategy === 'pairs-trading' && 'Trades correlated asset pairs'}
</p>
</div>
<div className="grid grid-cols-2 gap-4">

View file

@ -2,7 +2,6 @@ import type { BacktestStatus } from '../types';
import type { BacktestResult } from '../services/backtestApi';
import { MetricsCard } from './MetricsCard';
import { PositionsTable } from './PositionsTable';
import { TradeLog } from './TradeLog';
import { Chart } from '../../../components/charts';
import { useState, useMemo } from 'react';
@ -171,22 +170,107 @@ export function BacktestResults({ status, results, currentTime }: BacktestResult
})()}
</div>
{/* Trade Log */}
{/* Trade History Table */}
{results.trades && results.trades.length > 0 && (
<div className="bg-surface-secondary p-4 rounded-lg border border-border">
<h3 className="text-base font-medium text-text-primary mb-4">
Trade History
Trade History ({results.trades.length} trades)
</h3>
<TradeLog trades={results.trades.map(trade => ({
id: trade.id,
timestamp: trade.exitDate || trade.entryDate,
symbol: trade.symbol,
side: trade.side as 'buy' | 'sell',
quantity: trade.quantity,
price: trade.exitPrice,
commission: trade.commission,
pnl: trade.pnl
}))} />
<div className="overflow-x-auto">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-border">
<th className="text-left py-2 px-3 font-medium text-text-secondary">Date</th>
<th className="text-left py-2 px-3 font-medium text-text-secondary">Symbol</th>
<th className="text-center py-2 px-3 font-medium text-text-secondary">Side</th>
<th className="text-right py-2 px-3 font-medium text-text-secondary">Qty</th>
<th className="text-right py-2 px-3 font-medium text-text-secondary">Entry</th>
<th className="text-right py-2 px-3 font-medium text-text-secondary">Exit</th>
<th className="text-right py-2 px-3 font-medium text-text-secondary">P&L</th>
<th className="text-right py-2 px-3 font-medium text-text-secondary">Return</th>
<th className="text-right py-2 px-3 font-medium text-text-secondary">Duration</th>
</tr>
</thead>
<tbody>
{results.trades.slice().reverse().map((trade) => {
const formatDate = (dateStr: string) => {
const date = new Date(dateStr);
return date.toLocaleDateString('en-US', {
month: 'short',
day: 'numeric',
year: '2-digit'
});
};
const formatDuration = (ms: number) => {
const days = Math.floor(ms / (1000 * 60 * 60 * 24));
if (days > 0) return `${days}d`;
const hours = Math.floor(ms / (1000 * 60 * 60));
if (hours > 0) return `${hours}h`;
return '<1h';
};
return (
<tr key={trade.id} className="border-b border-border hover:bg-surface-tertiary">
<td className="py-2 px-3 text-text-muted whitespace-nowrap">
{formatDate(trade.entryDate)}
</td>
<td className="py-2 px-3 font-medium text-text-primary">
{trade.symbol}
</td>
<td className="text-center py-2 px-3">
<span className={`inline-flex px-2 py-0.5 text-xs font-medium rounded ${
trade.side === 'buy'
? 'bg-success/10 text-success'
: 'bg-error/10 text-error'
}`}>
{trade.side.toUpperCase()}
</span>
</td>
<td className="text-right py-2 px-3 text-text-primary">
{trade.quantity}
</td>
<td className="text-right py-2 px-3 text-text-primary">
${trade.entryPrice.toFixed(2)}
</td>
<td className="text-right py-2 px-3 text-text-primary">
${trade.exitPrice.toFixed(2)}
</td>
<td className={`text-right py-2 px-3 font-medium ${
trade.pnl >= 0 ? 'text-success' : 'text-error'
}`}>
{trade.pnl >= 0 ? '+' : ''}${trade.pnl.toFixed(2)}
</td>
<td className={`text-right py-2 px-3 font-medium ${
trade.pnlPercent >= 0 ? 'text-success' : 'text-error'
}`}>
{trade.pnlPercent >= 0 ? '+' : ''}{trade.pnlPercent.toFixed(2)}%
</td>
<td className="text-right py-2 px-3 text-text-muted">
{formatDuration(trade.duration)}
</td>
</tr>
);
})}
</tbody>
<tfoot className="border-t-2 border-border">
<tr className="font-medium">
<td colSpan={6} className="py-2 px-3 text-text-primary">
Total
</td>
<td className={`text-right py-2 px-3 ${
results.trades.reduce((sum, t) => sum + t.pnl, 0) >= 0 ? 'text-success' : 'text-error'
}`}>
${results.trades.reduce((sum, t) => sum + t.pnl, 0).toFixed(2)}
</td>
<td className="text-right py-2 px-3 text-text-secondary">
Avg: {(results.trades.reduce((sum, t) => sum + t.pnlPercent, 0) / results.trades.length).toFixed(2)}%
</td>
<td></td>
</tr>
</tfoot>
</table>
</div>
</div>
)}
</div>

View file

@ -75,7 +75,7 @@ export function useBacktest(): UseBacktestReturn {
setIsPolling(true);
pollingIntervalRef.current = setInterval(() => {
pollStatus(newBacktest.id);
}, 2000); // Poll every 2 seconds
}, 200); // Poll every 2 seconds
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to create backtest');

View file

@ -141,7 +141,7 @@ export class BacktestService {
static async pollBacktestUpdates(
id: string,
onUpdate: (status: BacktestStatus, progress?: number, currentTime?: number) => void,
interval: number = 1000
interval: number = 200
): Promise<() => void> {
let isPolling = true;