feat: 新增2h/6h时间框架支持,策略重构为增量指标计算

- 数据层: build_aggregates_sql 新增 2h/6h 聚合视图,默认起始时间调整为 2017-05
- 模型层: KlineInterval 类型扩展 2h/6h,DataService 新增对应表名和毫秒映射
- 指标层: 新增 incremental.py 增量指标模块 (EmaInc/AtrInc/RsiInc/BbInc),O(1) per bar
- 策略重构: long_short.py 和 regime_all.py 从批量 ema/atr 迁移至增量指标,避免每 bar 重复全量计算
- regime 探测器: RegimeDetector3 改为增量 EMA200,detect() 接口简化
- 回测扩展: regime_timeframe_comparison 从 4h/1d 扩展至 2h/4h/6h/1d
- 新增示例: multi_strategy_report, vol_break_compare/periods, intraday_explore, top3_trades 等分析脚本
This commit is contained in:
Rekey
2026-06-13 19:30:25 +08:00
parent b5cdb41993
commit edc50e8809
20 changed files with 484544 additions and 34 deletions
+14 -7
View File
@@ -31,7 +31,7 @@ from engine.common.base import BaseStrategy, Signal, StrategyConfig
from engine.common.models import Kline
from engine.common.config import config, DBConfig
from engine.data import DataService
from engine.indicators import ema, atr
from engine.indicators.incremental import EmaInc, AtrInc
from engine.backtest.models import BacktestConfig, BacktestMetrics, BacktestResult, BacktestTrade
@@ -364,7 +364,7 @@ class LongShortEmaConfig(StrategyConfig):
class LongShortEmaStrategy(BaseStrategy):
"""EMA金叉做多、死叉做空,始终在场"""
"""EMA金叉做多、死叉做空,始终在场 — 全部指标增量计算"""
strategy_type = "long_short_ema"
@@ -374,6 +374,9 @@ class LongShortEmaStrategy(BaseStrategy):
self._closes: list[float] = []
self._highs: list[float] = []
self._lows: list[float] = []
self._ema_fast = EmaInc(c.fast)
self._ema_slow = EmaInc(c.slow)
self._atr = AtrInc(14)
self._highest: float = 0.0
self._lowest: float = float('inf')
self._position_side: str = "" # "long" / "short"
@@ -382,15 +385,19 @@ class LongShortEmaStrategy(BaseStrategy):
self._closes.append(k.close)
self._highs.append(k.high)
self._lows.append(k.low)
# 增量更新(即使在热身期也要更新,保证后续状态正确)
self._ema_fast.update(k.close)
self._ema_slow.update(k.close)
self._atr.update(k.high, k.low, k.close)
n = len(self._closes)
if n < self.cfg.slow + 5:
return None
fast = ema(self._closes, self.cfg.fast)
slow = ema(self._closes, self.cfg.slow)
atr_vals = atr(self._highs, self._lows, self._closes, 14)
cur_f, cur_s, cur_atr = fast[-1], slow[-1], atr_vals[-1]
prev_f, prev_s = fast[-2], slow[-2]
cur_f, cur_s = self._ema_fast[-1], self._ema_slow[-1]
cur_atr = self._atr[-1]
prev_f, prev_s = self._ema_fast[-2], self._ema_slow[-2]
if cur_f == 0 or cur_s == 0 or cur_atr == 0:
return None