work on backtest
This commit is contained in:
parent
5a3a23a2ba
commit
143e2e1678
9 changed files with 613 additions and 46 deletions
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue