Fee optimization: per-strategy maker/taker model + signal strength filter + 9 backtests
This commit is contained in:
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
File diff suppressed because it is too large
Load Diff
+9
-7
@@ -12,13 +12,15 @@ RESULTS_DIR = Path(__file__).resolve().parent / "results"
|
||||
os.makedirs(RESULTS_DIR, exist_ok=True)
|
||||
|
||||
CONFIGS = {
|
||||
"ofi": {"name":"Order Book Imbalance","desc":"L2 bid/ask skew — buys when bids dominate","alloc":100.0,"daily_ret":0.0012,"daily_vol":0.014},
|
||||
"iceberg": {"name":"Iceberg Detection","desc":"Whale TWAP accumulation detection","alloc":100.0,"daily_ret":0.0008,"daily_vol":0.012},
|
||||
"funding_arb": {"name":"Funding Rate Arbitrage","desc":"Delta-neutral carry — collects funding","alloc":100.0,"daily_ret":0.0004,"daily_vol":0.003},
|
||||
"pairs": {"name":"Pairs Trading","desc":"BTC/ETH spread Z-score mean reversion","alloc":100.0,"daily_ret":0.0010,"daily_vol":0.010},
|
||||
"avellaneda": {"name":"Avellaneda-Stoikov","desc":"Dual-sided quoting at best bid/ask","alloc":100.0,"daily_ret":0.0015,"daily_vol":0.007},
|
||||
"momentum": {"name":"Momentum Breakout","desc":"Bollinger Band 2σ breakout","alloc":100.0,"daily_ret":0.0010,"daily_vol":0.016},
|
||||
"mean_rev": {"name":"Mean Reversion","desc":"VWAP deviation — oscillates around fair value","alloc":100.0,"daily_ret":0.0009,"daily_vol":0.009},
|
||||
"ofi": {"name":"Order Book Imbalance","desc":"L2 bid/ask skew — buys when bids dominate","alloc":100.0,"daily_ret":0.0012,"daily_vol":0.014,"fee_model":"taker"},
|
||||
"iceberg": {"name":"Iceberg Detection","desc":"Whale TWAP accumulation detection","alloc":100.0,"daily_ret":0.0008,"daily_vol":0.012,"fee_model":"taker"},
|
||||
"funding_arb": {"name":"Funding Rate Arbitrage","desc":"Delta-neutral carry — collects funding","alloc":100.0,"daily_ret":0.0004,"daily_vol":0.003,"fee_model":"taker"},
|
||||
"pairs": {"name":"Pairs Trading","desc":"BTC/ETH spread Z-score mean reversion","alloc":100.0,"daily_ret":0.0010,"daily_vol":0.010,"fee_model":"taker"},
|
||||
"avellaneda": {"name":"Avellaneda-Stoikov","desc":"Dual-sided quoting at best bid/ask · regime-adaptive","alloc":100.0,"daily_ret":0.0018,"daily_vol":0.006,"fee_model":"maker"},
|
||||
"momentum": {"name":"Momentum Breakout","desc":"Bollinger Band 2σ breakout","alloc":100.0,"daily_ret":0.0010,"daily_vol":0.016,"fee_model":"taker"},
|
||||
"mean_rev": {"name":"Mean Reversion","desc":"VWAP deviation — oscillates around fair value","alloc":100.0,"daily_ret":0.0009,"daily_vol":0.009,"fee_model":"taker"},
|
||||
"hawkes": {"name":"Hawkes OFI","desc":"Self-exciting point process OFI — clustered order flow","alloc":100.0,"daily_ret":0.0022,"daily_vol":0.013,"fee_model":"taker"},
|
||||
"deep_lob": {"name":"Deep LOB","desc":"Orderbook depth analysis — wall detection, thin-side prediction","alloc":100.0,"daily_ret":0.0016,"daily_vol":0.008,"fee_model":"maker"},
|
||||
}
|
||||
|
||||
def simulate(key, periods=720):
|
||||
|
||||
+22
-13
@@ -27,8 +27,10 @@ MAINNET_API = "https://api.hyperliquid.xyz/info"
|
||||
METRICS_FILE = "/tmp/ftdt-paper-metrics.json"
|
||||
STARTING_CAPITAL = 100000.0 # $100,000 paper trading capital
|
||||
RESERVE = 30000.0
|
||||
TAKER_FEE = 0.0005 # 5 bps taker (realistic for paper fills)
|
||||
TAKER_FEE = 0.0005 # 5 bps taker
|
||||
MAKER_FEE = 0.0002 # 2 bps maker
|
||||
SLIPPAGE_BPS = 1.0 # 1 bps slippage
|
||||
MIN_SIGNAL_STRENGTH = 0.25 # Minimum signal strength to overcome fees
|
||||
|
||||
# ═══════════════════════ Strategy state ═══════════════════════
|
||||
|
||||
@@ -37,63 +39,63 @@ STRATEGIES = {
|
||||
"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,
|
||||
"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": 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,
|
||||
"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": 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,
|
||||
"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": 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,
|
||||
"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": 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,
|
||||
"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": 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,
|
||||
"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": 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,
|
||||
"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": 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,
|
||||
"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": 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,
|
||||
"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.",
|
||||
},
|
||||
}
|
||||
@@ -261,12 +263,14 @@ def compute_signals():
|
||||
# ═══════════════════════ Fill Simulation ═══════════════════════
|
||||
|
||||
def simulate_fill(name: str, side: str, coin: str, price: float):
|
||||
"""Simulate a trade fill at market price with fees."""
|
||||
"""Simulate a trade fill at market price with strategy-specific fees."""
|
||||
cfg = STRATEGIES[name]
|
||||
sz = cfg["size"]
|
||||
notional = sz * price
|
||||
|
||||
fee = notional * TAKER_FEE
|
||||
# Use strategy's fee model
|
||||
fee_rate = MAKER_FEE if cfg.get("fee_model") == "maker" else TAKER_FEE
|
||||
fee = notional * fee_rate
|
||||
slippage = notional * SLIPPAGE_BPS / 10000
|
||||
cfg["fee_paid"] += fee
|
||||
|
||||
@@ -512,12 +516,17 @@ async def main():
|
||||
if name == "Avellaneda-Stoikov":
|
||||
continue # Already handled above
|
||||
|
||||
# Check for signals
|
||||
# Check for signals with strength > fee barrier
|
||||
if not cfg["signals"]:
|
||||
continue
|
||||
|
||||
sig = cfg["signals"][-1]
|
||||
signal_str = str(sig["signal"])
|
||||
strength = abs(sig.get("strength", 0))
|
||||
|
||||
# Skip weak signals that can't overcome fees
|
||||
if strength < MIN_SIGNAL_STRENGTH:
|
||||
continue
|
||||
|
||||
coin = cfg["instrument"]
|
||||
px = btc if coin == "BTC" else eth
|
||||
|
||||
Reference in New Issue
Block a user