Avi-L commited on
Commit
25f1782
·
verified ·
1 Parent(s): ee3d608

fee/slippage/funding cost models

Browse files
Files changed (1) hide show
  1. crypto_eval/costs.py +53 -0
crypto_eval/costs.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Fee, slippage and funding models for institutional-grade crypto backtests.
2
+
3
+ Conventions (Binance USDT-M VIP0, verified 2026-10):
4
+ - taker fee 5 bps, maker fee 2 bps
5
+ - funding: charged at 8h ticks, cost = funding_rate * notional, sign
6
+ follows the position side (longs pay when rate > 0)
7
+ - slippage: square-root impact model k * sqrt(notional / ADV), floor 2 bps
8
+
9
+ Everything here is a COST function: positive output = money lost.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import numpy as np
15
+ import pandas as pd
16
+
17
+ TAKER_FEE = 0.0005
18
+ MAKER_FEE = 0.0002
19
+ SLIPPAGE_FLOOR_BPS = 2.0
20
+ IMPACT_K = 0.5 # calibrated against Binance 20-level books; conservative
21
+
22
+
23
+ def fee(notional: float, side: str = "taker") -> float:
24
+ return abs(notional) * (TAKER_FEE if side == "taker" else MAKER_FEE)
25
+
26
+
27
+ def slippage_cost(notional: float, adv_notional: float) -> float:
28
+ """Square-root impact cost in quote currency."""
29
+ if adv_notional <= 0:
30
+ raise ValueError("adv_notional must be positive")
31
+ bps = max(SLIPPAGE_FLOOR_BPS, IMPACT_K * np.sqrt(abs(notional) / adv_notional) * 1e4)
32
+ return abs(notional) * bps / 1e4
33
+
34
+
35
+ def funding_cost(
36
+ positions: pd.Series,
37
+ funding: pd.Series,
38
+ prices: pd.Series,
39
+ ) -> pd.Series:
40
+ """Per-bar funding cost series, accrued only at funding timestamps.
41
+
42
+ positions: position in units of base asset, indexed by bar start time.
43
+ funding: funding_rate at each 8h tick.
44
+ prices: mark/close price at each bar, for notional valuation.
45
+
46
+ A bar starting at t covers [t, t+1h); a funding tick at t charges that
47
+ tick to the position held entering the tick, i.e. the position at t.
48
+ """
49
+ tick_times = funding.index
50
+ pos_at_tick = positions.reindex(tick_times, method="ffill")
51
+ price_at_tick = prices.reindex(tick_times, method="ffill")
52
+ cost = pos_at_tick * price_at_tick * funding # longs pay positive funding
53
+ return cost.reindex(positions.index, fill_value=0.0).fillna(0.0)