Download crypto_eval/costs.py from Avi-L/crypto-quant-eval: direct link, hf CLI and curl.
- Browser
- Download file 2.29 kB
-
https://huggingface.co/Avi-L/crypto-quant-eval/resolve/main/crypto_eval/costs.py
- Command line
-
hf download hf://Avi-L/crypto-quant-eval/crypto_eval/costs.py
-
curl -L -o costs.py https://huggingface.co/Avi-L/crypto-quant-eval/resolve/main/crypto_eval/costs.py
2.29 kB
| """Fee, slippage and funding models for institutional-grade crypto backtests. | |
| Conventions (Binance USDT-M VIP0, verified 2026-10): | |
| - taker fee 5 bps, maker fee 2 bps | |
| - funding: charged at 8h ticks, cost = funding_rate * notional, sign | |
| follows the position side (longs pay when rate > 0) | |
| - slippage: square-root impact model k * sqrt(notional / ADV), floor 2 bps | |
| Everything here is a COST function: positive output = money lost. | |
| """ | |
| from __future__ import annotations | |
| import numpy as np | |
| import pandas as pd | |
| TAKER_FEE = 0.0005 | |
| MAKER_FEE = 0.0002 | |
| SLIPPAGE_FLOOR_BPS = 2.0 | |
| IMPACT_K = 0.5 # square-root impact constant | |
| DAILY_VOL_BPS_FALLBACK = 200.0 # used only if the caller cannot supply realized vol | |
| def fee(notional: float, side: str = "taker") -> float: | |
| return abs(notional) * (TAKER_FEE if side == "taker" else MAKER_FEE) | |
| def slippage_cost(notional: float, adv_notional: float, | |
| daily_vol_bps: float = DAILY_VOL_BPS_FALLBACK) -> float: | |
| """Square-root impact cost in quote currency. | |
| impact_bps = k * sigma_daily_bps * sqrt(Q / ADV) | |
| sigma_daily_bps is REALIZED daily volatility (the engine passes the | |
| value computed from the data); the floor is 2 bps. | |
| """ | |
| if adv_notional <= 0: | |
| raise ValueError("adv_notional must be positive") | |
| bps = max( | |
| SLIPPAGE_FLOOR_BPS, | |
| IMPACT_K * daily_vol_bps * np.sqrt(abs(notional) / adv_notional), | |
| ) | |
| return abs(notional) * bps / 1e4 | |
| def funding_cost( | |
| positions: pd.Series, | |
| funding: pd.Series, | |
| prices: pd.Series, | |
| ) -> pd.Series: | |
| """Per-bar funding cost series, accrued only at funding timestamps. | |
| positions: position in units of base asset, indexed by bar start time. | |
| funding: funding_rate at each 8h tick. | |
| prices: mark/close price at each bar, for notional valuation. | |
| A bar starting at t covers [t, t+1h); a funding tick at t charges that | |
| tick to the position held entering the tick, i.e. the position at t. | |
| """ | |
| tick_times = funding.index | |
| pos_at_tick = positions.reindex(tick_times, method="ffill") | |
| price_at_tick = prices.reindex(tick_times, method="ffill") | |
| cost = pos_at_tick * price_at_tick * funding # longs pay positive funding | |
| return cost.reindex(positions.index, fill_value=0.0).fillna(0.0) | |