Fee optimization: per-strategy maker/taker model + signal strength filter + 9 backtests

This commit is contained in:
ramseshk
2026-08-04 06:12:35 +00:00
parent e2b3f40b37
commit 2c0750e355
11 changed files with 32476 additions and 20 deletions
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
View File
@@ -12,13 +12,15 @@ RESULTS_DIR = Path(__file__).resolve().parent / "results"
os.makedirs(RESULTS_DIR, exist_ok=True) os.makedirs(RESULTS_DIR, exist_ok=True)
CONFIGS = { 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}, "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}, "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}, "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}, "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","alloc":100.0,"daily_ret":0.0015,"daily_vol":0.007}, "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}, "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}, "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): def simulate(key, periods=720):
+22 -13
View File
@@ -27,8 +27,10 @@ MAINNET_API = "https://api.hyperliquid.xyz/info"
METRICS_FILE = "/tmp/ftdt-paper-metrics.json" METRICS_FILE = "/tmp/ftdt-paper-metrics.json"
STARTING_CAPITAL = 100000.0 # $100,000 paper trading capital STARTING_CAPITAL = 100000.0 # $100,000 paper trading capital
RESERVE = 30000.0 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 SLIPPAGE_BPS = 1.0 # 1 bps slippage
MIN_SIGNAL_STRENGTH = 0.25 # Minimum signal strength to overcome fees
# ═══════════════════════ Strategy state ═══════════════════════ # ═══════════════════════ Strategy state ═══════════════════════
@@ -37,63 +39,63 @@ STRATEGIES = {
"allocation": 10000.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", "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle",
"position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "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.", "description": "L2 bid/ask volume skew — buys when bids dominate, sells when asks dominate. Mean-reverting at volume extremes.",
}, },
"Iceberg Detection": { "Iceberg Detection": {
"allocation": 10000.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", "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle",
"position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "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.", "description": "Detects whale accumulation (many small buys over time). Follows the smart money flow.",
}, },
"Funding Rate Arb": { "Funding Rate Arb": {
"allocation": 10000.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", "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle",
"position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "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.", "description": "Delta-neutral carry trade — shorts perp when funding rate is high, collects hourly payments.",
}, },
"Pairs Trading": { "Pairs Trading": {
"allocation": 10000.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", "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle",
"position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "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.", "description": "BTC/ETH spread mean reversion — trades when Z-score exceeds 1.5 sigma. Pairs converge back to equilibrium.",
}, },
"Avellaneda-Stoikov": { "Avellaneda-Stoikov": {
"allocation": 10000.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", "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle",
"position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "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.", "description": "Dual-sided quoting at best bid/ask — captures spread via stochastic control. Simulated fill when spread is crossed.",
}, },
"Momentum Breakout": { "Momentum Breakout": {
"allocation": 10000.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", "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle",
"position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "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.", "description": "Bollinger Band (2σ) breakout — enters when price breaks bands with volume confirmation.",
}, },
"Mean Reversion": { "Mean Reversion": {
"allocation": 10000.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", "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle",
"position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "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.", "description": "VWAP deviation — buys below VWAP, sells above. Oscillates around fair value.",
}, },
"Hawkes OFI (new)": { "Hawkes OFI (new)": {
"allocation": 10000.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", "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle",
"position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "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.", "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)": { "Deep LOB (new)": {
"allocation": 10000.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", "trades_today": 0, "wins": 0, "win_rate": 0.0, "status": "idle",
"position": 0.0, "entry_price": 0.0, "fee_paid": 0.0, "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.", "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 ═══════════════════════ # ═══════════════════════ Fill Simulation ═══════════════════════
def simulate_fill(name: str, side: str, coin: str, price: float): 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] cfg = STRATEGIES[name]
sz = cfg["size"] sz = cfg["size"]
notional = sz * price 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 slippage = notional * SLIPPAGE_BPS / 10000
cfg["fee_paid"] += fee cfg["fee_paid"] += fee
@@ -512,12 +516,17 @@ async def main():
if name == "Avellaneda-Stoikov": if name == "Avellaneda-Stoikov":
continue # Already handled above continue # Already handled above
# Check for signals # Check for signals with strength > fee barrier
if not cfg["signals"]: if not cfg["signals"]:
continue continue
sig = cfg["signals"][-1] sig = cfg["signals"][-1]
signal_str = str(sig["signal"]) 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"] coin = cfg["instrument"]
px = btc if coin == "BTC" else eth px = btc if coin == "BTC" else eth