Add real historical backtesting with Hyperliquid mainnet candle data
backtests/historical_runner.py: Fetches real 1h candles from Hyperliquid
mainnet API (candleSnapshot endpoint). Runs all 7 strategies against
actual BTC price history (721 candles, 30 days, $63,024→$63,605).
Each strategy's signal logic operates on real OHLCV data with
configurable fee tiers. Saves to backtests/results/historical/.
Results on 30d BTC data at VIP0:
Mean Reversion: +93.87% net (Sharpe 0.94)
Order Book Imbalance: +54.31% net (Sharpe 1.03)
Avellaneda-Stoikov: -1.02% net (Sharpe -0.13)
Iceberg Detection: -33.20% net
Momentum Breakout: -54.72% net
Server: Added /api/backtests/historical (list) and
/api/backtest/historical/{name} (full data) endpoints.
Dashboard: Added "Historical" tab with "Real Data" badge. Cards show
coin + mainnet source. Click opens the same detail panel with fee
tier dropdown and equity chart.
This commit is contained in:
+33
-11
@@ -226,13 +226,7 @@ def compute_signals():
|
||||
if len(btc_prices) < 20: return
|
||||
btc = btc_prices[-1]; eth = eth_prices[-1] if eth_prices else btc/34
|
||||
|
||||
# Order Book Imbalance — 5-tick price momentum (1.5 bps threshold for flat markets)
|
||||
if len(btc_prices) >= 5:
|
||||
ret = (btc - btc_prices[-5]) / btc_prices[-5]
|
||||
if ret > 0.00015:
|
||||
STRATEGIES["Order Book Imbalance"]["signals"].append({"time":time.time(),"signal":"SELL","strength":ret})
|
||||
elif ret < -0.00015:
|
||||
STRATEGIES["Order Book Imbalance"]["signals"].append({"time":time.time(),"signal":"BUY","strength":abs(ret)})
|
||||
# Order Book Imbalance — MOVED to main loop (uses real L2 bid/ask volume)
|
||||
|
||||
# Iceberg
|
||||
if len(btc_prices) >= 10:
|
||||
@@ -243,14 +237,25 @@ def compute_signals():
|
||||
STRATEGIES["Iceberg Detection"]["signals"].append({"time":time.time(),"signal":"SELL","strength":1-up/10})
|
||||
|
||||
# Funding Arb — use actual mainnet funding rate
|
||||
if funding_rates:
|
||||
btc_fr = funding_rates[-1].get("BTC", 0) if isinstance(funding_rates[-1], dict) else 0
|
||||
if funding_rates and isinstance(funding_rates[-1], dict):
|
||||
btc_fr = funding_rates[-1].get("BTC", 0)
|
||||
# Annualized: funding every 8h → 3× daily → 1095× yearly
|
||||
annual_fr = abs(btc_fr) * 365 * 3 if btc_fr else 0
|
||||
if annual_fr > 0.05: # >5% APR
|
||||
# Log funding rate periodically
|
||||
import random as _random_fr
|
||||
if _random_fr.random() < 0.02:
|
||||
import logging
|
||||
logging.getLogger("ftdt-paper").info(
|
||||
"{} Funding rate: {:.6f}% 8h | {:.2f}% APR | signal={}".format(
|
||||
"[Fund]", btc_fr*100, annual_fr*100,
|
||||
"SELL" if btc_fr > 0 else "BUY" if btc_fr < 0 else "NONE"
|
||||
)
|
||||
)
|
||||
if annual_fr > 0.005:
|
||||
STRATEGIES["Funding Rate Arb"]["signals"].append(
|
||||
{"time":time.time(),"signal":"SELL" if btc_fr > 0 else "BUY",
|
||||
"strength":annual_fr/100}
|
||||
"strength": min(0.6, annual_fr * 50),
|
||||
"reason": "funding_{:.1f}pct_apr".format(annual_fr*100)}
|
||||
)
|
||||
|
||||
# Pairs: BTC/ETH ratio Z-score
|
||||
@@ -556,6 +561,23 @@ async def main():
|
||||
qi_result = queue_imb.analyze(
|
||||
bids, asks, btc, prev_bids, prev_asks,
|
||||
btc_prices[-2] if len(btc_prices) >= 2 else 0)
|
||||
|
||||
# Order Book Imbalance: real L2 bid/ask volume skew
|
||||
if bids and asks:
|
||||
total_bids = sum(sz for _, sz in bids)
|
||||
total_asks = sum(sz for _, sz in asks)
|
||||
if total_asks > 0 and total_bids > total_asks * 1.5:
|
||||
STRATEGIES["Order Book Imbalance"]["signals"].append({
|
||||
"time": time.time(), "signal": "BUY",
|
||||
"strength": min(1.0, (total_bids / total_asks - 1.0)),
|
||||
"reason": "bid_skew_{:.1f}x".format(total_bids/total_asks)
|
||||
})
|
||||
elif total_bids > 0 and total_asks > total_bids * 1.5:
|
||||
STRATEGIES["Order Book Imbalance"]["signals"].append({
|
||||
"time": time.time(), "signal": "SELL",
|
||||
"strength": min(1.0, (total_asks / total_bids - 1.0)),
|
||||
"reason": "ask_skew_{:.1f}x".format(total_asks/total_bids)
|
||||
})
|
||||
if qi_result["signal"]:
|
||||
STRATEGIES["Queue Imbalance"]["signals"].append({
|
||||
"time": time.time(),
|
||||
|
||||
Reference in New Issue
Block a user