卡尔曼策略新增md文件

This commit is contained in:
2025-11-07 16:37:16 +08:00
parent 2eec6452ee
commit 2ae9f2db9e
89 changed files with 39954 additions and 0 deletions

View File

@@ -0,0 +1,214 @@
import numpy as np
import pandas as pd
import talib
from collections import deque
from typing import Optional, Any, List, Dict
from src.core_data import Bar, Order
from src.indicators.base_indicators import Indicator
from src.indicators.indicators import Empty
from src.strategies.base_strategy import Strategy
# =============================================================================
# 策略实现 (V9 - 盘整突破版)
# =============================================================================
class ConsolidationBreakoutStrategy(Strategy):
"""
一个基于“盘整即突破之母”原则的终极波段策略。(V9)
本策略旨在通过量化的方法,识别市场能量的“压缩-释放”周期,
以捕捉趋势的主干行情,并严格规避无序的震荡。
1. 【战略层】: 使用长期均线定义宏观牛熊环境。
2. 【战术层】: 使用布林带宽度(BBW)的极度收缩,来识别“盘整准备”状态。
3. 【执行层】: 在准备状态下,等待价格“突破”凯尔特纳通道作为“大幅移动”的确认信号,
立即以市价入场并使用ATR动态追踪止盈来截取波段利润。
"""
def __init__(
self,
context: Any,
main_symbol: str,
enable_log: bool,
trade_volume: int,
# --- 【战略层】环境定义 ---
regime_ma_period: int = 200, # 定义牛熊市的长期均线
# --- 【战术层】盘整收缩识别 (Squeeze) ---
bollinger_period: int = 20, # 布林带周期
bollinger_std: float = 2.0, # 布林带标准差
bbw_lookback: int = 252, # BBW历史分位数回看窗口
bbw_squeeze_percentile: float = 15.0, # 定义“极度收缩”的百分位
# --- 【执行层】突破与风控 ---
keltner_period: int = 20, # 凯尔特纳通道周期
keltner_atr_multiplier: float = 1.5, # 凯尔特纳通道ATR乘数
atr_period: int = 20,
trailing_stop_atr_multiplier: float = 3.0,
initial_stop_atr_multiplier: float = 2.5,
order_direction: Optional[List[str]] = None,
indicators: Optional[List[Indicator]] = None,
):
super().__init__(context, main_symbol, enable_log)
if order_direction is None: order_direction = ['BUY', 'SELL']
self.trade_volume = trade_volume
self.regime_ma_period = regime_ma_period
self.bollinger_period = bollinger_period
self.bollinger_std = bollinger_std
self.bbw_lookback = bbw_lookback
self.bbw_squeeze_percentile = bbw_squeeze_percentile
self.keltner_period = keltner_period
self.keltner_atr_multiplier = keltner_atr_multiplier
self.atr_period = atr_period
self.trailing_stop_atr_multiplier = trailing_stop_atr_multiplier
self.initial_stop_atr_multiplier = initial_stop_atr_multiplier
self.order_direction = order_direction
# 状态变量
self._last_order_id: Optional[str] = None
self.position_meta: Dict[str, Any] = {}
self._bbw_history: deque = deque(maxlen=self.bbw_lookback)
self._in_squeeze_ready_state: bool = False
self.main_symbol = main_symbol
self.order_id_counter = 0
if indicators is None:
indicators = [Empty(), Empty()]
self.indicators = indicators
self.log("ConsolidationBreakoutStrategy (V9) Initialized.")
def on_init(self):
super().on_init()
self.cancel_all_pending_orders(self.main_symbol)
def on_open_bar(self, open_price: float, symbol: str):
self.symbol = symbol
bar_history = self.get_bar_history()
required_bars = max(self.regime_ma_period, self.bbw_lookback) + 1
if len(bar_history) < required_bars: return
# --- 数据预处理与指标计算 ---
highs = np.array([b.high for b in bar_history], dtype=float)
lows = np.array([b.low for b in bar_history], dtype=float)
closes = np.array([b.close for b in bar_history], dtype=float)
# 战略指标
regime_ma = talib.MA(closes, timeperiod=self.regime_ma_period)[-1]
# 战术指标 (Squeeze)
bb_upper, bb_mid, bb_lower = talib.BBANDS(closes, timeperiod=self.bollinger_period, nbdevup=self.bollinger_std,
nbdevdn=self.bollinger_std)
bb_width = (bb_upper[-1] - bb_lower[-1]) / bb_mid[-1] if bb_mid[-1] > 0 else 0
self._bbw_history.append(bb_width)
# 执行指标 (Breakout)
current_atr = talib.ATR(highs, lows, closes, self.atr_period)[-1]
keltner_ma = talib.MA(closes, timeperiod=self.keltner_period)
keltner_upper = keltner_ma[-1] + self.keltner_atr_multiplier * current_atr
keltner_lower = keltner_ma[-1] - self.keltner_atr_multiplier * current_atr
# --- 管理现有持仓 ---
position_volume = self.get_current_positions().get(self.symbol, 0)
if position_volume != 0:
self.manage_open_position(position_volume, bar_history[-1], current_atr)
return
# --- 评估新机会 ---
self.evaluate_entry_signal(bar_history[-1], regime_ma, bb_width, keltner_upper, keltner_lower)
def manage_open_position(self, volume: int, current_bar: Bar, current_atr: float):
"""使用ATR追踪止盈。"""
# (此部分与V6/V7版本代码逻辑相同成熟且有效)
meta = self.position_meta.get(self.symbol);
if not meta: return
initial_stop_price = meta['initial_stop_price']
if (volume > 0 and current_bar.low <= initial_stop_price) or (
volume < 0 and current_bar.high >= initial_stop_price):
self.log(f"Initial Stop Loss hit at {initial_stop_price:.2f}");
self.close_position("CLOSE_LONG" if volume > 0 else "CLOSE_SHORT", abs(volume));
return
trailing_stop_level = meta.get('trailing_stop_level', None)
if volume > 0:
meta['high_water_mark'] = max(meta.get('high_water_mark', meta['entry_price']), current_bar.high)
new_trailing_stop = meta['high_water_mark'] - self.trailing_stop_atr_multiplier * current_atr
if trailing_stop_level is None or new_trailing_stop > trailing_stop_level: trailing_stop_level = new_trailing_stop
trailing_stop_level = max(trailing_stop_level, initial_stop_price)
if current_bar.low <= trailing_stop_level: self.log(
f"ATR Trailing Stop hit for LONG at {trailing_stop_level:.2f}"); self.close_position("CLOSE_LONG",
abs(volume))
elif volume < 0:
meta['low_water_mark'] = min(meta.get('low_water_mark', meta['entry_price']), current_bar.low)
new_trailing_stop = meta['low_water_mark'] + self.trailing_stop_atr_multiplier * current_atr
if trailing_stop_level is None or new_trailing_stop < trailing_stop_level: trailing_stop_level = new_trailing_stop
trailing_stop_level = min(trailing_stop_level, initial_stop_price)
if current_bar.high >= trailing_stop_level: self.log(
f"ATR Trailing Stop hit for SHORT at {trailing_stop_level:.2f}"); self.close_position("CLOSE_SHORT",
abs(volume))
meta['trailing_stop_level'] = trailing_stop_level
def evaluate_entry_signal(self, current_bar: Bar, regime_ma: float, bb_width: float, keltner_upper: float,
keltner_lower: float):
"""执行“盘整-突破”的入场逻辑。"""
if len(self._bbw_history) < self.bbw_lookback: return
# 【战术层】检查是否进入“盘整准备”状态
bbw_threshold = np.percentile(list(self._bbw_history), self.bbw_squeeze_percentile)
if bb_width < bbw_threshold:
self._in_squeeze_ready_state = True
self.log(f"Squeeze Detected! BBW {bb_width:.4f} < Threshold {bbw_threshold:.4f}. Ready for breakout.")
return # 进入准备状态,等待突破
# 【执行层】在准备状态下,检查突破信号
if self._in_squeeze_ready_state:
direction = None
# 战略过滤 + 突破确认
if "BUY" in self.order_direction and current_bar.close > regime_ma and current_bar.close > keltner_upper and \
self.indicators[0].is_condition_met(*self.get_indicator_tuple()):
direction = "BUY"
elif "SELL" in self.order_direction and current_bar.close < regime_ma and current_bar.close < keltner_lower and \
self.indicators[1].is_condition_met(*self.get_indicator_tuple()):
direction = "SELL"
if direction:
self.log(f"Squeeze Fired! Direction: {direction}. Price broke Keltner Channel. Entering at market.")
# 市价单入场
entry_price = current_bar.close
current_atr = talib.ATR(
np.array([b.high for b in self.get_bar_history()[-self.atr_period:]], dtype=float),
np.array([b.low for b in self.get_bar_history()[-self.atr_period:]], dtype=float),
np.array([b.close for b in self.get_bar_history()[-self.atr_period:]], dtype=float),
self.atr_period
)[-1]
stop_loss_price = entry_price - self.initial_stop_atr_multiplier * current_atr if direction == "BUY" else entry_price + self.initial_stop_atr_multiplier * current_atr
meta = {'entry_price': entry_price, 'initial_stop_price': stop_loss_price}
self.send_market_order(direction, self.trade_volume, "OPEN", meta)
# 重置状态机
self._in_squeeze_ready_state = False
# --- 订单发送与仓位管理辅助函数 ---
def close_position(self, direction: str, volume: int):
self.send_market_order(direction, volume, offset="CLOSE");
if self.symbol in self.position_meta: del self.position_meta[self.symbol]
def send_market_order(self, direction: str, volume: int, offset: str, meta: Optional[Dict] = None):
if offset == "OPEN" and meta: self.position_meta[self.symbol] = meta
order_id = f"{self.symbol}_{direction}_MARKET_{self.order_id_counter}";
self.order_id_counter += 1;
order = Order(id=order_id, symbol=self.symbol, direction=direction, volume=volume, price_type="MARKET",
submitted_time=self.get_current_time(), offset=offset);
self.send_order(order)
def on_rollover(self, old_symbol: str, new_symbol: str):
super().on_rollover(old_symbol, new_symbol);
self._last_order_id = None;
self.position_meta = {};
self._bbw_history.clear();
self._in_squeeze_ready_state = False;
self.log("Rollover detected. All strategy states have been reset.")

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,200 @@
import numpy as np
import pandas as pd
import talib
from collections import deque
from typing import Optional, Any, List, Dict
from src.core_data import Bar, Order
from src.indicators.base_indicators import Indicator
from src.indicators.indicators import Empty
from src.strategies.base_strategy import Strategy
# =============================================================================
# 策略实现 (Adaptive Kalman Strategy)
# =============================================================================
class AdaptiveKalmanStrategy(Strategy):
"""
一个基于市场状态识别与结构化风控的自适应波段策略。
本策略旨在解决动能策略在“回调中吐出利润、震荡中持续亏损”的核心痛点。
它通过动态识别市场状态,在有利的环境下交易,并使用结构化的方法持有仓位。
1. 【状态过滤】: 使用ATR历史分位数构建“波动率状态机”在低波动率的
“震荡区”主动休眠,只在高波动率的“趋势区”寻找机会。
2. 【动能催化】: 沿用卡尔曼滤波器估算内在趋势当价格以ATR标准化的
力量“逃逸”出内在趋势时,视为入场信号。
3. 【结构化持仓】: 抛弃紧跟价格峰值的传统追踪止损,改用基于卡尔曼滤波线
本身的“结构化止损”,给趋势以充分的“呼吸空间”,旨在持有
一个完整的波段,避免在健康回调中被过早洗出。
"""
def __init__(
self,
context: Any,
main_symbol: str,
enable_log: bool,
trade_volume: int,
# --- 【信号层】卡尔曼滤波器参数 ---
kalman_process_noise: float = 0.01,
kalman_measurement_noise: float = 0.5,
# --- 【状态过滤】波动率状态机 ---
atr_period: int = 20,
atr_lookback: int = 100, # 用于计算ATR分位数的历史窗口
atr_percentile_threshold: float = 25.0, # ATR必须高于其历史的哪个百分位才认为是“趋势区”
# --- 【执行与风控】 ---
entry_threshold_atr: float = 2.5,
initial_stop_atr_multiplier: float = 2.0,
structural_stop_atr_multiplier: float = 2.5, # 结构化止损的ATR乘数
order_direction: Optional[List[str]] = None,
indicators: Optional[List[Indicator]] = None,
):
super().__init__(context, main_symbol, enable_log)
if order_direction is None: order_direction = ['BUY', 'SELL']
self.trade_volume = trade_volume
self.atr_period = atr_period
self.atr_lookback = atr_lookback
self.atr_percentile_threshold = atr_percentile_threshold
self.entry_threshold_atr = entry_threshold_atr
self.initial_stop_atr_multiplier = initial_stop_atr_multiplier
self.structural_stop_atr_multiplier = structural_stop_atr_multiplier
self.order_direction = order_direction
# 卡尔曼滤波器状态
self.Q = kalman_process_noise
self.R = kalman_measurement_noise
self.P = 1.0
self.x_hat = 0.0
self.kalman_initialized = False
# 状态机与持仓元数据
self._atr_history: deque = deque(maxlen=self.atr_lookback)
self.position_meta: Dict[str, Any] = {}
self.main_symbol = main_symbol
self.order_id_counter = 0
if indicators is None: indicators = [Empty(), Empty()]
self.indicators = indicators
self.log("AdaptiveKalmanStrategy Initialized.")
def on_init(self):
super().on_init()
self.cancel_all_pending_orders(self.main_symbol)
def on_open_bar(self, open_price: float, symbol: str):
self.symbol = symbol
bar_history = self.get_bar_history()
if len(bar_history) < max(self.atr_period, self.atr_lookback) + 2: return
# --- 数据预处理与指标计算 ---
highs = np.array([b.high for b in bar_history], dtype=float)
lows = np.array([b.low for b in bar_history], dtype=float)
closes = np.array([b.close for b in bar_history], dtype=float)
current_close = closes[-1]
current_atr = talib.ATR(highs, lows, closes, self.atr_period)[-1]
self._atr_history.append(current_atr)
if current_atr <= 0 or len(self._atr_history) < self.atr_lookback: return
# --- 卡尔曼滤波器更新 ---
if not self.kalman_initialized:
self.x_hat = current_close
self.kalman_initialized = True
x_hat_minus = self.x_hat
P_minus = self.P + self.Q
K = P_minus / (P_minus + self.R)
self.x_hat = x_hat_minus + K * (current_close - x_hat_minus)
self.P = (1 - K) * P_minus
kalman_price = self.x_hat
# --- 管理现有持仓 ---
position_volume = self.get_current_positions().get(self.symbol, 0)
if position_volume != 0:
self.manage_open_position(position_volume, bar_history[-1], current_atr, kalman_price)
return
# --- 【状态过滤】检查波动率状态机 ---
atr_threshold = np.percentile(list(self._atr_history), self.atr_percentile_threshold)
if current_atr < atr_threshold:
# self.log(f"Market in Chop Zone. ATR {current_atr:.4f} < Threshold {atr_threshold:.4f}. Standing by.")
return # 市场处于震荡区,休眠
# --- 评估新机会 ---
self.evaluate_entry_signal(bar_history[-1], kalman_price, current_atr)
def manage_open_position(self, volume: int, current_bar: Bar, current_atr: float, kalman_price: float):
"""采用两阶段止损系统:初始硬止损 + 结构化卡尔曼止损。"""
meta = self.position_meta.get(self.symbol)
if not meta: return
# 阶段一:始终检查初始硬止损
initial_stop_price = meta['initial_stop_price']
if (volume > 0 and current_bar.low <= initial_stop_price) or \
(volume < 0 and current_bar.high >= initial_stop_price):
self.log(f"Phase 1: Initial Stop Loss hit at {initial_stop_price:.4f}")
self.close_position("CLOSE_LONG" if volume > 0 else "CLOSE_SHORT", abs(volume))
return
# 阶段二:检查结构化卡尔曼止损
structural_stop_price = 0
if volume > 0: # 多头持仓
structural_stop_price = kalman_price - self.structural_stop_atr_multiplier * current_atr
# 确保结构止损不会比初始止损更差
structural_stop_price = max(structural_stop_price, initial_stop_price)
if current_bar.low <= structural_stop_price:
self.log(f"Phase 2: Structural Kalman Stop hit for LONG at {structural_stop_price:.4f}")
self.close_position("CLOSE_LONG", abs(volume))
elif volume < 0: # 空头持仓
structural_stop_price = kalman_price + self.structural_stop_atr_multiplier * current_atr
# 确保结构止损不会比初始止损更差
structural_stop_price = min(structural_stop_price, initial_stop_price)
if current_bar.high >= structural_stop_price:
self.log(f"Phase 2: Structural Kalman Stop hit for SHORT at {structural_stop_price:.4f}")
self.close_position("CLOSE_SHORT", abs(volume))
def evaluate_entry_signal(self, current_bar: Bar, kalman_price: float, current_atr: float):
"""在“趋势区”内,执行基于“动能催化”的入场逻辑。"""
deviation = current_bar.close - kalman_price
deviation_in_atr = deviation / current_atr
direction = None
if "BUY" in self.order_direction and deviation_in_atr > self.entry_threshold_atr and self.indicators[
0].is_condition_met(*self.get_indicator_tuple()):
direction = "SELL"
elif "SELL" in self.order_direction and deviation_in_atr < -self.entry_threshold_atr and self.indicators[
1].is_condition_met(*self.get_indicator_tuple()):
direction = "BUY"
if direction:
self.log(
f"Trend Zone Active! Momentum Catalyst Fired. Direction: {direction}. Deviation: {deviation_in_atr:.2f} ATRs.")
entry_price = current_bar.close
stop_loss_price = entry_price - self.initial_stop_atr_multiplier * current_atr if direction == "BUY" else entry_price + self.initial_stop_atr_multiplier * current_atr
meta = {'entry_price': entry_price, 'initial_stop_price': stop_loss_price}
self.send_market_order(direction, self.trade_volume, "OPEN", meta)
# --- 订单发送与仓位管理辅助函数 ---
def close_position(self, direction: str, volume: int):
self.send_market_order(direction, volume, offset="CLOSE")
if self.symbol in self.position_meta: del self.position_meta[self.symbol]
def send_market_order(self, direction: str, volume: int, offset: str, meta: Optional[Dict] = None):
if offset == "OPEN" and meta: self.position_meta[self.symbol] = meta
order_id = f"{self.symbol}_{direction}_MARKET_{self.order_id_counter}"
self.order_id_counter += 1
order = Order(id=order_id, symbol=self.symbol, direction=direction, volume=volume, price_type="MARKET",
submitted_time=self.get_current_time(), offset=offset)
self.send_order(order)
def on_rollover(self, old_symbol: str, new_symbol: str):
super().on_rollover(old_symbol, new_symbol)
self.position_meta = {}
self.kalman_initialized = False
self._atr_history.clear()
self.log("Rollover detected. All strategy states have been reset.")

