|
|
@@ -242,6 +242,7 @@ def initialize(context):
|
|
|
run_daily(check_open_and_stop, time='14:05:00', reference_security='IF1808.CCFX')
|
|
|
run_daily(check_open_and_stop, time='14:35:00', reference_security='IF1808.CCFX')
|
|
|
run_daily(check_open_and_stop, time='14:55:00', reference_security='IF1808.CCFX')
|
|
|
+ run_daily(check_ma_trailing_reactivation, time='14:55:00', reference_security='IF1808.CCFX')
|
|
|
|
|
|
# 收盘后
|
|
|
run_daily(after_market_close, time='15:30:00', reference_security='IF1808.CCFX')
|
|
|
@@ -571,6 +572,11 @@ def check_ma_trend_and_open_gap(context):
|
|
|
def check_open_and_stop(context):
|
|
|
"""统一的开仓和止损止盈检查函数"""
|
|
|
# 先检查换月移仓
|
|
|
+ log.info("=" * 60)
|
|
|
+ current_trading_day = get_current_trading_day(context.current_dt)
|
|
|
+ log.info(f"执行开仓和止损止盈检查 - 时间: {context.current_dt}, 交易日: {current_trading_day}")
|
|
|
+ log.info("=" * 60)
|
|
|
+ log.info(f"先检查换月:")
|
|
|
position_auto_switch(context)
|
|
|
|
|
|
# 获取当前时间
|
|
|
@@ -580,6 +586,7 @@ def check_open_and_stop(context):
|
|
|
is_night_session = (current_time in ['21', '22', '23', '00', '01', '02'])
|
|
|
|
|
|
# 第一步:检查开仓条件
|
|
|
+ log.info(f"检查开仓条件:")
|
|
|
if g.daily_ma_candidates:
|
|
|
log.info("=" * 60)
|
|
|
log.info(f"执行开仓检查 - 时间: {context.current_dt}, 候选品种数量: {len(g.daily_ma_candidates)}")
|
|
|
@@ -693,6 +700,7 @@ def check_open_and_stop(context):
|
|
|
log.info("=" * 60)
|
|
|
|
|
|
# 第二步:检查止损止盈
|
|
|
+ log.info(f"检查止损止盈条件:")
|
|
|
subportfolio = context.subportfolios[0]
|
|
|
long_positions = list(subportfolio.long_positions.values())
|
|
|
short_positions = list(subportfolio.short_positions.values())
|
|
|
@@ -722,8 +730,50 @@ def check_open_and_stop(context):
|
|
|
if skipped_count > 0:
|
|
|
log.info(f"夜盘时间跳过 {skipped_count} 个日间品种的止损止盈检查")
|
|
|
|
|
|
+
|
|
|
+def check_ma_trailing_reactivation(context):
|
|
|
+ """检查是否需要恢复均线跟踪止盈"""
|
|
|
+ subportfolio = context.subportfolios[0]
|
|
|
+ positions = list(subportfolio.long_positions.values()) + list(subportfolio.short_positions.values())
|
|
|
+
|
|
|
+ if not positions:
|
|
|
+ return
|
|
|
+
|
|
|
+ reenabled_count = 0
|
|
|
+ current_data = get_current_data()
|
|
|
+
|
|
|
+ for position in positions:
|
|
|
+ security = position.security
|
|
|
+ trade_info = g.trade_history.get(security)
|
|
|
+
|
|
|
+ if not trade_info or trade_info.get('ma_trailing_enabled', True):
|
|
|
+ continue
|
|
|
+
|
|
|
+ direction = trade_info['direction']
|
|
|
+ ma_values = calculate_realtime_ma_values(security, [5])
|
|
|
+ ma5_value = ma_values.get('ma5')
|
|
|
+
|
|
|
+ if ma5_value is None or security not in current_data:
|
|
|
+ continue
|
|
|
+
|
|
|
+ today_price = current_data[security].last_price
|
|
|
+
|
|
|
+ if direction == 'long' and today_price > ma5_value:
|
|
|
+ trade_info['ma_trailing_enabled'] = True
|
|
|
+ reenabled_count += 1
|
|
|
+ log.info(f"恢复均线跟踪止盈: {security} {direction}, 当前价 {today_price:.2f} > MA5 {ma5_value:.2f}")
|
|
|
+ elif direction == 'short' and today_price < ma5_value:
|
|
|
+ trade_info['ma_trailing_enabled'] = True
|
|
|
+ reenabled_count += 1
|
|
|
+ log.info(f"恢复均线跟踪止盈: {security} {direction}, 当前价 {today_price:.2f} < MA5 {ma5_value:.2f}")
|
|
|
+
|
|
|
+ if reenabled_count > 0:
|
|
|
+ log.info(f"恢复均线跟踪止盈持仓数量: {reenabled_count}")
|
|
|
+
|
|
|
+
|
|
|
def check_position_stop_loss_profit(context, position):
|
|
|
"""检查单个持仓的止损止盈"""
|
|
|
+ log.info(f"检查持仓: {position.security}")
|
|
|
security = position.security
|
|
|
|
|
|
if security not in g.trade_history:
|
|
|
@@ -749,6 +799,9 @@ def check_position_stop_loss_profit(context, position):
|
|
|
profit_rate = (entry_price - current_price) / entry_price
|
|
|
|
|
|
# 检查固定止损
|
|
|
+ log.info("=" * 60)
|
|
|
+ log.info(f"检查固定止损:")
|
|
|
+ log.info("=" * 60)
|
|
|
if profit_rate <= -g.fixed_stop_loss_rate:
|
|
|
log.info(f"触发固定止损 {security} {direction}, 当前亏损率: {profit_rate:.3%}, "
|
|
|
f"成本价: {entry_price:.2f}, 当前价格: {current_price:.2f}")
|
|
|
@@ -763,6 +816,9 @@ def check_position_stop_loss_profit(context, position):
|
|
|
return False
|
|
|
|
|
|
# 检查是否启用均线跟踪止盈
|
|
|
+ log.info("=" * 60)
|
|
|
+ log.info(f"检查是否启用均线跟踪止盈:")
|
|
|
+ log.info("=" * 60)
|
|
|
if not trade_info.get('ma_trailing_enabled', True):
|
|
|
return False
|
|
|
|
|
|
@@ -1064,12 +1120,15 @@ def open_position(context, security, target_hands, direction, reason=''):
|
|
|
}
|
|
|
|
|
|
ma_trailing_enabled = True
|
|
|
- if direction == 'long':
|
|
|
- ma_values_at_entry = calculate_realtime_ma_values(security, [5])
|
|
|
- ma5_value = ma_values_at_entry.get('ma5')
|
|
|
- if ma5_value is not None and order_price < ma5_value:
|
|
|
+ ma_values_at_entry = calculate_realtime_ma_values(security, [5])
|
|
|
+ ma5_value = ma_values_at_entry.get('ma5')
|
|
|
+ if ma5_value is not None:
|
|
|
+ if direction == 'long' and order_price < ma5_value:
|
|
|
ma_trailing_enabled = False
|
|
|
log.info(f"禁用均线跟踪止盈: {security} {direction}, 开仓价 {order_price:.2f} < MA5 {ma5_value:.2f}")
|
|
|
+ elif direction == 'short' and order_price > ma5_value:
|
|
|
+ ma_trailing_enabled = False
|
|
|
+ log.info(f"禁用均线跟踪止盈: {security} {direction}, 开仓价 {order_price:.2f} > MA5 {ma5_value:.2f}")
|
|
|
|
|
|
g.trade_history[security]['ma_trailing_enabled'] = ma_trailing_enabled
|
|
|
|