from okx_codex_trader.backtest import run_backtest from okx_codex_trader.models import Candle from okx_codex_trader.strategy import simple_moving_average def build_crossing_series() -> list[Candle]: closes = [ 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 140.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 50.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, 60.0, ] opens = list(closes) opens[21] = 100.0 opens[30] = 90.0 opens[40] = 80.0 candles = [] for index, (open_price, close_price) in enumerate(zip(opens, closes)): high = max(open_price, close_price) low = min(open_price, close_price) candles.append( Candle( symbol="BTC-USDT-SWAP", ts=index, open=open_price, high=high, low=low, close=close_price, volume=1_000.0, ) ) return candles def test_simple_moving_average_requires_full_window(): candles = [ Candle(symbol="BTC-USDT-SWAP", ts=index, open=close, high=close, low=close, close=close, volume=1_000.0) for index, close in enumerate([10.0, 20.0, 30.0, 40.0]) ] assert simple_moving_average(candles, 3) == [None, None, 20.0, 30.0] def test_backtest_runs_fixed_sma_crossover_series(): candles = build_crossing_series() result = run_backtest(candles=candles, leverage=2) assert result.initial_equity == 10_000 assert result.trade_count == 2 assert result.trades[0].entry_price == candles[21].open assert result.trades[0].exit_price == candles[30].open assert result.trades[0].margin_used == 10_000 assert result.trades[1].margin_used == result.trades[0].ending_equity assert result.ending_equity == result.trades[-1].ending_equity assert "total_return" in result.to_dict() assert "max_drawdown" in result.to_dict() assert result.win_rate == 0.5