|
@@ -3,6 +3,7 @@ from jqdata import *
|
|
|
from jqdata import finance
|
|
from jqdata import finance
|
|
|
import pandas as pd
|
|
import pandas as pd
|
|
|
import numpy as np
|
|
import numpy as np
|
|
|
|
|
+import math
|
|
|
from datetime import date, datetime, timedelta, time
|
|
from datetime import date, datetime, timedelta, time
|
|
|
import re
|
|
import re
|
|
|
|
|
|
|
@@ -56,6 +57,9 @@ def initialize(context):
|
|
|
g.check_intraday_spread = False # 是否检查日内价差(True: 检查, False: 跳过)
|
|
g.check_intraday_spread = False # 是否检查日内价差(True: 检查, False: 跳过)
|
|
|
g.ma_proximity_min_threshold = 8 # MA5与MA10贴近计数和的最低阈值
|
|
g.ma_proximity_min_threshold = 8 # MA5与MA10贴近计数和的最低阈值
|
|
|
g.ma_pattern_extreme_days_threshold = 4 # 极端趋势天数阈值
|
|
g.ma_pattern_extreme_days_threshold = 4 # 极端趋势天数阈值
|
|
|
|
|
+ g.ma_distribution_lookback_days = 5 # MA5分布过滤回溯天数
|
|
|
|
|
+ g.ma_distribution_min_ratio = 0.4 # MA5分布满足比例阈值
|
|
|
|
|
+ g.enable_ma_distribution_filter = True # 是否启用MA5分布过滤
|
|
|
|
|
|
|
|
# 均线价差策略方案选择
|
|
# 均线价差策略方案选择
|
|
|
g.ma_gap_strategy_mode = 3 # 策略模式选择(1: 原方案, 2: 新方案, 3: 方案3)
|
|
g.ma_gap_strategy_mode = 3 # 策略模式选择(1: 原方案, 2: 新方案, 3: 方案3)
|
|
@@ -79,6 +83,9 @@ def initialize(context):
|
|
|
log.info(f" 历史均线模式一致性阈值: {g.ma_pattern_consistency_threshold:.1%}")
|
|
log.info(f" 历史均线模式一致性阈值: {g.ma_pattern_consistency_threshold:.1%}")
|
|
|
log.info(f" 极端趋势天数阈值: {g.ma_pattern_extreme_days_threshold}")
|
|
log.info(f" 极端趋势天数阈值: {g.ma_pattern_extreme_days_threshold}")
|
|
|
log.info(f" 均线贴近计数阈值: {g.ma_proximity_min_threshold}")
|
|
log.info(f" 均线贴近计数阈值: {g.ma_proximity_min_threshold}")
|
|
|
|
|
+ log.info(f" MA5分布过滤天数: {g.ma_distribution_lookback_days}")
|
|
|
|
|
+ log.info(f" MA5分布最低比例: {g.ma_distribution_min_ratio:.0%}")
|
|
|
|
|
+ log.info(f" 启用MA5分布过滤: {g.enable_ma_distribution_filter}")
|
|
|
log.info(f" 是否检查日内价差: {g.check_intraday_spread}")
|
|
log.info(f" 是否检查日内价差: {g.check_intraday_spread}")
|
|
|
log.info(f" 固定止损: {g.fixed_stop_loss_rate:.1%}")
|
|
log.info(f" 固定止损: {g.fixed_stop_loss_rate:.1%}")
|
|
|
log.info(f" 均线跟踪止盈常规偏移: {g.ma_offset_ratio_normal:.1%}")
|
|
log.info(f" 均线跟踪止盈常规偏移: {g.ma_offset_ratio_normal:.1%}")
|
|
@@ -462,6 +469,30 @@ def check_ma_trend_and_open_gap(context):
|
|
|
}
|
|
}
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
|
|
+ if g.enable_ma_distribution_filter:
|
|
|
|
|
+ distribution_passed, distribution_stats = check_ma5_distribution_filter(
|
|
|
|
|
+ data_upto_yesterday,
|
|
|
|
|
+ g.ma_distribution_lookback_days,
|
|
|
|
|
+ direction,
|
|
|
|
|
+ g.ma_distribution_min_ratio
|
|
|
|
|
+ )
|
|
|
|
|
+ log.info(
|
|
|
|
|
+ f" MA5分布过滤: 方向 {direction}, 有效天数 "
|
|
|
|
|
+ f"{distribution_stats['valid_days']}/{distribution_stats['lookback_days']},"
|
|
|
|
|
+ f"满足天数 {distribution_stats['qualified_days']}/{distribution_stats['required_days']}"
|
|
|
|
|
+ )
|
|
|
|
|
+ if not distribution_passed:
|
|
|
|
|
+ insufficiency = distribution_stats['valid_days'] < distribution_stats['lookback_days']
|
|
|
|
|
+ reason = "有效数据不足" if insufficiency else "满足天数不足"
|
|
|
|
|
+ log.info(
|
|
|
|
|
+ f" {symbol}({dominant_future}) ✗ MA5分布过滤未通过({reason})"
|
|
|
|
|
+ )
|
|
|
|
|
+ g.excluded_contracts[dominant_future] = {
|
|
|
|
|
+ 'reason': 'ma5_distribution',
|
|
|
|
|
+ 'trading_day': current_trading_day
|
|
|
|
|
+ }
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
# 检查历史均线模式一致性
|
|
# 检查历史均线模式一致性
|
|
|
consistency_passed, consistency_ratio = check_historical_ma_pattern_consistency(
|
|
consistency_passed, consistency_ratio = check_historical_ma_pattern_consistency(
|
|
|
historical_data, direction, g.ma_pattern_lookback_days, g.ma_pattern_consistency_threshold
|
|
historical_data, direction, g.ma_pattern_lookback_days, g.ma_pattern_consistency_threshold
|
|
@@ -723,6 +754,9 @@ def check_position_stop_loss_profit(context, position):
|
|
|
f"成本价: {entry_price:.2f}, 当前价格: {current_price:.2f}")
|
|
f"成本价: {entry_price:.2f}, 当前价格: {current_price:.2f}")
|
|
|
close_position(context, security, direction)
|
|
close_position(context, security, direction)
|
|
|
return True
|
|
return True
|
|
|
|
|
+ else:
|
|
|
|
|
+ log.debug(f"未触发固定止损 {security} {direction}, 当前亏损率: {profit_rate:.3%}, "
|
|
|
|
|
+ f"成本价: {entry_price:.2f}, 当前价格: {current_price:.2f}")
|
|
|
|
|
|
|
|
if entry_trading_day is not None and entry_trading_day == current_trading_day:
|
|
if entry_trading_day is not None and entry_trading_day == current_trading_day:
|
|
|
log.info(f"{security} 建仓交易日内跳过动态止盈检查")
|
|
log.info(f"{security} 建仓交易日内跳过动态止盈检查")
|
|
@@ -751,9 +785,10 @@ def check_position_stop_loss_profit(context, position):
|
|
|
target_time = datetime.strptime('14:55:00', '%H:%M:%S').time()
|
|
target_time = datetime.strptime('14:55:00', '%H:%M:%S').time()
|
|
|
if current_time > target_time:
|
|
if current_time > target_time:
|
|
|
offset_ratio = g.ma_offset_ratio_close
|
|
offset_ratio = g.ma_offset_ratio_close
|
|
|
|
|
+ log.debug(f"当前时间是:{current_time},使用偏移量: {offset_ratio:.3%}")
|
|
|
else:
|
|
else:
|
|
|
offset_ratio = g.ma_offset_ratio_normal
|
|
offset_ratio = g.ma_offset_ratio_normal
|
|
|
-
|
|
|
|
|
|
|
+ log.debug(f"当前时间是:{current_time},使用偏移量: {offset_ratio:.3%}")
|
|
|
# 选择止损均线
|
|
# 选择止损均线
|
|
|
close_line = None
|
|
close_line = None
|
|
|
if today_change_rate >= 1.5 * avg_daily_change_rate:
|
|
if today_change_rate >= 1.5 * avg_daily_change_rate:
|
|
@@ -781,6 +816,10 @@ def check_position_stop_loss_profit(context, position):
|
|
|
f"当前价: {today_price:.2f}, 持仓天数: {holding_days}")
|
|
f"当前价: {today_price:.2f}, 持仓天数: {holding_days}")
|
|
|
close_position(context, security, direction)
|
|
close_position(context, security, direction)
|
|
|
return True
|
|
return True
|
|
|
|
|
+ else:
|
|
|
|
|
+ log.debug(f"未触发均线跟踪止盈 {security} {direction}, 止损均线: {close_line}, "
|
|
|
|
|
+ f"均线值: {ma_value:.2f}, 调整后: {adjusted_ma_value:.2f}, "
|
|
|
|
|
+ f"当前价: {today_price:.2f}, 持仓天数: {holding_days}")
|
|
|
|
|
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
@@ -877,6 +916,39 @@ def calculate_extreme_trend_days(data, periods, lookback_days):
|
|
|
return above_count, below_count
|
|
return above_count, below_count
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
+def check_ma5_distribution_filter(data, lookback_days, direction, min_ratio):
|
|
|
|
|
+ """检查近 lookback_days 天收盘价相对于MA5的分布情况"""
|
|
|
|
|
+ stats = {
|
|
|
|
|
+ 'lookback_days': lookback_days,
|
|
|
|
|
+ 'valid_days': 0,
|
|
|
|
|
+ 'qualified_days': 0,
|
|
|
|
|
+ 'required_days': max(0, math.ceil(lookback_days * min_ratio))
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ if lookback_days <= 0:
|
|
|
|
|
+ return True, stats
|
|
|
|
|
+
|
|
|
|
|
+ if len(data) < max(lookback_days, 5):
|
|
|
|
|
+ return False, stats
|
|
|
|
|
+
|
|
|
|
|
+ recent_closes = data['close'].iloc[-lookback_days:]
|
|
|
|
|
+ ma5_series = data['close'].rolling(window=5).mean().iloc[-lookback_days:]
|
|
|
|
|
+
|
|
|
|
|
+ for close_price, ma5_value in zip(recent_closes, ma5_series):
|
|
|
|
|
+ if pd.isna(ma5_value):
|
|
|
|
|
+ continue
|
|
|
|
|
+ stats['valid_days'] += 1
|
|
|
|
|
+ if direction == 'long' and close_price < ma5_value:
|
|
|
|
|
+ stats['qualified_days'] += 1
|
|
|
|
|
+ elif direction == 'short' and close_price > ma5_value:
|
|
|
|
|
+ stats['qualified_days'] += 1
|
|
|
|
|
+
|
|
|
|
|
+ if stats['valid_days'] < lookback_days:
|
|
|
|
|
+ return False, stats
|
|
|
|
|
+
|
|
|
|
|
+ return stats['qualified_days'] >= stats['required_days'], stats
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
def check_ma_pattern(ma_values, direction):
|
|
def check_ma_pattern(ma_values, direction):
|
|
|
"""检查均线排列模式是否符合方向要求
|
|
"""检查均线排列模式是否符合方向要求
|
|
|
|
|
|
|
@@ -1216,14 +1288,14 @@ def position_auto_switch(context, pindex=0, switch_func=None, callback=None):
|
|
|
underlying_symbol = match.groupdict()["underlying_symbol"]
|
|
underlying_symbol = match.groupdict()["underlying_symbol"]
|
|
|
trading_start = get_futures_config(underlying_symbol, 'trading_start_time', None)
|
|
trading_start = get_futures_config(underlying_symbol, 'trading_start_time', None)
|
|
|
has_night_session = get_futures_config(underlying_symbol, 'has_night_session', False)
|
|
has_night_session = get_futures_config(underlying_symbol, 'has_night_session', False)
|
|
|
- log.debug(f"移仓换月: {symbol}, 交易开始时间: {trading_start}, 夜盘: {has_night_session}")
|
|
|
|
|
|
|
+ # log.debug(f"移仓换月: {symbol}, 交易开始时间: {trading_start}, 夜盘: {has_night_session}")
|
|
|
if trading_start and not has_reached_trading_start(context.current_dt, trading_start, has_night_session):
|
|
if trading_start and not has_reached_trading_start(context.current_dt, trading_start, has_night_session):
|
|
|
- log.info("{} 当前时间 {} 未到达交易开始时间 {} (夜盘:{} ),跳过移仓".format(
|
|
|
|
|
- symbol,
|
|
|
|
|
- context.current_dt.strftime('%H:%M:%S'),
|
|
|
|
|
- trading_start,
|
|
|
|
|
- has_night_session
|
|
|
|
|
- ))
|
|
|
|
|
|
|
+ # log.info("{} 当前时间 {} 未到达交易开始时间 {} (夜盘:{} ),跳过移仓".format(
|
|
|
|
|
+ # symbol,
|
|
|
|
|
+ # context.current_dt.strftime('%H:%M:%S'),
|
|
|
|
|
+ # trading_start,
|
|
|
|
|
+ # has_night_session
|
|
|
|
|
+ # ))
|
|
|
continue
|
|
continue
|
|
|
dominant = get_dominant_future(underlying_symbol)
|
|
dominant = get_dominant_future(underlying_symbol)
|
|
|
cur = get_current_data()
|
|
cur = get_current_data()
|