1、卡尔曼策略
This commit is contained in:
File diff suppressed because one or more lines are too long
@@ -1,325 +0,0 @@
|
||||
# src/strategies/ReversalVolatilityStrategy.py
|
||||
|
||||
import numpy as np
|
||||
import talib
|
||||
from typing import Optional, Dict, Any, List, Tuple
|
||||
|
||||
from src.core_data import Bar, Order
|
||||
from src.indicators.indicators import StochasticOscillator, RSI
|
||||
from src.strategies.base_strategy import Strategy
|
||||
|
||||
|
||||
# def calculate_atr(bars: List[Bar], period: int) -> float:
|
||||
# """
|
||||
# 从Bar对象列表中计算平均真实波幅(ATR)。
|
||||
# """
|
||||
# if len(bars) < period + 1:
|
||||
# return 0.0
|
||||
|
||||
# true_ranges = []
|
||||
# for i in range(1, len(bars)):
|
||||
# high_low = bars[i].high - bars[i].low
|
||||
# high_prev_close = abs(bars[i].high - bars[i - 1].close)
|
||||
# low_prev_close = abs(bars[i].low - bars[i - 1].close)
|
||||
# tr = max(high_low, high_prev_close, low_prev_close)
|
||||
# true_ranges.append(tr)
|
||||
|
||||
# return np.mean(true_ranges[-(period):])
|
||||
|
||||
|
||||
def find_fvg(bars: List[Bar]) -> Optional[Tuple[str, float, float]]:
|
||||
"""
|
||||
使用最近的三根K线识别公允价值缺口(FVG)。
|
||||
|
||||
返回:
|
||||
一个元组 (方向, 上沿价格, 下沿价格) 或 None (如果没有找到FVG)。
|
||||
'bullish': K线1的最高价和K线3的最低价之间的缺口。
|
||||
'bearish': K线1的最低价和K线3的最高价之间的缺口。
|
||||
"""
|
||||
if len(bars) < 3:
|
||||
return None
|
||||
|
||||
bar1, bar2, bar3 = bars[-3], bars[-2], bars[-1]
|
||||
|
||||
# 检查看涨FVG (向上的失衡)
|
||||
if bar1.high < bar3.low:
|
||||
# 缺口本身就意味着中间的K线是强劲的
|
||||
return ('bullish', bar3.low, bar1.high)
|
||||
|
||||
# 检查看跌FVG (向下的失衡)
|
||||
if bar1.low > bar3.high:
|
||||
return ('bearish', bar1.low, bar3.high)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
class ReversalVolatilityStrategy(Strategy):
|
||||
"""
|
||||
一个反转波动率策略,该策略在价格回调至公允价值缺口(FVG)时入场,
|
||||
并使用基于ATR的两阶段止盈系统,其中第二阶段为ATR回撤止盈。
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
context: Any,
|
||||
main_symbol: str,
|
||||
enable_log: bool,
|
||||
trade_volume: int,
|
||||
atr_period: int = 14,
|
||||
stop_loss_atr_multiplier: float = 1.0,
|
||||
partial_profit_atr_multiplier: float | List[float] = 2.0,
|
||||
# full_profit_atr_multiplier: float = 4.0, # 移除固定止盈乘数
|
||||
trailing_stop_atr_multiplier: float = 1.0, # 新增:回撤止盈ATR乘数
|
||||
open_atr_multiplier: float | List[float] = 1.0,
|
||||
order_direction=None,
|
||||
indicators=[None, None],
|
||||
):
|
||||
"""
|
||||
Args:
|
||||
context: 回测上下文。
|
||||
symbol (str): 主要交易的合约代码。
|
||||
trade_volume (int): 每笔交易的总手数。必须是偶数。
|
||||
atr_period (int): 计算ATR的回看周期。
|
||||
stop_loss_atr_multiplier (float): ATR的乘数,用于设置止损。
|
||||
partial_profit_atr_multiplier (float): ATR的乘数,用于设置第一止盈目标。
|
||||
trailing_stop_atr_multiplier (float): ATR的乘数,用于设置最大回撤止盈。
|
||||
"""
|
||||
super().__init__(context, main_symbol, enable_log)
|
||||
if order_direction is None:
|
||||
order_direction = ['BUY', 'SELL', ]
|
||||
# if trade_volume % 2 != 0:
|
||||
# raise ValueError("为了实现部分止盈, trade_volume必须是偶数。")
|
||||
|
||||
self.trade_volume = trade_volume
|
||||
self.atr_period = atr_period
|
||||
self.stop_loss_atr_multiplier = stop_loss_atr_multiplier
|
||||
if isinstance(partial_profit_atr_multiplier, float) or isinstance(partial_profit_atr_multiplier, int):
|
||||
self.partial_profit_atr_multiplier = [partial_profit_atr_multiplier, partial_profit_atr_multiplier]
|
||||
elif isinstance(partial_profit_atr_multiplier, list):
|
||||
self.partial_profit_atr_multiplier = partial_profit_atr_multiplier
|
||||
|
||||
self.trailing_stop_atr_multiplier = trailing_stop_atr_multiplier
|
||||
if isinstance(open_atr_multiplier, float) or isinstance(open_atr_multiplier, int):
|
||||
self.open_atr_multiplier = [open_atr_multiplier, open_atr_multiplier]
|
||||
elif isinstance(open_atr_multiplier, list):
|
||||
self.open_atr_multiplier = open_atr_multiplier # 初始化新增参数
|
||||
self.order_direction = order_direction
|
||||
|
||||
self.indicator_long = indicators[0]
|
||||
self.indicator_short = indicators[1]
|
||||
|
||||
self.main_symbol = main_symbol
|
||||
self.order_id_counter = 0
|
||||
self._last_order_id: Optional[str] = None
|
||||
|
||||
# 字典,用于存储交易的元数据,如入场价、入场时的ATR和最高/最低价
|
||||
self.position_meta: Dict[str, Any] = {}
|
||||
|
||||
self.log(
|
||||
"ReversalVolatilityStrategy 初始化参数: \n"
|
||||
f"交易量={self.trade_volume}, ATR周期={self.atr_period}, \n"
|
||||
f"止损ATR乘数={self.stop_loss_atr_multiplier}, 部分止盈ATR乘数={self.partial_profit_atr_multiplier}, \n"
|
||||
f"回撤止盈ATR乘数={self.trailing_stop_atr_multiplier}" # 更新日志信息
|
||||
)
|
||||
|
||||
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
|
||||
current_time = self.get_current_time()
|
||||
|
||||
# --- 1. 取消上一根K线未成交的限价单 ---
|
||||
if self._last_order_id and self._last_order_id in self.get_pending_orders():
|
||||
self.cancel_order(self._last_order_id)
|
||||
self.log(f"已取消上一根K线的挂单: {self._last_order_id}")
|
||||
self._last_order_id = None
|
||||
|
||||
bar_history = self.get_bar_history()
|
||||
if len(bar_history) < self.atr_period + 3:
|
||||
return # 数据不足,无法计算指标
|
||||
|
||||
# --- 2. 计算指标 ---
|
||||
# current_atr = calculate_atr(bar_history, self.atr_period)
|
||||
current_atr = talib.ATR(np.array([bar.high for bar in bar_history]),
|
||||
np.array([bar.low for bar in bar_history]),
|
||||
np.array([bar.close for bar in bar_history]),
|
||||
timeperiod=self.atr_period)[-1]
|
||||
if current_atr == 0:
|
||||
return # 避免除以零或在不活跃的市场中交易
|
||||
|
||||
fvg_signal = find_fvg(bar_history)
|
||||
|
||||
# --- 3. 管理现有持仓 (平仓逻辑) ---
|
||||
current_positions = self.get_current_positions()
|
||||
position_volume = current_positions.get(self.symbol, 0)
|
||||
meta = self.position_meta.get(self.symbol)
|
||||
|
||||
if position_volume != 0 and meta:
|
||||
# 传递当前Bar的最高价和最低价用于更新最优价格
|
||||
current_bar_high = bar_history[-1].high
|
||||
current_bar_low = bar_history[-1].low
|
||||
self.manage_open_position(position_volume, open_price, current_bar_high, current_bar_low, meta, current_atr)
|
||||
return # 如果正在管理平仓,则不评估进场信号
|
||||
|
||||
# --- 4. 评估新机会 (开仓逻辑) ---
|
||||
if position_volume == 0 and fvg_signal:
|
||||
self.evaluate_entry_signal(fvg_signal, open_price, current_atr)
|
||||
|
||||
def manage_open_position(self, volume: int, current_price: float, current_bar_high: float, current_bar_low: float,
|
||||
meta: Dict, current_atr: float):
|
||||
entry_price = meta['entry_price']
|
||||
entry_atr = meta['entry_atr']
|
||||
partially_exited = meta.get('partially_exited', False)
|
||||
optimal_price = meta.get('optimal_price', entry_price) # 初始化或获取最优价格
|
||||
|
||||
# 定义基于入场时ATR的止损目标价格
|
||||
# stop_loss_price = entry_price - entry_atr * self.stop_loss_atr_multiplier if volume > 0 else entry_price + entry_atr * self.stop_loss_atr_multiplier
|
||||
stop_loss_price = entry_price - 5 if volume > 0 else entry_price + 5
|
||||
partial_profit_atr_multiplier = self.partial_profit_atr_multiplier[0] if volume > 0 else \
|
||||
self.partial_profit_atr_multiplier[1]
|
||||
partial_tp_price = entry_price + entry_atr * partial_profit_atr_multiplier if volume > 0 else entry_price - entry_atr * partial_profit_atr_multiplier
|
||||
|
||||
# 检查多头持仓
|
||||
if volume > 0:
|
||||
# 更新最优价格 (最高价)
|
||||
meta['optimal_price'] = max(optimal_price, current_bar_high)
|
||||
optimal_price = meta['optimal_price']
|
||||
|
||||
# # 最大回撤止盈 (只在部分止盈后激活)
|
||||
# if partially_exited:
|
||||
# trailing_stop_level = optimal_price - self.trailing_stop_atr_multiplier * current_atr
|
||||
# if current_price <= trailing_stop_level:
|
||||
# self.log(f"多头回撤止盈到达 {current_price:.2f}. 关闭剩余仓位。")
|
||||
# self.close_position("CLOSE_LONG", abs(volume))
|
||||
# 部分止盈
|
||||
if current_price >= partial_tp_price and not partially_exited:
|
||||
self.log(f"多头部分止盈到达 {current_price:.2f}. 关闭仓位。")
|
||||
# self.send_market_order("CLOSE_LONG", self.trade_volume)
|
||||
self.close_position("CLOSE_LONG", abs(volume))
|
||||
meta['partially_exited'] = True
|
||||
# 首次触发部分止盈时,将当前价格设置为最优价格,作为回撤止盈的起点
|
||||
meta['optimal_price'] = current_price
|
||||
# 止损
|
||||
elif current_price <= stop_loss_price:
|
||||
self.log(f"多头止损到达 {current_price:.2f}. 关闭全部仓位。")
|
||||
self.close_position("CLOSE_LONG", abs(volume))
|
||||
|
||||
# 检查空头持仓
|
||||
elif volume < 0:
|
||||
# 更新最优价格 (最低价)
|
||||
meta['optimal_price'] = min(optimal_price, current_bar_low)
|
||||
optimal_price = meta['optimal_price']
|
||||
|
||||
# # 最大回撤止盈 (只在部分止盈后激活)
|
||||
# if partially_exited:
|
||||
# trailing_stop_level = optimal_price + self.trailing_stop_atr_multiplier * current_atr
|
||||
# if current_price >= trailing_stop_level:
|
||||
# self.log(f"空头回撤止盈到达 {current_price:.2f}. 关闭剩余仓位。")
|
||||
# self.close_position("CLOSE_SHORT", abs(volume))
|
||||
# 部分止盈
|
||||
if current_price <= partial_tp_price and not partially_exited:
|
||||
self.log(f"空头部分止盈到达 {current_price:.2f}. 关闭一半仓位。")
|
||||
# self.send_market_order("CLOSE_SHORT", self.trade_volume)
|
||||
self.close_position("CLOSE_SHORT", abs(volume))
|
||||
meta['partially_exited'] = True
|
||||
# 首次触发部分止盈时,将当前价格设置为最优价格,作为回撤止盈的起点
|
||||
meta['optimal_price'] = current_price
|
||||
# 止损
|
||||
elif current_price >= stop_loss_price:
|
||||
self.log(f"空头止损到达 {current_price:.2f}. 关闭全部仓位。")
|
||||
self.close_position("CLOSE_SHORT", abs(volume))
|
||||
|
||||
def evaluate_entry_signal(self, fvg_signal, open_price, current_atr):
|
||||
direction, fvg_high, fvg_low = fvg_signal
|
||||
open_atr_multiplier = self.open_atr_multiplier[0] if 'bullish' == direction else self.open_atr_multiplier[1]
|
||||
|
||||
# 多头入场信号
|
||||
if 'bullish' == direction:
|
||||
# 在FVG上沿挂限价单,等待价格回调
|
||||
if self.indicator_long is None or self.indicator_long.is_condition_met(*self.get_indicator_tuple()):
|
||||
limit_price = fvg_low + open_atr_multiplier * current_atr
|
||||
# 避免追高
|
||||
if open_price > limit_price:
|
||||
self.log(f"检测到看涨FVG。在 {limit_price:.2f} 挂限价买单。")
|
||||
new_order = self.send_limit_order("BUY", limit_price, self.trade_volume, {'entry_atr': current_atr})
|
||||
if new_order:
|
||||
self._last_order_id = new_order.id
|
||||
|
||||
# 空头入场信号
|
||||
elif 'bearish' == direction:
|
||||
# 在FVG下沿挂限价单
|
||||
if self.indicator_short is None or self.indicator_short.is_condition_met(*self.get_indicator_tuple()):
|
||||
limit_price = fvg_high - open_atr_multiplier * current_atr
|
||||
# 避免追低
|
||||
if open_price < limit_price:
|
||||
self.log(f"检测到看跌FVG。在 {limit_price:.2f} 挂限价卖单。")
|
||||
new_order = self.send_limit_order("SELL", limit_price, self.trade_volume, {'entry_atr': current_atr})
|
||||
if new_order:
|
||||
self._last_order_id = new_order.id
|
||||
|
||||
def close_position(self, direction: str, volume: int):
|
||||
self.send_market_order(direction, volume)
|
||||
if self.symbol in self.position_meta:
|
||||
del self.position_meta[self.symbol] # 完全平仓后清理元数据
|
||||
|
||||
def send_limit_order(self, direction: str, limit_price: float, volume: int, meta: Dict) -> Optional[Order]:
|
||||
if direction not in self.order_direction:
|
||||
return None
|
||||
|
||||
order_id = f"{self.symbol}_{direction}_{self.get_current_time().strftime('%Y%m%d%H%M%S')}_{self.order_id_counter}"
|
||||
self.order_id_counter += 1
|
||||
order = Order(
|
||||
id=order_id,
|
||||
symbol=self.symbol,
|
||||
direction=direction,
|
||||
volume=volume,
|
||||
price_type="LIMIT",
|
||||
limit_price=limit_price,
|
||||
submitted_time=self.get_current_time(),
|
||||
offset="OPEN",
|
||||
)
|
||||
|
||||
# 存储开仓时的元数据,包括入场价和入场时的ATR
|
||||
self.position_meta[self.symbol] = {
|
||||
"entry_price": limit_price,
|
||||
"entry_atr": meta.get('entry_atr', 0),
|
||||
"partially_exited": False,
|
||||
"optimal_price": limit_price # 初始最优价格设置为入场价格
|
||||
}
|
||||
|
||||
entry_price = limit_price
|
||||
entry_atr = meta.get('entry_atr', 0)
|
||||
stop_loss_price = entry_price - 5 if direction == "BUY" else entry_price + 5
|
||||
partial_profit_atr_multiplier = self.partial_profit_atr_multiplier[0] if volume > 0 else \
|
||||
self.partial_profit_atr_multiplier[1]
|
||||
partial_tp_price = entry_price + entry_atr * partial_profit_atr_multiplier if volume > 0 else entry_price - entry_atr * partial_profit_atr_multiplier
|
||||
|
||||
self.log(
|
||||
f'atr: {meta.get("entry_atr", 0)}, stop_loss_price: {stop_loss_price}, partial_tp_price: {partial_tp_price}')
|
||||
|
||||
return self.send_order(order)
|
||||
|
||||
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')}_{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="CLOSE" # 在本策略中,所有市价单都是为了平仓
|
||||
)
|
||||
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.log("检测到换月。已清空挂单和持仓元数据。")
|
||||
|
||||
def on_close_bar(self, bar: Bar, next_bar_open: Optional[float] = None):
|
||||
self.cancel_all_pending_orders(self.main_symbol)
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -1,169 +0,0 @@
|
||||
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
@@ -1,178 +0,0 @@
|
||||
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()
|
||||
@@ -1,188 +0,0 @@
|
||||
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
|
||||
|
||||
|
||||
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,
|
||||
):
|
||||
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]] = {}
|
||||
|
||||
# --- 【核心修改】状态缓存重构 ---
|
||||
# 只缓存上一个时间点的霍克斯强度值 (未缩放)
|
||||
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
|
||||
lower_break_event = last_close < trend_lower and prev_close > trend_lower
|
||||
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
@@ -1,183 +0,0 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "initial_id",
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2025-07-22T07:44:51.375234Z",
|
||||
"start_time": "2025-07-22T07:44:51.352161Z"
|
||||
},
|
||||
"collapsed": true
|
||||
},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from datetime import datetime\n",
|
||||
"%load_ext autoreload\n",
|
||||
"%autoreload 2\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "a559dfcf",
|
||||
"metadata": {
|
||||
"ExecuteTime": {
|
||||
"end_time": "2025-07-22T07:44:56.927700Z",
|
||||
"start_time": "2025-07-22T07:44:51.391111Z"
|
||||
}
|
||||
},
|
||||
"outputs": [
|
||||
{
|
||||
"ename": "ModuleNotFoundError",
|
||||
"evalue": "No module named 'src'",
|
||||
"output_type": "error",
|
||||
"traceback": [
|
||||
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
|
||||
"\u001b[31mModuleNotFoundError\u001b[39m Traceback (most recent call last)",
|
||||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[2]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mturtle\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m down\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01msrc\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01manalysis\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mresult_analyzer\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m ResultAnalyzer\n\u001b[32m 3\u001b[39m \u001b[38;5;66;03m# 导入所有必要的模块\u001b[39;00m\n\u001b[32m 4\u001b[39m \u001b[38;5;28;01mfrom\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01msrc\u001b[39;00m\u001b[34;01m.\u001b[39;00m\u001b[34;01mdata_manager\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[38;5;28;01mimport\u001b[39;00m DataManager\n",
|
||||
"\u001b[31mModuleNotFoundError\u001b[39m: No module named 'src'"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"\n",
|
||||
"from turtle import down\n",
|
||||
"from src.analysis.result_analyzer import ResultAnalyzer\n",
|
||||
"# 导入所有必要的模块\n",
|
||||
"from src.data_manager import DataManager\n",
|
||||
"from src.backtest_engine import BacktestEngine\n",
|
||||
"from src.indicators.indicator_list import INDICATOR_LIST\n",
|
||||
"from src.indicators.indicators import RSI, BollingerBandwidth, HistoricalRange, NormalizedATR, RateOfChange, StochasticOscillator\n",
|
||||
"from src.strategies.OpenTwoFactorStrategy import SimpleLimitBuyStrategyLong, SimpleLimitBuyStrategyShort, SimpleLimitBuyStrategy\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# --- 配置参数 ---\n",
|
||||
"# 获取当前脚本所在目录,假设数据文件在项目根目录下的 data 文件夹内\n",
|
||||
"# data_file_path = '/mnt/d/PyProject/NewQuant/data/data/SHFE_rb2510/SHFE_rb2510_min60.csv'\n",
|
||||
"# data_file_path = \"/mnt/d/PyProject/NewQuant/data/data/KQ_m@CZCE_MA/KQ_m@CZCE_MA_min60.csv\"\n",
|
||||
"# data_file_path = \"/mnt/d/PyProject/NewQuant/data/data/KQ_m@SHFE_rb/KQ_m@SHFE_rb_min60.csv\"\n",
|
||||
"data_file_path = \"/mnt/d/PyProject/NewQuant/data/data/KQ_m@CZCE_MA/KQ_m@CZCE_MA_min60.csv\"\n",
|
||||
"\n",
|
||||
"initial_capital = 100000.0\n",
|
||||
"slippage_rate = 0.000 # 假设每笔交易0.1%的滑点\n",
|
||||
"commission_rate = 0.0001 # 假设每笔交易0.02%的佣金\n",
|
||||
"\n",
|
||||
"global_config = {\n",
|
||||
" 'symbol': 'KQ_m@CZCE_MA',\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"# Short 可用\n",
|
||||
"strategy_parameters = {\n",
|
||||
" 'main_symbol': \"MA\", # 根据您的数据文件中的品种名称调整\n",
|
||||
" 'trade_volume': 1,\n",
|
||||
" 'lag': 7,\n",
|
||||
" # 'range_factor': 1.8, # 示例值,需要通过网格搜索优化\n",
|
||||
" # 'profit_factor': 2.8, # 示例值\n",
|
||||
" # 'range_factor': 1.6, # 示例值,需要通过网格搜索优化\n",
|
||||
" # 'profit_factor': 2.1, # 示例值\n",
|
||||
" 'range_factor_l': 1.8, # 示例值,需要通过网格搜索优化\n",
|
||||
" 'profit_factor_l': 2.8, # 示例值\n",
|
||||
" 'range_factor_s': 1.6, # 示例值,需要通过网格搜索优化\n",
|
||||
" 'profit_factor_s': 2.1, # 示例值\n",
|
||||
" 'max_position': 10,\n",
|
||||
" 'enable_log': True,\n",
|
||||
" 'stop_loss_points': 20,\n",
|
||||
" 'use_indicator': True,\n",
|
||||
" # 'indicator': HistoricalRange(11, 25, 20),\n",
|
||||
" # 'indicator': BollingerBandwidth(window=20, nbdev=2.0, down_bound=1.9, up_bound=3.25),\n",
|
||||
" 'indicator_l': HistoricalRange(11, 25, 20),\n",
|
||||
" 'indicator_s': BollingerBandwidth(window=20, nbdev=2.0, down_bound=1.9, up_bound=3.25),\n",
|
||||
"}\n",
|
||||
"start_time = datetime(2021, 1, 1)\n",
|
||||
"end_time = datetime(2024, 6, 1)\n",
|
||||
"\n",
|
||||
"start_time = datetime(2024, 6, 1)\n",
|
||||
"end_time = datetime(2025, 8, 1)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"# --- 1. 初始化数据管理器 ---\n",
|
||||
"print(\"初始化数据管理器...\")\n",
|
||||
"data_manager = DataManager(file_path=data_file_path, symbol=global_config['symbol'], start_time=start_time, end_time=end_time)\n",
|
||||
"# 确保 DataManager 能够重置以进行多次回测\n",
|
||||
"# data_manager.reset() # 首次运行不需要重置\n",
|
||||
"\n",
|
||||
"# --- 2. 初始化回测引擎并运行 ---\n",
|
||||
"print(\"\\n初始化回测引擎...\")\n",
|
||||
"engine = BacktestEngine(\n",
|
||||
" data_manager=data_manager,\n",
|
||||
" strategy_class=SimpleLimitBuyStrategy,\n",
|
||||
" # current_segment_symbol=strategy_parameters['symbol'],\n",
|
||||
" strategy_params=strategy_parameters,\n",
|
||||
" initial_capital=initial_capital,\n",
|
||||
" slippage_rate=slippage_rate,\n",
|
||||
" commission_rate=commission_rate,\n",
|
||||
" roll_over_mode=True,\n",
|
||||
" start_time=start_time,\n",
|
||||
" end_time=end_time,\n",
|
||||
" indicators=INDICATOR_LIST\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(\"\\n开始运行回测...\")\n",
|
||||
"engine.run_backtest()\n",
|
||||
"print(\"\\n回测运行完毕。\")\n",
|
||||
"\n",
|
||||
"# --- 3. 获取回测结果 ---\n",
|
||||
"results = engine.get_backtest_results()\n",
|
||||
"portfolio_snapshots = results[\"portfolio_snapshots\"]\n",
|
||||
"trade_history = results[\"trade_history\"]\n",
|
||||
"initial_capital_result = results[\"initial_capital\"]\n",
|
||||
"bars = results[\"all_bars\"]\n",
|
||||
"\n",
|
||||
"# --- 4. 结果分析与可视化 ---\n",
|
||||
"if portfolio_snapshots:\n",
|
||||
" analyzer = ResultAnalyzer(portfolio_snapshots, trade_history, bars, initial_capital_result, INDICATOR_LIST)\n",
|
||||
"\n",
|
||||
" analyzer.generate_report()\n",
|
||||
" analyzer.plot_performance()\n",
|
||||
" metrics = analyzer.calculate_all_metrics()\n",
|
||||
" print(metrics)\n",
|
||||
"\n",
|
||||
" analyzer.analyze_indicators()\n",
|
||||
"else:\n",
|
||||
" print(\"\\n没有生成投资组合快照,无法进行结果分析。\")\n",
|
||||
"\n",
|
||||
"# --- 4. 结果分析与可视化 (待实现) ---\n",
|
||||
"# if portfolio_snapshots:\n",
|
||||
"# analyzer = ResultAnalyzer(portfolio_snapshots, trade_history, initial_capital_result)\n",
|
||||
"# metrics = analyzer.calculate_all_metrics()\n",
|
||||
"# print(\"\\n--- 绩效指标 ---\")\n",
|
||||
"# for key, value in metrics.items():\n",
|
||||
"# print(f\" {key}: {value:.4f}\")\n",
|
||||
"#\n",
|
||||
"# print(\"\\n--- 绘制绩效图表 ---\")\n",
|
||||
"# analyzer.plot_performance()\n",
|
||||
"# else:\n",
|
||||
"# print(\"\\n没有生成投资组合快照,无法进行结果分析。\")\n",
|
||||
"\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "quant",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.12.11"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -109,6 +109,7 @@ class TrendlineHawkesStrategy(Strategy):
|
||||
self._hawkes_window = np.array([], dtype=np.float64)
|
||||
|
||||
def on_open_bar(self, open_price: float, symbol: str):
|
||||
self.symbol = symbol
|
||||
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:
|
||||
|
||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user