import importlib.util import sys from pathlib import Path import pandas as pd import pytest from okx_codex_trader.models import Candle def load_module(): path = Path(__file__).resolve().parents[1] / "scripts" / "search_recent_regime_router_v2.py" spec = importlib.util.spec_from_file_location("search_recent_regime_router_v2", path) assert spec is not None module = importlib.util.module_from_spec(spec) assert spec.loader is not None sys.modules[spec.name] = module spec.loader.exec_module(module) return module def test_required_horizons_are_declared(): module = load_module() assert [label for label, _ in module.HORIZONS] == ["7d", "14d", "30d", "90d", "6m", "1y", "3y"] def test_exit_price_for_risk_hit_uses_open_on_short_stop_gap(): module = load_module() position = {"side": "short", "stop_price": 101.0, "take_profit_price": 98.0} candle = Candle(symbol="ETH-USDT-SWAP", ts=1, open=102.0, high=103.0, low=99.0, close=100.0, volume=1.0) assert module.exit_price_for_risk_hit(position, candle) == pytest.approx(102.0) def test_weak_trend_routes_to_cash(): module = load_module() spec = module.RouterSpec( name="test", trend_sma=3, btc_momentum_lookback=2, eth_momentum_lookback=2, vol_lookback=3, corr_lookback=3, ratio_lookback=3, btc_trend_min=0.01, btc_momentum_min=0.01, eth_momentum_min=0.01, max_btc_vol=0.01, max_eth_vol=0.01, min_corr=0.4, ratio_z_entry=0.5, stop_loss_pct=0.01, take_profit_pct=0.02, max_hold_bars=10, ) index = 3 assert module.regime_side( index=index, eth_close=pd.Series([100.0, 100.1, 100.0, 100.05]), btc_close=pd.Series([100.0, 100.1, 100.0, 100.05]), eth_sma=pd.Series([float("nan"), float("nan"), 100.0, 100.0]), btc_sma=pd.Series([float("nan"), float("nan"), 100.0, 100.0]), eth_vol=pd.Series([float("nan"), float("nan"), 0.001, 0.001]), btc_vol=pd.Series([float("nan"), float("nan"), 0.001, 0.001]), corr=pd.Series([float("nan"), float("nan"), 0.8, 0.8]), ratio_z=pd.Series([float("nan"), float("nan"), 0.0, 0.0]), spec=spec, ) == ("cash", "weak_trend_cash")