Compare commits
3 Commits
a09954017e
...
156ea40e78
| 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(
|
def simulate_strategy_on_candles(
|
||||||
key: str,
|
key: str,
|
||||||
candles: list[dict],
|
candles: list[dict],
|
||||||
|
coin_name: str = "BTC",
|
||||||
allocation: float = 100.0,
|
allocation: float = 100.0,
|
||||||
fee_tier: int = 0,
|
fee_tier: int = 0,
|
||||||
staking_tier: str = "none",
|
staking_tier: str = "none",
|
||||||
@@ -289,7 +290,7 @@ def simulate_strategy_on_candles(
|
|||||||
return {
|
return {
|
||||||
"strategy": name,
|
"strategy": name,
|
||||||
"strategy_key": key,
|
"strategy_key": key,
|
||||||
"coin": candles[0]["t"] if candles else "unknown",
|
"coin": coin_name, # actual ticker (BTC, ETH, etc.)
|
||||||
"allocation": allocation,
|
"allocation": allocation,
|
||||||
"start_time": curve[0]["t"] if curve else "",
|
"start_time": curve[0]["t"] if curve else "",
|
||||||
"end_time": curve[-1]["t"] if curve else "",
|
"end_time": curve[-1]["t"] if curve else "",
|
||||||
@@ -319,7 +320,7 @@ def simulate_strategy_on_candles(
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
p = argparse.ArgumentParser(description="FTDT Historical Backtest Runner")
|
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("--strategy", "-s", choices=list(STRATEGIES) + ["all"], default="all")
|
||||||
p.add_argument("--fee-tier", type=int, default=0, choices=range(7))
|
p.add_argument("--fee-tier", type=int, default=0, choices=range(7))
|
||||||
p.add_argument("--staking-tier", default="none", choices=list(STAKING_TIERS.keys()))
|
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}...")
|
print(f"\n Running: {cfg['name']} on {a.coin}...")
|
||||||
|
|
||||||
result = simulate_strategy_on_candles(
|
result = simulate_strategy_on_candles(
|
||||||
key, candles,
|
key, candles, a.coin,
|
||||||
fee_tier=a.fee_tier,
|
fee_tier=a.fee_tier,
|
||||||
staking_tier=a.staking_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": "Funding Rate Arb",
|
||||||
"strategy_key": "funding_arb",
|
"strategy_key": "funding_arb",
|
||||||
"coin": 1783234800000,
|
"coin": "BTC",
|
||||||
"allocation": 100.0,
|
"allocation": 100.0,
|
||||||
"start_time": "2026-07-05T07:00:00",
|
"start_time": "2026-07-06T05:00:00",
|
||||||
"end_time": "2026-08-04T07:00:00",
|
"end_time": "2026-08-05T05:00:00",
|
||||||
"start_equity": 100.0,
|
"start_equity": 100.0,
|
||||||
"end_equity": 100.0,
|
"end_equity": 100.0,
|
||||||
"pnl": 0.0,
|
"pnl": 0.0,
|
||||||
@@ -22,94 +22,6 @@
|
|||||||
"win_rate": 0.0,
|
"win_rate": 0.0,
|
||||||
"total_trades": 0,
|
"total_trades": 0,
|
||||||
"equity_curve": [
|
"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",
|
"t": "2026-07-06T05:00:00",
|
||||||
"v": 100.0
|
"v": 100.0
|
||||||
@@ -2905,10 +2817,98 @@
|
|||||||
{
|
{
|
||||||
"t": "2026-08-04T07:00:00",
|
"t": "2026-08-04T07:00:00",
|
||||||
"v": 100.0
|
"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": [],
|
"trades": [],
|
||||||
"num_periods": 721,
|
"num_periods": 721,
|
||||||
"data_source": "Hyperliquid Mainnet",
|
"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": "Funding Rate Arb",
|
||||||
"strategy_key": "funding_arb",
|
"strategy_key": "funding_arb",
|
||||||
"coin": 1783234800000,
|
"coin": "ETH",
|
||||||
"allocation": 100.0,
|
"allocation": 100.0,
|
||||||
"start_time": "2026-07-05T07:00:00",
|
"start_time": "2026-07-06T05:00:00",
|
||||||
"end_time": "2026-08-04T07:00:00",
|
"end_time": "2026-08-05T05:00:00",
|
||||||
"start_equity": 100.0,
|
"start_equity": 100.0,
|
||||||
"end_equity": 100.0,
|
"end_equity": 100.0,
|
||||||
"pnl": 0.0,
|
"pnl": 0.0,
|
||||||
@@ -22,94 +22,6 @@
|
|||||||
"win_rate": 0.0,
|
"win_rate": 0.0,
|
||||||
"total_trades": 0,
|
"total_trades": 0,
|
||||||
"equity_curve": [
|
"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",
|
"t": "2026-07-06T05:00:00",
|
||||||
"v": 100.0
|
"v": 100.0
|
||||||
@@ -2905,10 +2817,98 @@
|
|||||||
{
|
{
|
||||||
"t": "2026-08-04T07:00:00",
|
"t": "2026-08-04T07:00:00",
|
||||||
"v": 100.0
|
"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": [],
|
"trades": [],
|
||||||
"num_periods": 721,
|
"num_periods": 721,
|
||||||
"data_source": "Hyperliquid Mainnet",
|
"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 [feeTier, setFeeTier] = useState(0);
|
||||||
const [stakingTier, setStakingTier] = useState("none");
|
const [stakingTier, setStakingTier] = useState("none");
|
||||||
const [posOpen, setPosOpen] = useState(false);
|
const [posOpen, setPosOpen] = useState(false);
|
||||||
|
const [tickerFilter, setTickerFilter] = useState("ALL");
|
||||||
|
|
||||||
useEffect(() => { fetchHistorical().then(setHistorical); }, []);
|
useEffect(() => { fetchHistorical().then(setHistorical); }, []);
|
||||||
|
|
||||||
@@ -301,6 +302,18 @@ export default function Dashboard() {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<main className="max-w-[1440px] mx-auto px-6 py-6">
|
<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">
|
<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">
|
<AnimatePresence mode="popLayout">
|
||||||
{Object.entries(strategies).map(([name, s], i) => (
|
{Object.entries(strategies).map(([name, s], i) => (
|
||||||
@@ -309,10 +322,11 @@ export default function Dashboard() {
|
|||||||
</motion.div>
|
</motion.div>
|
||||||
))}
|
))}
|
||||||
</AnimatePresence>
|
</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 }}>
|
<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")}
|
<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)}%` }]}
|
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" />
|
pnlPct={b.pnl_pct} status="REAL DATA" />
|
||||||
</motion.div>
|
</motion.div>
|
||||||
|
|||||||
@@ -11,12 +11,13 @@ interface StrategyCardProps {
|
|||||||
tab: "live" | "paper" | "backtest" | "historical";
|
tab: "live" | "paper" | "backtest" | "historical";
|
||||||
onClick: () => void;
|
onClick: () => void;
|
||||||
badge?: string;
|
badge?: string;
|
||||||
|
coin?: string;
|
||||||
stats?: { label: string; value: string; negative?: boolean }[];
|
stats?: { label: string; value: string; negative?: boolean }[];
|
||||||
pnlPct?: number;
|
pnlPct?: number;
|
||||||
status?: string;
|
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) {
|
if (strategy) {
|
||||||
const equity = strategy.allocation + (strategy.pnl ?? 0);
|
const equity = strategy.allocation + (strategy.pnl ?? 0);
|
||||||
const isUp = equity >= strategy.allocation;
|
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>
|
<p className="text-[9px] text-muted-foreground mt-0.5">{badge ?? "Backtest"}</p>
|
||||||
</div>
|
</div>
|
||||||
<Badge variant="secondary" className="text-[8px] h-4 px-1.5">{status ?? "BACKTEST"}</Badge>
|
<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>
|
||||||
<div className={`text-xl font-mono font-bold mb-2 flex items-center gap-1 ${(pnlPct ?? 0) >= 0 ? "text-green-500" : "text-red-500"}`}>
|
<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" />}
|
{(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 list: import("./types").BacktestSummary[] = await res.json();
|
||||||
const byStrat: Record<string, import("./types").BacktestSummary> = {};
|
const byStrat: Record<string, import("./types").BacktestSummary> = {};
|
||||||
for (const b of list) {
|
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;
|
return byStrat;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ export interface BacktestSummary {
|
|||||||
max_dd: number;
|
max_dd: number;
|
||||||
win_rate: number;
|
win_rate: number;
|
||||||
total_trades: number;
|
total_trades: number;
|
||||||
coin?: string;
|
coin: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BacktestFull {
|
export interface BacktestFull {
|
||||||
|
|||||||
Reference in New Issue
Block a user