Multi-ticker historical backtests: ticker filter + per-coin grouping
- Historical cards now deduplicated by strategy+ticker (28 entries: 7×4) - Ticker filter bar: ALL | BTC | ETH | HYPE | VVV - Coin badge on each card - BacktestSummary.coin now required string field - fetchHistorical groups by strategy · coin composite key
This commit is contained in:
@@ -32,6 +32,7 @@ export default function Dashboard() {
|
||||
const [feeTier, setFeeTier] = useState(0);
|
||||
const [stakingTier, setStakingTier] = useState("none");
|
||||
const [posOpen, setPosOpen] = useState(false);
|
||||
const [tickerFilter, setTickerFilter] = useState("ALL");
|
||||
|
||||
useEffect(() => { fetchHistorical().then(setHistorical); }, []);
|
||||
|
||||
@@ -301,6 +302,18 @@ export default function Dashboard() {
|
||||
</div>
|
||||
|
||||
<main className="max-w-[1440px] mx-auto px-6 py-6">
|
||||
{/* Ticker filter for Historical tab */}
|
||||
{tab === "historical" && (
|
||||
<div className="flex items-center gap-2 mb-4 flex-wrap">
|
||||
<span className="text-[9px] text-muted-foreground uppercase tracking-wider mr-1">Ticker:</span>
|
||||
{["ALL", "BTC", "ETH", "HYPE", "VVV"].map((t) => (
|
||||
<button key={t} onClick={() => setTickerFilter(t)}
|
||||
className={`text-[10px] px-3 py-1 rounded-md border transition-colors ${tickerFilter === t ? "bg-primary text-primary-foreground border-primary" : "bg-card text-muted-foreground border-border hover:border-primary/50"}`}>
|
||||
{t}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4 gap-3 mb-6">
|
||||
<AnimatePresence mode="popLayout">
|
||||
{Object.entries(strategies).map(([name, s], i) => (
|
||||
@@ -309,10 +322,11 @@ export default function Dashboard() {
|
||||
</motion.div>
|
||||
))}
|
||||
</AnimatePresence>
|
||||
{tab === "historical" && Object.entries(historical).map(([name, b], i) => (
|
||||
{tab === "historical" && Object.entries(historical).filter(([, b]) => tickerFilter === "ALL" || b.coin === tickerFilter).map(([name, b], i) => (
|
||||
<motion.div key={name} layout initial={{ opacity: 0, y: 8 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.2, delay: i * 0.03 }}>
|
||||
<StrategyCard name={name} tab="historical" onClick={() => handleCardClick(name, "historical")}
|
||||
badge={`30d · ${b.coin ?? "BTC"} · Mainnet`}
|
||||
coin={String(b.coin ?? "?")}
|
||||
badge={`30d · Mainnet`}
|
||||
stats={[{ label: "Sharpe", value: b.sharpe.toFixed(2) }, { label: "Max DD", value: `${(b.max_dd * 100).toFixed(1)}%`, negative: true }, { label: "Win", value: `${Math.round(b.win_rate * 100)}%` }]}
|
||||
pnlPct={b.pnl_pct} status="REAL DATA" />
|
||||
</motion.div>
|
||||
|
||||
Reference in New Issue
Block a user