spb/crypto-trade-bot
Public
Python 54.7%
TypeScript 42.5%
SQL 1.9%
1import math23import numpy as np4import pandas as pd56from ktrade.backtest.metrics import block_bootstrap_ci, deflated_sharpe, drawdown_series7from ktrade.backtest.validation import param_grid, walk_forward8from ktrade.strategy.meanrev import ou_half_life9from ktrade.strategy.momentum import TimeSeriesMomentum10from tests.conftest import synthetic_candles111213def test_drawdown():14 dd = drawdown_series(pd.Series([100, 110, 99, 120, 60]))15 assert math.isclose(dd.min(), 0.5 - 1)161718def test_ou_half_life_detects_mean_reversion_and_random_walk():19 rng = np.random.default_rng(0)20 x = np.zeros(2000)21 for i in range(1, 2000):22 x[i] = 0.9 * x[i - 1] + rng.normal()23 hl = ou_half_life(pd.Series(x))24 assert 4 < hl < 10 # theory: -ln2/ln(0.9) ≈ 6.625 rw = pd.Series(np.cumsum(rng.normal(size=2000)))26 assert math.isnan(ou_half_life(rw)) or ou_half_life(rw) > 100272829def test_bootstrap_ci_contains_point_estimate():30 rng = np.random.default_rng(1)31 r = rng.normal(0.0005, 0.01, 2000)32 bpy = 365 * 2433 ci = block_bootstrap_ci(r, bpy, n_boot=200)34 s = r.mean() / r.std(ddof=1) * math.sqrt(bpy)35 assert ci["sharpe_ci"][0] <= s <= ci["sharpe_ci"][1]363738def test_deflated_sharpe_decreases_with_trials():39 a = deflated_sharpe(1.5, 2000, 365 * 24, 1, 0.0, 0.0)["dsr"]40 b = deflated_sharpe(1.5, 2000, 365 * 24, 100, 0.0, 0.0)["dsr"]41 assert a > b424344def test_walk_forward_produces_oos_folds():45 df = synthetic_candles(n=1500, seed=11, drift=0.0005)46 wf = walk_forward(TimeSeriesMomentum, df, interval_min=60, grid={"lookback": [24, 48], "ema_fast": [10], "ema_slow": [40]}, n_folds=3)47 assert len(wf["folds"]) == 3 and wf["n_trials"] == 248 for f in wf["folds"]:49 assert f["train"][1] < f["test"][0] # OOS strictly after IS50 assert len(param_grid(TimeSeriesMomentum, {"lookback": [1, 24]})) == 1 # 1 < min → dropped51