卡尔曼策略新增md文件
This commit is contained in:
@@ -0,0 +1,232 @@
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from typing import Optional, Dict, Any, List
|
||||
|
||||
# 假设这些是你项目中的模块
|
||||
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 DualModeVolumeIntensityStrategy(Strategy):
|
||||
"""
|
||||
双模式成交量强度策略 (V7 - 无状态高效版):
|
||||
- 【核心命名】将 "霍克斯过程" 概念替换为更准确的 "成交量强度 (Volume Intensity)"。
|
||||
- 【核心优化】使用 NumPy 的卷积操作 (np.convolve) 代替循环,高效地计算滑动窗口的成交量强度,彻底消除路径依赖,同时保证高性能。
|
||||
- 策略行为在任何时间点只与最近的固定窗口数据相关,保证了回测与实盘的绝对一致性。
|
||||
- 完整保留了双模式(趋势/回归)和冲突解决机制。
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
context: Any,
|
||||
main_symbol: str,
|
||||
trade_volume: int = 1,
|
||||
trend_params: Dict[str, Any] = None,
|
||||
reversion_params: Dict[str, Any] = None,
|
||||
enabled_modes: Optional[List[str]] = None,
|
||||
conflict_resolution: str = 'TREND_PRIORITY',
|
||||
enable_log: bool = True,
|
||||
):
|
||||
super().__init__(context, main_symbol, enable_log)
|
||||
self.main_symbol = main_symbol
|
||||
self.trade_volume = trade_volume
|
||||
|
||||
default_params = {
|
||||
"order_direction": ["BUY", "SELL"],
|
||||
"trendline_n": 50,
|
||||
# 【命名修改】参数名也同步调整
|
||||
"intensity_kappa": 0.1, # 衰减因子
|
||||
"intensity_lookback": 50, # 回看窗口
|
||||
"intensity_entry_percent": 0.95,
|
||||
"intensity_exit_percent": 0.50,
|
||||
}
|
||||
self.trend_params = default_params.copy()
|
||||
if trend_params:
|
||||
self.trend_params.update(trend_params)
|
||||
|
||||
self.reversion_params = default_params.copy()
|
||||
if reversion_params:
|
||||
self.reversion_params.update(reversion_params)
|
||||
|
||||
self.enabled_modes = enabled_modes or ['TREND', 'REVERSION']
|
||||
self.conflict_resolution = conflict_resolution
|
||||
self.pos_meta: Dict[str, Dict[str, Any]] = {}
|
||||
|
||||
print("DualModeVolumeIntensityStrategy (V7) initialized.")
|
||||
print(f"Enabled modes: {self.enabled_modes}")
|
||||
print(f"Conflict resolution: {self.conflict_resolution}")
|
||||
print("Volume Intensity calculation is STATELESS and EFFICIENT (using np.convolve).")
|
||||
|
||||
# --- 【核心修改】使用卷积实现无状态、高效的窗口计算 ---
|
||||
def _calculate_volume_intensity_window(self, volumes: np.ndarray, kappa: float, lookback: int) -> np.ndarray:
|
||||
"""
|
||||
使用一维卷积高效计算成交量强度窗口。
|
||||
这是一个纯函数,无任何副作用和路径依赖。
|
||||
|
||||
:param volumes: 历史成交量序列。长度应为 2*lookback - 1。
|
||||
:param kappa: 强度衰减因子。
|
||||
:param lookback: 强度计算的回看窗口,也是返回的强度窗口的长度。
|
||||
:return: 一个长度为 `lookback` 的成交量强度值窗口。
|
||||
"""
|
||||
# 权重是指数衰减的,越近的成交量权重越高
|
||||
# weights = [exp(-kappa*lookback), ..., exp(-kappa*1)]
|
||||
weights = np.exp(-kappa * np.arange(lookback, 0, -1))
|
||||
|
||||
# 使用'valid'模式的卷积,本质上是一个滑动的点积运算
|
||||
# 结果的长度将是 len(volumes) - len(weights) + 1 = (2*lookback-1) - lookback + 1 = lookback
|
||||
intensity_unscaled = np.convolve(volumes, weights, mode='valid')
|
||||
|
||||
return intensity_unscaled * kappa
|
||||
|
||||
def on_init(self):
|
||||
super().on_init()
|
||||
self.pos_meta.clear()
|
||||
|
||||
def on_open_bar(self, open_price: float, symbol: str):
|
||||
bar_history = self.get_bar_history()
|
||||
|
||||
# 确保有足够的数据来满足最长的回看需求
|
||||
# 趋势线需要 trendline_n + 1 个价格
|
||||
# 强度计算需要 2 * lookback - 1 个成交量
|
||||
min_bars_required = max(
|
||||
self.trend_params['trendline_n'] + 2, 2 * self.trend_params['intensity_lookback'],
|
||||
self.reversion_params['trendline_n'] + 2, 2 * self.reversion_params['intensity_lookback']
|
||||
)
|
||||
if len(bar_history) < min_bars_required:
|
||||
return
|
||||
|
||||
all_volumes = np.array([b.volume for b in bar_history], dtype=float)
|
||||
|
||||
# 为每个模式计算其独立的成交量强度窗口
|
||||
trend_intensity_window = np.array([], dtype=np.float64)
|
||||
if 'TREND' in self.enabled_modes:
|
||||
lookback = self.trend_params['intensity_lookback']
|
||||
# 截取计算所需的、正确长度的成交量数据
|
||||
volumes_slice = all_volumes[-(2 * lookback - 1):]
|
||||
trend_intensity_window = self._calculate_volume_intensity_window(
|
||||
volumes_slice, self.trend_params['intensity_kappa'], lookback
|
||||
)
|
||||
|
||||
reversion_intensity_window = np.array([], dtype=np.float64)
|
||||
if 'REVERSION' in self.enabled_modes:
|
||||
lookback = self.reversion_params['intensity_lookback']
|
||||
volumes_slice = all_volumes[-(2 * lookback - 1):]
|
||||
reversion_intensity_window = self._calculate_volume_intensity_window(
|
||||
volumes_slice, self.reversion_params['intensity_kappa'], lookback
|
||||
)
|
||||
|
||||
self.cancel_all_pending_orders(symbol)
|
||||
pos = self.get_current_positions().get(symbol, 0)
|
||||
|
||||
# --- 1. 平仓逻辑 ---
|
||||
meta = self.pos_meta.get(symbol)
|
||||
if meta and pos != 0:
|
||||
strategy_mode = meta.get('strategy_mode')
|
||||
params_to_use = self.trend_params if strategy_mode == 'TREND' else self.reversion_params
|
||||
window_to_use = trend_intensity_window if strategy_mode == 'TREND' else reversion_intensity_window
|
||||
|
||||
if window_to_use.size > 0:
|
||||
latest_intensity_value = window_to_use[-1]
|
||||
exit_threshold = np.quantile(window_to_use, params_to_use['intensity_exit_percent'])
|
||||
|
||||
if latest_intensity_value < exit_threshold:
|
||||
self.log(f"[{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:
|
||||
trend_signal = self._calculate_entry_signal(
|
||||
'TREND', bar_history, self.trend_params, trend_intensity_window
|
||||
) if 'TREND' in self.enabled_modes else None
|
||||
|
||||
reversion_signal = self._calculate_entry_signal(
|
||||
'REVERSION', bar_history, self.reversion_params, reversion_intensity_window
|
||||
) if 'REVERSION' in self.enabled_modes else None
|
||||
|
||||
# ... 冲突解决和下单逻辑保持不变 ...
|
||||
final_direction, winning_mode = self.resolve_signals(trend_signal, reversion_signal)
|
||||
|
||||
if final_direction and winning_mode:
|
||||
params_to_use = self.trend_params if winning_mode == 'TREND' else self.reversion_params
|
||||
if final_direction in params_to_use['order_direction']:
|
||||
self.log(f"[{winning_mode}模式] 开仓信号确认: {final_direction}")
|
||||
self.send_open_order(final_direction, open_price, self.trade_volume, winning_mode)
|
||||
|
||||
def _calculate_entry_signal(self, mode: str, bar_history: List[Bar], params: Dict, intensity_window: np.ndarray) -> \
|
||||
Optional[str]:
|
||||
if intensity_window.size == 0:
|
||||
return None
|
||||
|
||||
# 1. 成交量强度确认
|
||||
latest_intensity_value = intensity_window[-1]
|
||||
entry_threshold = np.quantile(intensity_window, params['intensity_entry_percent'])
|
||||
intensity_confirmation = latest_intensity_value > entry_threshold
|
||||
|
||||
if not intensity_confirmation:
|
||||
return None
|
||||
|
||||
# 2. 趋势线突破事件
|
||||
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
|
||||
|
||||
def resolve_signals(self, trend_signal: Optional[str], reversion_signal: Optional[str]) -> (Optional[str],
|
||||
Optional[str]):
|
||||
if trend_signal and reversion_signal:
|
||||
self.log(f"信号冲突:趋势模式 ({trend_signal}) vs 回归模式 ({reversion_signal})")
|
||||
if self.conflict_resolution == 'TREND_PRIORITY':
|
||||
return trend_signal, 'TREND'
|
||||
elif self.conflict_resolution == 'REVERSION_PRIORITY':
|
||||
return reversion_signal, 'REVERSION'
|
||||
else:
|
||||
self.log("冲突解决策略为'NONE',本次不开仓。")
|
||||
return None, None
|
||||
elif trend_signal:
|
||||
return trend_signal, 'TREND'
|
||||
elif reversion_signal:
|
||||
return reversion_signal, 'REVERSION'
|
||||
return None, None
|
||||
|
||||
# send_open_order, send_market_order, on_rollover 等辅助函数保持不变
|
||||
# ... (代码与之前版本相同)
|
||||
def send_open_order(self, direction: str, entry_price: float, volume: int, strategy_mode: str):
|
||||
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,
|
||||
"strategy_mode": strategy_mode
|
||||
}
|
||||
self.log(f"发送开仓订单 ({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()
|
||||
Reference in New Issue
Block a user