|
|
@@ -123,21 +123,29 @@ def initialize(context):
|
|
|
'PK': {'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5, 'trading_start_time': '09:00'},
|
|
|
'JD': {'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 10, 'trading_start_time': '09:00'},
|
|
|
'LH': {'margin_rate': {'long': 0.1, 'short': 0.1}, 'multiplier': 16, 'trading_start_time': '09:00'},
|
|
|
- 'UR': {'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 20, 'trading_start_time': '21:00'}
|
|
|
+ 'UR': {'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 20, 'trading_start_time': '09:00'}
|
|
|
}
|
|
|
|
|
|
# 策略品种选择配置
|
|
|
- g.strategy_focus_symbols = ['TL'] # 交易品种列表
|
|
|
+ g.strategy_focus_symbols = ['LH'] # 交易品种列表
|
|
|
|
|
|
# ==================== 底仓左侧多头配置 ====================
|
|
|
# 价格-数量网格:价格越低,买入手数越多
|
|
|
g.base_position_grid = {
|
|
|
'TL': {118: 1, 117: 1, 116: 1, 115: 1, 114: 2, 113: 2},
|
|
|
+ 'SA': {1400: 4, 1300: 6, 1200: 8, 1100: 12, 1000: 14, 900: 16},
|
|
|
+ 'M': {2800: 4, 2750: 6, 2700: 8, 2650: 12, 2600: 14, 2550: 16},
|
|
|
+ 'UR': {1750: 4, 1700: 6, 1650: 8, 1600: 12, 1550: 14, 1500: 16},
|
|
|
+ 'LH': {13000: 1, 12500: 1, 12000: 1, 11500: 1, 11000: 2},
|
|
|
}
|
|
|
|
|
|
# 底仓退出价格(止盈)
|
|
|
g.base_position_exit_price = {
|
|
|
'TL': 121,
|
|
|
+ 'SA': 1550,
|
|
|
+ 'M': 3800,
|
|
|
+ 'UR': 2400,
|
|
|
+ 'LH': 14600,
|
|
|
}
|
|
|
|
|
|
# ==================== 网格多头配置 ====================
|
|
|
@@ -148,11 +156,34 @@ def initialize(context):
|
|
|
'quantity_per_grid': 1, # 每网格数量
|
|
|
'exit_grid_size': 1 # 退出网格大小
|
|
|
},
|
|
|
+ 'SA': {
|
|
|
+ 'start_price': 1250, # 开始价格
|
|
|
+ 'grid_size': 50, # 网格大小
|
|
|
+ 'quantity_per_grid': 5, # 每网格数量
|
|
|
+ 'exit_grid_size': 50 # 退出网格大小
|
|
|
+ },
|
|
|
+ 'M': {
|
|
|
+ 'start_price': 2800,
|
|
|
+ 'grid_size': 100,
|
|
|
+ 'quantity_per_grid': 10,
|
|
|
+ 'exit_grid_size': 100
|
|
|
+ },
|
|
|
+ 'UR': {
|
|
|
+ 'start_price': 1800,
|
|
|
+ 'grid_size': 50,
|
|
|
+ 'quantity_per_grid': 10,
|
|
|
+ 'exit_grid_size': 50
|
|
|
+ },
|
|
|
+ 'LH': {
|
|
|
+ 'start_price': 13500,
|
|
|
+ 'grid_size': 500,
|
|
|
+ 'quantity_per_grid': 1,
|
|
|
+ 'exit_grid_size': 500
|
|
|
+ },
|
|
|
}
|
|
|
|
|
|
# ==================== 空头对冲配置 ====================
|
|
|
- g.hedge_profit_pullback_pct = 0.003 # 止盈回撤百分比(0.3%)
|
|
|
- g.hedge_stoploss_pct = 0.01 # 止损百分比(1%)
|
|
|
+ g.hedge_stoploss_pct = 0.02 # 止损百分比(1%)
|
|
|
g.hedge_level1_recovery_pct = 0.30 # 一级止盈回升百分比(30%)
|
|
|
g.hedge_level2_recovery_pct = 0.60 # 二级止盈回升百分比(60%)
|
|
|
g.hedge_cost_area_pct = 0.02 # 成本区域百分比(2%)
|
|
|
@@ -163,6 +194,9 @@ def initialize(context):
|
|
|
g.grid_positions = {} # 网格持仓 {symbol: [{'entry_price': price, 'quantity': qty, 'target_price': target_price, 'entry_date': date}]}
|
|
|
g.hedge_positions = {} # 对冲持仓 {symbol: {level_key: {'entry_price': price, 'quantity': qty, 'entry_date': date}}}
|
|
|
|
|
|
+ # 底仓待成交限价单跟踪(问题2修复)
|
|
|
+ g.base_pending_orders = {} # {symbol: {level_key: {'order_id': xxx, 'price': xxx, 'quantity': xxx, 'submit_time': xxx}}}
|
|
|
+
|
|
|
# 网格状态跟踪
|
|
|
g.grid_buy_levels = {} # 网格已建仓目标价集合 {symbol: set()}
|
|
|
|
|
|
@@ -180,6 +214,7 @@ def initialize(context):
|
|
|
run_daily(check_stop_profit_loss, time='11:05:00', reference_security='IF1808.CCFX')
|
|
|
run_daily(check_stop_profit_loss, time='13:35:00', reference_security='IF1808.CCFX')
|
|
|
run_daily(check_stop_profit_loss, time='14:05:00', reference_security='IF1808.CCFX')
|
|
|
+ run_daily(check_stop_profit_loss, time='14:35:00', reference_security='IF1808.CCFX')
|
|
|
run_daily(check_stop_profit_loss, time='14:55:00', reference_security='IF1808.CCFX')
|
|
|
|
|
|
# 建仓逻辑(开盘时执行:夜盘21:05,日盘09:05)
|
|
|
@@ -329,10 +364,12 @@ def check_stop_profit_loss(context):
|
|
|
# 策略组件2: 网格多头止盈(可配置)
|
|
|
if g.enable_grid_long:
|
|
|
check_grid_trading_close(context, symbol, dominant_future, current_price)
|
|
|
+ check_grid_trading_open(context, symbol, dominant_future, current_price)
|
|
|
|
|
|
# 策略组件3: 空头对冲止损止盈(可配置)
|
|
|
if g.enable_hedge_short:
|
|
|
check_hedge_position_close(context, symbol, dominant_future, current_price)
|
|
|
+ check_hedge_position_open(context, symbol, dominant_future, current_price)
|
|
|
|
|
|
except Exception as e:
|
|
|
log.warning(f"止损止盈检查处理{symbol}时出错: {str(e)}")
|
|
|
@@ -340,6 +377,78 @@ def check_stop_profit_loss(context):
|
|
|
|
|
|
############################ 策略组件1: 底仓左侧多头 ###################################
|
|
|
|
|
|
+def update_base_pending_orders(context, symbol, dominant_future):
|
|
|
+ """更新底仓待成交限价单状态
|
|
|
+
|
|
|
+ 检查待成交限价单的状态:
|
|
|
+ - 如果订单已完全成交,移到 g.base_positions
|
|
|
+ - 如果订单被取消或失败,从 g.base_pending_orders 中删除
|
|
|
+ - 如果订单部分成交或仍在挂单,保持在 g.base_pending_orders 中
|
|
|
+
|
|
|
+ Args:
|
|
|
+ context: 上下文对象
|
|
|
+ symbol: 品种代码
|
|
|
+ dominant_future: 主力合约
|
|
|
+ """
|
|
|
+ if dominant_future not in g.base_pending_orders:
|
|
|
+ return
|
|
|
+
|
|
|
+ if not g.base_pending_orders[dominant_future]:
|
|
|
+ return
|
|
|
+
|
|
|
+ # 获取所有订单
|
|
|
+ try:
|
|
|
+ all_orders = get_orders()
|
|
|
+ except Exception as e:
|
|
|
+ log.warning(f"更新待成交限价单失败: 无法获取订单信息 - {str(e)}")
|
|
|
+ return
|
|
|
+
|
|
|
+ levels_to_remove = []
|
|
|
+ levels_to_add_to_positions = []
|
|
|
+
|
|
|
+ for level_key, pending_info in g.base_pending_orders[dominant_future].items():
|
|
|
+ order_id = pending_info.get('order_id')
|
|
|
+ if not order_id or order_id not in all_orders:
|
|
|
+ # 订单不存在,可能已被系统清理,从待成交列表中移除
|
|
|
+ log.info(f"底仓限价单 {level_key} 订单 {order_id} 不存在于订单列表,移除待成交记录")
|
|
|
+ levels_to_remove.append(level_key)
|
|
|
+ continue
|
|
|
+
|
|
|
+ order_obj = all_orders[order_id]
|
|
|
+ status = str(order_obj.status).lower()
|
|
|
+ filled_amount = getattr(order_obj, 'filled', 0) or 0
|
|
|
+ order_amount = getattr(order_obj, 'amount', 0) or 0
|
|
|
+
|
|
|
+ # 检查订单状态
|
|
|
+ if status in ('filled', 'held'):
|
|
|
+ # 订单已完全成交
|
|
|
+ actual_entry_price = pending_info['price'] # 限价单成交价等于限价
|
|
|
+ levels_to_add_to_positions.append((level_key, {
|
|
|
+ 'entry_price': actual_entry_price,
|
|
|
+ 'quantity': pending_info['quantity'],
|
|
|
+ 'entry_date': pending_info['submit_time']
|
|
|
+ }))
|
|
|
+ levels_to_remove.append(level_key)
|
|
|
+ log.info(f"底仓限价单 {level_key} 已成交,订单 {order_id},移至持仓记录")
|
|
|
+ elif status in ('canceled', 'rejected'):
|
|
|
+ # 订单已取消或被拒绝
|
|
|
+ levels_to_remove.append(level_key)
|
|
|
+ log.info(f"底仓限价单 {level_key} 已{status},订单 {order_id},从待成交列表移除")
|
|
|
+ elif status in ('open', 'pending', 'new', 'part_filled'):
|
|
|
+ # 订单仍在等待成交或部分成交,保持在待成交列表
|
|
|
+ log.info(f"底仓限价单 {level_key} 状态 {status},已成交 {filled_amount}/{order_amount} 手,继续等待")
|
|
|
+ else:
|
|
|
+ # 未知状态,记录日志
|
|
|
+ log.warning(f"底仓限价单 {level_key} 订单 {order_id} 状态未知: {status}")
|
|
|
+
|
|
|
+ # 将已成交的订单移到持仓记录
|
|
|
+ for level_key, position_info in levels_to_add_to_positions:
|
|
|
+ g.base_positions[dominant_future][level_key] = position_info
|
|
|
+
|
|
|
+ # 从待成交列表中移除
|
|
|
+ for level_key in levels_to_remove:
|
|
|
+ del g.base_pending_orders[dominant_future][level_key]
|
|
|
+
|
|
|
def check_base_position_open(context, symbol, dominant_future, current_price):
|
|
|
"""检查底仓左侧多头建仓
|
|
|
|
|
|
@@ -365,8 +474,17 @@ def check_base_position_open(context, symbol, dominant_future, current_price):
|
|
|
if dominant_future not in g.base_positions:
|
|
|
g.base_positions[dominant_future] = {}
|
|
|
|
|
|
+ # 初始化待成交限价单记录(问题2修复)
|
|
|
+ if dominant_future not in g.base_pending_orders:
|
|
|
+ g.base_pending_orders[dominant_future] = {}
|
|
|
+
|
|
|
+ # 问题2修复:检查待成交限价单的状态,将已成交的订单移到持仓记录
|
|
|
+ update_base_pending_orders(context, symbol, dominant_future)
|
|
|
+
|
|
|
log.info(f"底仓建仓检查: {symbol} 当前价格 {current_price}")
|
|
|
log.info(f"底仓持仓: {g.base_positions[dominant_future]}")
|
|
|
+ log.info(f"底仓待成交限价单: {g.base_pending_orders[dominant_future]}")
|
|
|
+
|
|
|
# 收集未建仓的档位
|
|
|
market_order_levels = [] # 需要市价单的档位
|
|
|
limit_order_levels = [] # 需要限价单的档位
|
|
|
@@ -378,6 +496,17 @@ def check_base_position_open(context, symbol, dominant_future, current_price):
|
|
|
if level_key in g.base_positions[dominant_future]:
|
|
|
continue
|
|
|
|
|
|
+ # 问题2修复:跳过已有待成交限价单的档位
|
|
|
+ if level_key in g.base_pending_orders[dominant_future]:
|
|
|
+ log.info(f" 档位 {price_level} 已有待成交限价单,跳过")
|
|
|
+ continue
|
|
|
+
|
|
|
+ # 问题1修复:过滤价格过低的档位,避免无效挂单
|
|
|
+ # 当档位价格 < 当前价格的95%时,跳过该档位(相当于当前价格 > 档位价格 * 1.05)
|
|
|
+ if price_level < current_price * 0.95:
|
|
|
+ log.info(f" 档位 {price_level} 过低(< 当前价 {current_price} * 0.95 = {current_price * 0.95:.2f}),跳过挂单")
|
|
|
+ continue
|
|
|
+
|
|
|
# 判断使用市价单还是限价单
|
|
|
if price_level >= current_price:
|
|
|
# 档位价格 >= 当前价格,使用市价单
|
|
|
@@ -391,7 +520,7 @@ def check_base_position_open(context, symbol, dominant_future, current_price):
|
|
|
for price_level, quantity in market_order_levels:
|
|
|
level_key = f"{price_level}"
|
|
|
strategy_info = {'strategy_type': '基础左侧', 'target_price': price_level}
|
|
|
- success = open_position(context, dominant_future, quantity, 'long',
|
|
|
+ _, success = open_position(context, dominant_future, quantity, 'long',
|
|
|
f'底仓市价建仓(目标档位{price_level})',
|
|
|
strategy_info=strategy_info)
|
|
|
if success:
|
|
|
@@ -413,17 +542,19 @@ def check_base_position_open(context, symbol, dominant_future, current_price):
|
|
|
for price_level, quantity in top_two_levels:
|
|
|
level_key = f"{price_level}"
|
|
|
strategy_info = {'strategy_type': '基础左侧', 'target_price': price_level}
|
|
|
- success = open_position(context, dominant_future, quantity, 'long',
|
|
|
+ order_id, success = open_position(context, dominant_future, quantity, 'long',
|
|
|
f'底仓限价建仓@{price_level}',
|
|
|
limit_price=price_level,
|
|
|
strategy_info=strategy_info)
|
|
|
if success:
|
|
|
- g.base_positions[dominant_future][level_key] = {
|
|
|
- 'entry_price': price_level, # 限价单按委托价记录
|
|
|
+ # 问题2修复:限价单提交成功后,记录到待成交列表,而不是直接记录到持仓
|
|
|
+ g.base_pending_orders[dominant_future][level_key] = {
|
|
|
+ 'order_id': order_id,
|
|
|
+ 'price': price_level,
|
|
|
'quantity': quantity,
|
|
|
- 'entry_date': context.current_dt
|
|
|
+ 'submit_time': context.current_dt
|
|
|
}
|
|
|
- log.info(f"底仓限价单挂单: {symbol} 档位 {price_level}, 数量 {quantity}")
|
|
|
+ log.info(f"底仓限价单挂单: {symbol} 档位 {price_level}, 数量 {quantity}, order_id={order_id}, 待成交确认")
|
|
|
|
|
|
def check_base_position_close(context, symbol, dominant_future, current_price):
|
|
|
"""检查底仓左侧多头平仓(止盈)"""
|
|
|
@@ -521,7 +652,7 @@ def check_grid_trading_open(context, symbol, dominant_future, current_price):
|
|
|
# 执行市价单
|
|
|
for _, target_price in market_order_grids:
|
|
|
strategy_info = {'strategy_type': '网格', 'target_price': target_price}
|
|
|
- success = open_position(context, dominant_future, quantity_per_grid, 'long',
|
|
|
+ _, success = open_position(context, dominant_future, quantity_per_grid, 'long',
|
|
|
f'网格市价买入(目标价{target_price})',
|
|
|
strategy_info=strategy_info)
|
|
|
if success:
|
|
|
@@ -543,7 +674,7 @@ def check_grid_trading_open(context, symbol, dominant_future, current_price):
|
|
|
|
|
|
for _, target_price in top_two_grids:
|
|
|
strategy_info = {'strategy_type': '网格', 'target_price': target_price}
|
|
|
- success = open_position(context, dominant_future, quantity_per_grid, 'long',
|
|
|
+ _, success = open_position(context, dominant_future, quantity_per_grid, 'long',
|
|
|
f'网格限价买入@目标价{target_price}',
|
|
|
limit_price=target_price,
|
|
|
strategy_info=strategy_info)
|
|
|
@@ -610,6 +741,7 @@ def check_hedge_position_open(context, symbol, dominant_future, current_price):
|
|
|
1. 对冲数量只对应基础左侧持仓,不包括网格持仓
|
|
|
2. 对冲永远使用限价单,价格为各档位的目标价格
|
|
|
3. 针对每个基础左侧档位分别建立对应的空头对冲单
|
|
|
+ 4. **关键约束**:只对今天开立的左侧持仓建立对冲
|
|
|
"""
|
|
|
|
|
|
# 时间检查:判断是否已达到该品种的交易开始时间
|
|
|
@@ -634,22 +766,112 @@ def check_hedge_position_open(context, symbol, dominant_future, current_price):
|
|
|
log.info(f"对冲建仓检查: {symbol} 当前无基础左侧持仓,无需对冲")
|
|
|
return
|
|
|
|
|
|
- log.info(f"对冲建仓检查: {symbol} 当前价格 {current_price}")
|
|
|
+ # **关键检查**:筛选今天开立的左侧持仓
|
|
|
+ today_date = context.current_dt.date()
|
|
|
+ today_opened_positions = {}
|
|
|
|
|
|
- # 针对每个基础左侧档位建立对应的对冲单
|
|
|
for level_key, position_info in g.base_positions[dominant_future].items():
|
|
|
+ entry_date = position_info.get('entry_date')
|
|
|
+ if entry_date is None:
|
|
|
+ log.warning(f"对冲建仓检查: {symbol} 档位 {level_key} 缺少entry_date,跳过")
|
|
|
+ continue
|
|
|
+
|
|
|
+ # 将entry_date转换为date对象进行比较
|
|
|
+ if hasattr(entry_date, 'date'):
|
|
|
+ entry_date_obj = entry_date.date()
|
|
|
+ elif hasattr(entry_date, 'to_pydatetime'):
|
|
|
+ entry_date_obj = entry_date.to_pydatetime().date()
|
|
|
+ else:
|
|
|
+ entry_date_obj = entry_date
|
|
|
+
|
|
|
+ # 只记录今天开立的持仓
|
|
|
+ if entry_date_obj == today_date:
|
|
|
+ today_opened_positions[level_key] = position_info
|
|
|
+ log.info(f"对冲建仓检查: {symbol} 档位 {level_key} 今天开立,可建立对冲")
|
|
|
+ else:
|
|
|
+ log.info(f"对冲建仓检查: {symbol} 档位 {level_key} 开立日期 {entry_date_obj} ≠ 今天 {today_date},跳过对冲")
|
|
|
+
|
|
|
+ # **关键判断**:如果今天没有开立任何左侧持仓,完全跳过对冲逻辑
|
|
|
+ if not today_opened_positions:
|
|
|
+ # log.info(f"对冲建仓检查: {symbol} 今天未开立任何左侧持仓,跳过所有对冲建仓逻辑")
|
|
|
+ return
|
|
|
+
|
|
|
+ log.info(f"对冲建仓检查: {symbol} 当前价格 {current_price}, 今天开立的左侧持仓档位: {list(today_opened_positions.keys())}")
|
|
|
+
|
|
|
+ # 收集对应基础左侧订单的成交情况,仅在订单成交后才建立对冲
|
|
|
+ level_fill_status = {}
|
|
|
+ order_strategy_info_map = getattr(g, 'order_strategy_info', {}) if hasattr(g, 'order_strategy_info') else {}
|
|
|
+ try:
|
|
|
+ all_orders = get_orders()
|
|
|
+ except Exception as e:
|
|
|
+ log.warning(f"对冲建仓检查: {symbol} 获取订单信息失败,等待下次检查。错误: {str(e)}")
|
|
|
+ all_orders = {}
|
|
|
+
|
|
|
+ if all_orders:
|
|
|
+ for order_id, order_obj in all_orders.items():
|
|
|
+ if order_id not in order_strategy_info_map:
|
|
|
+ continue
|
|
|
+ strategy_info = order_strategy_info_map[order_id]
|
|
|
+ if strategy_info.get('strategy_type') != '基础左侧':
|
|
|
+ continue
|
|
|
+ if getattr(order_obj, 'security', None) != dominant_future:
|
|
|
+ continue
|
|
|
+ target_price_key = f"{strategy_info.get('target_price')}"
|
|
|
+ if target_price_key not in today_opened_positions:
|
|
|
+ continue
|
|
|
+ status_value = getattr(order_obj, 'status', '')
|
|
|
+ status_str = str(status_value)
|
|
|
+ status_lower = status_str.lower()
|
|
|
+ filled_amount = getattr(order_obj, 'filled', 0) or 0
|
|
|
+ order_amount = getattr(order_obj, 'amount', 0) or 0
|
|
|
+ info = level_fill_status.setdefault(target_price_key, {
|
|
|
+ 'filled': 0,
|
|
|
+ 'amount': 0,
|
|
|
+ 'pending': False,
|
|
|
+ 'waiting': False,
|
|
|
+ 'statuses': set()
|
|
|
+ })
|
|
|
+ info['filled'] += filled_amount
|
|
|
+ info['amount'] += order_amount
|
|
|
+ info['statuses'].add(status_str)
|
|
|
+ if status_lower in ('open', 'part_filled', 'pending', 'submitted'):
|
|
|
+ info['pending'] = True
|
|
|
+ if status_lower in ('open', 'submitted') and filled_amount == 0:
|
|
|
+ info['waiting'] = True
|
|
|
+
|
|
|
+ # 只针对今天开立的基础左侧档位建立对应的对冲单
|
|
|
+ for level_key, position_info in today_opened_positions.items():
|
|
|
# 跳过已经建立对冲的档位
|
|
|
if level_key in g.hedge_positions[dominant_future]:
|
|
|
+ log.info(f" 对冲档位 {level_key}: 已存在对冲持仓,跳过")
|
|
|
+ continue
|
|
|
+
|
|
|
+ fill_info = level_fill_status.get(level_key)
|
|
|
+ if not fill_info:
|
|
|
+ log.info(f" 对冲档位 {level_key}: 底仓订单未找到成交记录,等待成交后再建立对冲")
|
|
|
+ continue
|
|
|
+
|
|
|
+ required_quantity = position_info['quantity']
|
|
|
+ filled_quantity = fill_info.get('filled', 0)
|
|
|
+
|
|
|
+ status_list = sorted(fill_info.get('statuses'))
|
|
|
+
|
|
|
+ if fill_info.get('waiting', False):
|
|
|
+ log.info(f" 对冲档位 {level_key}: 底仓限价单仍在挂单等待,状态集合 {status_list}")
|
|
|
+ continue
|
|
|
+
|
|
|
+ if filled_quantity < required_quantity:
|
|
|
+ # log.info(f" 对冲档位 {level_key}: 底仓订单成交量不足 (成交 {filled_quantity} 手 / 目标 {required_quantity} 手,状态集合 {status_list}),跳过对冲")
|
|
|
continue
|
|
|
|
|
|
target_price = level_key # 目标价格本质就是对冲档位
|
|
|
quantity = position_info['quantity']
|
|
|
|
|
|
- log.info(f" 对冲档位 {level_key}: 目标价格 {target_price}, 数量 {quantity}手")
|
|
|
+ log.info(f" 对冲档位 {level_key}: 目标价格 {target_price}, 数量 {quantity}手(对应今天开立的左侧持仓)")
|
|
|
|
|
|
# 建立空头对冲(永远使用限价单,价格为档位目标价)
|
|
|
strategy_info = {'strategy_type': '对冲', 'target_price': target_price}
|
|
|
- success = open_position(context, dominant_future, quantity, 'short',
|
|
|
+ _, success = open_position(context, dominant_future, quantity, 'short',
|
|
|
f'建立空头对冲@{target_price}',
|
|
|
limit_price=target_price,
|
|
|
strategy_info=strategy_info)
|
|
|
@@ -691,9 +913,28 @@ def check_hedge_position_close(context, symbol, dominant_future, current_price):
|
|
|
if not has_hedge_position:
|
|
|
return
|
|
|
|
|
|
+ # 问题1修复:获取当前日期,用于过滤当日新开仓的对冲头寸
|
|
|
+ today_date = context.current_dt.date()
|
|
|
+
|
|
|
# 遍历每个档位的对冲持仓
|
|
|
levels_to_remove = []
|
|
|
for level_key, hedge_info in g.hedge_positions[dominant_future].items():
|
|
|
+ # 问题1修复:检查对冲头寸的开仓日期
|
|
|
+ entry_date = hedge_info.get('entry_date')
|
|
|
+ if entry_date is not None:
|
|
|
+ # 将entry_date转换为date对象进行比较
|
|
|
+ if hasattr(entry_date, 'date'):
|
|
|
+ entry_date_obj = entry_date.date()
|
|
|
+ elif hasattr(entry_date, 'to_pydatetime'):
|
|
|
+ entry_date_obj = entry_date.to_pydatetime().date()
|
|
|
+ else:
|
|
|
+ entry_date_obj = entry_date
|
|
|
+
|
|
|
+ # 如果是当日新开仓的对冲头寸,跳过止损止盈检查
|
|
|
+ if entry_date_obj == today_date:
|
|
|
+ log.info(f"对冲止损止盈检查: {symbol} 档位 {level_key} 于今日开仓,跳过当日止损止盈检查")
|
|
|
+ continue
|
|
|
+
|
|
|
try:
|
|
|
entry_price = float(hedge_info['entry_price'])
|
|
|
except (TypeError, ValueError):
|
|
|
@@ -710,9 +951,9 @@ def check_hedge_position_close(context, symbol, dominant_future, current_price):
|
|
|
g.hedge_state[dominant_future]['max_profit_price'] = current_price
|
|
|
|
|
|
# 更新盈利阶段
|
|
|
- if profit_rate >= g.hedge_level2_recovery_pct * g.hedge_profit_pullback_pct:
|
|
|
+ if profit_rate >= g.hedge_level2_recovery_pct:
|
|
|
g.hedge_state[dominant_future]['profit_stage'] = 2
|
|
|
- elif profit_rate >= g.hedge_level1_recovery_pct * g.hedge_profit_pullback_pct:
|
|
|
+ elif profit_rate >= g.hedge_level1_recovery_pct:
|
|
|
g.hedge_state[dominant_future]['profit_stage'] = 1
|
|
|
|
|
|
# 检查止损条件
|
|
|
@@ -722,17 +963,6 @@ def check_hedge_position_close(context, symbol, dominant_future, current_price):
|
|
|
levels_to_remove.append(level_key)
|
|
|
continue
|
|
|
|
|
|
- # 检查回撤止盈条件
|
|
|
- max_profit = g.hedge_state[dominant_future]['max_profit']
|
|
|
- profit_drawdown = max_profit - profit_rate
|
|
|
-
|
|
|
- if profit_drawdown >= g.hedge_profit_pullback_pct:
|
|
|
- log.info(f"对冲触发回撤止盈: {symbol} 档位 {level_key}, 最大盈利 {max_profit:.2%}, "
|
|
|
- f"当前盈利 {profit_rate:.2%}, 回撤 {profit_drawdown:.2%}")
|
|
|
- close_position(context, dominant_future, quantity, 'short', f'对冲回撤止盈@档位{level_key}')
|
|
|
- levels_to_remove.append(level_key)
|
|
|
- continue
|
|
|
-
|
|
|
# 检查成本区域止盈条件
|
|
|
if abs(profit_rate) <= g.hedge_cost_area_pct:
|
|
|
profit_stage = g.hedge_state[dominant_future]['profit_stage']
|
|
|
@@ -765,6 +995,9 @@ def open_position(context, security, quantity, direction, reason='', limit_price
|
|
|
limit_price: 限价单价格,None表示使用市价单
|
|
|
strategy_info: 策略信息字典 {'strategy_type': '策略类型', 'target_price': 目标价格}
|
|
|
|
|
|
+ Returns:
|
|
|
+ tuple: (order_id, success) - 订单ID和是否成功
|
|
|
+
|
|
|
注:订单提交后filled=0,实际成交信息需要在收盘后通过get_trades()获取
|
|
|
"""
|
|
|
try:
|
|
|
@@ -790,15 +1023,15 @@ def open_position(context, security, quantity, direction, reason='', limit_price
|
|
|
g.order_strategy_info[order_obj.order_id] = strategy_info
|
|
|
log.info(f"订单策略信息已记录: {strategy_info}")
|
|
|
|
|
|
- return True
|
|
|
+ return order_obj.order_id, True
|
|
|
else:
|
|
|
log.warning(f"订单提交失败: {security} {direction} {quantity}手")
|
|
|
- return False
|
|
|
+ return None, False
|
|
|
|
|
|
except Exception as e:
|
|
|
log.warning(f"开仓异常 {security}: {str(e)}")
|
|
|
|
|
|
- return False
|
|
|
+ return None, False
|
|
|
|
|
|
def close_position(context, security, quantity, direction, reason=''):
|
|
|
"""平仓
|
|
|
@@ -910,15 +1143,15 @@ def is_trading_time_reached(context, symbol):
|
|
|
else:
|
|
|
# 当前在凌晨(已经过了配置的开始时间)
|
|
|
result = True
|
|
|
- log.info(f"[时间检查] {symbol} 配置夜盘开始 {trading_start_time}, 当前时间 {current_time.strftime('%H:%M:%S')}, 判断结果: {result}")
|
|
|
+ # log.debug(f"[时间检查] {symbol} 配置夜盘开始 {trading_start_time}, 当前时间 {current_time.strftime('%H:%M:%S')}, 判断结果: {result}")
|
|
|
return result
|
|
|
elif is_current_day:
|
|
|
# 夜盘品种在日盘时段,允许交易
|
|
|
- log.info(f"[时间检查] {symbol} 夜盘品种在日盘时段 {current_time.strftime('%H:%M:%S')}, 允许交易")
|
|
|
+ # log.debug(f"[时间检查] {symbol} 夜盘品种在日盘时段 {current_time.strftime('%H:%M:%S')}, 允许交易")
|
|
|
return True
|
|
|
else:
|
|
|
# 其他时段不允许交易
|
|
|
- log.info(f"[时间检查] {symbol} 夜盘品种在非交易时段 {current_time.strftime('%H:%M:%S')}, 不允许交易")
|
|
|
+ # log.debug(f"[时间检查] {symbol} 夜盘品种在非交易时段 {current_time.strftime('%H:%M:%S')}, 不允许交易")
|
|
|
return False
|
|
|
else:
|
|
|
# 配置为日盘开始时间
|
|
|
@@ -1118,9 +1351,9 @@ def print_daily_trading_summary(context, trades, open_orders):
|
|
|
continue
|
|
|
entry_price = float(entry_info.get('entry_price', 0))
|
|
|
entry_date = entry_info.get('entry_date')
|
|
|
- # log.info(f"标的种类: {display_symbol}, 数量:{quantity}手, 成交价:{entry_price:.2f}, "
|
|
|
- # f"策略:基础左侧, 策略对应价格:{level_key}, 当前标的价格:{current_price:.2f}, "
|
|
|
- # f"成交日期时间: {format_datetime_value(entry_date)}")
|
|
|
+ log.info(f"标的种类: {display_symbol}, 数量:{quantity}手, 成交价:{entry_price:.2f}, "
|
|
|
+ f"策略:基础左侧, 策略对应价格:{level_key}, 当前标的价格:{current_price:.2f}, "
|
|
|
+ f"成交日期时间: {format_datetime_value(entry_date)}")
|
|
|
symbol_logged = True
|
|
|
holdings_logged = True
|
|
|
grid_entries = g.grid_positions.get(security, []) if hasattr(g, 'grid_positions') else []
|
|
|
@@ -1131,14 +1364,14 @@ def print_daily_trading_summary(context, trades, open_orders):
|
|
|
entry_price = float(entry_info.get('entry_price', 0))
|
|
|
target_price = entry_info.get('target_price', 0)
|
|
|
entry_date = entry_info.get('entry_date')
|
|
|
- # log.info(f"标的种类: {display_symbol}, 数量:{quantity}手, 成交价:{entry_price:.2f}, "
|
|
|
- # f"策略:网格, 策略对应价格:{target_price}, 当前标的价格:{current_price:.2f}, "
|
|
|
- # f"成交日期时间: {format_datetime_value(entry_date)}")
|
|
|
+ log.info(f"标的种类: {display_symbol}, 数量:{quantity}手, 成交价:{entry_price:.2f}, "
|
|
|
+ f"策略:网格, 策略对应价格:{target_price}, 当前标的价格:{current_price:.2f}, "
|
|
|
+ f"成交日期时间: {format_datetime_value(entry_date)}")
|
|
|
symbol_logged = True
|
|
|
holdings_logged = True
|
|
|
if not symbol_logged:
|
|
|
- # log.info(f"标的种类: {display_symbol}, 数量:{position.total_amount}手, 成交价:{position.avg_cost:.2f}, "
|
|
|
- # f"策略:未分类, 策略对应价格:--, 当前标的价格:{current_price:.2f}, 成交日期时间: --")
|
|
|
+ log.info(f"标的种类: {display_symbol}, 数量:{position.total_amount}手, 成交价:{position.avg_cost:.2f}, "
|
|
|
+ f"策略:未分类, 策略对应价格:--, 当前标的价格:{current_price:.2f}, 成交日期时间: --")
|
|
|
holdings_logged = True
|
|
|
|
|
|
for security, position in subportfolio.short_positions.items():
|
|
|
@@ -1295,6 +1528,12 @@ def update_positions_after_switch(old_symbol, new_symbol, side):
|
|
|
del g.base_positions[old_symbol]
|
|
|
log.info(f"底仓持仓记录更新: {old_symbol} -> {new_symbol}")
|
|
|
|
|
|
+ # 更新底仓待成交限价单记录(问题2修复)
|
|
|
+ if old_symbol in g.base_pending_orders:
|
|
|
+ g.base_pending_orders[new_symbol] = g.base_pending_orders[old_symbol]
|
|
|
+ del g.base_pending_orders[old_symbol]
|
|
|
+ log.info(f"底仓待成交限价单记录更新: {old_symbol} -> {new_symbol}")
|
|
|
+
|
|
|
# 更新网格持仓记录
|
|
|
if old_symbol in g.grid_positions:
|
|
|
g.grid_positions[new_symbol] = g.grid_positions[old_symbol]
|