feat: add 5-day reversal strategy
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+4
-4
@@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
"""End-to-end smoke test: download data -> backtest SMA crossover -> print results."""
|
"""End-to-end smoke test: download data -> backtest 5-day reversal -> print results."""
|
||||||
import logging
|
import logging
|
||||||
from backtest.config import BacktestConfig
|
from backtest.config import BacktestConfig
|
||||||
from backtest.runner import BacktestRunner
|
from backtest.runner import BacktestRunner
|
||||||
from strategies.base import SmaCross
|
from strategies.reversal import FiveDayReversal
|
||||||
from analysis.report import print_results
|
from analysis.report import print_results
|
||||||
|
|
||||||
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(message)s")
|
||||||
@@ -11,14 +11,14 @@ logging.basicConfig(level=logging.INFO, format="%(asctime)s [%(levelname)s] %(me
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
config = BacktestConfig(
|
config = BacktestConfig(
|
||||||
symbols=["sh600000"], # 浦发银行
|
symbols=["sh600000", "sz000001", "sh600519"], # 浦发银行, 平安银行, 贵州茅台
|
||||||
start_date="2023-01-01",
|
start_date="2023-01-01",
|
||||||
end_date="2024-12-31",
|
end_date="2024-12-31",
|
||||||
initial_cash=1_000_000,
|
initial_cash=1_000_000,
|
||||||
)
|
)
|
||||||
|
|
||||||
runner = BacktestRunner(config)
|
runner = BacktestRunner(config)
|
||||||
results = runner.run(SmaCross)
|
results = runner.run(FiveDayReversal)
|
||||||
print_results(results, config.initial_cash)
|
print_results(results, config.initial_cash)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import backtrader as bt
|
||||||
|
|
||||||
|
|
||||||
|
class FiveDayReversal(bt.Strategy):
|
||||||
|
"""Buy on 5-day oversold signal, sell on bounce or time stop."""
|
||||||
|
|
||||||
|
params = (
|
||||||
|
("lookback", 5),
|
||||||
|
("entry_threshold", -0.05), # buy when 5-day return < -5%
|
||||||
|
("exit_threshold", 0.0), # sell when 1-day return > 0%
|
||||||
|
("max_hold", 3), # max hold days
|
||||||
|
)
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.roc5 = bt.indicators.RateOfChange(self.data.close, period=self.params.lookback)
|
||||||
|
self.hold_counter = 0
|
||||||
|
|
||||||
|
def next(self):
|
||||||
|
if not self.position:
|
||||||
|
if self.roc5[0] < self.params.entry_threshold: # RateOfChange returns a fraction, not %
|
||||||
|
self.buy()
|
||||||
|
self.hold_counter = 0
|
||||||
|
else:
|
||||||
|
self.hold_counter += 1
|
||||||
|
roc1 = (self.data.close[0] / self.data.close[-1] - 1) * 100
|
||||||
|
if roc1 > self.params.exit_threshold * 100 or self.hold_counter >= self.params.max_hold:
|
||||||
|
self.close()
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import pytest
|
||||||
|
from backtest.config import BacktestConfig
|
||||||
|
from backtest.runner import BacktestRunner
|
||||||
|
from strategies.reversal import FiveDayReversal
|
||||||
|
|
||||||
|
|
||||||
|
def test_reversal_smoke():
|
||||||
|
"""Smoke test: run a minimal reversal backtest and check results exist."""
|
||||||
|
config = BacktestConfig(
|
||||||
|
symbols=["sh600000"],
|
||||||
|
start_date="2024-01-01",
|
||||||
|
end_date="2024-03-31",
|
||||||
|
initial_cash=100_000,
|
||||||
|
)
|
||||||
|
runner = BacktestRunner(config)
|
||||||
|
results = runner.run(FiveDayReversal)
|
||||||
|
assert results is not None
|
||||||
|
assert len(results) == 1
|
||||||
|
# Check analyzers exist
|
||||||
|
sharpe = results[0].analyzers.sharpe.get_analysis()
|
||||||
|
assert "sharperatio" in sharpe
|
||||||
Reference in New Issue
Block a user