from dataclasses import replace import pytest from okx_codex_trader.bb_squeeze_strategy import EMPTY_STATE, signal_from_frame, strategy_name from okx_codex_trader.models import Candle from scripts.run_bb_squeeze_executor import aligned_frame_from_candles from tests.test_run_bb_squeeze_executor import btc_up_candles, middle_exit_candles def live_frame(candles: list[Candle]): return aligned_frame_from_candles(candles, btc_up_candles([candle.ts for candle in candles])) def test_strategy_name_includes_breakeven_parameters() -> None: assert strategy_name().endswith("-be0.008-0.001") def test_breakeven_protection_uses_prior_confirmed_mfe() -> None: candles = middle_exit_candles(1.0) candles[-1] = Candle( symbol="ETH-USDT-SWAP", ts=candles[-1].ts, open=100.4, high=100.5, low=100.05, close=100.2, volume=1_000.0, ) state = replace(EMPTY_STATE, active_side="long", entry_price=100.0, entry_candle_ts=candles[-2].ts, last_candle_ts=candles[-2].ts, max_favorable_move_pct=0.008) next_state, signal = signal_from_frame(live_frame(candles), state) assert signal["signal"] == "exit_breakeven" assert signal["target_side"] == "flat" assert next_state.active_side is None def test_favorable_move_updates_without_same_candle_breakeven_exit() -> None: candles = middle_exit_candles(1.0) candles[-1] = Candle( symbol="ETH-USDT-SWAP", ts=candles[-1].ts, open=100.0, high=100.9, low=100.0, close=100.4, volume=1_000.0, ) state = replace(EMPTY_STATE, active_side="long", entry_price=100.0, entry_candle_ts=candles[-2].ts, last_candle_ts=candles[-2].ts, max_favorable_move_pct=0.0) next_state, signal = signal_from_frame(live_frame(candles), state) assert signal["signal"] == "hold" assert next_state.max_favorable_move_pct == pytest.approx(0.009)