import numpy as np import pandas as pd from typing import Optional, Dict, Any, List from collections import deque # 假设这些是你项目中的模块 from src.core_data import Bar, Order from src.strategies.base_strategy import Strategy from src.algo.TrendLine import calculate_latest_trendline_values_v2 class PriceRangeVolumeIntensityStrategy(Strategy): """ 价格区间成交量强度策略 (V11 - 市场冲击强度版): - 【核心修改】策略的强度指标从单纯的 `volume` 升级为 `volume * (bar.high - bar.low)`。 - 这个新指标能更准确地衡量市场的“冲击力”或“有效动能”,过滤掉高换手但价格停滞的无效信号。 - 继承了 V10 版本的高效无状态 deque 实现,仅改变了指标的输入源。 """ def __init__( self, context: Any, main_symbol: str, strategy_mode: str, trade_volume: int = 1, trendline_n: int = 50, intensity_kappa: float = 0.1, intensity_lookback: int = 24, intensity_entry_percent: float = 0.95, intensity_exit_percent: float = 0.50, order_direction: Optional[List[str]] = None, enable_log: bool = True, ): super().__init__(context, main_symbol, enable_log) self.main_symbol = main_symbol self.trade_volume = trade_volume if strategy_mode not in ['TREND', 'REVERSION']: raise ValueError(f"strategy_mode 必须是 'TREND' 或 'REVERSION', 但收到了 '{strategy_mode}'") self.strategy_mode = strategy_mode self.params = { "order_direction": order_direction if order_direction is not None else ["BUY", "SELL"], "trendline_n": trendline_n, "intensity_kappa": intensity_kappa, "intensity_lookback": intensity_lookback, "intensity_entry_percent": intensity_entry_percent, "intensity_exit_percent": intensity_exit_percent, } self.weights = np.exp(-self.params['intensity_kappa'] * np.arange(self.params['intensity_lookback'], 0, -1)) self.intensity_window: Optional[deque] = None self.pos_meta: Dict[str, Dict[str, Any]] = {} print(f"PriceRangeVolumeIntensityStrategy (V11) initialized.") print(f"--- Strategy Mode SET TO: {self.strategy_mode} ---") print(f"Intensity metric: volume * (high - low)") def _calculate_single_intensity(self, metric_slice: np.ndarray) -> float: """根据一段“市场冲击”指标切片,计算最新的一个强度值。""" intensity_unscaled = np.dot(metric_slice, self.weights) return intensity_unscaled * self.params['intensity_kappa'] def on_init(self): super().on_init() self.pos_meta.clear() self.intensity_window = None def on_open_bar(self, open_price: float, symbol: str): bar_history = self.get_bar_history() lookback = self.params['intensity_lookback'] min_bars_required = max(self.params['trendline_n'] + 2, 2 * lookback) if len(bar_history) < min_bars_required: return # --- 【核心修改】计算新的“市场冲击”指标序列 --- # 替代了之前的 all_volumes market_impact_metric = np.array( [(b.high - b.low) * 1 for b in bar_history], dtype=float ) # 简单的零值处理,防止 (high-low) 为 0 或负数(异常数据)导致的问题 market_impact_metric[market_impact_metric <= 0] = 1e-9 # --- 策略预热 (Warm-up) 与 增量更新 (逻辑不变,数据源已更新) --- if self.intensity_window is None: self.intensity_window = deque(maxlen=lookback) print("Warming up the market impact intensity window...") for i in range(len(market_impact_metric) - lookback, len(market_impact_metric)): metric_slice = market_impact_metric[i - lookback : i] intensity_value = self._calculate_single_intensity(metric_slice) self.intensity_window.append(intensity_value) print("Warm-up complete.") else: latest_metric_slice = market_impact_metric[-lookback:] latest_intensity_value = self._calculate_single_intensity(latest_metric_slice) self.intensity_window.append(latest_intensity_value) self.cancel_all_pending_orders(symbol) pos = self.get_current_positions().get(symbol, 0) # --- 平仓与开仓逻辑 (完全不变,因为它们依赖于 intensity_window 的结果) --- latest_intensity = self.intensity_window[-1] # 1. 平仓逻辑 meta = self.pos_meta.get(symbol) if meta and pos != 0: exit_threshold = np.quantile(list(self.intensity_window), self.params['intensity_exit_percent']) if latest_intensity < exit_threshold: self.log(f"[{self.strategy_mode}模式] 市场冲击强度平仓信号,平仓。") self.send_market_order("CLOSE_LONG" if meta['direction'] == "BUY" else "CLOSE_SHORT", abs(pos)) del self.pos_meta[symbol] return # 2. 开仓逻辑 if pos == 0: signal = self._calculate_entry_signal( self.strategy_mode, bar_history, self.params, self.intensity_window ) if signal and signal in self.params['order_direction']: self.log(f"[{self.strategy_mode}模式] 市场冲击强度开仓信号: {signal}") self.send_open_order(signal, open_price, self.trade_volume) # _calculate_entry_signal 函数完全不变,因为它只消费 intensity_window def _calculate_entry_signal(self, mode: str, bar_history: List[Bar], params: Dict, intensity_window: deque) -> \ Optional[str]: latest_intensity_value = intensity_window[-1] entry_threshold = np.quantile(list(intensity_window), params['intensity_entry_percent']) if not (latest_intensity_value > entry_threshold): return None close_prices = np.array([b.close for b in bar_history]) prices_for_trendline = close_prices[-params['trendline_n'] - 1:-1] trend_upper, trend_lower = calculate_latest_trendline_values_v2(prices_for_trendline) if trend_upper is not None and trend_lower is not None: prev_close = bar_history[-2].close last_close = bar_history[-1].close upper_break_event = last_close > trend_upper and prev_close < trend_upper lower_break_event = last_close < trend_lower and prev_close > trend_lower if upper_break_event: return "BUY" if mode == 'TREND' else "SELL" if lower_break_event: return "SELL" if mode == 'TREND' else "BUY" return None # 其他辅助函数 (send_order, on_rollover) 保持不变 def send_open_order(self, direction: str, entry_price: float, volume: int): current_time = self.get_current_time() order_id = f"{self.symbol}_{direction}_{current_time.strftime('%Y%m%d%H%M%S')}" order_direction = "BUY" if direction == "BUY" else "SELL" order = Order(id=order_id, symbol=self.symbol, direction=order_direction, volume=volume, price_type="MARKET", submitted_time=current_time, offset="OPEN") self.send_order(order) self.pos_meta[self.symbol] = { "direction": direction, "volume": volume, "entry_price": entry_price } self.log( f"发送开仓订单 ({self.strategy_mode}): {direction} {volume}手 @ Market Price (执行价约 {entry_price:.2f})") def send_market_order(self, direction: str, volume: int): current_time = self.get_current_time() order_id = f"{self.symbol}_{direction}_{current_time.strftime('%Y%m%d%H%M%S')}" order = Order(id=order_id, symbol=self.symbol, direction=direction, volume=volume, price_type="MARKET", submitted_time=current_time, offset="CLOSE") self.send_order(order) self.log(f"发送平仓订单: {direction} {volume}手 @ Market Price") def on_rollover(self, old_symbol: str, new_symbol: str): super().on_rollover(old_symbol, new_symbol) self.cancel_all_pending_orders(new_symbol) self.pos_meta.clear() self.intensity_window = None