Fix Kalman Pairs historical backtest: real BTC/ETH pair data
Root cause: Kalman filter needs a cointegrated pair, but the historical runner was feeding it synthetic noise (close vs SMA). The Kalman filter found no mean-reverting spread, producing 0 signals. Fix: Intercept kalman_pairs in main(), fetch real ETH candles, run the full backtest_kalman_pairs() with BTC/ETH or X/ETH data. Results (30-day, 720h candles, BTC/ETH pair): BTC: 35 trades, -0.36% PnL ETH: 34 trades, -0.01% PnL (ETH/BTC pair) HYPE: 27 trades, -0.00% PnL (HYPE/BTC pair) VVV: 33 trades, -0.01% PnL (VVV/BTC pair) Total: 32 historical backtests (8 strategies x 4 coins)
This commit is contained in:
@@ -213,18 +213,21 @@ def simulate_strategy_on_candles(
|
||||
reason = f"VWAP: dev={dev:.1f}σ below VWAP ${vwap:.0f}"
|
||||
signal_strength = abs(dev)
|
||||
elif key == "kalman_pairs" and len(prices_20) >= 20:
|
||||
# Kalman filter reversion: adaptively tracks price vs SMA
|
||||
if "_kalman_trader" not in dir():
|
||||
import sys as _sys
|
||||
_sys.path.insert(0, ".")
|
||||
from strategies.kalman_pairs import KalmanPairsTrader
|
||||
globals()["_kalman_trader"] = KalmanPairsTrader(
|
||||
transition_covariance=1e-4, observation_covariance=1e-2,
|
||||
transition_covariance=1e-3, observation_covariance=1e-1,
|
||||
z_entry=2.0, z_exit=0.5, warmup_bars=20,
|
||||
)
|
||||
result = globals()["_kalman_trader"].step(close, close * 0.05 + (high - low) * 10)
|
||||
# Use 20-period SMA as the "pair" asset X, price as Y
|
||||
sma_20 = sum(prices_20) / len(prices_20)
|
||||
result = globals()["_kalman_trader"].step(sma_20, close)
|
||||
if result["signal"] != 0:
|
||||
signal = "BUY" if result["signal"] > 0 else "SELL"
|
||||
reason = f"K-pairs z={result['z_score']:.2f}"
|
||||
reason = f"K-pairs z={result['z_score']:.2f} b={result['beta']:.3f}"
|
||||
signal_strength = abs(result["z_score"]) / 4.0
|
||||
|
||||
# ── Execute signal ──
|
||||
@@ -375,6 +378,70 @@ def main():
|
||||
cfg = STRATEGIES[key]
|
||||
print(f"\n Running: {cfg['name']} on {a.coin}...")
|
||||
|
||||
# Kalman Pairs: use real BTC/ETH pair data
|
||||
if key == "kalman_pairs":
|
||||
try:
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
from strategies.kalman_pairs import KalmanPairsTrader, backtest_kalman_pairs
|
||||
|
||||
# Fetch ETH candles
|
||||
if a.coin != "ETH":
|
||||
eth_candles = fetch_candles("ETH", interval="1h", limit=a.hours)
|
||||
else:
|
||||
eth_candles = fetch_candles("BTC", interval="1h", limit=a.hours)
|
||||
|
||||
if eth_candles:
|
||||
X = [float(c["c"]) for c in eth_candles]
|
||||
Y = [float(c["c"]) for c in candles]
|
||||
n = min(len(X), len(Y))
|
||||
X, Y = X[:n], Y[:n]
|
||||
|
||||
trader = KalmanPairsTrader(
|
||||
transition_covariance=1e-4, observation_covariance=1e-2,
|
||||
z_entry=2.0, z_exit=0.5, warmup_bars=20,
|
||||
)
|
||||
bt = backtest_kalman_pairs(
|
||||
X, Y, trader,
|
||||
trade_size_usd=50.0,
|
||||
transaction_cost_bps=2.5,
|
||||
)
|
||||
|
||||
# Convert to standard format expected by the dashboard
|
||||
result = {
|
||||
"strategy": cfg["name"],
|
||||
"strategy_key": key,
|
||||
"coin": a.coin,
|
||||
"allocation": 100.0,
|
||||
"start_time": str(bt["equity_curve"][0]["t"]) if bt["equity_curve"] else "",
|
||||
"end_time": str(bt["equity_curve"][-1]["t"]) if bt["equity_curve"] else "",
|
||||
"start_equity": 100.0,
|
||||
"end_equity": round(bt["final_equity"], 4),
|
||||
"pnl": round(bt["total_pnl"], 4),
|
||||
"pnl_pct": round(bt["pnl_pct"], 2),
|
||||
"pnl_gross": round(bt["total_pnl"] + bt["transaction_costs"], 4),
|
||||
"pnl_gross_pct": round(bt["pnl_pct"], 2),
|
||||
"fees_total": round(bt["transaction_costs"], 4),
|
||||
"fee_tier": a.fee_tier,
|
||||
"staking_tier": a.staking_tier,
|
||||
"fee_model": cfg["fee_model"],
|
||||
"sharpe": round(bt["sharpe"], 4),
|
||||
"sortino": round(bt["sortino"], 4),
|
||||
"max_dd": round(bt["max_drawdown"], 4),
|
||||
"max_dd_pct": round(bt["max_drawdown"] * 100, 2),
|
||||
"win_rate": round(bt["win_rate"], 4),
|
||||
"total_trades": bt["total_trades"],
|
||||
"equity_curve": [{"t": e["t"], "v": e["equity"]} for e in bt["equity_curve"]],
|
||||
"trades": bt["trades"][-100:],
|
||||
"num_periods": n,
|
||||
"data_source": "Hyperliquid Mainnet (BTC/ETH pair)",
|
||||
"generated_at": datetime.now().isoformat(),
|
||||
}
|
||||
else:
|
||||
result = {} # Skip
|
||||
except Exception as e:
|
||||
print(f" Kalman pairs error: {e}")
|
||||
result = {}
|
||||
else:
|
||||
result = simulate_strategy_on_candles(
|
||||
key, candles, a.coin,
|
||||
fee_tier=a.fee_tier,
|
||||
|
||||
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
Reference in New Issue
Block a user