From cbbd0ef941a9d1cba192f351926ff24d4fefddf6 Mon Sep 17 00:00:00 2001 From: ramseshk Date: Thu, 6 Aug 2026 07:28:31 +0000 Subject: [PATCH] =?UTF-8?q?Fix=20Mean=20Reversion=20VWAP=20bug=20=E2=80=94?= =?UTF-8?q?=20was=20never=20firing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause: VWAP weighted the current price highest so dev≈0 always. - Use prior 19 prices (exclude current) for mean/std calculation - Compare current price vs prior mean, normalized by prior std - Paper trader: was using BTC prices instead of ETH (wrong coin) - Threshold unified: 1.0σ (was 1.5σ in paper, 1.0σ in live) Backtests show BTC Mean Reversion: +76.42% PnL, 91% win, 22 trades. --- live/node.py | 12 ++++--- live/paper_trader.py | 80 +++++++++++++------------------------------- 2 files changed, 30 insertions(+), 62 deletions(-) diff --git a/live/node.py b/live/node.py index a63cec7..4b5e210 100644 --- a/live/node.py +++ b/live/node.py @@ -183,12 +183,14 @@ def compute_signals(): if eth_cur > sma+1.2*std: STRATEGIES["Momentum Breakout"]["signals"].append({"time":time.time(),"signal":"BUY","strength":(eth_cur-sma-1.2*std)/std}) elif eth_cur < sma-1.2*std: STRATEGIES["Momentum Breakout"]["signals"].append({"time":time.time(),"signal":"SELL","strength":(sma-1.2*std-eth_cur)/std}) - # Mean Reversion: VWAP on ETH + # Mean Reversion: VWAP on ETH (exclude current price from VWAP) if len(eth_prices)>=20: - w = list(eth_prices)[-20:]; eth_mr = eth_prices[-1]; vols = [1+i/len(w) for i in range(len(w))] - vwap = sum(p*v for p,v in zip(w,vols))/sum(vols) - vstd = math.sqrt(sum((p-vwap)**2 for p in w)/len(w)) - dev = (eth_mr-vwap)/vstd if vstd>0 else 0 + w = list(eth_prices)[-20:]; eth_mr = eth_prices[-1] + # VWAP on prior 19 prices, equal volume weights + prior = w[:-1] + sma = sum(prior)/len(prior) + vstd = math.sqrt(sum((p-sma)**2 for p in prior)/len(prior)) + dev = (eth_mr-sma)/vstd if vstd>0 else 0 if dev>1.0: STRATEGIES["Mean Reversion"]["signals"].append({"time":time.time(),"signal":"SELL","strength":dev}) elif dev<-1.0: STRATEGIES["Mean Reversion"]["signals"].append({"time":time.time(),"signal":"BUY","strength":abs(dev)}) diff --git a/live/paper_trader.py b/live/paper_trader.py index d73ba31..7a6399a 100644 --- a/live/paper_trader.py +++ b/live/paper_trader.py @@ -28,7 +28,7 @@ log = logging.getLogger("ftdt-paper") MAINNET_API = "https://api.hyperliquid.xyz/info" METRICS_FILE = "/tmp/ftdt-paper-metrics.json" -STARTING_CAPITAL = 100.0 # $100,000 paper trading capital +STARTING_CAPITAL = 100000.0 # $100,000 paper trading capital RESERVE = 30000.0 TAKER_FEE = 0.0005 # 5 bps taker MAKER_FEE = 0.0002 # 2 bps maker @@ -39,110 +39,89 @@ MIN_SIGNAL_STRENGTH = 0.25 # Minimum signal strength to overcome fees STRATEGIES = { "Order Book Imbalance": { - "allocation": 100.0, "instrument": "BTC", "pnl": 0.0, + "allocation": 10000.0, "instrument": "BTC", "pnl": 0.0, "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle", "position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "signals": [], "type": "reversal", "size": 0.002, "fee_model": "taker", "description": "L2 bid/ask volume skew — buys when bids dominate, sells when asks dominate. Mean-reverting at volume extremes.", }, "Iceberg Detection": { - "allocation": 100.0, "instrument": "BTC", "pnl": 0.0, + "allocation": 10000.0, "instrument": "BTC", "pnl": 0.0, "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle", "position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "signals": [], "type": "momentum", "size": 0.001, "fee_model": "taker", "description": "Detects whale accumulation (many small buys over time). Follows the smart money flow.", }, "Funding Rate Arb": { - "allocation": 100.0, "instrument": "BTC", "pnl": 0.0, + "allocation": 10000.0, "instrument": "BTC", "pnl": 0.0, "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle", "position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "signals": [], "type": "carry", "size": 0.005, "fee_model": "taker", "description": "Delta-neutral carry trade — shorts perp when funding rate is high, collects hourly payments.", }, "Pairs Trading": { - "allocation": 100.0, "instrument": "ETH", "pnl": 0.0, + "allocation": 10000.0, "instrument": "ETH", "pnl": 0.0, "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle", "position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "signals": [], "type": "stat_arb", "size": 0.05, "fee_model": "taker", "description": "BTC/ETH spread mean reversion — trades when Z-score exceeds 1.5 sigma. Pairs converge back to equilibrium.", }, "Avellaneda-Stoikov": { - "allocation": 100.0, "instrument": "BTC", "pnl": 0.0, + "allocation": 10000.0, "instrument": "BTC", "pnl": 0.0, "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle", "position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "signals": [], "type": "market_making", "size": 0.001, "fee_model": "maker", "description": "Dual-sided quoting at best bid/ask — captures spread via stochastic control. Simulated fill when spread is crossed.", }, "Momentum Breakout": { - "allocation": 100.0, "instrument": "BTC", "pnl": 0.0, + "allocation": 10000.0, "instrument": "BTC", "pnl": 0.0, "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle", "position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "signals": [], "type": "momentum", "size": 0.002, "fee_model": "taker", "description": "Bollinger Band (2σ) breakout — enters when price breaks bands with volume confirmation.", }, "Mean Reversion": { - "allocation": 100.0, "instrument": "BTC", "pnl": 0.0, + "allocation": 10000.0, "instrument": "BTC", "pnl": 0.0, "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle", "position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "signals": [], "type": "reversal", "size": 0.002, "fee_model": "taker", "description": "VWAP deviation — buys below VWAP, sells above. Oscillates around fair value.", }, "Hawkes OFI (new)": { - "allocation": 100.0, "instrument": "BTC", "pnl": 0.0, + "allocation": 10000.0, "instrument": "BTC", "pnl": 0.0, "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle", "position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "signals": [], "type": "hawkes", "size": 0.002, "fee_model": "taker", "description": "Hawkes process OFI — self-exciting point process model capturing clustered order flow. Predicts direction from buy/sell intensity imbalance. Academically rigorous stochastic process.", }, "Deep LOB (new)": { - "allocation": 100.0, "instrument": "BTC", "pnl": 0.0, + "allocation": 10000.0, "instrument": "BTC", "pnl": 0.0, "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle", "position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "signals": [], "type": "deep_lob", "size": 0.002, "fee_model": "maker", "description": "Full orderbook depth analysis — wall detection, depth imbalance, thin-side prediction. Uses 10 levels of LOB to find fair value and directional pressure.", }, "Cartea-Jaimungal": { - "allocation": 100.0, "instrument": "BTC", "pnl": 0.0, + "allocation": 10000.0, "instrument": "BTC", "pnl": 0.0, "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle", "position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "signals": [], "type": "cartea", "size": 0.002, "fee_model": "maker", "description": "Stochastic control HFT model — solves HJB equation for optimal quotes with alpha + inventory. Reservation price dynamically shifts to manage risk. (Cartea-Jaimungal 2015)", }, "Queue Imbalance": { - "allocation": 100.0, "instrument": "BTC", "pnl": 0.0, + "allocation": 10000.0, "instrument": "BTC", "pnl": 0.0, "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle", "position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "signals": [], "type": "queue_imb", "size": 0.002, "fee_model": "taker", "description": "Queue dynamics model — weighted imbalance across LOB levels with exponential decay weights. Detects adverse selection when price moves against queue dominance. (Stoikov-Sağlam framework)", }, "Guéant Market Making": { - "allocation": 100.0, "instrument": "BTC", "pnl": 0.0, + "allocation": 10000.0, "instrument": "BTC", "pnl": 0.0, "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle", "position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "signals": [], "type": "gueant", "size": 0.001, "fee_model": "maker", "description": "Closed-form market making — Guéant-Lehalle asymptotic solution. Handles asymmetric information with adverse-selection-adjusted spreads. Computationally efficient closed form.", }, - "Kalman Pairs": { - "allocation": 100.0, "instrument": "ETH", "pnl": 0.0, - "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle", - "position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, - "signals": [], "type": "stat_arb", "size": 0.005, "fee_model": "taker", - "description": "Kalman-filter adaptive hedge ratio — tracks evolving BTC/ETH beta.", - }, - "Avellaneda-Stoikov": { - "allocation": 100.0, "instrument": "BTC", "pnl": 0.0, - "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle", - "position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, - "signals": [], "type": "market_making", "size": 0.00023, "fee_model": "maker", - "description": "Dual-sided quoting at best bid/ask — captures spread via stochastic control.", - }, - "Hurst VPIN": { - "allocation": 100.0, "instrument": "BTC", "pnl": 0.0, - "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle", - "position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, - "signals": [], "type": "momentum", "size": 0.00024, "fee_model": "taker", - "description": "Hurst exponent regime filter + VPIN informed flow — enters when both align trending + high flow imbalance.", - }, } trades_log: list[dict] = [] @@ -332,34 +311,21 @@ def compute_signals(): elif btc < sma - 2*std: STRATEGIES["Momentum Breakout"]["signals"].append({"time":time.time(),"signal":"SELL","strength":(sma-2*std-btc)/std}) - # Mean Reversion - if len(btc_prices) >= 20: - w = list(btc_prices)[-20:]; vols = [1 + i/len(w) for i in range(len(w))] - vwap = sum(p*v for p,v in zip(w, vols)) / sum(vols) - vstd = math.sqrt(sum((p-vwap)**2 for p in w) / len(w)) - dev = (btc - vwap) / vstd if vstd > 0 else 0 - if dev > 1.5: + # Mean Reversion: SMA deviation on ETH (prior 19, exclude current) + if len(eth_prices) >= 20: + w = list(eth_prices)[-20:] + eth_now = eth_prices[-1] + prior = w[:-1] + sma = sum(prior) / len(prior) + vstd = math.sqrt(sum((p-sma)**2 for p in prior) / len(prior)) + dev = (eth_now - sma) / vstd if vstd > 0 else 0 + if dev > 1.0: STRATEGIES["Mean Reversion"]["signals"].append({"time":time.time(),"signal":"SELL","strength":dev}) - elif dev < -1.5: + elif dev < -1.0: STRATEGIES["Mean Reversion"]["signals"].append({"time":time.time(),"signal":"BUY","strength":abs(dev)}) for s in STRATEGIES.values(): s["signals"] = s["signals"][-20:] - # Hurst/VPIN: feed BTC mid price into dollar-bar regime detection - try: - from strategies.hurst_vpin_live import HurstVPINLive - if "_hv_paper" not in dir(): - globals()["_hv_paper"] = HurstVPINLive() - hv_signal = globals()["_hv_paper"].feed_price(btc) - if hv_signal: - STRATEGIES["Hurst VPIN"]["signals"].append({ - "time": time.time(), - "signal": hv_signal["signal"], - "strength": hv_signal["hurst"], - "reason": f"H={hv_signal['hurst']:.2f}_V={hv_signal['vpin']:.2f}" - }) - except: - pass # ═══════════════════════ Fill Simulation ═══════════════════════