|
@@ -206,7 +206,7 @@ def initialize(context):
|
|
|
|
|
|
|
|
# 策略品种选择策略配置
|
|
# 策略品种选择策略配置
|
|
|
# 方案1:全品种策略 - 考虑所有配置的期货品种
|
|
# 方案1:全品种策略 - 考虑所有配置的期货品种
|
|
|
- g.strategy_focus_symbols = ['AO'] # 空列表表示考虑所有品种
|
|
|
|
|
|
|
+ g.strategy_focus_symbols = ['P'] # 空列表表示考虑所有品种
|
|
|
|
|
|
|
|
# 方案2:精选品种策略 - 只交易流动性较好的特定品种(如需使用请取消下行注释)
|
|
# 方案2:精选品种策略 - 只交易流动性较好的特定品种(如需使用请取消下行注释)
|
|
|
# g.strategy_focus_symbols = ['RM', 'CJ', 'CY', 'JD', 'L', 'LC', 'SF', 'SI']
|
|
# g.strategy_focus_symbols = ['RM', 'CJ', 'CY', 'JD', 'L', 'LC', 'SF', 'SI']
|
|
@@ -369,7 +369,7 @@ def check_ma_trend_and_open_gap(context):
|
|
|
del g.excluded_contracts[dominant_future]
|
|
del g.excluded_contracts[dominant_future]
|
|
|
|
|
|
|
|
# 步骤3.4:检查是否已有持仓
|
|
# 步骤3.4:检查是否已有持仓
|
|
|
- if check_symbol_prefix_match(dominant_future, set(g.trade_history.keys())):
|
|
|
|
|
|
|
+ if check_symbol_prefix_match(dominant_future, context, set(g.trade_history.keys())):
|
|
|
log.info(f"{symbol} 已有持仓,跳过")
|
|
log.info(f"{symbol} 已有持仓,跳过")
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
@@ -556,6 +556,16 @@ def check_open_and_stop(context):
|
|
|
if blocked_trading_day == current_trading_day_normalized:
|
|
if blocked_trading_day == current_trading_day_normalized:
|
|
|
log.info(f"当晚操作已被禁止(订单状态为'new',无夜盘),跳过所有操作")
|
|
log.info(f"当晚操作已被禁止(订单状态为'new',无夜盘),跳过所有操作")
|
|
|
return
|
|
return
|
|
|
|
|
+
|
|
|
|
|
+ # 得到当前未完成订单
|
|
|
|
|
+ orders = get_open_orders()
|
|
|
|
|
+ # 循环,撤销订单
|
|
|
|
|
+ if len(orders) == 0:
|
|
|
|
|
+ log.debug(f"无未完成订单")
|
|
|
|
|
+ else:
|
|
|
|
|
+ for _order in orders.values():
|
|
|
|
|
+ log.debug(f"order: {_order}")
|
|
|
|
|
+ cancel_order(_order)
|
|
|
|
|
|
|
|
# 第一步:检查开仓条件
|
|
# 第一步:检查开仓条件
|
|
|
log.info(f"检查开仓条件:")
|
|
log.info(f"检查开仓条件:")
|
|
@@ -575,7 +585,7 @@ def check_open_and_stop(context):
|
|
|
yesterday_open = candidate_info.get('yesterday_open')
|
|
yesterday_open = candidate_info.get('yesterday_open')
|
|
|
|
|
|
|
|
# 检查是否已有持仓
|
|
# 检查是否已有持仓
|
|
|
- if check_symbol_prefix_match(dominant_future, set(g.trade_history.keys())):
|
|
|
|
|
|
|
+ if check_symbol_prefix_match(dominant_future, context, set(g.trade_history.keys())):
|
|
|
log.info(f"{symbol} 已有持仓,从候选列表移除")
|
|
log.info(f"{symbol} 已有持仓,从候选列表移除")
|
|
|
candidates_to_remove.append(dominant_future)
|
|
candidates_to_remove.append(dominant_future)
|
|
|
continue
|
|
continue
|
|
@@ -1251,6 +1261,9 @@ def close_position(context, security, direction):
|
|
|
# 从交易历史中移除
|
|
# 从交易历史中移除
|
|
|
if security in g.trade_history:
|
|
if security in g.trade_history:
|
|
|
del g.trade_history[security]
|
|
del g.trade_history[security]
|
|
|
|
|
+ log.debug(f"从交易历史中移除: {security}")
|
|
|
|
|
+ else:
|
|
|
|
|
+ log.info(f"平仓失败: {security} {direction} {order.filled}手")
|
|
|
return True
|
|
return True
|
|
|
|
|
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
@@ -1356,9 +1369,14 @@ def calculate_target_hands(context, security, direction):
|
|
|
|
|
|
|
|
return actual_hands, single_hand_margin
|
|
return actual_hands, single_hand_margin
|
|
|
|
|
|
|
|
-def check_symbol_prefix_match(symbol, hold_symbols):
|
|
|
|
|
|
|
+def check_symbol_prefix_match(symbol, context, hold_symbols):
|
|
|
"""检查是否有相似的持仓品种"""
|
|
"""检查是否有相似的持仓品种"""
|
|
|
|
|
+ log.debug(f"检查持仓")
|
|
|
symbol_prefix = symbol[:-9]
|
|
symbol_prefix = symbol[:-9]
|
|
|
|
|
+
|
|
|
|
|
+ long_positions = context.subportfolios[0].long_positions
|
|
|
|
|
+ short_positions = context.subportfolios[0].short_positions
|
|
|
|
|
+ log.debug(f"long_positions: {long_positions}, short_positions: {short_positions}")
|
|
|
|
|
|
|
|
for hold_symbol in hold_symbols:
|
|
for hold_symbol in hold_symbols:
|
|
|
hold_symbol_prefix = hold_symbol[:-9] if len(hold_symbol) > 9 else hold_symbol
|
|
hold_symbol_prefix = hold_symbol[:-9] if len(hold_symbol) > 9 else hold_symbol
|
|
@@ -1381,10 +1399,41 @@ def calculate_realtime_ma_values(security, ma_periods):
|
|
|
ma_values = {f'ma{period}': sum(close_prices[-period:]) / period for period in ma_periods}
|
|
ma_values = {f'ma{period}': sum(close_prices[-period:]) / period for period in ma_periods}
|
|
|
return ma_values
|
|
return ma_values
|
|
|
|
|
|
|
|
|
|
+def sync_trade_history_with_positions(context):
|
|
|
|
|
+ """同步g.trade_history与实际持仓,清理已平仓但记录未删除的持仓"""
|
|
|
|
|
+ if not g.trade_history:
|
|
|
|
|
+ return
|
|
|
|
|
+
|
|
|
|
|
+ subportfolio = context.subportfolios[0]
|
|
|
|
|
+ actual_positions = set(subportfolio.long_positions.keys()) | set(subportfolio.short_positions.keys())
|
|
|
|
|
+
|
|
|
|
|
+ # 找出g.trade_history中有记录但实际已平仓的合约
|
|
|
|
|
+ stale_records = []
|
|
|
|
|
+ for security in g.trade_history.keys():
|
|
|
|
|
+ if security not in actual_positions:
|
|
|
|
|
+ stale_records.append(security)
|
|
|
|
|
+
|
|
|
|
|
+ # 清理这些过期记录
|
|
|
|
|
+ if stale_records:
|
|
|
|
|
+ log.info("=" * 60)
|
|
|
|
|
+ log.info("发现持仓记录与实际持仓不同步,进行清理:")
|
|
|
|
|
+ for security in stale_records:
|
|
|
|
|
+ trade_info = g.trade_history[security]
|
|
|
|
|
+ underlying_symbol = security.split('.')[0][:-4]
|
|
|
|
|
+ log.info(f" 清理过期记录: {underlying_symbol}({security}) {trade_info['direction']}, "
|
|
|
|
|
+ f"成本价: {trade_info['entry_price']:.2f}, "
|
|
|
|
|
+ f"入场时间: {trade_info['entry_time']}")
|
|
|
|
|
+ del g.trade_history[security]
|
|
|
|
|
+ log.info(f"共清理 {len(stale_records)} 条过期持仓记录")
|
|
|
|
|
+ log.info("=" * 60)
|
|
|
|
|
+
|
|
|
def after_market_close(context):
|
|
def after_market_close(context):
|
|
|
"""收盘后运行函数"""
|
|
"""收盘后运行函数"""
|
|
|
log.info(str('函数运行时间(after_market_close):'+str(context.current_dt.time())))
|
|
log.info(str('函数运行时间(after_market_close):'+str(context.current_dt.time())))
|
|
|
|
|
|
|
|
|
|
+ # 同步检查:清理g.trade_history中已平仓但记录未删除的持仓
|
|
|
|
|
+ sync_trade_history_with_positions(context)
|
|
|
|
|
+
|
|
|
# 清空候选列表(每天重新检查)
|
|
# 清空候选列表(每天重新检查)
|
|
|
g.daily_ma_candidates = {}
|
|
g.daily_ma_candidates = {}
|
|
|
|
|
|