View File

@@ -0,0 +1,258 @@
import numpy as np
import pandas as pd
import talib
from collections import deque
from typing import Optional, Any, List, Dict
from src.core_data import Bar, Order
from src.indicators.base_indicators import Indicator
from src.indicators.indicators import Empty
from src.strategies.base_strategy import Strategy
# =============================================================================
# 策略实现 (Dual-Mode Kalman Strategy)
# =============================================================================
class DualModeKalmanStrategy(Strategy):
"""
一个内置两种相反交易逻辑(趋势跟踪 vs. 均值回归)的自适应策略。
本策略旨在通过一个核心参数 `strategy_mode`,在两种市场范式间切换,
以适应不同品种的内在“性格”。
人格1: 'TREND' (趋势模式)
- 哲学: 价格的强力突破会引发自我强化的趋势。
- 入场: 价格向上/下“逃逸”卡尔曼线 -> 做多/做空。
- 出场: 使用结构化卡尔曼止损,让利润奔跑。
人格2: 'REVERSION' (均值回归模式)
- 哲学: 价格的极端偏离是不可持续的,终将回归均值。
- 入场: 价格向上/下极端偏离(超买/超卖) -> 做空/做多。
- 出场: 当价格回归至卡爾曼線時获利了结。
"""
def __init__(
self,
context: Any,
main_symbol: str,
enable_log: bool,
trade_volume: int,
# --- 【核心人格切换】 ---
strategy_mode: str = 'TREND', # 可选: 'TREND' 或 'REVERSION'
# --- 【通用参数】 ---
kalman_process_noise: float = 0.01,
kalman_measurement_noise: float = 0.5,
atr_period: int = 20,
atr_lookback: int = 100,
atr_percentile_threshold: float = 25.0,
entry_threshold_atr: float = 2.5,
initial_stop_atr_multiplier: float = 2.0,
# --- 【趋势模式专用】 ---
structural_stop_atr_multiplier: float = 2.5,
order_direction: Optional[List[str]] = None,
indicators: Optional[List[Indicator]] = None,
):
super().__init__(context, main_symbol, enable_log)
if order_direction is None: order_direction = ['BUY', 'SELL']
# --- 参数验证与赋值 ---
if strategy_mode.upper() not in ['TREND', 'REVERSION']:
raise ValueError(f"strategy_mode must be 'TREND' or 'REVERSION', but got {strategy_mode}")
self.strategy_mode = strategy_mode.upper()
self.trade_volume = trade_volume
self.atr_period = atr_period
self.atr_lookback = atr_lookback
self.atr_percentile_threshold = atr_percentile_threshold
self.entry_threshold_atr = entry_threshold_atr
self.initial_stop_atr_multiplier = initial_stop_atr_multiplier
self.structural_stop_atr_multiplier = structural_stop_atr_multiplier
self.order_direction = order_direction
# 卡尔曼滤波器状态
self.Q = kalman_process_noise
self.R = kalman_measurement_noise
self.P = 1.0;
self.x_hat = 0.0;
self.kalman_initialized = False
self._atr_history: deque = deque(maxlen=self.atr_lookback)
self.position_meta: Dict[str, Any] = self.context.load_state()
self.main_symbol = main_symbol
self.order_id_counter = 0
if indicators is None: indicators = [Empty(), Empty()]
self.indicators = indicators
self.log(f"DualModeKalmanStrategy Initialized with Personality: [{self.strategy_mode}]")
def on_init(self):
super().on_init()
self.cancel_all_pending_orders(self.main_symbol)
self.position_meta = self.context.load_state()
def on_open_bar(self, open_price: float, symbol: str):
self.symbol = symbol
bar_history = self.get_bar_history()
if len(bar_history) < max(self.atr_period, self.atr_lookback) + 2: return
# --- 通用数据计算 ---
highs = np.array([b.high for b in bar_history], dtype=float)
lows = np.array([b.low for b in bar_history], dtype=float)
closes = np.array([b.close for b in bar_history], dtype=float)
current_atr = talib.ATR(highs, lows, closes, self.atr_period)[-1]
self._atr_history.append(current_atr)
if current_atr <= 0 or len(self._atr_history) < self.atr_lookback: return
if not self.kalman_initialized: self.x_hat = closes[-1]; self.kalman_initialized = True
x_hat_minus = self.x_hat
P_minus = self.P + self.Q
K = P_minus / (P_minus + self.R)
self.x_hat = x_hat_minus + K * (closes[-1] - x_hat_minus)
self.P = (1 - K) * P_minus
kalman_price = self.x_hat
# --- 分模式管理持仓 ---
position_volume = self.get_current_positions().get(self.symbol, 0)
meta = self.position_meta.get(symbol)
if position_volume != 0 and not meta:
self.log(f"警告:检测到实际持仓({position_volume})与策略状态(无记录)不一致!"
f"可能由状态加载失败导致。将强制平仓以同步状态。", level='WARNING')
direction_to_close = "CLOSE_LONG" if position_volume > 0 else "CLOSE_SHORT"
self.send_market_order(direction_to_close, abs(position_volume), 'CLOSE')
return
if position_volume == 0 and meta:
self.log(f"信息:检测到策略状态({meta.get('direction')})与实际持仓(0)不一致meta:{meta}"
f"可能是外部平仓导致。正在清理过时状态。", level='INFO')
new_pos_meta = {k: v for k, v in self.position_meta.items() if k != symbol}
self.position_meta = new_pos_meta
self.save_state(new_pos_meta)
if not self.trading:
return
if position_volume != 0:
self.log(f'manage_open_position')
self.manage_open_position(position_volume, bar_history[-1], current_atr, kalman_price)
return
# --- 状态过滤 (对两种模式都适用) ---
atr_threshold = np.percentile(list(self._atr_history), self.atr_percentile_threshold)
if current_atr < atr_threshold: return
# --- 分模式评估新机会 ---
self.log(f'evaluate_entry_signal')
self.evaluate_entry_signal(bar_history[-1], kalman_price, current_atr)
def manage_open_position(self, volume: int, current_bar: Bar, current_atr: float, kalman_price: float):
meta = self.position_meta.get(self.symbol)
if not meta: return
# --- 通用逻辑:首先检查初始硬止损 ---
initial_stop_price = meta['initial_stop_price']
if (volume > 0 and current_bar.low <= initial_stop_price) or \
(volume < 0 and current_bar.high >= initial_stop_price):
self.log(f"Initial Stop Loss hit at {initial_stop_price:.4f}")
self.close_position("CLOSE_LONG" if volume > 0 else "CLOSE_SHORT", abs(volume))
return
# --- 分模式出场逻辑 ---
if self.strategy_mode == 'TREND':
# 【趋势模式】: 使用结构化卡尔曼止损
stop_price = 0
if volume > 0:
stop_price = max(kalman_price - self.structural_stop_atr_multiplier * current_atr, initial_stop_price)
self.log(f'stop_price: {stop_price:.4f}, kalman_price: {kalman_price:.4f}')
if current_bar.low <= stop_price:
self.log(f"TREND Mode: Structural Stop hit for LONG at {stop_price:.4f}")
self.close_position("CLOSE_LONG", abs(volume))
else: # volume < 0
stop_price = min(kalman_price + self.structural_stop_atr_multiplier * current_atr, initial_stop_price)
self.log(f'stop_price: {stop_price:.4f}, kalman_price: {kalman_price:.4f}')
if current_bar.high >= stop_price:
self.log(f"TREND Mode: Structural Stop hit for SHORT at {stop_price:.4f}")
self.close_position("CLOSE_SHORT", abs(volume))
elif self.strategy_mode == 'REVERSION':
# 【回归模式】: 检查是否触及均值作为止盈
if volume > 0:
stop_price = max(kalman_price - self.structural_stop_atr_multiplier * current_atr, initial_stop_price)
self.log(f'stop_price: {stop_price:.4f}, kalman_price: {kalman_price:.4f}')
if current_bar.low <= stop_price:
self.log(f"TREND Mode: Structural Stop hit for LONG at {stop_price:.4f}")
self.close_position("CLOSE_LONG", abs(volume))
else: # volume < 0
stop_price = min(kalman_price + self.structural_stop_atr_multiplier * current_atr, initial_stop_price)
self.log(f'stop_price: {stop_price:.4f}, kalman_price: {kalman_price:.4f}')
if current_bar.high >= stop_price:
self.log(f"TREND Mode: Structural Stop hit for SHORT at {stop_price:.4f}")
self.close_position("CLOSE_SHORT", abs(volume))
def evaluate_entry_signal(self, current_bar: Bar, kalman_price: float, current_atr: float):
deviation = current_bar.close - kalman_price
deviation_in_atr = deviation / current_atr
direction = None
if self.strategy_mode == 'TREND':
# 【趋势模式】入场逻辑
if "BUY" in self.order_direction and deviation_in_atr > self.entry_threshold_atr and self.indicators[
0].is_condition_met(*self.get_indicator_tuple()):
direction = "BUY"
elif "SELL" in self.order_direction and deviation_in_atr < -self.entry_threshold_atr and self.indicators[
1].is_condition_met(*self.get_indicator_tuple()):
direction = "SELL"
elif self.strategy_mode == 'REVERSION':
# 【回归模式】入场逻辑 (完全相反)
if "SELL" in self.order_direction and deviation_in_atr > self.entry_threshold_atr and self.indicators[
1].is_condition_met(*self.get_indicator_tuple()):
direction = "SELL" # 价格超买 -> 做空
elif "BUY" in self.order_direction and deviation_in_atr < -self.entry_threshold_atr and self.indicators[
0].is_condition_met(*self.get_indicator_tuple()):
direction = "BUY" # 价格超卖 -> 做多
if direction:
self.log(
f"{self.strategy_mode} Mode: Catalyst Fired. Direction: {direction}. Deviation: {deviation_in_atr:.2f} ATRs.")
entry_price = current_bar.close + (1 if direction == "BUY" else -1)
stop_loss_price = entry_price - self.initial_stop_atr_multiplier * current_atr if direction in ["BUY",
"CLOSE_SHORT"] else entry_price + self.initial_stop_atr_multiplier * current_atr
meta = {'entry_price': entry_price, 'initial_stop_price': stop_loss_price}
self.send_limit_order(entry_price, direction, self.trade_volume, "OPEN", meta)
self.save_state(self.position_meta)
# ... (辅助函数 close_position, send_market_order, on_rollover 保持不变) ...
def close_position(self, direction: str, volume: int):
self.send_market_order(direction, volume, offset="CLOSE")
if self.symbol in self.position_meta:
self.position_meta = {}
self.save_state(self.position_meta)
def send_market_order(self, direction: str, volume: int, offset: str, meta: Optional[Dict] = None):
if offset == "OPEN" and meta: self.position_meta[self.symbol] = meta
order_id = f"{self.symbol}_{direction}_MARKET_{self.order_id_counter}"
self.order_id_counter += 1
order = Order(id=order_id, symbol=self.symbol, direction=direction, volume=volume, price_type="MARKET",
submitted_time=self.get_current_time(), offset=offset)
self.send_order(order)
def send_limit_order(self, limit_price: float, direction: str, volume: int, offset: str,
meta: Optional[Dict] = None):
if offset == "OPEN" and meta: self.position_meta[self.symbol] = meta
order_id = f"{self.symbol}_{direction}_MARKET_{self.order_id_counter}"
self.order_id_counter += 1
order = Order(id=order_id, symbol=self.symbol, direction=direction, volume=volume, price_type="LIMIT",
submitted_time=self.get_current_time(), offset=offset, limit_price=limit_price)
self.send_order(order)
def on_rollover(self, old_symbol: str, new_symbol: str):
super().on_rollover(old_symbol, new_symbol)
self.position_meta = {}
self.kalman_initialized = False
self._atr_history.clear()
self.log("Rollover detected. All strategy states have been reset.")

