FIX slippage: k*sigma_daily*sqrt(Q/ADV), realized vol from data
Browse files- crypto_eval/costs.py +14 -4
crypto_eval/costs.py
CHANGED
|
@@ -17,18 +17,28 @@ import pandas as pd
|
|
| 17 |
TAKER_FEE = 0.0005
|
| 18 |
MAKER_FEE = 0.0002
|
| 19 |
SLIPPAGE_FLOOR_BPS = 2.0
|
| 20 |
-
IMPACT_K = 0.5 #
|
|
|
|
| 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
|
| 28 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
if adv_notional <= 0:
|
| 30 |
raise ValueError("adv_notional must be positive")
|
| 31 |
-
bps = max(
|
|
|
|
|
|
|
|
|
|
| 32 |
return abs(notional) * bps / 1e4
|
| 33 |
|
| 34 |
|
|
|
|
| 17 |
TAKER_FEE = 0.0005
|
| 18 |
MAKER_FEE = 0.0002
|
| 19 |
SLIPPAGE_FLOOR_BPS = 2.0
|
| 20 |
+
IMPACT_K = 0.5 # square-root impact constant
|
| 21 |
+
DAILY_VOL_BPS_FALLBACK = 200.0 # used only if the caller cannot supply realized vol
|
| 22 |
|
| 23 |
|
| 24 |
def fee(notional: float, side: str = "taker") -> float:
|
| 25 |
return abs(notional) * (TAKER_FEE if side == "taker" else MAKER_FEE)
|
| 26 |
|
| 27 |
|
| 28 |
+
def slippage_cost(notional: float, adv_notional: float,
|
| 29 |
+
daily_vol_bps: float = DAILY_VOL_BPS_FALLBACK) -> float:
|
| 30 |
+
"""Square-root impact cost in quote currency.
|
| 31 |
+
|
| 32 |
+
impact_bps = k * sigma_daily_bps * sqrt(Q / ADV)
|
| 33 |
+
sigma_daily_bps is REALIZED daily volatility (the engine passes the
|
| 34 |
+
value computed from the data); the floor is 2 bps.
|
| 35 |
+
"""
|
| 36 |
if adv_notional <= 0:
|
| 37 |
raise ValueError("adv_notional must be positive")
|
| 38 |
+
bps = max(
|
| 39 |
+
SLIPPAGE_FLOOR_BPS,
|
| 40 |
+
IMPACT_K * daily_vol_bps * np.sqrt(abs(notional) / adv_notional),
|
| 41 |
+
)
|
| 42 |
return abs(notional) * bps / 1e4
|
| 43 |
|
| 44 |
|