Kaynağa Gözat

fix: restore true fixed-window sma semantics

lxy 1 ay önce
ebeveyn
işleme
b49d9deaa7
2 değiştirilmiş dosya ile 54 ekleme ve 42 silme
  1. 4 2
      okx_codex_trader/strategy.py
  2. 50 40
      tests/test_backtest.py

+ 4 - 2
okx_codex_trader/strategy.py

@@ -11,8 +11,10 @@ def simple_moving_average(candles: list[Candle], window: int) -> list[float | No
         running_total += candle.close
         if index >= window:
             running_total -= candles[index - window].close
-        divisor = window if index + 1 >= window else index + 1
-        averages.append(running_total / divisor)
+        if index + 1 < window:
+            averages.append(None)
+            continue
+        averages.append(running_total / window)
 
     return averages
 

+ 50 - 40
tests/test_backtest.py

@@ -1,57 +1,58 @@
 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 = [
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.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,
+        60.0,
+        140.0,
         50.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.0,
-        40.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[20] = 100.0
+    opens[21] = 100.0
     opens[30] = 90.0
     opens[40] = 80.0
 
@@ -73,6 +74,15 @@ def build_crossing_series() -> list[Candle]:
     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()
 
@@ -80,7 +90,7 @@ def test_backtest_runs_fixed_sma_crossover_series():
 
     assert result.initial_equity == 10_000
     assert result.trade_count == 2
-    assert result.trades[0].entry_price == candles[20].open
+    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