View File

@@ -0,0 +1,290 @@
import numpy as np
import pandas as pd
import talib
from collections import deque
from typing import Optional, Any, List, Dict
from src.core_data import Bar, Order
from src.indicators.base_indicators import Indicator
from src.indicators.indicators import Empty
from src.strategies.base_strategy import Strategy
# =============================================================================
# 策略实现 (Dual-Mode Kalman Strategy) - 优化版
# =============================================================================
class DualModeKalmanStrategy(Strategy):
"""
一个内置两种相反交易逻辑(趋势跟踪 vs. 均值回归)的自适应策略。
本策略旨在通过一个核心参数 `strategy_mode`,在两种市场范式间切换,
以适应不同品种的内在“性格”。
人格1: 'TREND' (趋势模式)
- 哲学: 价格的强力突破会引发自我强化的趋势。
- 入场: 价格向上/下“逃逸”卡尔曼线 -> 做多/做空。
- 出场: 使用真正的单向追踪止盈Trailing Stop锁定利润。
人格2: 'REVERSION' (均值回归模式)
- 哲学: 价格的极端偏离是不可持续的,终将回归均值。
- 入场: 价格向上/下极端偏离(超买/超卖) -> 做空/做多。
- 出场: 当价格回归至卡尔曼线时获利了结。
"""
def __init__(
self,
context: Any,
main_symbol: str,
enable_log: bool,
trade_volume: int,
# --- 【核心人格切换】 ---
strategy_mode: str = 'TREND', # 可选: 'TREND' 或 'REVERSION'
# --- 【通用参数】 ---
kalman_process_noise: float = 0.01,
kalman_measurement_noise: float = 0.5,
atr_period: int = 20,
atr_lookback: int = 100,
atr_percentile_threshold: float = 25.0,
entry_threshold_atr: float = 2.5,
initial_stop_atr_multiplier: float = 2.0,
# --- 【趋势模式专用】 ---
structural_stop_atr_multiplier: float = 2.5,
order_direction: Optional[List[str]] = None,
indicators: Optional[List[Indicator]] = None,
):
super().__init__(context, main_symbol, enable_log)
if order_direction is None: order_direction = ['BUY', 'SELL']
# --- 参数验证与赋值 ---
if strategy_mode.upper() not in ['TREND', 'REVERSION']:
raise ValueError(f"strategy_mode must be 'TREND' or 'REVERSION', but got {strategy_mode}")
self.strategy_mode = strategy_mode.upper()
self.trade_volume = trade_volume
self.atr_period = atr_period
self.atr_lookback = atr_lookback
self.atr_percentile_threshold = atr_percentile_threshold
self.entry_threshold_atr = entry_threshold_atr
self.initial_stop_atr_multiplier = initial_stop_atr_multiplier
self.structural_stop_atr_multiplier = structural_stop_atr_multiplier
self.order_direction = order_direction
# 卡尔曼滤波器状态
self.Q = kalman_process_noise
self.R = kalman_measurement_noise
self.P = 1.0;
self.x_hat = 0.0;
self.kalman_initialized = False
self._atr_history: deque = deque(maxlen=self.atr_lookback)
self.position_meta: Dict[str, Any] = self.context.load_state()
self.main_symbol = main_symbol
self.order_id_counter = 0
if indicators is None: indicators = [Empty(), Empty()]
self.indicators = indicators
self.log(f"DualModeKalmanStrategy Initialized with Personality: [{self.strategy_mode}]")
def on_init(self):
super().on_init()
self.cancel_all_pending_orders(self.main_symbol)
self.position_meta = self.context.load_state()
def on_open_bar(self, open_price: float, symbol: str):
self.symbol = symbol
bar_history = self.get_bar_history()
if len(bar_history) < max(self.atr_period, self.atr_lookback) + 2: return
# --- 通用数据计算 ---
highs = np.array([b.high for b in bar_history], dtype=float)
lows = np.array([b.low for b in bar_history], dtype=float)
closes = np.array([b.close for b in bar_history], dtype=float)
current_atr = talib.ATR(highs, lows, closes, self.atr_period)[-1]
self._atr_history.append(current_atr)
if current_atr <= 0 or len(self._atr_history) < self.atr_lookback: return
if not self.kalman_initialized: self.x_hat = closes[-1]; self.kalman_initialized = True
x_hat_minus = self.x_hat
P_minus = self.P + self.Q
K = P_minus / (P_minus + self.R)
self.x_hat = x_hat_minus + K * (closes[-1] - x_hat_minus)
self.P = (1 - K) * P_minus
kalman_price = self.x_hat
# --- 分模式管理持仓 ---
position_volume = self.get_current_positions().get(self.symbol, 0)
meta = self.position_meta.get(symbol)
if position_volume != 0 and not meta:
self.log(f"警告:检测到实际持仓({position_volume})与策略状态(无记录)不一致!"
f"可能由状态加载失败导致。将强制平仓以同步状态。", level='WARNING')
direction_to_close = "CLOSE_LONG" if position_volume > 0 else "CLOSE_SHORT"
self.send_market_order(direction_to_close, abs(position_volume), 'CLOSE')
return
if position_volume == 0 and meta:
self.log(f"信息:检测到策略状态({meta.get('direction')})与实际持仓(0)不一致meta:{meta}"
f"可能是外部平仓导致。正在清理过时状态。", level='INFO')
new_pos_meta = {k: v for k, v in self.position_meta.items() if k != symbol}
self.position_meta = new_pos_meta
self.save_state(new_pos_meta)
if not self.trading:
return
if position_volume != 0:
self.manage_open_position(position_volume, bar_history[-1], current_atr, kalman_price)
return
# --- 状态过滤 (对两种模式都适用) ---
atr_threshold = np.percentile(list(self._atr_history), self.atr_percentile_threshold)
if current_atr < atr_threshold: return
# --- 分模式评估新机会 ---
self.evaluate_entry_signal(bar_history[-1], kalman_price, current_atr)
# --- [OPTIMIZED] ---
# 重写 manage_open_position 方法,以实现真正的追踪止盈和修正回归模式逻辑
def manage_open_position(self, volume: int, current_bar: Bar, current_atr: float, kalman_price: float):
meta = self.position_meta.get(self.symbol)
if not meta: return
# --- 分模式出场逻辑 ---
if self.strategy_mode == 'TREND':
# 【趋势模式】: 实现真正的单向追踪止盈 (Trailing Stop)
# 1. 从 meta 中获取当前的追踪止损位
# (使用 .get() 是为了兼容可能没有此字段的旧版状态文件)
current_trailing_stop = meta.get('trailing_stop_price', meta['initial_stop_price'])
new_potential_stop = 0
if volume > 0: # 持有多单
# 2. 计算基于当前卡尔曼线和ATR的新“潜在”止损位
new_potential_stop = kalman_price - self.structural_stop_atr_multiplier * current_atr
# 3. 核心逻辑: 只让止损位朝有利方向(向上)移动,绝不回撤
updated_trailing_stop = max(current_trailing_stop, new_potential_stop)
self.log(
f"TREND LONG: Trailing Stop Updated to {updated_trailing_stop:.4f} (from {current_trailing_stop:.4f}). Potential new stop was {new_potential_stop:.4f}.")
# 4. 检查价格是否触及更新后的追踪止损位
if current_bar.low <= updated_trailing_stop:
self.log(f"TREND Mode: Trailing Stop hit for LONG at {updated_trailing_stop:.4f}")
self.close_position("CLOSE_LONG", abs(volume))
return
else: # 持有空单
# 2. 计算新“潜在”止损位
new_potential_stop = kalman_price + self.structural_stop_atr_multiplier * current_atr
# 3. 核心逻辑: 只让止损位朝有利方向(向下)移动,绝不回撤
updated_trailing_stop = min(current_trailing_stop, new_potential_stop)
self.log(
f"TREND SHORT: Trailing Stop Updated to {updated_trailing_stop:.4f} (from {current_trailing_stop:.4f}). Potential new stop was {new_potential_stop:.4f}.")
# 4. 检查价格是否触及更新后的追踪止损位
if current_bar.high >= updated_trailing_stop:
self.log(f"TREND Mode: Trailing Stop hit for SHORT at {updated_trailing_stop:.4f}")
self.close_position("CLOSE_SHORT", abs(volume))
return
# 5. 【状态持久化】将更新后的追踪止损位存回 meta用于下一根K线
if updated_trailing_stop != current_trailing_stop:
self.position_meta[self.symbol]['trailing_stop_price'] = updated_trailing_stop
self.save_state(self.position_meta)
elif self.strategy_mode == 'REVERSION':
# 【回归模式】: 目标是回归均值,因此止盈逻辑是触及卡尔曼线
# 首先,检查初始硬止损,作为最后的安全防线
initial_stop_price = meta['initial_stop_price']
if (volume > 0 and current_bar.low <= initial_stop_price) or \
(volume < 0 and current_bar.high >= initial_stop_price):
self.log(f"REVERSION Mode: Initial Stop Loss hit at {initial_stop_price:.4f}")
self.close_position("CLOSE_LONG" if volume > 0 else "CLOSE_SHORT", abs(volume))
return
# 其次,检查止盈条件:价格是否已回归至卡尔曼线
if volume > 0: # 持有从下方回归的多单
if current_bar.high >= kalman_price:
self.log(f"REVERSION Mode: Take Profit for LONG as price hit Kalman line at {kalman_price:.4f}")
self.close_position("CLOSE_LONG", abs(volume))
else: # 持有从上方回归的空单
if current_bar.low <= kalman_price:
self.log(f"REVERSION Mode: Take Profit for SHORT as price hit Kalman line at {kalman_price:.4f}")
self.close_position("CLOSE_SHORT", abs(volume))
# --- [OPTIMIZED] ---
# 修改 evaluate_entry_signal 方法,以在开仓时初始化追踪止盈状态
def evaluate_entry_signal(self, current_bar: Bar, kalman_price: float, current_atr: float):
deviation = current_bar.close - kalman_price
deviation_in_atr = deviation / current_atr
direction = None
if self.strategy_mode == 'TREND':
if "BUY" in self.order_direction and deviation_in_atr > self.entry_threshold_atr and self.indicators[
0].is_condition_met(*self.get_indicator_tuple()):
direction = "BUY"
elif "SELL" in self.order_direction and deviation_in_atr < -self.entry_threshold_atr and self.indicators[
1].is_condition_met(*self.get_indicator_tuple()):
direction = "SELL"
elif self.strategy_mode == 'REVERSION':
if "SELL" in self.order_direction and deviation_in_atr > self.entry_threshold_atr and self.indicators[
1].is_condition_met(*self.get_indicator_tuple()):
direction = "SELL"
elif "BUY" in self.order_direction and deviation_in_atr < -self.entry_threshold_atr and self.indicators[
0].is_condition_met(*self.get_indicator_tuple()):
direction = "BUY"
if direction:
self.log(
f"{self.strategy_mode} Mode: Catalyst Fired. Direction: {direction}. Deviation: {deviation_in_atr:.2f} ATRs.")
entry_price = current_bar.close
stop_loss_price = entry_price - self.initial_stop_atr_multiplier * current_atr if direction == "BUY" else entry_price + self.initial_stop_atr_multiplier * current_atr
# --- 【状态持久化】---
# 在 meta 中增加 trailing_stop_price 字段,并用初始止损位为其赋值
# 这样,追踪止盈就从一个固定的“硬止损”开始了
meta = {
'entry_price': entry_price,
'initial_stop_price': stop_loss_price,
'trailing_stop_price': stop_loss_price # 关键:初始化追踪止损
}
# 使用市价单确保入场
self.send_market_order(direction, self.trade_volume, "OPEN", meta)
self.save_state(self.position_meta)
def close_position(self, direction: str, volume: int):
self.send_market_order(direction, volume, offset="CLOSE")
if self.symbol in self.position_meta:
# 使用 pop 来安全地删除键,并返回其值(虽然这里我们不需要)
self.position_meta.pop(self.symbol, None)
self.save_state(self.position_meta)
def send_market_order(self, direction: str, volume: int, offset: str, meta: Optional[Dict] = None):
if offset == "OPEN" and meta: self.position_meta[self.symbol] = meta
order_id = f"{self.symbol}_{direction}_MARKET_{self.order_id_counter}"
self.order_id_counter += 1
order = Order(id=order_id, symbol=self.symbol, direction=direction, volume=volume, price_type="MARKET",
submitted_time=self.get_current_time(), offset=offset)
self.send_order(order)
def send_limit_order(self, limit_price: float, direction: str, volume: int, offset: str,
meta: Optional[Dict] = None):
if offset == "OPEN" and meta: self.position_meta[self.symbol] = meta
order_id = f"{self.symbol}_{direction}_LIMIT_{self.order_id_counter}"
self.order_id_counter += 1
order = Order(id=order_id, symbol=self.symbol, direction=direction, volume=volume, price_type="LIMIT",
submitted_time=self.get_current_time(), offset=offset, limit_price=limit_price)
self.send_order(order)
def on_rollover(self, old_symbol: str, new_symbol: str):
super().on_rollover(old_symbol, new_symbol)
# 重置所有与特定合约相关的状态
self.position_meta = {}
self.kalman_initialized = False
self._atr_history.clear()
self.save_state({}) # 清空持久化状态
self.log("Rollover detected. All strategy states have been reset.")

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long