Compare commits

...

3 Commits

Author SHA1 Message Date
ramseshk 156ea40e78 Add multi-ticker historical backtests: 28 results (7 strategies × 4 coins)
- Cleaned old timestamp-named files
- New files with proper ticker naming: {strategy}_{TICKER}_{timestamp}.json
- BTC, ETH, HYPE, VVV backtests for all 7 strategies
- Mean Reversion BTC: +76.42%, VVV: +0.04%, ETH: -0.12%, HYPE: -0.02%
2026-08-05 05:11:32 +00:00
ramseshk 6665d0cd2a 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
2026-08-05 05:11:32 +00:00
ramseshk 96ca132fa2 Fix historical runner: store actual ticker name (BTC/ETH/HYPE/VVV) not timestamp
- Added HYPE and VVV to --coin choices
- Fixed coin field to store ticker name instead of first candle timestamp
- Added coin_name parameter to simulate_strategy_on_candles
2026-08-05 05:11:32 +00:00
33 changed files with 55133 additions and 12720 deletions
+4 -3
View File
@@ -81,6 +81,7 @@ def fetch_candles(coin: str, interval: str = "1h", limit: int = 720) -> list[dic
def simulate_strategy_on_candles(
key: str,
candles: list[dict],
coin_name: str = "BTC",
allocation: float = 100.0,
fee_tier: int = 0,
staking_tier: str = "none",
@@ -289,7 +290,7 @@ def simulate_strategy_on_candles(
return {
"strategy": name,
"strategy_key": key,
"coin": candles[0]["t"] if candles else "unknown",
"coin": coin_name, # actual ticker (BTC, ETH, etc.)
"allocation": allocation,
"start_time": curve[0]["t"] if curve else "",
"end_time": curve[-1]["t"] if curve else "",
@@ -319,7 +320,7 @@ def simulate_strategy_on_candles(
def main():
p = argparse.ArgumentParser(description="FTDT Historical Backtest Runner")
p.add_argument("--coin", default="BTC", choices=["BTC", "ETH", "SOL"], help="Coin to backtest")
p.add_argument("--coin", default="BTC", choices=["BTC", "ETH", "SOL", "HYPE", "VVV"], help="Coin to backtest")
p.add_argument("--strategy", "-s", choices=list(STRATEGIES) + ["all"], default="all")
p.add_argument("--fee-tier", type=int, default=0, choices=range(7))
p.add_argument("--staking-tier", default="none", choices=list(STAKING_TIERS.keys()))
@@ -355,7 +356,7 @@ def main():
print(f"\n Running: {cfg['name']} on {a.coin}...")
result = simulate_strategy_on_candles(
key, candles,
key, candles, a.coin,
fee_tier=a.fee_tier,
staking_tier=a.staking_tier,
)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,10 +1,10 @@
{
"strategy": "Funding Rate Arb",
"strategy_key": "funding_arb",
"coin": 1783234800000,
"coin": "BTC",
"allocation": 100.0,
"start_time": "2026-07-05T07:00:00",
"end_time": "2026-08-04T07:00:00",
"start_time": "2026-07-06T05:00:00",
"end_time": "2026-08-05T05:00:00",
"start_equity": 100.0,
"end_equity": 100.0,
"pnl": 0.0,
@@ -22,94 +22,6 @@
"win_rate": 0.0,
"total_trades": 0,
"equity_curve": [
{
"t": "2026-07-05T07:00:00",
"v": 100.0
},
{
"t": "2026-07-05T08:00:00",
"v": 100.0
},
{
"t": "2026-07-05T09:00:00",
"v": 100.0
},
{
"t": "2026-07-05T10:00:00",
"v": 100.0
},
{
"t": "2026-07-05T11:00:00",
"v": 100.0
},
{
"t": "2026-07-05T12:00:00",
"v": 100.0
},
{
"t": "2026-07-05T13:00:00",
"v": 100.0
},
{
"t": "2026-07-05T14:00:00",
"v": 100.0
},
{
"t": "2026-07-05T15:00:00",
"v": 100.0
},
{
"t": "2026-07-05T16:00:00",
"v": 100.0
},
{
"t": "2026-07-05T17:00:00",
"v": 100.0
},
{
"t": "2026-07-05T18:00:00",
"v": 100.0
},
{
"t": "2026-07-05T19:00:00",
"v": 100.0
},
{
"t": "2026-07-05T20:00:00",
"v": 100.0
},
{
"t": "2026-07-05T21:00:00",
"v": 100.0
},
{
"t": "2026-07-05T22:00:00",
"v": 100.0
},
{
"t": "2026-07-05T23:00:00",
"v": 100.0
},
{
"t": "2026-07-06T00:00:00",
"v": 100.0
},
{
"t": "2026-07-06T01:00:00",
"v": 100.0
},
{
"t": "2026-07-06T02:00:00",
"v": 100.0
},
{
"t": "2026-07-06T03:00:00",
"v": 100.0
},
{
"t": "2026-07-06T04:00:00",
"v": 100.0
},
{
"t": "2026-07-06T05:00:00",
"v": 100.0
@@ -2905,10 +2817,98 @@
{
"t": "2026-08-04T07:00:00",
"v": 100.0
},
{
"t": "2026-08-04T08:00:00",
"v": 100.0
},
{
"t": "2026-08-04T09:00:00",
"v": 100.0
},
{
"t": "2026-08-04T10:00:00",
"v": 100.0
},
{
"t": "2026-08-04T11:00:00",
"v": 100.0
},
{
"t": "2026-08-04T12:00:00",
"v": 100.0
},
{
"t": "2026-08-04T13:00:00",
"v": 100.0
},
{
"t": "2026-08-04T14:00:00",
"v": 100.0
},
{
"t": "2026-08-04T15:00:00",
"v": 100.0
},
{
"t": "2026-08-04T16:00:00",
"v": 100.0
},
{
"t": "2026-08-04T17:00:00",
"v": 100.0
},
{
"t": "2026-08-04T18:00:00",
"v": 100.0
},
{
"t": "2026-08-04T19:00:00",
"v": 100.0
},
{
"t": "2026-08-04T20:00:00",
"v": 100.0
},
{
"t": "2026-08-04T21:00:00",
"v": 100.0
},
{
"t": "2026-08-04T22:00:00",
"v": 100.0
},
{
"t": "2026-08-04T23:00:00",
"v": 100.0
},
{
"t": "2026-08-05T00:00:00",
"v": 100.0
},
{
"t": "2026-08-05T01:00:00",
"v": 100.0
},
{
"t": "2026-08-05T02:00:00",
"v": 100.0
},
{
"t": "2026-08-05T03:00:00",
"v": 100.0
},
{
"t": "2026-08-05T04:00:00",
"v": 100.0
},
{
"t": "2026-08-05T05:00:00",
"v": 100.0
}
],
"trades": [],
"num_periods": 721,
"data_source": "Hyperliquid Mainnet",
"generated_at": "2026-08-04T07:28:17.886718"
"generated_at": "2026-08-05T05:07:39.878052"
}
@@ -1,10 +1,10 @@
{
"strategy": "Funding Rate Arb",
"strategy_key": "funding_arb",
"coin": 1783234800000,
"coin": "ETH",
"allocation": 100.0,
"start_time": "2026-07-05T07:00:00",
"end_time": "2026-08-04T07:00:00",
"start_time": "2026-07-06T05:00:00",
"end_time": "2026-08-05T05:00:00",
"start_equity": 100.0,
"end_equity": 100.0,
"pnl": 0.0,
@@ -22,94 +22,6 @@
"win_rate": 0.0,
"total_trades": 0,
"equity_curve": [
{
"t": "2026-07-05T07:00:00",
"v": 100.0
},
{
"t": "2026-07-05T08:00:00",
"v": 100.0
},
{
"t": "2026-07-05T09:00:00",
"v": 100.0
},
{
"t": "2026-07-05T10:00:00",
"v": 100.0
},
{
"t": "2026-07-05T11:00:00",
"v": 100.0
},
{
"t": "2026-07-05T12:00:00",
"v": 100.0
},
{
"t": "2026-07-05T13:00:00",
"v": 100.0
},
{
"t": "2026-07-05T14:00:00",
"v": 100.0
},
{
"t": "2026-07-05T15:00:00",
"v": 100.0
},
{
"t": "2026-07-05T16:00:00",
"v": 100.0
},
{
"t": "2026-07-05T17:00:00",
"v": 100.0
},
{
"t": "2026-07-05T18:00:00",
"v": 100.0
},
{
"t": "2026-07-05T19:00:00",
"v": 100.0
},
{
"t": "2026-07-05T20:00:00",
"v": 100.0
},
{
"t": "2026-07-05T21:00:00",
"v": 100.0
},
{
"t": "2026-07-05T22:00:00",
"v": 100.0
},
{
"t": "2026-07-05T23:00:00",
"v": 100.0
},
{
"t": "2026-07-06T00:00:00",
"v": 100.0
},
{
"t": "2026-07-06T01:00:00",
"v": 100.0
},
{
"t": "2026-07-06T02:00:00",
"v": 100.0
},
{
"t": "2026-07-06T03:00:00",
"v": 100.0
},
{
"t": "2026-07-06T04:00:00",
"v": 100.0
},
{
"t": "2026-07-06T05:00:00",
"v": 100.0
@@ -2905,10 +2817,98 @@
{
"t": "2026-08-04T07:00:00",
"v": 100.0
},
{
"t": "2026-08-04T08:00:00",
"v": 100.0
},
{
"t": "2026-08-04T09:00:00",
"v": 100.0
},
{
"t": "2026-08-04T10:00:00",
"v": 100.0
},
{
"t": "2026-08-04T11:00:00",
"v": 100.0
},
{
"t": "2026-08-04T12:00:00",
"v": 100.0
},
{
"t": "2026-08-04T13:00:00",
"v": 100.0
},
{
"t": "2026-08-04T14:00:00",
"v": 100.0
},
{
"t": "2026-08-04T15:00:00",
"v": 100.0
},
{
"t": "2026-08-04T16:00:00",
"v": 100.0
},
{
"t": "2026-08-04T17:00:00",
"v": 100.0
},
{
"t": "2026-08-04T18:00:00",
"v": 100.0
},
{
"t": "2026-08-04T19:00:00",
"v": 100.0
},
{
"t": "2026-08-04T20:00:00",
"v": 100.0
},
{
"t": "2026-08-04T21:00:00",
"v": 100.0
},
{
"t": "2026-08-04T22:00:00",
"v": 100.0
},
{
"t": "2026-08-04T23:00:00",
"v": 100.0
},
{
"t": "2026-08-05T00:00:00",
"v": 100.0
},
{
"t": "2026-08-05T01:00:00",
"v": 100.0
},
{
"t": "2026-08-05T02:00:00",
"v": 100.0
},
{
"t": "2026-08-05T03:00:00",
"v": 100.0
},
{
"t": "2026-08-05T04:00:00",
"v": 100.0
},
{
"t": "2026-08-05T05:00:00",
"v": 100.0
}
],
"trades": [],
"num_periods": 721,
"data_source": "Hyperliquid Mainnet",
"generated_at": "2026-08-04T07:29:03.636645"
"generated_at": "2026-08-05T05:07:40.408407"
}
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+16 -2
View File
@@ -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>
@@ -11,12 +11,13 @@ interface StrategyCardProps {
tab: "live" | "paper" | "backtest" | "historical";
onClick: () => void;
badge?: string;
coin?: string;
stats?: { label: string; value: string; negative?: boolean }[];
pnlPct?: number;
status?: string;
}
export function StrategyCard({ name, strategy, tab, onClick, badge, stats, pnlPct, status }: StrategyCardProps) {
export function StrategyCard({ name, strategy, tab, onClick, badge, stats, pnlPct, status, coin }: StrategyCardProps) {
if (strategy) {
const equity = strategy.allocation + (strategy.pnl ?? 0);
const isUp = equity >= strategy.allocation;
@@ -76,6 +77,7 @@ export function StrategyCard({ name, strategy, tab, onClick, badge, stats, pnlPc
<p className="text-[9px] text-muted-foreground mt-0.5">{badge ?? "Backtest"}</p>
</div>
<Badge variant="secondary" className="text-[8px] h-4 px-1.5">{status ?? "BACKTEST"}</Badge>
{coin && <Badge variant="outline" className="text-[8px] h-4 px-1.5 bg-blue-500/10 text-blue-400 border-0">{coin}</Badge>}
</div>
<div className={`text-xl font-mono font-bold mb-2 flex items-center gap-1 ${(pnlPct ?? 0) >= 0 ? "text-green-500" : "text-red-500"}`}>
{(pnlPct ?? 0) >= 0 ? <TrendingUp className="w-4 h-4" /> : <TrendingDown className="w-4 h-4" />}
+2 -1
View File
@@ -82,7 +82,8 @@ export async function fetchHistorical(): Promise<Record<string, import("./types"
const list: import("./types").BacktestSummary[] = await res.json();
const byStrat: Record<string, import("./types").BacktestSummary> = {};
for (const b of list) {
if (!byStrat[b.strategy]) byStrat[b.strategy] = b;
const key = `${b.strategy} · ${b.coin ?? "?"}`;
if (!byStrat[key]) byStrat[key] = b;
}
return byStrat;
}
+1 -1
View File
@@ -93,7 +93,7 @@ export interface BacktestSummary {
max_dd: number;
win_rate: number;
total_trades: number;
coin?: string;
coin: string;
}
export interface BacktestFull {