1、vp策略

This commit is contained in:
2025-10-05 00:09:59 +08:00
parent f43a6b2822
commit 4712565749
70 changed files with 59950 additions and 1175 deletions

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,328 @@
import numpy as np
import pandas as pd
from typing import Optional, Dict, Any, List, Union
import talib
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
from src.algo.TrendLine import calculate_latest_trendline_values
class _SignalGenerator:
"""
内部帮助类,用于封装单个策略(趋势或回归)的霍克斯过程信号生成所需的所有状态和逻辑。
"""
def __init__(self, hawkes_kappa: float, hawkes_lookback: int, volume_norm_n: int):
self.hawkes_kappa = hawkes_kappa
self.hawkes_lookback = hawkes_lookback
self.volume_norm_n = volume_norm_n
# 状态变量
self._last_hawkes_unscaled: float = 0.0
self._hawkes_window: np.ndarray = np.array([], dtype=np.float64)
self._hawkes_alpha: float = np.exp(-self.hawkes_kappa)
self._volume_window: np.ndarray = np.zeros(self.volume_norm_n, dtype=np.float64)
self._volume_sum: float = 0.0
self._volume_sum_sq: float = 0.0
self._volume_pointer: int = 0
self._is_volume_window_full: bool = False
def reset(self):
"""重置所有状态"""
self._last_hawkes_unscaled = 0.0
self._hawkes_window = np.array([], dtype=np.float64)
self._volume_window.fill(0)
self._volume_sum = 0.0
self._volume_sum_sq = 0.0
self._volume_pointer = 0
self._is_volume_window_full = False
def initialize_state(self, initial_volumes: np.ndarray):
"""用历史数据批量初始化状态"""
normalized_volumes = []
for vol in initial_volumes:
self._update_volume_stats_incrementally(vol)
mean, std = self._get_current_volume_stats()
z_score = 0.0 if std <= 1e-9 else (vol - mean) / std
normalized_volumes.append(z_score)
temp_hawkes_history = np.zeros_like(normalized_volumes, dtype=np.float64)
if len(normalized_volumes) > 0:
temp_hawkes_history[0] = normalized_volumes[0]
for i in range(1, len(normalized_volumes)):
temp_hawkes_history[i] = temp_hawkes_history[i - 1] * self._hawkes_alpha + normalized_volumes[i]
self._last_hawkes_unscaled = temp_hawkes_history[-1] if len(temp_hawkes_history) > 0 else 0.0
self._hawkes_window = (temp_hawkes_history * self.hawkes_kappa)[-self.hawkes_lookback:]
def update_state_incrementally(self, latest_volume: float):
"""在每个bar上增量更新状态"""
self._update_volume_stats_incrementally(latest_volume)
mean, std = self._get_current_volume_stats()
normalized_volume = 0.0 if std <= 1e-9 else (latest_volume - mean) / std
new_hawkes_unscaled = self._last_hawkes_unscaled * self._hawkes_alpha + normalized_volume
self._last_hawkes_unscaled = new_hawkes_unscaled
new_hawkes_scaled = new_hawkes_unscaled * self.hawkes_kappa
if self._hawkes_window.size < self.hawkes_lookback:
self._hawkes_window = np.append(self._hawkes_window, new_hawkes_scaled)
else:
self._hawkes_window = np.roll(self._hawkes_window, -1)
self._hawkes_window[-1] = new_hawkes_scaled
def _update_volume_stats_incrementally(self, latest_volume: float):
oldest_volume = self._volume_window[self._volume_pointer]
self._volume_sum += latest_volume - oldest_volume
self._volume_sum_sq += latest_volume ** 2 - oldest_volume ** 2
self._volume_window[self._volume_pointer] = latest_volume
self._volume_pointer = (self._volume_pointer + 1) % self.volume_norm_n
if not self._is_volume_window_full and self._volume_pointer == 0:
self._is_volume_window_full = True
def _get_current_volume_stats(self) -> (float, float):
n = self.volume_norm_n if self._is_volume_window_full else self._volume_pointer
if n == 0: return 0.0, 0.0
mean = self._volume_sum / n
variance = max(0, (self._volume_sum_sq / n) - mean ** 2)
std = np.sqrt(variance)
return mean, std
def get_latest_hawkes_value(self) -> Optional[float]:
return self._hawkes_window[-1] if self._hawkes_window.size > 0 else None
def get_hawkes_quantile(self, percentile: float) -> Optional[float]:
return np.quantile(self._hawkes_window, percentile) if self._hawkes_window.size > 0 else None
class DualModeTrendlineHawkesStrategy(Strategy):
"""
趋势线与霍克斯过程双重确认策略 (V11 - 完全独立信号版):
- 为趋势(Trend)和均值回归(Reversion)策略分别维护一套完全独立的信号生成器。
- 每个策略使用各自的 trendline_n, hawkes_kappa, hawkes_lookback, volume_norm_n 参数。
- 信号生成完全分离,确保逻辑独立性。
"""
def __init__(
self,
context: Any,
main_symbol: str,
trend_enabled: bool = True,
reversion_enabled: bool = True,
conflict_resolution_mode: str = 'trend_priority',
trend_params: Dict[str, Any] = None,
reversion_params: Dict[str, Any] = None,
enable_log: bool = True,
indicators: Union[Indicator, List[Indicator]] = None,
):
super().__init__(context, main_symbol, enable_log)
self.main_symbol = main_symbol
self.trend_enabled = trend_enabled
self.reversion_enabled = reversion_enabled
if conflict_resolution_mode not in ['trend_priority', 'reversion_priority', 'none']:
raise ValueError("conflict_resolution_mode 必须是 'trend_priority', 'reversion_priority', 或 'none'")
self.conflict_resolution_mode = conflict_resolution_mode
default_params = {
"trade_volume": 1,
"order_direction": ["BUY", "SELL"],
"hawkes_entry_percent": 0.95,
"hawkes_exit_percent": 0.50,
"enable_atr_stop_loss": True,
"atr_period": 14,
"atr_multiplier": 2.0,
"trendline_n": 50,
"hawkes_kappa": 0.1,
"hawkes_lookback": 50,
"volume_norm_n": 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.trend_generator = _SignalGenerator(
hawkes_kappa=self.trend_params['hawkes_kappa'],
hawkes_lookback=self.trend_params['hawkes_lookback'],
volume_norm_n=self.trend_params['volume_norm_n']
)
self.reversion_generator = _SignalGenerator(
hawkes_kappa=self.reversion_params['hawkes_kappa'],
hawkes_lookback=self.reversion_params['hawkes_lookback'],
volume_norm_n=self.reversion_params['volume_norm_n']
)
self.pos_meta: Dict[str, Dict[str, Any]] = {}
self.indicators = indicators or [Empty(), Empty()]
self._is_initialized = False
def on_init(self):
super().on_init()
self.pos_meta.clear()
self.trend_generator.reset()
self.reversion_generator.reset()
self._is_initialized = False
def on_open_bar(self, open_price: float, symbol: str):
bar_history = self.get_bar_history()
min_bars_required = max(
self.trend_params['trendline_n'] + 2, self.reversion_params['trendline_n'] + 2,
self.trend_params['hawkes_lookback'] + 2, self.reversion_params['hawkes_lookback'] + 2,
self.trend_params['volume_norm_n'] + 2, self.reversion_params['volume_norm_n'] + 2,
self.trend_params['atr_period'] + 2, self.reversion_params['atr_period'] + 2
)
if len(bar_history) < min_bars_required:
return
latest_volume = float(bar_history[-1].volume)
# --- 【核心修改】初始化或更新两个独立的生成器 ---
if not self._is_initialized:
initial_volumes = np.array([b.volume for b in bar_history[:-1]], dtype=float)
self.log("首次运行,正在初始化趋势信号生成器...")
self.trend_generator.initialize_state(initial_volumes)
self.log("正在初始化回归信号生成器...")
self.reversion_generator.initialize_state(initial_volumes)
self._is_initialized = True
self.trend_generator.update_state_incrementally(latest_volume)
self.reversion_generator.update_state_incrementally(latest_volume)
self.cancel_all_pending_orders(symbol)
pos = self.get_current_positions().get(symbol, 0)
# --- 平仓逻辑 ---
meta = self.pos_meta.get(symbol)
if meta and pos != 0:
strategy_type = meta.get('strategy_type', 'trend')
# 根据开仓类型选择正确的参数和生成器
params, generator = (self.trend_params, self.trend_generator) if strategy_type == 'trend' \
else (self.reversion_params, self.reversion_generator)
latest_hawkes_value = generator.get_latest_hawkes_value()
latest_hawkes_lower = generator.get_hawkes_quantile(params['hawkes_exit_percent'])
close_reason = None
if latest_hawkes_value is not None and latest_hawkes_lower is not None and latest_hawkes_value < latest_hawkes_lower:
close_reason = f"[{strategy_type.upper()}] 霍克斯出场信号(强度: {latest_hawkes_value:.4f} < 阈值: {latest_hawkes_lower:.4f})"
if params['enable_atr_stop_loss'] and 'stop_loss_price' in meta and meta['stop_loss_price'] is not None:
if (meta['direction'] == "BUY" and bar_history[-1].close < meta['stop_loss_price']) or \
(meta['direction'] == "SELL" and bar_history[-1].close > meta['stop_loss_price']):
close_reason = f"[{strategy_type.upper()}] ATR止损触发"
if close_reason:
self.log(close_reason)
self.send_market_order("CLOSE_LONG" if meta['direction'] == "BUY" else "CLOSE_SHORT", abs(pos))
if symbol in self.pos_meta: del self.pos_meta[symbol]
return
# --- 开仓逻辑 ---
if pos == 0:
trend_signal, reversion_signal = None, None
close_prices = np.array([b.close for b in bar_history])
prev_close, last_close = bar_history[-2].close, bar_history[-1].close
# 1. 检查趋势策略信号
if self.trend_enabled:
prices_for_trendline = close_prices[-self.trend_params['trendline_n'] - 1:-1]
trend_upper, trend_lower = calculate_latest_trendline_values(prices_for_trendline)
if trend_upper is not None:
upper_break = last_close > trend_upper and prev_close < trend_upper
lower_break = last_close < trend_lower and prev_close > trend_lower
hawkes_val = self.trend_generator.get_latest_hawkes_value()
hawkes_thresh = self.trend_generator.get_hawkes_quantile(self.trend_params['hawkes_entry_percent'])
if hawkes_val is not None and hawkes_thresh is not None and hawkes_val > hawkes_thresh and (
upper_break or lower_break):
direction = "BUY" if upper_break else "SELL"
if direction in self.trend_params['order_direction']:
trend_signal = direction
# 2. 检查均值回归策略信号
if self.reversion_enabled:
prices_for_trendline = close_prices[-self.reversion_params['trendline_n'] - 1:-1]
trend_upper, trend_lower = calculate_latest_trendline_values(prices_for_trendline)
if trend_upper is not None:
upper_break = last_close > trend_upper and prev_close < trend_upper
lower_break = last_close < trend_lower and prev_close > trend_lower
hawkes_val = self.reversion_generator.get_latest_hawkes_value()
hawkes_thresh = self.reversion_generator.get_hawkes_quantile(
self.reversion_params['hawkes_entry_percent'])
if hawkes_val is not None and hawkes_thresh is not None and hawkes_val > hawkes_thresh and (
upper_break or lower_break):
direction = "SELL" if upper_break else "BUY" # 方向相反
if direction in self.reversion_params['order_direction']:
reversion_signal = direction
# 3. 解决信号冲突和下单
final_direction, final_params, strategy_type, generator = None, None, None, None
if trend_signal and not reversion_signal:
final_direction, final_params, strategy_type, generator = trend_signal, self.trend_params, 'trend', self.trend_generator
elif not trend_signal and reversion_signal:
final_direction, final_params, strategy_type, generator = reversion_signal, self.reversion_params, 'reversion', self.reversion_generator
elif trend_signal and reversion_signal:
self.log(
f"开仓信号冲突: 趋势={trend_signal}, 回归={reversion_signal}. 模式: {self.conflict_resolution_mode}")
if self.conflict_resolution_mode == 'trend_priority':
final_direction, final_params, strategy_type, generator = trend_signal, self.trend_params, 'trend', self.trend_generator
elif self.conflict_resolution_mode == 'reversion_priority':
final_direction, final_params, strategy_type, generator = reversion_signal, self.reversion_params, 'reversion', self.reversion_generator
if final_direction:
sl_price = None
if final_params['enable_atr_stop_loss']:
atr_val = self._calculate_atr(bar_history[:-1], final_params['atr_period'])
if atr_val is not None:
sl_price = open_price - atr_val * final_params[
'atr_multiplier'] if final_direction == "BUY" else open_price + atr_val * final_params[
'atr_multiplier']
self.log(
f"[{strategy_type.upper()}] 开仓信号确认 (霍克斯强度: {generator.get_latest_hawkes_value():.4f} > 阈值: {generator.get_hawkes_quantile(final_params['hawkes_entry_percent']):.4f})")
self.send_open_order(final_direction, open_price, final_params['trade_volume'], sl_price, strategy_type)
# --- 辅助函数 (与之前版本相同) ---
def _calculate_atr(self, bar_history: List[Bar], period: int) -> Optional[float]:
# ... (代码不变)
if len(bar_history) < period + 1: return None
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)
atr_values = talib.ATR(highs, lows, closes, timeperiod=period)
return atr_values[-1] if not np.isnan(atr_values[-1]) else None
def send_open_order(self, direction: str, entry_price: float, volume: int, stop_loss_price: Optional[float],
strategy_type: str):
# ... (代码不变)
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="BUY" if direction == "BUY" else "SELL", 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,
"stop_loss_price": stop_loss_price, "strategy_type": strategy_type}
self.log(f"[{strategy_type.upper()}] 发送开仓订单: {direction} {volume}")
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}")
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()

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,284 @@
import numpy as np
import pandas as pd
from typing import Optional, Dict, Any, List, Union
import talib # <-- 【新增】导入talib库
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
from src.algo.TrendLine import calculate_latest_trendline_values
import numpy as np
import pandas as pd
from typing import Optional, Dict, Any, List, Union
import talib
class TrendlineHawkesStrategy(Strategy):
"""
趋势线与霍克斯过程双重确认策略 (V8 - O(1) 滚动统计终极版):
- 对交易量Z-score的计算进行了极致优化采用增量方式维护滚动窗口的统计量。
- 每次更新均值和标准差的计算复杂度从 O(N) 降为 O(1)。
- 这是目前性能最高的实现方式,适用于非常高频的场景。
"""
def __init__(
self,
context: Any,
main_symbol: str,
# --- 所有参数与V7完全相同 ---
trade_volume: int = 1,
order_direction: Optional[List[str]] = None,
reverse_logic: bool = False,
trendline_n: int = 50,
hawkes_kappa: float = 0.1,
hawkes_lookback: int = 50,
hawkes_entry_percent: float = 0.95,
hawkes_exit_percent: float = 0.25,
volume_norm_n: int = 50,
enable_atr_stop_loss: bool = True,
atr_period: int = 14,
atr_multiplier: float = 1.0,
enable_log: bool = True,
indicators: Union[Indicator, List[Indicator]] = None,
):
super().__init__(context, main_symbol, enable_log)
# --- 参数赋值 (与V7相同) ---
# ... (省略) ...
self.main_symbol = main_symbol
self.trade_volume = trade_volume
self.order_direction = order_direction or ["BUY", "SELL"]
self.reverse_logic = reverse_logic
self.trendline_n = trendline_n
self.hawkes_kappa = hawkes_kappa
self.hawkes_lookback = hawkes_lookback
self.hawkes_entry_percent = hawkes_entry_percent
self.hawkes_exit_percent = hawkes_exit_percent
self.volume_norm_n = volume_norm_n
self.enable_atr_stop_loss = enable_atr_stop_loss
self.atr_period = atr_period
self.atr_multiplier = atr_multiplier
self.pos_meta: Dict[str, Dict[str, Any]] = {}
if indicators is None:
indicators = [Empty(), Empty()]
self.indicators = indicators
# --- 霍克斯过程状态 (与V7相同) ---
self._last_hawkes_unscaled: float = 0.0
self._hawkes_window: np.ndarray = np.array([], dtype=np.float64)
self._hawkes_alpha = np.exp(-self.hawkes_kappa)
# --- 【核心修改】O(1) 滚动统计状态 ---
# 预分配一个固定长度的数组作为循环缓冲区
self._volume_window: np.ndarray = np.zeros(self.volume_norm_n, dtype=np.float64)
self._volume_sum: float = 0.0 # 窗口内元素的和
self._volume_sum_sq: float = 0.0 # 窗口内元素平方的和
self._volume_pointer: int = 0 # 指向窗口中最旧元素的指针
self._is_volume_window_full: bool = False # 窗口是否已填满的标志
def on_init(self):
super().on_init()
self.pos_meta.clear()
# 重置霍克斯状态
self._last_hawkes_unscaled = 0.0
self._hawkes_window = np.array([], dtype=np.float64)
# 【核心修改】重置所有滚动统计状态
self._volume_window.fill(0)
self._volume_sum = 0.0
self._volume_sum_sq = 0.0
self._volume_pointer = 0
self._is_volume_window_full = False
# 【核心修改】_initialize_state 和 _update_state_incrementally 被重构
def _initialize_state(self, initial_volumes: np.ndarray):
"""
在策略开始时调用一次,用历史数据填充所有状态。
这个函数现在也会以增量方式填充滚动统计量。
"""
print("首次运行,正在以增量方式初始化所有状态...")
# 1. 增量填充交易量窗口并计算历史Z-score
normalized_volumes = []
for vol in initial_volumes:
# 调用增量更新函数,该函数会更新窗口、和、平方和
self._update_volume_stats_incrementally(vol)
# 计算Z-score
mean, std = self._get_current_volume_stats()
z_score = 0.0
if std > 1e-9:
z_score = (vol - mean) / std
normalized_volumes.append(z_score)
# 2. 使用标准化的交易量历史来初始化霍克斯过程 (逻辑与V7相同)
print("正在基于标准化的交易量初始化霍克斯过程...")
alpha = self._hawkes_alpha
temp_hawkes_history = np.zeros_like(normalized_volumes, dtype=np.float64)
if len(normalized_volumes) > 0:
temp_hawkes_history[0] = normalized_volumes[0]
for i in range(1, len(normalized_volumes)):
temp_hawkes_history[i] = temp_hawkes_history[i - 1] * alpha + normalized_volumes[i]
# 3. 记录最后的状态
self._last_hawkes_unscaled = temp_hawkes_history[-1] if len(temp_hawkes_history) > 0 else 0.0
self._hawkes_window = (temp_hawkes_history * self.hawkes_kappa)[-self.hawkes_lookback:]
print("状态初始化完成。")
def _update_volume_stats_incrementally(self, latest_volume: float):
"""O(1) 增量更新交易量窗口的统计数据"""
# 获取即将被替换的最旧的元素
oldest_volume = self._volume_window[self._volume_pointer]
# 更新和与平方和
self._volume_sum += latest_volume - oldest_volume
self._volume_sum_sq += latest_volume ** 2 - oldest_volume ** 2
# 在循环缓冲区中替换旧值
self._volume_window[self._volume_pointer] = latest_volume
# 移动指针
self._volume_pointer += 1
if self._volume_pointer >= self.volume_norm_n:
self._volume_pointer = 0
self._is_volume_window_full = True # 窗口在指针第一次循环时被填满
def _get_current_volume_stats(self) -> (float, float):
"""O(1) 获取当前的均值和标准差"""
# 在窗口未满时,我们按实际元素数量计算
n = self.volume_norm_n if self._is_volume_window_full else self._volume_pointer
if n == 0:
return 0.0, 0.0
mean = self._volume_sum / n
# 为防止浮点误差导致极小的负数,使用 max(0, ...)
variance = max(0, (self._volume_sum_sq / n) - mean ** 2)
std = np.sqrt(variance)
return mean, std
def _update_state_incrementally(self, latest_volume: float):
"""【重构】每个Bar上调用的主增量更新函数"""
# 1. O(1) 更新交易量统计
self._update_volume_stats_incrementally(latest_volume)
# 2. O(1) 计算最新Z-score
mean, std = self._get_current_volume_stats()
normalized_volume = 0.0
if std > 1e-9:
normalized_volume = (latest_volume - mean) / std
# 3. 更新霍克斯过程 (逻辑与V7相同)
new_hawkes_unscaled = self._last_hawkes_unscaled * self._hawkes_alpha + normalized_volume
self._last_hawkes_unscaled = new_hawkes_unscaled
new_hawkes_scaled = new_hawkes_unscaled * self.hawkes_kappa
if self._hawkes_window.size < self.hawkes_lookback:
self._hawkes_window = np.append(self._hawkes_window, new_hawkes_scaled)
else:
self._hawkes_window = np.roll(self._hawkes_window, -1)
self._hawkes_window[-1] = new_hawkes_scaled
# on_open_bar 逻辑不变,它只负责调用 _update_state_incrementally
def on_open_bar(self, open_price: float, symbol: str):
bar_history = self.get_bar_history()
min_bars_required = max(self.trendline_n + 2, self.hawkes_lookback + 2, self.volume_norm_n + 2,
self.atr_period + 2)
if len(bar_history) < min_bars_required:
return
# 状态更新 (调用重构后的函数)
if self._hawkes_window.size == 0:
initial_volumes = np.array([b.volume for b in bar_history], dtype=float)
self._initialize_state(initial_volumes[:-1])
self._update_state_incrementally(float(bar_history[-1].volume))
# --- 后续交易逻辑 (与V7完全相同) ---
# ... (此处省略代码与V7的 on_open_bar 后半部分完全一样) ...
self.cancel_all_pending_orders(symbol)
pos = self.get_current_positions().get(symbol, 0)
latest_hawkes_value = self._hawkes_window[-1]
latest_hawkes_lower = np.quantile(self._hawkes_window, self.hawkes_exit_percent)
meta = self.pos_meta.get(symbol)
if meta and pos != 0:
close_reason = None
if latest_hawkes_value < latest_hawkes_lower:
close_reason = f"霍克斯出场信号(强度: {latest_hawkes_value:.4f} < 阈值: {latest_hawkes_lower:.4f})"
if self.enable_atr_stop_loss and 'stop_loss_price' in meta and meta['stop_loss_price'] is not None:
last_close = bar_history[-1].close
stop_loss_price = meta['stop_loss_price']
if (meta['direction'] == "BUY" and last_close < stop_loss_price) or \
(meta['direction'] == "SELL" and last_close > stop_loss_price):
close_reason = f"ATR止损触发(收盘价: {last_close:.2f}, 止损价: {stop_loss_price:.2f})"
if close_reason:
self.log(close_reason)
self.send_market_order("CLOSE_LONG" if meta['direction'] == "BUY" else "CLOSE_SHORT", abs(pos))
if symbol in self.pos_meta: del self.pos_meta[symbol]
return
if pos == 0:
latest_hawkes_upper = np.quantile(self._hawkes_window, self.hawkes_entry_percent)
close_prices = np.array([b.close for b in bar_history])
prices_for_trendline = close_prices[-self.trendline_n - 1:-1]
trend_upper, trend_lower = calculate_latest_trendline_values(prices_for_trendline)
if trend_upper is not None and trend_lower is not None:
prev_close, last_close = bar_history[-2].close, bar_history[-1].close
upper_break = last_close > trend_upper and prev_close < trend_upper and self.indicators[0].is_condition_met(*self.get_indicator_tuple())
lower_break = last_close < trend_lower and prev_close > trend_lower and self.indicators[1].is_condition_met(*self.get_indicator_tuple())
hawkes_confirm = latest_hawkes_value > latest_hawkes_upper
if hawkes_confirm and (upper_break or lower_break):
direction = "BUY"
if upper_break:
direction = "SELL" if self.reverse_logic else "BUY"
elif lower_break:
direction = "BUY" if self.reverse_logic else "SELL"
if direction in self.order_direction:
sl_price = None
if self.enable_atr_stop_loss:
atr_val = self._calculate_atr(bar_history[:-1], self.atr_period)
if atr_val is not None:
sl_price = open_price - atr_val * self.atr_multiplier if direction == "BUY" else open_price + atr_val * self.atr_multiplier
self.log(f"ATR({self.atr_period})={atr_val:.4f}, 止损价设置为: {sl_price:.2f}")
self.log(
f"开仓信号确认(霍克斯强度: {latest_hawkes_value:.4f} > 阈值: {latest_hawkes_upper:.4f})")
self.send_open_order(direction, open_price, self.trade_volume, sl_price)
# ATR计算函数及其他下单函数与V7完全相同
def _calculate_atr(self, bar_history: List[Bar], period: int) -> Optional[float]:
if len(bar_history) < period + 1: return None
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)
atr_values = talib.ATR(highs, lows, closes, timeperiod=period)
latest_atr = atr_values[-1]
return latest_atr if not np.isnan(latest_atr) else None
def send_open_order(self, direction: str, entry_price: float, volume: int, stop_loss_price: Optional[float] = None):
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,
"stop_loss_price": stop_loss_price
}
self.log(f"发送开仓订单: {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()

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,169 @@
import numpy as np
import talib
from typing import Optional, Dict, Any, List, Tuple, Union
from src.algo.TrendLine import calculate_latest_trendline_values
# 假设这些是你项目中的基础模块
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
class TrendlineBreakoutStrategy(Strategy):
"""
趋势线突破策略 V3 (优化版):
1. 策略逻辑与 V2 相同,但趋势线计算被重构为一个独立的、
高性能的辅助方法。
2. 该方法只计算最新的趋势线值,避免不必要的数组生成。
开仓信号:
- 做多: 上一根收盘价上穿下趋势线
- 做空: 上一根收盘价下穿上趋势线
平仓逻辑:
- 采用 ATR 滑动止损 (Trailing Stop)。
"""
def __init__(
self,
context: Any,
main_symbol: str,
trendline_n: int = 50,
trade_volume: int = 1,
order_direction: Optional[List[str]] = None,
atr_period: int = 14,
atr_multiplier: float = 1.0,
enable_log: bool = True,
indicators: Union[Indicator, List[Indicator]] = None,
):
super().__init__(context, main_symbol, enable_log)
self.main_symbol = main_symbol
self.trendline_n = trendline_n
self.trade_volume = trade_volume
self.order_direction = order_direction or ["BUY", "SELL"]
self.atr_period = atr_period
self.atr_multiplier = atr_multiplier
self.pos_meta: Dict[str, Dict[str, Any]] = {}
if indicators is None:
indicators = [Empty(), Empty()]
self.indicators = indicators
if self.trendline_n < 3:
raise ValueError("trendline_n 必须大于或等于 3")
log_message = (
f"TrendlineBreakoutStrategy (V3 Optimized) 初始化:\n"
f"交易标的={self.main_symbol}, 交易量={self.trade_volume}\n"
f"趋势线周期={self.trendline_n}, ATR周期={self.atr_period}, ATR倍数={self.atr_multiplier}"
)
self.log(log_message)
def _calculate_atr(self, bar_history: List[Bar]) -> Optional[float]:
# (此函数与上一版本完全相同,保持不变)
if len(bar_history) < self.atr_period + 1: return None
highs = np.array([b.high for b in bar_history])
lows = np.array([b.low for b in bar_history])
closes = np.array([b.close for b in bar_history])
atr = talib.ATR(highs, lows, closes, timeperiod=self.atr_period)
return atr[-1] if not np.isnan(atr[-1]) else None
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()
min_bars_required = self.trendline_n + 2
if len(bar_history) < min_bars_required:
return
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:
current_atr = self._calculate_atr(bar_history[:-1])
if current_atr:
trailing_stop = meta['trailing_stop']
direction = meta['direction']
last_close = bar_history[-1].close
if direction == "BUY":
new_stop_level = last_close - current_atr * self.atr_multiplier
trailing_stop = max(trailing_stop, new_stop_level)
else: # SELL
new_stop_level = last_close + current_atr * self.atr_multiplier
trailing_stop = min(trailing_stop, new_stop_level)
self.pos_meta[symbol]['trailing_stop'] = trailing_stop
if (direction == "BUY" and open_price <= trailing_stop) or \
(direction == "SELL" and open_price >= trailing_stop):
self.log(f"ATR滑动止损触发: 价格 {open_price:.2f} 触及止损位 {trailing_stop:.2f}")
self.send_market_order("CLOSE_LONG" if direction == "BUY" else "CLOSE_SHORT", abs(pos))
del self.pos_meta[symbol]
return
# 2. 开仓逻辑 (调用优化后的方法)
if pos == 0:
prices_for_trendline = np.array([b.close for b in bar_history[-self.trendline_n - 1:-1]])
# --- 调用新的独立方法 ---
trendline_val_upper, trendline_val_lower = calculate_latest_trendline_values(prices_for_trendline)
if trendline_val_upper is None or trendline_val_lower is None:
return # 无法计算趋势线,跳过
prev_close = bar_history[-2].close
last_close = bar_history[-1].close
current_atr = self._calculate_atr(bar_history[:-1])
if not current_atr:
return
# if "BUY" in self.order_direction and last_close > trendline_val_upper and self.indicators[0].is_condition_met(*self.get_indicator_tuple()):
# self.log(f"做多信号: Close({last_close:.2f}) 上穿下趋势线({trendline_val_upper:.2f})")
# self.send_open_order("BUY", open_price, self.trade_volume, current_atr)
#
# elif "SELL" in self.order_direction and last_close < trendline_val_lower and self.indicators[1].is_condition_met(*self.get_indicator_tuple()):
# self.log(f"做空信号: Close({last_close:.2f}) 下穿上趋势线({trendline_val_lower:.2f})")
# self.send_open_order("SELL", open_price, self.trade_volume, current_atr)
if "BUY" in self.order_direction and last_close > trendline_val_upper and self.indicators[0].is_condition_met(*self.get_indicator_tuple()):
self.log(f"做多信号: Close({last_close:.2f}) 上穿下趋势线({trendline_val_upper:.2f})")
self.send_open_order("BUY", open_price, self.trade_volume, current_atr)
elif "SELL" in self.order_direction and last_close < trendline_val_lower and self.indicators[1].is_condition_met(*self.get_indicator_tuple()):
self.log(f"做空信号: Close({last_close:.2f}) 下穿上趋势线({trendline_val_lower:.2f})")
self.send_open_order("SELL", open_price, self.trade_volume, current_atr)
# send_open_order, send_market_order, on_rollover 等方法与上一版本完全相同,保持不变
def send_open_order(self, direction: str, entry_price: float, volume: int, current_atr: float):
if direction == "BUY":
initial_stop = entry_price - current_atr * self.atr_multiplier
else:
initial_stop = entry_price + current_atr * self.atr_multiplier
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,
"trailing_stop": initial_stop}
self.log(
f"发送开仓订单: {direction} {volume}手 @ Market Price (执行价约 {entry_price:.2f}), 初始ATR止损位: {initial_stop:.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()

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,178 @@
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
from src.algo.HawksProcess import calculate_hawkes_bands
class TrendlineHawkesStrategy(Strategy):
"""
趋势线与霍克斯过程双重确认策略 (V2 - 支持逻辑反转):
入场信号 (双重确认):
1. 趋势线事件: 收盘价突破上轨(标准模式做多)或下轨(标准模式做空)。
2. 霍克斯确认: 同时,成交量霍克斯强度必须高于其近期高位分位数。
出场逻辑 (基于霍克斯过程):
- 当成交量霍克斯强度从高位回落至近期低位分位数以下时,平仓。
逻辑反转 (`reverse_logic=True`):
- 趋势线突破上轨时,开【空】仓。
- 趋势线突破下轨时,开【多】仓。
"""
def __init__(
self,
context: Any,
main_symbol: str,
trade_volume: int = 1,
order_direction: Optional[List[str]] = None,
# --- 新增: 逻辑反转开关 ---
reverse_logic: bool = False,
# --- 趋势线参数 ---
trendline_n: int = 50,
# --- 霍克斯过程参数 ---
hawkes_kappa: float = 0.1,
hawkes_lookback: int = 50,
hawkes_entry_percent: float = 0.95,
hawkes_exit_percent: float = 0.50,
enable_log: bool = True,
):
super().__init__(context, main_symbol, enable_log)
self.main_symbol = main_symbol
self.trade_volume = trade_volume
self.order_direction = order_direction or ["BUY", "SELL"]
# --- 新增 ---
self.reverse_logic = reverse_logic
self.trendline_n = trendline_n
self.hawkes_kappa = hawkes_kappa
self.hawkes_lookback = hawkes_lookback
self.hawkes_entry_percent = hawkes_entry_percent
self.hawkes_exit_percent = hawkes_exit_percent
self.pos_meta: Dict[str, Dict[str, Any]] = {}
if self.trendline_n < 3:
raise ValueError("trendline_n 必须大于或等于 3")
log_message = (
f"TrendlineHawkesStrategy 初始化:\n"
f"【逻辑模式】: {'反转 (Reversal)' if self.reverse_logic else '标准 (Breakout)'}\n"
f"趋势线周期={self.trendline_n}\n"
f"霍克斯参数: kappa={self.hawkes_kappa}, lookback={self.hawkes_lookback}, "
f"entry_pct={self.hawkes_entry_percent}, exit_pct={self.hawkes_exit_percent}"
)
self.log(log_message)
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()
min_bars_required = max(self.trendline_n + 2, self.hawkes_lookback + 2)
if len(bar_history) < min_bars_required:
return
self.cancel_all_pending_orders(symbol)
pos = self.get_current_positions().get(symbol, 0)
# --- 数据准备 (与之前相同) ---
close_prices = np.array([b.close for b in bar_history])
volume_series = pd.Series(
[b.volume for b in bar_history],
index=pd.to_datetime([b.datetime for b in bar_history])
)
vol_hawkes, hawkes_upper_band, hawkes_lower_band = calculate_hawkes_bands(
volume_series, self.hawkes_lookback, self.hawkes_kappa,
self.hawkes_entry_percent, self.hawkes_exit_percent
)
latest_hawkes_value = vol_hawkes.iloc[-1]
latest_hawkes_upper = hawkes_upper_band.iloc[-1]
latest_hawkes_lower = hawkes_lower_band.iloc[-1]
# 1. 优先处理平仓逻辑 (逻辑保持不变)
meta = self.pos_meta.get(symbol)
if meta and pos != 0:
if latest_hawkes_value < latest_hawkes_lower:
self.log(f"霍克斯出场信号: 强度({latest_hawkes_value:.2f}) < 阈值({latest_hawkes_lower:.2f})")
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:
prices_for_trendline = close_prices[-self.trendline_n - 1:-1]
trend_upper, trend_lower = calculate_latest_trendline_values(prices_for_trendline)
if trend_upper is None or trend_lower is None:
return
prev_close = bar_history[-2].close
last_close = bar_history[-1].close
# --- a) 定义基础的突破【事件】 ---
upper_break_event = last_close > trend_upper and prev_close < trend_upper
lower_break_event = last_close < trend_lower and prev_close > trend_lower
# --- b) 定义霍克斯【确认】---
hawkes_confirmation = latest_hawkes_value > latest_hawkes_upper
# 只有当基础事件和霍克斯确认都发生时,才考虑开仓
if hawkes_confirmation and (upper_break_event or lower_break_event):
# --- c) 【核心修改】根据 reverse_logic 决定最终交易方向 ---
trade_direction = None
if upper_break_event: # 价格向上突破上轨
# 标准模式:做多 (动量)
# 反转模式:做空 (力竭反转)
trade_direction = "SELL" if self.reverse_logic else "BUY"
elif lower_break_event: # 价格向下突破下轨
# 标准模式:做空 (动量)
# 反转模式:做多 (恐慌探底反转)
trade_direction = "BUY" if self.reverse_logic else "SELL"
# d) 执行交易
if trade_direction and trade_direction in self.order_direction:
event_type = "向上突破" if upper_break_event else "向下突破"
logic_type = "反转" if self.reverse_logic else "标准"
self.log(
f"{logic_type}模式 {trade_direction} 信号: "
f"价格{event_type} & 霍克斯强度({latest_hawkes_value:.2f}) > 阈值({latest_hawkes_upper:.2f})"
)
self.send_open_order(trade_direction, open_price, self.trade_volume)
# send_open_order, send_market_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"发送开仓订单: {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()

View File

@@ -0,0 +1,194 @@
import numpy as np
import pandas as pd
from typing import Optional, Dict, Any, List, Union
# 假设这些是你项目中的模块
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
from src.algo.TrendLine import calculate_latest_trendline_values
class TrendlineHawkesStrategy(Strategy):
"""
趋势线与霍克斯过程双重确认策略 (V4 - 终极性能版):
- 霍克斯过程和滚动分位数都实现为高效的有状态增量计算。
- 使用固定长度的Numpy数组作为滚动窗口避免Pandas.rolling的开销和不一致性。
"""
def __init__(
self,
context: Any,
main_symbol: str,
# ... 参数与V3完全相同 ...
trade_volume: int = 1,
order_direction: Optional[List[str]] = None,
reverse_logic: bool = False,
trendline_n: int = 50,
hawkes_kappa: float = 0.1,
hawkes_lookback: int = 50,
hawkes_entry_percent: float = 0.95,
hawkes_exit_percent: float = 0.50,
enable_log: bool = True,
indicators: Union[Indicator, List[Indicator]] = None,
):
super().__init__(context, main_symbol, enable_log)
# ... 参数赋值与V3完全相同 ...
self.main_symbol = main_symbol
self.trade_volume = trade_volume
self.order_direction = order_direction or ["BUY", "SELL"]
self.reverse_logic = reverse_logic
self.trendline_n = trendline_n
self.hawkes_kappa = hawkes_kappa
self.hawkes_lookback = hawkes_lookback
self.hawkes_entry_percent = hawkes_entry_percent
self.hawkes_exit_percent = hawkes_exit_percent
self.pos_meta: Dict[str, Dict[str, Any]] = {}
if indicators is None:
indicators = [Empty(), Empty()]
self.indicators = indicators
# --- 【核心修改】状态缓存重构 ---
# 只缓存上一个时间点的霍克斯强度值 (未缩放)
self._last_hawkes_unscaled: float = 0.0
# 只维护一个固定长度的滚动窗口,用于计算分位数
self._hawkes_window: np.ndarray = np.array([], dtype=np.float64)
# 衰减因子
self._hawkes_alpha = np.exp(-self.hawkes_kappa)
# ... 日志与V3相同 ...
def _initialize_state(self, initial_volumes: np.ndarray):
"""
仅在策略开始时调用一次,用于填充初始的滚动窗口。
"""
print("首次运行,正在初始化霍克斯状态和滚动窗口...")
alpha = self._hawkes_alpha
kappa = self.hawkes_kappa
# 完整计算一次历史强度,只为填充窗口
temp_hawkes_history = np.zeros_like(initial_volumes, dtype=np.float64)
if len(initial_volumes) > 0:
temp_hawkes_history[0] = initial_volumes[0] if not np.isnan(initial_volumes[0]) else 0.0
for i in range(1, len(initial_volumes)):
temp_hawkes_history[i] = temp_hawkes_history[i - 1] * alpha + (
initial_volumes[i] if not np.isnan(initial_volumes[i]) else 0.0)
# 记录最后一个点的强度值,作为下一次增量计算的起点
self._last_hawkes_unscaled = temp_hawkes_history[-1] if len(temp_hawkes_history) > 0 else 0.0
# 用历史强度值的最后 hawkes_lookback 个点来填充滚动窗口
self._hawkes_window = (temp_hawkes_history * kappa)[-self.hawkes_lookback:]
print("状态初始化完成。")
def _update_state_incrementally(self, latest_volume: float):
"""
【增量计算】在每个新的Bar上调用更新强度值和滚动窗口。
"""
# 1. 计算最新的霍克斯强度值 (未缩放)
new_hawkes_unscaled = self._last_hawkes_unscaled * self._hawkes_alpha + (
latest_volume if not np.isnan(latest_volume) else 0.0)
# 2. 更新上一个点的状态,为下一次计算做准备
self._last_hawkes_unscaled = new_hawkes_unscaled
# 3. 将新的缩放后的强度值推入滚动窗口
new_hawkes_scaled = new_hawkes_unscaled * self.hawkes_kappa
# np.roll 会高效地将数组元素移动,然后我们将新值放在第一个位置
# 这比 append + delete 的效率高得多
self._hawkes_window = np.roll(self._hawkes_window, -1)
self._hawkes_window[-1] = new_hawkes_scaled
def on_init(self):
super().on_init()
self.pos_meta.clear()
# 重置状态
self._last_hawkes_unscaled = 0.0
self._hawkes_window = np.array([], dtype=np.float64)
def on_open_bar(self, open_price: float, symbol: str):
bar_history = self.get_bar_history()
min_bars_required = max(self.trendline_n + 2, self.hawkes_lookback + 2)
if len(bar_history) < min_bars_required:
return
# --- 【核心修改】霍克斯过程的状态更新 ---
# 检查是否是第一次运行
if self._hawkes_window.size == 0:
initial_volumes = np.array([b.volume for b in bar_history], dtype=float)
self._initialize_state(initial_volumes[:-1]) # 用到上一根bar为止的数据初始化
# 增量更新当前bar的状态
self._update_state_incrementally(float(bar_history[-1].volume))
# --- 后续逻辑使用更新后的状态进行计算 ---
self.cancel_all_pending_orders(symbol)
pos = self.get_current_positions().get(symbol, 0)
# 【核心修改】直接在固定长度的窗口上计算分位数
# 这比pandas.rolling快几个数量级且结果稳定
latest_hawkes_value = self._hawkes_window[-1]
latest_hawkes_upper = np.quantile(self._hawkes_window, self.hawkes_entry_percent)
latest_hawkes_lower = np.quantile(self._hawkes_window, self.hawkes_exit_percent)
# 1. 平仓逻辑 (完全不变)
meta = self.pos_meta.get(symbol)
if meta and pos != 0:
if latest_hawkes_value < latest_hawkes_lower:
self.log(f"霍克斯出场信号...") # 日志简化
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:
close_prices = np.array([b.close for b in bar_history])
prices_for_trendline = close_prices[-self.trendline_n - 1:-1]
trend_upper, trend_lower = calculate_latest_trendline_values(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 and self.indicators[0].is_condition_met(*self.get_indicator_tuple())
lower_break_event = last_close < trend_lower and prev_close > trend_lower and self.indicators[1].is_condition_met(*self.get_indicator_tuple())
hawkes_confirmation = latest_hawkes_value > latest_hawkes_upper
if hawkes_confirmation and (upper_break_event or lower_break_event):
trade_direction = None
if upper_break_event:
trade_direction = "SELL" if self.reverse_logic else "BUY"
elif lower_break_event:
trade_direction = "BUY" if self.reverse_logic else "SELL"
if trade_direction and trade_direction in self.order_direction:
self.log(f"开仓信号确认...") # 日志简化
self.send_open_order(trade_direction, open_price, self.trade_volume)
# send_open_order, send_market_order, on_rollover 等方法保持不变
# ... (代码省略,与之前版本相同) ...
# send_open_order, send_market_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"发送开仓订单: {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()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long