import { useState } from 'react' import { useBacktest } from '../hooks/useApi' import clsx from 'clsx' import { AreaChart, Area, XAxis, YAxis, Tooltip, ResponsiveContainer, CartesianGrid, ReferenceLine, } from 'recharts' import { History, Play, TrendingUp, TrendingDown, AlertTriangle } from 'lucide-react' import type { BacktestResult } from '../types' const STRATEGIES = [ { key: 'long_call', label: 'Long Call' }, { key: 'long_put', label: 'Long Put' }, { key: 'bull_call_spread', label: 'Bull Call Spread' }, { key: 'bear_put_spread', label: 'Bear Put Spread' }, ] const SYMBOLS = [ 'GLD', 'USO', 'WEAT', 'UNG', 'SPY', 'QQQ', 'GDX', 'COPX', 'XLE', 'FXE', 'XOM', 'LMT', 'BA', 'RTX', ] function StatCard({ label, value, sub, positive }: { label: string; value: string; sub?: string; positive?: boolean }) { return (
{label}
{value}
{sub &&
{sub}
}
) } export default function Backtest() { const { mutate: runBacktest, data: result, isPending } = useBacktest() const [form, setForm] = useState({ symbol: 'GLD', start_date: '2022-01-01', end_date: '2024-12-31', strategy: 'long_call', strike_offset_pct: 0.05, expiry_days: 90, capital: 1000, }) const set = (k: string, v: unknown) => setForm(f => ({ ...f, [k]: v })) const run = () => runBacktest(form as Record) const typed = result as BacktestResult | undefined const hasResult = typed && !typed.error return (

Backtest & Simulation

Test your options strategies on real historical data

{/* Config panel */}
Configuration
{SYMBOLS.slice(0, 7).map(s => ( ))}
set('symbol', e.target.value.toUpperCase())} className="w-full bg-dark-700 border border-slate-700 rounded px-2 py-1.5 text-sm text-white focus:outline-none focus:border-blue-500" />
{STRATEGIES.map(s => ( ))}
set('start_date', e.target.value)} className="w-full bg-dark-700 border border-slate-700 rounded px-2 py-1.5 text-sm text-white mb-1 focus:outline-none focus:border-blue-500" /> set('end_date', e.target.value)} className="w-full bg-dark-700 border border-slate-700 rounded px-2 py-1.5 text-sm text-white focus:outline-none focus:border-blue-500" />
set('strike_offset_pct', Number(e.target.value))} className="w-full accent-blue-500" />
{[30, 60, 90, 180].map(d => ( ))}
set('capital', Number(e.target.value))} className="w-full bg-dark-700 border border-slate-700 rounded px-2 py-1.5 text-sm text-white focus:outline-none focus:border-blue-500" />
{/* Results panel */}
{typed?.error && (
{typed.error}
)} {hasResult && ( <> {/* KPIs */}
= 0 ? '+' : ''}${typed.total_return_pct.toFixed(2)}%`} positive={typed.total_return_pct >= 0} /> = 50} /> = 1} />
{/* Equity curve */}
Equity curve — {form.symbol} {STRATEGIES.find(s => s.key === form.strategy)?.label}
Final capital: {typed.final_capital.toFixed(2)}€ {' '}(initial: {form.capital}€ · P&L: {typed.total_pnl >= 0 ? '+' : ''}{typed.total_pnl.toFixed(2)}€)
= 0 ? '#10b981' : '#ef4444'} stopOpacity={0.3} /> = 0 ? '#10b981' : '#ef4444'} stopOpacity={0} /> `${v.toFixed(0)}€`} /> [`${v.toFixed(2)}€`, 'Capital']} /> = 0 ? '#10b981' : '#ef4444'} fill="url(#equity-grad)" strokeWidth={2} dot={false} />
{/* Last trades */}
Last executed trades
{typed.trades.map((t, i) => { const pnl = t.pnl as number return ( ) })}
Entry Exit Entry price Strike Premium Exit price P&L Capital
{t.entry_date as string} {t.exit_date as string} ${(t.S_entry as number).toFixed(2)} ${(t.K as number).toFixed(2)} ${(t.premium as number).toFixed(4)} ${(t.S_expiry as number).toFixed(2)} = 0 ? 'positive' : 'negative')}> {pnl >= 0 ? '+' : ''}{pnl.toFixed(2)}€ {(t.capital as number).toFixed(2)}€
)} {!hasResult && !isPending && !typed?.error && (
Configure and run a backtest
yfinance data — full history available
)}
) }