1、新增dp策略
This commit is contained in:
@@ -0,0 +1,315 @@
|
||||
# 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)
|
||||
avg_entry_price = self.get_average_position_price(self.symbol)
|
||||
|
||||
if position_volume != 0:
|
||||
# 传递当前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, avg_entry_price, 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,
|
||||
entry_price: float, current_atr: float):
|
||||
|
||||
# current_atr = self.position_meta[self.symbol]['entry_atr']
|
||||
|
||||
# 定义基于入场时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 + current_atr * partial_profit_atr_multiplier if volume > 0 else entry_price - current_atr * partial_profit_atr_multiplier
|
||||
|
||||
self.log(f'持仓:{volume}, pos:{current_price - entry_price}, current_price:{current_price}, stop_loss_price:{stop_loss_price}, partial_profit_atr_multiplier:{partial_profit_atr_multiplier}, partial_tp_price:{partial_tp_price}')
|
||||
|
||||
# 检查多头持仓
|
||||
|
||||
if volume > 0:
|
||||
|
||||
# # 最大回撤止盈 (只在部分止盈后激活)
|
||||
# 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:
|
||||
self.log(f"多头部分止盈到达 {current_price:.2f}. 关闭仓位。")
|
||||
# self.send_market_order("CLOSE_LONG", self.trade_volume)
|
||||
self.close_position("CLOSE_LONG", abs(volume))
|
||||
# 首次触发部分止盈时,将当前价格设置为最优价格,作为回撤止盈的起点
|
||||
# 止损
|
||||
elif current_price <= stop_loss_price:
|
||||
self.log(f"多头止损到达 {current_price:.2f}. 关闭全部仓位。")
|
||||
self.close_position("CLOSE_LONG", abs(volume))
|
||||
|
||||
# 检查空头持仓
|
||||
elif volume < 0:
|
||||
|
||||
# # 最大回撤止盈 (只在部分止盈后激活)
|
||||
# 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:
|
||||
self.log(f"空头部分止盈到达 {current_price:.2f}. 关闭一半仓位。")
|
||||
# self.send_market_order("CLOSE_SHORT", self.trade_volume)
|
||||
self.close_position("CLOSE_SHORT", abs(volume))
|
||||
# 止损
|
||||
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} 挂限价买单。fvg_high:{fvg_low},current_atr:{current_atr}")
|
||||
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} 挂限价卖单。fvg_high:{fvg_high},current_atr:{current_atr}")
|
||||
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
@@ -0,0 +1,325 @@
|
||||
# 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
1289
futures_trading_strategies/MA/ReversalVolatilityStrategy/main.ipynb
Normal file
1289
futures_trading_strategies/MA/ReversalVolatilityStrategy/main.ipynb
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,315 @@
|
||||
# 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)
|
||||
avg_entry_price = self.get_average_position_price(self.symbol)
|
||||
|
||||
if position_volume != 0:
|
||||
# 传递当前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, avg_entry_price, 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,
|
||||
entry_price: float, current_atr: float):
|
||||
|
||||
# current_atr = self.position_meta[self.symbol]['entry_atr']
|
||||
|
||||
# 定义基于入场时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 + current_atr * partial_profit_atr_multiplier if volume > 0 else entry_price - current_atr * partial_profit_atr_multiplier
|
||||
|
||||
self.log(f'持仓:{volume}, pos:{current_price - entry_price}, current_price:{current_price}, stop_loss_price:{stop_loss_price}, partial_profit_atr_multiplier:{partial_profit_atr_multiplier}, partial_tp_price:{partial_tp_price}')
|
||||
|
||||
# 检查多头持仓
|
||||
|
||||
if volume > 0:
|
||||
|
||||
# # 最大回撤止盈 (只在部分止盈后激活)
|
||||
# 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:
|
||||
self.log(f"多头部分止盈到达 {current_price:.2f}. 关闭仓位。")
|
||||
# self.send_market_order("CLOSE_LONG", self.trade_volume)
|
||||
self.close_position("CLOSE_LONG", abs(volume))
|
||||
# 首次触发部分止盈时,将当前价格设置为最优价格,作为回撤止盈的起点
|
||||
# 止损
|
||||
elif current_price <= stop_loss_price:
|
||||
self.log(f"多头止损到达 {current_price:.2f}. 关闭全部仓位。")
|
||||
self.close_position("CLOSE_LONG", abs(volume))
|
||||
|
||||
# 检查空头持仓
|
||||
elif volume < 0:
|
||||
|
||||
# # 最大回撤止盈 (只在部分止盈后激活)
|
||||
# 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:
|
||||
self.log(f"空头部分止盈到达 {current_price:.2f}. 关闭一半仓位。")
|
||||
# self.send_market_order("CLOSE_SHORT", self.trade_volume)
|
||||
self.close_position("CLOSE_SHORT", abs(volume))
|
||||
# 止损
|
||||
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} 挂限价买单。fvg_high:{fvg_low},current_atr:{current_atr}")
|
||||
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} 挂限价卖单。fvg_high:{fvg_high},current_atr:{current_atr}")
|
||||
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
@@ -0,0 +1,313 @@
|
||||
# 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)
|
||||
avg_entry_price = self.get_average_position_price(self.symbol)
|
||||
|
||||
if position_volume != 0:
|
||||
# 传递当前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, avg_entry_price, 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,
|
||||
entry_price: float, current_atr: float):
|
||||
|
||||
# 定义基于入场时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 + current_atr * partial_profit_atr_multiplier if volume > 0 else entry_price - current_atr * partial_profit_atr_multiplier
|
||||
|
||||
self.log(f'持仓:{volume}, pos:{current_price - entry_price}, current_price:{current_price}, stop_loss_price:{stop_loss_price}, partial_profit_atr_multiplier:{partial_profit_atr_multiplier}, partial_tp_price:{partial_tp_price}')
|
||||
|
||||
# 检查多头持仓
|
||||
|
||||
if volume > 0:
|
||||
|
||||
# # 最大回撤止盈 (只在部分止盈后激活)
|
||||
# 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:
|
||||
self.log(f"多头部分止盈到达 {current_price:.2f}. 关闭仓位。")
|
||||
# self.send_market_order("CLOSE_LONG", self.trade_volume)
|
||||
self.close_position("CLOSE_LONG", abs(volume))
|
||||
# 首次触发部分止盈时,将当前价格设置为最优价格,作为回撤止盈的起点
|
||||
# 止损
|
||||
elif current_price <= stop_loss_price:
|
||||
self.log(f"多头止损到达 {current_price:.2f}. 关闭全部仓位。")
|
||||
self.close_position("CLOSE_LONG", abs(volume))
|
||||
|
||||
# 检查空头持仓
|
||||
elif volume < 0:
|
||||
|
||||
# # 最大回撤止盈 (只在部分止盈后激活)
|
||||
# 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:
|
||||
self.log(f"空头部分止盈到达 {current_price:.2f}. 关闭一半仓位。")
|
||||
# self.send_market_order("CLOSE_SHORT", self.trade_volume)
|
||||
self.close_position("CLOSE_SHORT", abs(volume))
|
||||
# 止损
|
||||
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} 挂限价买单。fvg_high:{fvg_low},current_atr:{current_atr}")
|
||||
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} 挂限价卖单。fvg_high:{fvg_high},current_atr:{current_atr}")
|
||||
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
1029
futures_trading_strategies/SA/ReversalVolatilityStrategy/main2.ipynb
Normal file
1029
futures_trading_strategies/SA/ReversalVolatilityStrategy/main2.ipynb
Normal file
File diff suppressed because one or more lines are too long
12377
futures_trading_strategies/SA/main2.ipynb
Normal file
12377
futures_trading_strategies/SA/main2.ipynb
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,316 @@
|
||||
# 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)
|
||||
avg_entry_price = self.get_average_position_price(self.symbol)
|
||||
|
||||
if position_volume != 0:
|
||||
# 传递当前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, avg_entry_price, current_atr)
|
||||
return # 如果正在管理平仓,则不评估进场信号
|
||||
|
||||
# --- 4. 评估新机会 (开仓逻辑) ---
|
||||
if position_volume == 0 and fvg_signal:
|
||||
self.log(f'datetime: {bar_history[-1].datetime}')
|
||||
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,
|
||||
entry_price: float, current_atr: float):
|
||||
|
||||
# current_atr = self.position_meta[self.symbol]['entry_atr']
|
||||
|
||||
# 定义基于入场时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 + current_atr * partial_profit_atr_multiplier if volume > 0 else entry_price - current_atr * partial_profit_atr_multiplier
|
||||
|
||||
self.log(f'持仓:{volume}, pos:{current_price - entry_price}, current_price:{current_price}, stop_loss_price:{stop_loss_price}, partial_profit_atr_multiplier:{partial_profit_atr_multiplier}, partial_tp_price:{partial_tp_price}')
|
||||
|
||||
# 检查多头持仓
|
||||
|
||||
if volume > 0:
|
||||
|
||||
# # 最大回撤止盈 (只在部分止盈后激活)
|
||||
# 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:
|
||||
self.log(f"多头部分止盈到达 {current_price:.2f}. 关闭仓位。")
|
||||
# self.send_market_order("CLOSE_LONG", self.trade_volume)
|
||||
self.close_position("CLOSE_LONG", abs(volume))
|
||||
# 首次触发部分止盈时,将当前价格设置为最优价格,作为回撤止盈的起点
|
||||
# 止损
|
||||
elif current_price <= stop_loss_price:
|
||||
self.log(f"多头止损到达 {current_price:.2f}. 关闭全部仓位。")
|
||||
self.close_position("CLOSE_LONG", abs(volume))
|
||||
|
||||
# 检查空头持仓
|
||||
elif volume < 0:
|
||||
|
||||
# # 最大回撤止盈 (只在部分止盈后激活)
|
||||
# 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:
|
||||
self.log(f"空头部分止盈到达 {current_price:.2f}. 关闭一半仓位。")
|
||||
# self.send_market_order("CLOSE_SHORT", self.trade_volume)
|
||||
self.close_position("CLOSE_SHORT", abs(volume))
|
||||
# 止损
|
||||
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} 挂限价买单。fvg_high:{fvg_low},current_atr:{current_atr}")
|
||||
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} 挂限价卖单。fvg_high:{fvg_high},current_atr:{current_atr}")
|
||||
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
1496
futures_trading_strategies/SF/ReversalVolatilityStrategy/main2.ipynb
Normal file
1496
futures_trading_strategies/SF/ReversalVolatilityStrategy/main2.ipynb
Normal file
File diff suppressed because one or more lines are too long
666
futures_trading_strategies/SR/main2.ipynb
Normal file
666
futures_trading_strategies/SR/main2.ipynb
Normal file
File diff suppressed because one or more lines are too long
@@ -0,0 +1,315 @@
|
||||
# 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)
|
||||
avg_entry_price = self.get_average_position_price(self.symbol)
|
||||
|
||||
if position_volume != 0:
|
||||
# 传递当前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, avg_entry_price, 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,
|
||||
entry_price: float, current_atr: float):
|
||||
|
||||
# current_atr = self.position_meta[self.symbol]['entry_atr']
|
||||
|
||||
# 定义基于入场时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 + current_atr * partial_profit_atr_multiplier if volume > 0 else entry_price - current_atr * partial_profit_atr_multiplier
|
||||
|
||||
self.log(f'持仓:{volume}, pos:{current_price - entry_price}, current_price:{current_price}, stop_loss_price:{stop_loss_price}, partial_profit_atr_multiplier:{partial_profit_atr_multiplier}, partial_tp_price:{partial_tp_price}')
|
||||
|
||||
# 检查多头持仓
|
||||
|
||||
if volume > 0:
|
||||
|
||||
# # 最大回撤止盈 (只在部分止盈后激活)
|
||||
# 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:
|
||||
self.log(f"多头部分止盈到达 {current_price:.2f}. 关闭仓位。")
|
||||
# self.send_market_order("CLOSE_LONG", self.trade_volume)
|
||||
self.close_position("CLOSE_LONG", abs(volume))
|
||||
# 首次触发部分止盈时,将当前价格设置为最优价格,作为回撤止盈的起点
|
||||
# 止损
|
||||
elif current_price <= stop_loss_price:
|
||||
self.log(f"多头止损到达 {current_price:.2f}. 关闭全部仓位。")
|
||||
self.close_position("CLOSE_LONG", abs(volume))
|
||||
|
||||
# 检查空头持仓
|
||||
elif volume < 0:
|
||||
|
||||
# # 最大回撤止盈 (只在部分止盈后激活)
|
||||
# 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:
|
||||
self.log(f"空头部分止盈到达 {current_price:.2f}. 关闭一半仓位。")
|
||||
# self.send_market_order("CLOSE_SHORT", self.trade_volume)
|
||||
self.close_position("CLOSE_SHORT", abs(volume))
|
||||
# 止损
|
||||
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} 挂限价买单。fvg_high:{fvg_low},current_atr:{current_atr}")
|
||||
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} 挂限价卖单。fvg_high:{fvg_high},current_atr:{current_atr}")
|
||||
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
1008
futures_trading_strategies/c/ReversalVolatilityStrategy/main2.ipynb
Normal file
1008
futures_trading_strategies/c/ReversalVolatilityStrategy/main2.ipynb
Normal file
File diff suppressed because one or more lines are too long
968
futures_trading_strategies/fu/main2.ipynb
Normal file
968
futures_trading_strategies/fu/main2.ipynb
Normal file
File diff suppressed because one or more lines are too long
716
futures_trading_strategies/jm/main2_lag17.ipynb
Normal file
716
futures_trading_strategies/jm/main2_lag17.ipynb
Normal file
File diff suppressed because one or more lines are too long
710
futures_trading_strategies/m/main2.ipynb
Normal file
710
futures_trading_strategies/m/main2.ipynb
Normal file
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
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
704
futures_trading_strategies/rb/open_two_factor/main2_lag17.ipynb
Normal file
704
futures_trading_strategies/rb/open_two_factor/main2_lag17.ipynb
Normal file
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user