Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 156ea40e78 | |||
| 6665d0cd2a | |||
| 96ca132fa2 |
@@ -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,
|
||||
)
|
||||
|
||||
+2011
-2081
File diff suppressed because it is too large
Load Diff
+1312
-1522
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
+92
-92
@@ -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"
|
||||
}
|
||||
+92
-92
@@ -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
+806
-816
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+955
-1035
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+799
-809
File diff suppressed because it is too large
Load Diff
+996
-966
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
+798
-798
File diff suppressed because it is too large
Load Diff
+867
-897
File diff suppressed because it is too large
Load Diff
+932
-882
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
+816
-816
File diff suppressed because it is too large
Load Diff
+1138
-1108
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
+798
-798
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
@@ -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" />}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -93,7 +93,7 @@ export interface BacktestSummary {
|
||||
max_dd: number;
|
||||
win_rate: number;
|
||||
total_trades: number;
|
||||
coin?: string;
|
||||
coin: string;
|
||||
}
|
||||
|
||||
export interface BacktestFull {
|
||||
|
||||
Reference in New Issue
Block a user