卡尔曼策略新增md文件
This commit is contained in:
@@ -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
200
futures_trading_strategies/rb/KalmanStrategy/KalmanStrategy.py
Normal file
200
futures_trading_strategies/rb/KalmanStrategy/KalmanStrategy.py
Normal 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.")
|
||||
258
futures_trading_strategies/rb/KalmanStrategy/KalmanStrategy2.py
Normal file
258
futures_trading_strategies/rb/KalmanStrategy/KalmanStrategy2.py
Normal 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.")
|
||||
290
futures_trading_strategies/rb/KalmanStrategy/KalmanStrategy3.py
Normal file
290
futures_trading_strategies/rb/KalmanStrategy/KalmanStrategy3.py
Normal 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
Reference in New Issue
Block a user