Regime-switching Avellaneda-Stoikov + Advanced Strategies research doc
Implemented regime detection in paper trader: - Rolling 30-tick volatility classifies market as LOW_VOL/NORMAL/HIGH_VOL - A-S fill probability adapts: 25% (low vol), 15% (normal), 8% (high vol) - HIGH_VOL with spreads >$30: skip trading (adverse selection protection) - Regime shown on dashboard header with color-coded badge Added docs/ADVANCED_STRATEGIES.md — comprehensive research covering: 1. Deep Learning LOB Prediction (Transformers/TLOB) 2. Latency Arbitrage in Fragmented Markets 3. Hawkes Process OFI Modeling 4. Cross-Chain MEV Arbitrage 5. Institutional Capital Flow Arbitrage (ETF flows) 6. Hybrid Transformer + Hawkes Fusion 7. Implementation Roadmap (Phase 1-4) All strategies referenced with papers from arXiv, SSRN, and empirical studies.
This commit is contained in:
+58
-9
@@ -88,6 +88,36 @@ btc_prices: deque = deque(maxlen=120)
|
||||
eth_prices: deque = deque(maxlen=120)
|
||||
funding_rates: deque = deque(maxlen=100)
|
||||
|
||||
# ═══════════════════════ Regime Detection ═══════════════════════
|
||||
# Uses rolling volatility to classify market regime:
|
||||
# LOW_VOL: quiet markets → tight spreads, aggressive size
|
||||
# NORMAL: standard conditions → baseline parameters
|
||||
# HIGH_VOL: turbulence → wide spreads, reduced size, cautious signals
|
||||
|
||||
current_regime = "NORMAL"
|
||||
regime_confidence = 0.5
|
||||
|
||||
def detect_regime():
|
||||
"""Classify market regime from rolling BTC price volatility."""
|
||||
global current_regime, regime_confidence
|
||||
if len(btc_prices) < 30:
|
||||
return "NORMAL"
|
||||
|
||||
window = list(btc_prices)[-30:]
|
||||
# Compute 30-tick log returns
|
||||
returns = [math.log(window[i] / window[i-1]) for i in range(1, len(window))]
|
||||
realized_vol = math.sqrt(sum(r**2 for r in returns) / len(returns))
|
||||
|
||||
# Annualize (30 ticks at ~1s each → 30s window, annualize to 1yr)
|
||||
annual_vol = realized_vol * math.sqrt(365 * 24 * 60 * 60 / 30)
|
||||
regime_confidence = min(0.95, max(0.2, annual_vol / 2.0))
|
||||
|
||||
if annual_vol < 0.15: # <15% annualized
|
||||
return "LOW_VOL"
|
||||
elif annual_vol > 0.60: # >60% annualized
|
||||
return "HIGH_VOL"
|
||||
return "NORMAL"
|
||||
|
||||
# ═══════════════════════ Mainnet Data ═══════════════════════
|
||||
|
||||
def get_mainnet_prices():
|
||||
@@ -266,20 +296,35 @@ def simulate_fill(name: str, side: str, coin: str, price: float):
|
||||
# ═══════════════════════ A-S Spread Capture ═══════════════════════
|
||||
|
||||
def simulate_avellaneda(btc_bid, btc_ask):
|
||||
"""Avellaneda-Stoikov: simulate spread capture when orders are at best bid/ask."""
|
||||
"""Avellaneda-Stoikov: regime-adaptive spread capture.
|
||||
|
||||
Regime-dependent behavior:
|
||||
LOW_VOL → fill_prob=25%, tight margins (capture small spreads frequently)
|
||||
NORMAL → fill_prob=15%, baseline
|
||||
HIGH_VOL → fill_prob=8%, skip if spread too wide (adverse selection risk)
|
||||
"""
|
||||
cfg = STRATEGIES["Avellaneda-Stoikov"]
|
||||
if btc_bid <= 0 or btc_ask <= 0:
|
||||
return
|
||||
|
||||
# Each tick, there's a chance our quotes get hit
|
||||
# On mainnet, this happens frequently. Simulate with probability.
|
||||
if random.random() < 0.15: # 15% per tick = fill every ~7 seconds on average
|
||||
# Our bid gets hit (we buy at bid, sell at ask later for profit)
|
||||
regime = current_regime
|
||||
spread = btc_ask - btc_bid
|
||||
|
||||
# Regime-dependent fill probability
|
||||
if regime == "LOW_VOL":
|
||||
fill_prob = 0.25
|
||||
elif regime == "HIGH_VOL":
|
||||
fill_prob = 0.08
|
||||
# During high vol with wide spreads, avoid getting picked off
|
||||
if spread > 30: # >$30 spread = dangerous
|
||||
return
|
||||
else:
|
||||
fill_prob = 0.15
|
||||
|
||||
if random.random() < fill_prob:
|
||||
if cfg["position"] <= 0:
|
||||
# Buy at bid
|
||||
bid_fill_price = btc_bid
|
||||
else:
|
||||
# Sell at ask (close position)
|
||||
bid_fill_price = btc_ask
|
||||
|
||||
side = "BUY" if cfg["position"] <= 0 else "SELL"
|
||||
@@ -342,6 +387,8 @@ def write_metrics():
|
||||
"status": "running",
|
||||
"btc_price": btc_prices[-1] if btc_prices else 0,
|
||||
"eth_price": eth_prices[-1] if eth_prices else 0,
|
||||
"regime": current_regime,
|
||||
"regime_confidence": regime_confidence,
|
||||
}
|
||||
try:
|
||||
with open(METRICS_FILE, "w") as f:
|
||||
@@ -354,7 +401,7 @@ async def main():
|
||||
log.info("="*60)
|
||||
log.info(" FTDT Quant Lab — PAPER TRADING (Mainnet Data)")
|
||||
log.info(f" Capital: ${STARTING_CAPITAL:,} | Reserve: ${RESERVE:,}")
|
||||
log.info(f" 7 strategies × $1,000 allocation")
|
||||
log.info(f" 7 strategies × ${STRATEGIES['Order Book Imbalance']['allocation']:,.0f} allocation")
|
||||
log.info(f" Fees: {TAKER_FEE*100:.2f}% taker | Slippage: {SLIPPAGE_BPS} bps")
|
||||
log.info(f" Data: Hyperliquid MAINNET")
|
||||
log.info(f" Dashboard: https://ftdt.io/cv")
|
||||
@@ -390,6 +437,7 @@ async def main():
|
||||
|
||||
# Compute signals every 5 ticks
|
||||
if tick % 5 == 0:
|
||||
current_regime = detect_regime()
|
||||
compute_signals()
|
||||
|
||||
# Execute signals every 3-5 ticks
|
||||
@@ -443,7 +491,8 @@ async def main():
|
||||
btc_now = btc_prices[-1] if btc_prices else 0
|
||||
log.info(
|
||||
f"Tick {tick:4d} | BTC: ${btc_now:,.0f} | "
|
||||
f"PnL: ${tp:+.2f} | Trades: {tr:3d} | Fees: ${tf:.2f}"
|
||||
f"PnL: ${tp:+.2f} | Trades: {tr:3d} | Fees: ${tf:.2f} | "
|
||||
f"Regime: {current_regime}"
|
||||
)
|
||||
|
||||
await asyncio.sleep(1)
|
||||
|
||||
Reference in New Issue
Block a user