Files
fxhnt/tests/unit/test_isv_adaptive_weights.py
2026-06-21 23:32:36 +02:00

53 lines
2.4 KiB
Python

import math
from fxhnt.application.paper_book import isv_adaptive_weights
def _series(vals, start=1): # {epoch_day: ret}
return {start + i: v for i, v in enumerate(vals)}
def test_empty_active_returns_empty():
assert isv_adaptive_weights({}, []) == {}
def test_no_history_is_equal_weight():
w = isv_adaptive_weights({"a": {}, "b": {}}, ["a", "b"])
assert w == {"a": 0.5, "b": 0.5}
def test_low_vol_sleeve_gets_higher_weight():
lo = _series([0.001, -0.001] * 30) # ~60 obs low vol
hi = _series([0.05, -0.05] * 30) # ~60 obs high vol
w = isv_adaptive_weights({"lo": lo, "hi": hi}, ["lo", "hi"])
assert math.isclose(sum(w.values()), 1.0, abs_tol=1e-9)
assert w["lo"] > w["hi"]
def test_rising_vol_regime_shrinks_lookback():
# calm must extend through the PRIOR block (indices 40-49); only the last 10 (indices 50-59) are hot,
# so std(recent L_min=10) > std(prior L_min=10). Expose L_t via the debug return for determinism.
calm = [0.01, -0.01] * 25 # 50 obs low vol (covers the prior block)
hot = [0.06, -0.06] * 5 # last 10 obs high vol (the recent block)
s = _series(calm + hot)
_, dbg = isv_adaptive_weights({"x": s, "y": _series([0.01, -0.01] * 30)},
["x", "y"], _debug=True)
assert dbg["L_t"] < 60
flat = _series([0.01, -0.01] * 40)
_, dbg2 = isv_adaptive_weights({"x": flat, "y": flat}, ["x", "y"], _debug=True)
assert dbg2["L_t"] == 60
def test_cap_never_below_one_over_n():
s = _series([0.02, -0.02] * 30)
_, dbg = isv_adaptive_weights({"a": s, "b": s, "c": s}, ["a", "b", "c"], _debug=True)
assert dbg["cap_t"] >= 1.0 / 3 - 1e-9
def test_floor_holds_after_waterfill():
# one very-low-vol sleeve would dominate; floor keeps the other above phi/N.
dom = _series([0.0001, -0.0001] * 40)
weak = _series([0.2, -0.2] * 40)
w = isv_adaptive_weights({"dom": dom, "weak": weak}, ["dom", "weak"])
assert math.isclose(sum(w.values()), 1.0, abs_tol=1e-9)
assert w["weak"] >= 0.5 / 2 - 1e-9 # w_floor = phi/N = 0.25
assert w["dom"] <= 1.0 + 1e-9
def test_mixed_history_cold_sleeve_gets_floor():
has = _series([0.02, -0.02] * 30)
w = isv_adaptive_weights({"has": has, "cold": {1: 0.01}}, ["has", "cold"]) # cold has <2 obs
assert math.isclose(w["cold"], 0.5 / 2, abs_tol=1e-9) # exactly w_floor
assert math.isclose(sum(w.values()), 1.0, abs_tol=1e-9)