1、trend + hawks 策略

This commit is contained in:
2025-09-24 23:14:14 +08:00
parent bc93a547f0
commit f43a6b2822
33 changed files with 20646 additions and 1609 deletions

File diff suppressed because one or more lines are too long

View File

@@ -187,6 +187,7 @@ class AsymmetricOriginalDPStrategy(Strategy):
self._send_market_order('SELL', self.volume, latest_bar)
def _exit_logic(self, pos: int, latest_bar: Bar):
# 1. ATR追踪止损 (优先,保持不变)
if pos > 0:
new_stop_price = latest_bar.close - self.atr * self.atr_stop_multiplier
if self.trailing_stop_price is None: self.trailing_stop_price = new_stop_price
@@ -201,11 +202,32 @@ class AsymmetricOriginalDPStrategy(Strategy):
if latest_bar.high >= self.trailing_stop_price:
self._send_market_order('CLOSE_SHORT', abs(pos), latest_bar)
return
dominance = self.dp_long - self.dp_short
if pos > 0 and dominance < 0:
self._send_market_order('CLOSE_LONG', abs(pos), latest_bar)
elif pos < 0 and dominance > 0:
self._send_market_order('CLOSE_SHORT', abs(pos), latest_bar)
# 2. 策略性离场 (非对称逻辑)
if pos > 0:
# --- 多头出场逻辑 (保持不变): 寻找多头优势的丧失 ---
dominance = self.dp_long - self.dp_short
if dominance < 0: # 可以简化为一个固定的阈值,或者使用之前的动态阈值
self.log(f"多头策略性离场: Dominance {dominance:.2f} < 0")
self._send_market_order('CLOSE_LONG', abs(pos), latest_bar)
elif pos < 0:
# --- 【全新】空头出场逻辑: 寻找多头力量的重新集结 ---
if len(self.dp_long_history) < self.atr_period: return
# 找到近期多头力量的最低点
recent_min_dp_long = min(self.dp_long_history)
# 计算反弹阈值
rebound_threshold = recent_min_dp_long * (1 + self.short_params['long_power_falloff_pct'])
# 考虑到dp_long可能为0或很小增加一个绝对量的阈值防止过早离场
absolute_rebound_threshold = np.std(list(self.dp_long_history)) * 0.5
final_threshold = max(rebound_threshold, absolute_rebound_threshold)
if self.dp_long > final_threshold:
self.log(
f"空头策略性离场: 多头力量 {self.dp_long:.2f} 从低点 {recent_min_dp_long:.2f} 反弹超过阈值 {final_threshold:.2f}")
self._send_market_order('CLOSE_SHORT', abs(pos), latest_bar)
def _send_market_order(self, direction: str, vol: int, latest_bar: Bar):
offset = 'OPEN' if direction in ('BUY', 'SELL') else 'CLOSE'

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,208 @@
from collections import deque
from typing import List, Union, Optional
import numpy as np
from src.core_data import Bar, Order
from src.indicators.base_indicators import Indicator
from src.strategies.base_strategy import Strategy
from src.indicators.indicators import Empty
class SymmetricalDPStrategy(Strategy):
"""
机构级思路 · 最终对称版 v14.1 (双哲学参数)
通过两个核心哲学参数(进攻性、不对称性)对称地推导四种力量权重。
"""
def __init__(self, context, main_symbol: str,
trade_volume: int = 1,
lookback_period: int = 120,
decay_factor: float = 0.95,
dominance_multiplier: float = 1.5,
activity_multiplier: float = 1.0,
exit_multiplier: float = 0.5,
# --- 【核心】两个新的“哲学”参数 ---
aggression_factor: float = 1.5, # 进攻性因子
asymmetry_factor: float = 1.0, # 多空不对称因子
atr_period: int = 20,
atr_stop_multiplier: float = 2.0,
order_direction: Optional[list] = None,
enable_log: bool = False,
# ... 其他参数 ...
):
super().__init__(context, main_symbol, enable_log=enable_log)
# --- 对称的参数 ---
self.decay_factor = decay_factor
self.dominance_multiplier = dominance_multiplier
self.activity_multiplier = activity_multiplier
self.exit_multiplier = exit_multiplier
# --- 【核心】根据两个哲学参数,对称地推导出四个权重 ---
base_long_weight = 1.0
base_short_weight = 1.0 * asymmetry_factor
self.weights = {
'offensive_long': base_long_weight * aggression_factor,
'collapse_short': base_long_weight,
'offensive_short': base_short_weight * aggression_factor,
'collapse_long': base_short_weight,
}
# ... 其他 ...
self.lookback_period = lookback_period
self.atr_period = atr_period
self.atr_stop_multiplier = atr_stop_multiplier
self.symbol = main_symbol
self.volume = trade_volume
self.order_direction = order_direction or ['BUY', 'SELL']
self.min_bars_required = max(self.lookback_period, self.atr_period) + 2
# --- DP状态变量 ---
self.dp_long = 0.0
self.dp_short = 0.0
self.dominance_history = deque(maxlen=self.atr_period)
self.activity_history = deque(maxlen=self.atr_period)
self.previous_bar: Optional[Bar] = None
self.volume_history = deque(maxlen=self.lookback_period)
self.tr_history = deque(maxlen=self.atr_period)
self.atr = 0.0
self.trailing_stop_price: Optional[float] = None
if indicators is None: indicators = [Empty(), Empty()]
self.indicators = indicators
# on_open_bar 逻辑不变
def on_open_bar(self, open_price: float, symbol: str):
bars = self.get_bar_history()
if len(bars) < self.min_bars_required: return
latest_bar = bars[-1]
self.cancel_all_pending_orders(symbol)
self._update_dp_state(latest_bar)
pos = self.get_current_positions().get(symbol, 0)
if pos != 0:
self._exit_logic(pos, latest_bar)
return
for side_idx, direction in enumerate([1, -1]):
if (direction > 0 and 'BUY' not in self.order_direction) or \
(direction < 0 and 'SELL' not in self.order_direction):
continue
if self._entry_logic(side_idx, latest_bar):
break
# _update_dp_state 使用推导出的四个权重其他逻辑与v14.0相同
def _update_dp_state(self, latest_bar: Bar):
# 1. 更新历史数据和基础指标 (不变)
if self.previous_bar is None:
self.previous_bar = latest_bar
self.volume_history.append(latest_bar.volume)
return
self.volume_history.append(latest_bar.volume)
recent_avg_volume = np.mean(list(self.volume_history))
price_change = latest_bar.close - self.previous_bar.close
oi_change = getattr(latest_bar, 'close_oi', 0) - getattr(self.previous_bar, 'close_oi', 0)
epsilon = 1e-9
volume_factor = latest_bar.volume / (recent_avg_volume + epsilon)
volume_factor = np.clip(volume_factor, 0, 3)
o, h, l, c = latest_bar.open, latest_bar.high, latest_bar.low, latest_bar.close
range_size = h - l if h > l else epsilon
candlestick_factor = ((c - l) - (h - c)) / range_size
# 2. 计算多空基础分数
bullish_base_score = 0.0
bearish_base_score = 0.0
bullish_intensity = max(0, candlestick_factor)
bearish_intensity = max(0, -candlestick_factor)
if price_change > 0:
if oi_change > 0:
bullish_base_score = self.weights['offensive_long'] * bullish_intensity
elif oi_change < 0:
bullish_base_score = self.weights['collapse_short'] * bullish_intensity
elif price_change < 0:
if oi_change > 0:
bearish_base_score = self.weights['offensive_short'] * bearish_intensity
elif oi_change < 0:
bearish_base_score = self.weights['collapse_long'] * bearish_intensity
# 3. 应用状态转移方程
final_bullish_impulse = bullish_base_score * volume_factor
final_bearish_impulse = bearish_base_score * volume_factor
self.dp_long = self.dp_long * self.decay_factor + final_bullish_impulse
self.dp_short = self.dp_short * self.decay_factor + final_bearish_impulse
# 4. 更新历史和日志
dominance = self.dp_long - self.dp_short
activity = self.dp_long + self.dp_short
self.dominance_history.append(dominance)
self.activity_history.append(activity)
true_range = max(h - l, abs(h - self.previous_bar.close), abs(l - self.previous_bar.close))
self.tr_history.append(true_range)
self.atr = np.mean(list(self.tr_history))
self.previous_bar = latest_bar
self.log(f"DP更新: Dominance={dominance:.2f}, Activity={activity:.2f}")
# --- 决策和出场逻辑完全对称,无需修改 ---
def _entry_logic(self, side_idx: int, latest_bar: Bar):
# ... (与v12.0版本相同)
dominance = self.dp_long - self.dp_short
activity = self.dp_long + self.dp_short
if len(self.dominance_history) < self.atr_period: return False
dominance_std = np.std(list(self.dominance_history))
activity_std = np.std(list(self.activity_history))
if dominance_std == 0 or activity_std == 0: return False
dynamic_dom_thresh = dominance_std * self.dominance_multiplier
dynamic_act_thresh = activity_std * self.activity_multiplier
if activity < dynamic_act_thresh: return False
signal, side_str = False, ''
if side_idx == 0:
if dominance > dynamic_dom_thresh: signal, side_str = True, 'BUY'
else:
if dominance < -dynamic_dom_thresh: signal, side_str = True, 'SELL'
if not signal: return False
if not self.indicators[side_idx].is_condition_met(*self.get_indicator_tuple()): return
self.log(f"触发{side_str}信号: Dominance={dominance:.2f}, Activity={activity:.2f}")
self._send_market_order(side_str, self.volume, latest_bar)
return True
def _exit_logic(self, pos: int, latest_bar: Bar):
# ... (与v12.0版本相同)
if pos > 0:
new_stop_price = latest_bar.close - self.atr * self.atr_stop_multiplier
if self.trailing_stop_price is None: self.trailing_stop_price = new_stop_price
self.trailing_stop_price = max(self.trailing_stop_price, new_stop_price)
if latest_bar.low <= self.trailing_stop_price:
self._send_market_order('CLOSE_LONG', abs(pos), latest_bar)
return
elif pos < 0:
new_stop_price = latest_bar.close + self.atr * self.atr_stop_multiplier
if self.trailing_stop_price is None: self.trailing_stop_price = new_stop_price
self.trailing_stop_price = min(self.trailing_stop_price, new_stop_price)
if latest_bar.high >= self.trailing_stop_price:
self._send_market_order('CLOSE_SHORT', abs(pos), latest_bar)
return
dominance = self.dp_long - self.dp_short
dominance_std = np.std(list(self.dominance_history))
if dominance_std == 0: return
dynamic_exit_thresh = dominance_std * self.exit_multiplier
if pos > 0 and dominance < dynamic_exit_thresh:
self._send_market_order('CLOSE_LONG', abs(pos), latest_bar)
elif pos < 0 and dominance > -dynamic_exit_thresh:
self._send_market_order('CLOSE_SHORT', abs(pos), latest_bar)
def _send_market_order(self, direction: str, vol: int, latest_bar: Bar):
# ... (与v12.0版本相同)
offset = 'OPEN' if direction in ('BUY', 'SELL') else 'CLOSE'
if offset == 'OPEN':
if direction == 'BUY':
self.trailing_stop_price = latest_bar.close - self.atr * self.atr_stop_multiplier
else:
self.trailing_stop_price = latest_bar.close + self.atr * self.atr_stop_multiplier
else:
self.trailing_stop_price = None
oid = f"{self.symbol}_{direction}_{self.get_current_time():%Y%m%d%H%M%S}"
self.send_order(
Order(id=oid, symbol=self.symbol, direction=direction, volume=vol, price_type='MARKET', offset=offset))

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,181 @@
import numpy as np
import pandas as pd
import talib
from typing import Optional, Dict, Any, List
from src.core_data import Bar, Order
from src.strategies.base_strategy import Strategy
class RsiStrategy(Strategy):
"""
反转策略RSI 2-24 → PCA → 模型预测 → 极端值反向开仓
开仓:下一根 Open 价挂限价单
平仓:满足以下任一条件后市价平仓
1. 价格触及固定价差止损线
2. 持有满 holding_bars 根 K 线
"""
def __init__(
self,
context: Any,
main_symbol: str,
model: Any,
pca: Any,
scaler: Any,
lower_bound: float,
upper_bound: float,
trade_volume: int = 1,
order_direction: Optional[List[str]] = None,
holding_bars: int = 5,
# --- MODIFICATION START ---
stop_loss_points: Optional[float] = 5, # 止损点数, e.g., 50.0 for 50个价格点
# --- MODIFICATION END ---
enable_log: bool = False,
use_talib: bool = True,
):
super().__init__(context, main_symbol, enable_log)
self.main_symbol = main_symbol
self.trade_volume = trade_volume
self.model = model
self.pca = pca
self.scaler = scaler
self.lower_bound = lower_bound
self.upper_bound = upper_bound
self.order_direction = order_direction or ["BUY", "SELL"]
self.holding_bars = holding_bars
# --- MODIFICATION START ---
self.stop_loss_points = stop_loss_points
# --- MODIFICATION END ---
self.use_talib = use_talib
self.close_cache: List[float] = []
self.cache_size = 500
self.pos_meta: Dict[str, Dict[str, Any]] = {}
# --- MODIFICATION START ---
log_message = (
f"RsiPcaReversalStrategy 初始化:\n"
f"交易量={self.trade_volume}, lower={self.lower_bound}, upper={self.upper_bound}\n"
f"时间出场={self.holding_bars} bars\n"
f"固定价差止损={self.stop_loss_points if self.stop_loss_points else 'N/A'} points"
)
self.log(log_message)
# --- MODIFICATION END ---
# ... (工具函数保持不变) ...
def update_close_cache(self, bar_history: List[Bar]) -> None:
self.close_cache = [b.close for b in bar_history[-self.cache_size:]]
def calc_rsi_vector(self) -> np.ndarray:
close = np.array(self.close_cache, dtype=float)
rsi_vec = []
for i in range(2, 25):
if self.use_talib:
# talib 版本(最快)
rsi = talib.RSI(close, timeperiod=i)[-1]
else:
# 原滚动均值版本(与旧代码逻辑完全一致)
gain = np.where(np.diff(close) > 0, np.diff(close), 0)
loss = np.where(np.diff(close) < 0, -np.diff(close), 0)
avg_gain = pd.Series(gain).rolling(window=i, min_periods=i).mean().iloc[-1]
avg_loss = pd.Series(loss).rolling(window=i, min_periods=i).mean().iloc[-1]
rs = avg_gain / avg_loss if avg_loss != 0 else 100
rsi = 100 - (100 / (1 + rs))
rsi_vec.append(rsi)
return np.array(rsi_vec)
def predict_ret5(self, rsi_vec: np.ndarray) -> float:
vec_std = self.scaler.transform(rsi_vec.reshape(1, -1))
vec_pca = self.pca.transform(vec_std)
return self.model.predict(vec_pca)[0]
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()
if len(bar_history) < 30:
return
self.cancel_all_pending_orders(symbol)
pos = self.get_current_positions().get(symbol, 0)
# 1. 更新缓存 & 计算特征
self.update_close_cache(bar_history)
rsi_vec = self.calc_rsi_vector()
if np.isnan(rsi_vec).any():
return
pred = self.predict_ret5(rsi_vec)
# 2. 平仓逻辑
meta = self.pos_meta.get(symbol)
if meta and pos:
exit_reason = None
# --- MODIFICATION START ---
# 2a. 检查固定价差止损
if self.stop_loss_points is not None:
current_price = open_price # 使用当前bar的开盘价作为检查价格
entry_price = meta['entry_price']
direction = meta['direction']
if direction == "BUY" and current_price <= entry_price - self.stop_loss_points:
exit_reason = f"Stop Loss Hit ({entry_price - self.stop_loss_points})"
elif direction == "SELL" and current_price >= entry_price + self.stop_loss_points:
exit_reason = f"Stop Loss Hit ({entry_price + self.stop_loss_points})"
# --- MODIFICATION END ---
# 2b. 检查时间出场
if not exit_reason and len(bar_history) >= meta['expiry_idx']:
exit_reason = "Time Expiry"
if exit_reason:
self.log(f"平仓信号触发: {exit_reason}")
self.send_market_order(
"CLOSE_LONG" if meta['direction'] == "BUY" else "CLOSE_SHORT",
meta['volume'],
)
del self.pos_meta[symbol]
return
# 3. 开仓逻辑 (保持不变)
if pos == 0:
entry_price = open_price
if pred < self.lower_bound and "SELL" in self.order_direction:
self.send_limit_order("SELL", entry_price, self.trade_volume, bar_history[-1].datetime)
elif pred > self.upper_bound and "BUY" in self.order_direction:
self.send_limit_order("BUY", entry_price, self.trade_volume, bar_history[-1].datetime)
def send_open_order(self, direction: str, limit_price: float, volume: int, entry_dt: Any):
# (此函数逻辑已在上个版本中更新记录entry_price保持不变)
order_id = f"{self.symbol}_{direction}_{entry_dt.strftime('%Y%m%d%H%M%S')}"
order = Order(
id=order_id, symbol=self.symbol, direction=direction, volume=volume,
price_type="MARKET", submitted_time=entry_dt, offset="OPEN",
)
self.send_order(order)
self.pos_meta[self.symbol] = {
"direction": direction,
"volume": volume,
"expiry_idx": len(self.get_bar_history()) + self.holding_bars,
"entry_price": limit_price
}
self.log(f"开仓信号: {direction} at {limit_price}")
def send_market_order(self, direction: str, volume: int):
# ... (此函数保持不变) ...
order_id = f"{self.symbol}_{direction}_{self.get_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=self.get_current_time(), offset="CLOSE",
)
self.send_order(order)
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 it is too large Load Diff

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()

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long