|
@@ -44,42 +44,107 @@ def initialize(context):
|
|
|
g.max_ma_crosses = 4 # 最大允许的均线交叉数量
|
|
g.max_ma_crosses = 4 # 最大允许的均线交叉数量
|
|
|
g.ma_cross_check_days = 10 # 检查均线交叉的天数
|
|
g.ma_cross_check_days = 10 # 检查均线交叉的天数
|
|
|
|
|
|
|
|
- # 定义默认的保证金比例
|
|
|
|
|
- g.margin_rates = {
|
|
|
|
|
- 'long': {'A': 0.07, 'AG': 0.04, 'AL': 0.05, 'AO': 0.05, 'AP': 0.08, 'AU': 0.04, 'B': 0.05,
|
|
|
|
|
- 'BC': 0.13, 'BR': 0.07, 'BU': 0.04, 'C': 0.07, 'CF': 0.05, 'CJ': 0.07, 'CS': 0.07,
|
|
|
|
|
- 'CU': 0.05, 'CY': 0.05, 'EB': 0.12, 'EC': 0.12, 'EG': 0.05, 'FG': 0.05, 'FU': 0.08,
|
|
|
|
|
- 'HC': 0.04, 'I': 0.1, 'J': 0.22, 'JD': 0.08, 'JM': 0.22,
|
|
|
|
|
- 'L': 0.07, 'LC': 0.05, 'LH': 0.1, 'LR': 0.05, 'LU': 0.15, 'M': 0.07, 'MA': 0.05, 'NI': 0.05, 'NR': 0.13, 'OI': 0.05,
|
|
|
|
|
- 'P': 0.05, 'PB': 0.05, 'PF': 0.1, 'PG': 0.05, 'PK': 0.05,
|
|
|
|
|
- 'PP': 0.07, 'RB': 0.05, 'RI': 0.05, 'RM': 0.05, 'RU': 0.05,
|
|
|
|
|
- 'SA': 0.05, 'SC': 0.12, 'SF': 0.05, 'SH': 0.05, 'SI': 0.13, 'SM': 0.05, 'SN': 0.05, 'SP': 0.1, 'SR': 0.05,
|
|
|
|
|
- 'SS': 0.05, 'TA': 0.05, 'UR': 0.09, 'V': 0.07,
|
|
|
|
|
- 'Y': 0.05, 'ZC': 0.05, 'ZN': 0.05},
|
|
|
|
|
- 'short': {'A': 0.07, 'AG': 0.04, 'AL': 0.05, 'AO': 0.05, 'AP': 0.08, 'AU': 0.04, 'B': 0.05,
|
|
|
|
|
- 'BC': 0.13, 'BR': 0.07, 'BU': 0.04, 'C': 0.07, 'CF': 0.05, 'CJ': 0.07, 'CS': 0.07,
|
|
|
|
|
- 'CU': 0.05, 'CY': 0.05, 'EB': 0.12, 'EC': 0.12, 'EG': 0.05, 'FG': 0.05, 'FU': 0.08,
|
|
|
|
|
- 'HC': 0.04, 'I': 0.1, 'J': 0.22, 'JD': 0.08, 'JM': 0.22,
|
|
|
|
|
- 'L': 0.07, 'LC': 0.05, 'LH': 0.1, 'LR': 0.05, 'LU': 0.15, 'M': 0.07, 'MA': 0.05, 'NI': 0.05, 'NR': 0.13, 'OI': 0.05,
|
|
|
|
|
- 'P': 0.05, 'PB': 0.05, 'PF': 0.1, 'PG': 0.05, 'PK': 0.05,
|
|
|
|
|
- 'PP': 0.07, 'RB': 0.05, 'RI': 0.05, 'RM': 0.05, 'RU': 0.05,
|
|
|
|
|
- 'SA': 0.05, 'SC': 0.12, 'SF': 0.05, 'SH': 0.05, 'SI': 0.13, 'SM': 0.05, 'SN': 0.05, 'SP': 0.1, 'SR': 0.05,
|
|
|
|
|
- 'SS': 0.05, 'TA': 0.05, 'UR': 0.09, 'V': 0.07,
|
|
|
|
|
- 'Y': 0.05, 'ZC': 0.05, 'ZN': 0.05}
|
|
|
|
|
|
|
+ # 止损止盈策略参数
|
|
|
|
|
+ g.gap_ratio_threshold = 0.002 # 跳空比例:0.2%
|
|
|
|
|
+ g.market_close_times = ["14:55:00"] # 盘尾时间
|
|
|
|
|
+ g.profit_thresholds = [5000, 15000] # 价格分区
|
|
|
|
|
+ g.stop_ratios = [0.0025, 0.005, 0.01, 0.02] # 止盈止损比例:[0.25%, 0.5%, 1%, 2%]
|
|
|
|
|
+
|
|
|
|
|
+ # 期货品种完整配置字典
|
|
|
|
|
+ g.futures_config = {
|
|
|
|
|
+ # 贵金属
|
|
|
|
|
+ 'AU': {'has_night_session': True, 'margin_rate': {'long': 0.04, 'short': 0.04}, 'multiplier': 1000},
|
|
|
|
|
+ 'AG': {'has_night_session': True, 'margin_rate': {'long': 0.04, 'short': 0.04}, 'multiplier': 15},
|
|
|
|
|
+
|
|
|
|
|
+ # 有色金属
|
|
|
|
|
+ 'CU': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
|
|
|
|
|
+ 'AL': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
|
|
|
|
|
+ 'ZN': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
|
|
|
|
|
+ 'PB': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
|
|
|
|
|
+ 'NI': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 1},
|
|
|
|
|
+ 'SN': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 1},
|
|
|
|
|
+ 'SS': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
|
|
|
|
|
+
|
|
|
|
|
+ # 黑色系
|
|
|
|
|
+ 'RB': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
|
|
|
|
|
+ 'HC': {'has_night_session': True, 'margin_rate': {'long': 0.04, 'short': 0.04}, 'multiplier': 10},
|
|
|
|
|
+ 'I': {'has_night_session': True, 'margin_rate': {'long': 0.1, 'short': 0.1}, 'multiplier': 100},
|
|
|
|
|
+ 'JM': {'has_night_session': True, 'margin_rate': {'long': 0.22, 'short': 0.22}, 'multiplier': 100},
|
|
|
|
|
+ 'J': {'has_night_session': True, 'margin_rate': {'long': 0.22, 'short': 0.22}, 'multiplier': 60},
|
|
|
|
|
+
|
|
|
|
|
+ # 能源化工
|
|
|
|
|
+ 'SP': {'has_night_session': True, 'margin_rate': {'long': 0.1, 'short': 0.1}, 'multiplier': 10},
|
|
|
|
|
+ 'FU': {'has_night_session': True, 'margin_rate': {'long': 0.08, 'short': 0.08}, 'multiplier': 10},
|
|
|
|
|
+ 'BU': {'has_night_session': True, 'margin_rate': {'long': 0.04, 'short': 0.04}, 'multiplier': 10},
|
|
|
|
|
+ 'RU': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
|
|
|
|
|
+ 'BR': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 5},
|
|
|
|
|
+ 'AO': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 20},
|
|
|
|
|
+ 'SC': {'has_night_session': True, 'margin_rate': {'long': 0.12, 'short': 0.12}, 'multiplier': 1000},
|
|
|
|
|
+ 'NR': {'has_night_session': True, 'margin_rate': {'long': 0.13, 'short': 0.13}, 'multiplier': 10},
|
|
|
|
|
+ 'LU': {'has_night_session': True, 'margin_rate': {'long': 0.15, 'short': 0.15}, 'multiplier': 10},
|
|
|
|
|
+ 'BC': {'has_night_session': True, 'margin_rate': {'long': 0.13, 'short': 0.13}, 'multiplier': 5},
|
|
|
|
|
+
|
|
|
|
|
+ # 化工
|
|
|
|
|
+ 'FG': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 20},
|
|
|
|
|
+ 'TA': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
|
|
|
|
|
+ 'MA': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
|
|
|
|
|
+ 'SA': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 20},
|
|
|
|
|
+ 'L': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 5},
|
|
|
|
|
+ 'V': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 5},
|
|
|
|
|
+ 'EG': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
|
|
|
|
|
+ 'PP': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 5},
|
|
|
|
|
+ 'EB': {'has_night_session': True, 'margin_rate': {'long': 0.12, 'short': 0.12}, 'multiplier': 5},
|
|
|
|
|
+ 'PG': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 20},
|
|
|
|
|
+ 'CY': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
|
|
|
|
|
+ 'SH': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 30},
|
|
|
|
|
+ 'PX': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
|
|
|
|
|
+
|
|
|
|
|
+ # 农产品
|
|
|
|
|
+ 'RM': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
|
|
|
|
|
+ 'OI': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
|
|
|
|
|
+ 'CF': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
|
|
|
|
|
+ 'SR': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
|
|
|
|
|
+ 'PF': {'has_night_session': True, 'margin_rate': {'long': 0.1, 'short': 0.1}, 'multiplier': 5},
|
|
|
|
|
+ 'C': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 10},
|
|
|
|
|
+ 'CS': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 10},
|
|
|
|
|
+ 'A': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 10},
|
|
|
|
|
+ 'B': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
|
|
|
|
|
+ 'M': {'has_night_session': True, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 10},
|
|
|
|
|
+ 'Y': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
|
|
|
|
|
+ 'P': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
|
|
|
|
|
+ 'PR': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
|
|
|
|
|
+ 'AD': {'has_night_session': True, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 10},
|
|
|
|
|
+
|
|
|
|
|
+ # 无夜盘品种
|
|
|
|
|
+ 'IF': {'has_night_session': False, 'margin_rate': {'long': 0.08, 'short': 0.08}, 'multiplier': 300},
|
|
|
|
|
+ 'IH': {'has_night_session': False, 'margin_rate': {'long': 0.08, 'short': 0.08}, 'multiplier': 300},
|
|
|
|
|
+ 'IC': {'has_night_session': False, 'margin_rate': {'long': 0.08, 'short': 0.08}, 'multiplier': 200},
|
|
|
|
|
+ 'IM': {'has_night_session': False, 'margin_rate': {'long': 0.08, 'short': 0.08}, 'multiplier': 200},
|
|
|
|
|
+ 'EC': {'has_night_session': False, 'margin_rate': {'long': 0.12, 'short': 0.12}, 'multiplier': 50},
|
|
|
|
|
+ 'SF': {'has_night_session': False, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
|
|
|
|
|
+ 'SM': {'has_night_session': False, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
|
|
|
|
|
+ 'UR': {'has_night_session': False, 'margin_rate': {'long': 0.09, 'short': 0.09}, 'multiplier': 20},
|
|
|
|
|
+ 'AP': {'has_night_session': False, 'margin_rate': {'long': 0.08, 'short': 0.08}, 'multiplier': 10},
|
|
|
|
|
+ 'CJ': {'has_night_session': False, 'margin_rate': {'long': 0.07, 'short': 0.07}, 'multiplier': 5},
|
|
|
|
|
+ 'PK': {'has_night_session': False, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
|
|
|
|
|
+ 'JD': {'has_night_session': False, 'margin_rate': {'long': 0.08, 'short': 0.08}, 'multiplier': 5},
|
|
|
|
|
+ 'LH': {'has_night_session': False, 'margin_rate': {'long': 0.1, 'short': 0.1}, 'multiplier': 16},
|
|
|
|
|
+ 'SI': {'has_night_session': False, 'margin_rate': {'long': 0.13, 'short': 0.13}, 'multiplier': 5},
|
|
|
|
|
+ 'LC': {'has_night_session': False, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 1},
|
|
|
|
|
+ 'PS': {'has_night_session': False, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5},
|
|
|
|
|
+ 'LG': {'has_night_session': False, 'margin_rate': {'long': 0.05, 'short': 0.05}, 'multiplier': 5}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- g.multiplier = {
|
|
|
|
|
- 'A': 10, 'AG': 15, 'AL': 5, 'AO': 20, 'AP': 10, 'AU': 1000, 'B': 10,
|
|
|
|
|
- 'BC': 5, 'BR': 5, 'BU': 10, 'C': 10, 'CF': 5, 'CJ': 5, 'CS': 10,
|
|
|
|
|
- 'CU': 5, 'CY': 5, 'EB': 5, 'EC': 50, 'EG': 10, 'FG': 20, 'FU': 10,
|
|
|
|
|
- 'HC': 10, 'I': 100, 'J': 60, 'JD': 5, 'JM': 100,
|
|
|
|
|
- 'L': 5, 'LC': 1, 'LH': 16, 'LR': 0.05, 'LU': 10, 'M': 10, 'MA': 10, 'NI': 1, 'NR': 10, 'OI': 10,
|
|
|
|
|
- 'P': 10, 'PB': 5, 'PF': 5, 'PG': 20, 'PK': 5,
|
|
|
|
|
- 'PP': 5, 'RB': 10, 'RI': 0.05, 'RM': 10, 'RU': 10,
|
|
|
|
|
- 'SA': 20, 'SC': 1000, 'SF': 5, 'SH': 30, 'SI': 5, 'SM': 5, 'SN': 1, 'SP': 10, 'SR': 10,
|
|
|
|
|
- 'SS': 5, 'TA': 5, 'UR': 20, 'V': 5,
|
|
|
|
|
- 'Y': 10, 'ZC': 0.05, 'ZN': 5
|
|
|
|
|
- }
|
|
|
|
|
|
|
+ # 当前策略关注的标的列表(可以根据需要调整,为空则考虑所有品种)
|
|
|
|
|
+ g.strategy_focus_symbols = ['RM']
|
|
|
|
|
+
|
|
|
|
|
+ # 如果关注列表为空,则使用所有配置的品种
|
|
|
|
|
+ if not g.strategy_focus_symbols:
|
|
|
|
|
+ g.strategy_focus_symbols = list(g.futures_config.keys())
|
|
|
|
|
+ log.info("策略关注品种列表为空,将考虑所有配置的品种")
|
|
|
|
|
+
|
|
|
|
|
+ # 打印配置摘要
|
|
|
|
|
+ log.info(f"策略关注品种总数: {len(g.strategy_focus_symbols)}")
|
|
|
|
|
|
|
|
# 交易记录和数据存储
|
|
# 交易记录和数据存储
|
|
|
g.trade_history = {}
|
|
g.trade_history = {}
|
|
@@ -88,8 +153,9 @@ def initialize(context):
|
|
|
g.minute_data_cache = {} # 存储今日分钟数据缓存
|
|
g.minute_data_cache = {} # 存储今日分钟数据缓存
|
|
|
g.ma_data_cache = {} # 存储均线数据缓存
|
|
g.ma_data_cache = {} # 存储均线数据缓存
|
|
|
g.ma_cross_filtered_futures = {} # 存储通过均线交叉检查的品种(每日缓存)
|
|
g.ma_cross_filtered_futures = {} # 存储通过均线交叉检查的品种(每日缓存)
|
|
|
|
|
+ g.gap_check_results = {} # 存储跳空检查结果(每日缓存)
|
|
|
|
|
|
|
|
- # 保证金比例管理(g.margin_rates会根据实际交易校准)
|
|
|
|
|
|
|
+ # 保证金比例管理(g.futures_config中的保证金比例会根据实际交易校准)
|
|
|
g.margin_rate_history = {} # 保证金比例变化历史记录
|
|
g.margin_rate_history = {} # 保证金比例变化历史记录
|
|
|
g.today_trades = [] # 当日交易记录
|
|
g.today_trades = [] # 当日交易记录
|
|
|
|
|
|
|
@@ -236,19 +302,23 @@ def task_1_get_tradable_futures(context):
|
|
|
"""任务1: 获取所有可交易品种(分白天和晚上)"""
|
|
"""任务1: 获取所有可交易品种(分白天和晚上)"""
|
|
|
# log.info("执行任务1: 获取可交易品种")
|
|
# log.info("执行任务1: 获取可交易品种")
|
|
|
|
|
|
|
|
- # 夜盘品种
|
|
|
|
|
- # potential_night_list = ['NI', 'CF', 'PF', 'Y', 'M', 'B', 'SN', 'RM', 'RB', 'HC', 'I', 'J', 'JM']
|
|
|
|
|
- potential_night_list = ['PF', 'Y', 'SN']
|
|
|
|
|
- # 日盘品种
|
|
|
|
|
- potential_day_list = ['PK']
|
|
|
|
|
- # potential_day_list = ['JD', 'UR', 'AP', 'CJ', 'PK']
|
|
|
|
|
-
|
|
|
|
|
current_time = str(context.current_dt.time())[:2]
|
|
current_time = str(context.current_dt.time())[:2]
|
|
|
|
|
+
|
|
|
|
|
+ # 从策略关注列表中筛选可交易品种
|
|
|
|
|
+ # 如果关注列表为空,则使用所有配置的品种
|
|
|
|
|
+ focus_symbols = g.strategy_focus_symbols if g.strategy_focus_symbols else list(g.futures_config.keys())
|
|
|
|
|
+
|
|
|
|
|
+ potential_icon_list = []
|
|
|
|
|
+
|
|
|
if current_time in ('21', '22'):
|
|
if current_time in ('21', '22'):
|
|
|
- potential_icon_list = potential_night_list
|
|
|
|
|
- log.info(f"夜盘时间,可交易品种: {potential_night_list}")
|
|
|
|
|
|
|
+ # 夜盘时间:只考虑有夜盘的品种
|
|
|
|
|
+ for symbol in focus_symbols:
|
|
|
|
|
+ if get_futures_config(symbol, 'has_night_session', False):
|
|
|
|
|
+ potential_icon_list.append(symbol)
|
|
|
|
|
+ log.info(f"夜盘时间,可交易品种: {potential_icon_list}")
|
|
|
else:
|
|
else:
|
|
|
- potential_icon_list = potential_day_list + potential_night_list
|
|
|
|
|
|
|
+ # 日盘时间:所有关注的品种都可以交易
|
|
|
|
|
+ potential_icon_list = focus_symbols[:]
|
|
|
log.info(f"日盘时间,可交易品种: {potential_icon_list}")
|
|
log.info(f"日盘时间,可交易品种: {potential_icon_list}")
|
|
|
|
|
|
|
|
potential_future_list = []
|
|
potential_future_list = []
|
|
@@ -349,14 +419,28 @@ def task_3_check_ma_crosses(context):
|
|
|
return filtered_futures
|
|
return filtered_futures
|
|
|
|
|
|
|
|
def task_4_update_realtime_data(context):
|
|
def task_4_update_realtime_data(context):
|
|
|
- """任务4: 获取所有可交易品种今天所需的分钟数据作为今天数据,和2中的数据合并出最新的均线数据,并保存到内存中"""
|
|
|
|
|
- # log.info("执行任务4: 更新实时数据和均线")
|
|
|
|
|
|
|
+ """任务4: 获取所有可交易品种和持仓品种今天所需的分钟数据作为今天数据,和2中的数据合并出最新的均线数据,并保存到内存中"""
|
|
|
|
|
+ log.info("执行任务4: 更新实时数据和均线")
|
|
|
|
|
|
|
|
- if not hasattr(g, 'tradable_futures'):
|
|
|
|
|
|
|
+ # 收集需要更新数据的品种
|
|
|
|
|
+ update_symbols = set()
|
|
|
|
|
+
|
|
|
|
|
+ # 添加可交易品种
|
|
|
|
|
+ if hasattr(g, 'tradable_futures') and g.tradable_futures:
|
|
|
|
|
+ update_symbols.update(g.tradable_futures)
|
|
|
|
|
+
|
|
|
|
|
+ # 添加持仓品种(用于止损止盈)
|
|
|
|
|
+ if hasattr(g, 'trade_history') and g.trade_history:
|
|
|
|
|
+ update_symbols.update(g.trade_history.keys())
|
|
|
|
|
+
|
|
|
|
|
+ if not update_symbols:
|
|
|
|
|
+ log.info("没有需要更新的品种")
|
|
|
return
|
|
return
|
|
|
|
|
|
|
|
- for future_code in g.tradable_futures:
|
|
|
|
|
- log.info(f"任务4 future_code: {future_code}")
|
|
|
|
|
|
|
+ today_date = context.current_dt.date()
|
|
|
|
|
+
|
|
|
|
|
+ for future_code in update_symbols:
|
|
|
|
|
+ # log.info(f"任务4 future_code: {future_code}")
|
|
|
try:
|
|
try:
|
|
|
# 获取今日分钟数据
|
|
# 获取今日分钟数据
|
|
|
minute_data = get_today_minute_data(context, future_code)
|
|
minute_data = get_today_minute_data(context, future_code)
|
|
@@ -364,11 +448,35 @@ def task_4_update_realtime_data(context):
|
|
|
if minute_data is None:
|
|
if minute_data is None:
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
- # 获取历史数据
|
|
|
|
|
|
|
+ # 获取历史数据,如果缓存中没有则现场获取
|
|
|
historical_data = g.daily_data_cache.get(future_code)
|
|
historical_data = g.daily_data_cache.get(future_code)
|
|
|
|
|
+ if historical_data is None:
|
|
|
|
|
+ # 为持仓品种临时获取历史数据
|
|
|
|
|
+ try:
|
|
|
|
|
+ data = attribute_history(future_code, 50, '1d',
|
|
|
|
|
+ ['open', 'close', 'high', 'low', 'volume'],
|
|
|
|
|
+ df=True)
|
|
|
|
|
+ if data is not None and len(data) > 0:
|
|
|
|
|
+ # 排除今天的数据
|
|
|
|
|
+ today = context.current_dt.date()
|
|
|
|
|
+ data = data[data.index.date < today]
|
|
|
|
|
+ g.daily_data_cache[future_code] = data
|
|
|
|
|
+ historical_data = data
|
|
|
|
|
+ log.info(f"为持仓品种 {future_code} 临时获取历史数据 {len(data)} 条")
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ log.warning(f"获取{future_code}历史数据失败: {str(e)}")
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
if historical_data is None:
|
|
if historical_data is None:
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
|
|
+ # 检查跳空(只在第一次获取今日数据时检查)
|
|
|
|
|
+ if future_code not in g.gap_check_results:
|
|
|
|
|
+ gap_result = check_gap_opening(historical_data, minute_data)
|
|
|
|
|
+ g.gap_check_results[future_code] = gap_result
|
|
|
|
|
+ if gap_result['has_gap']:
|
|
|
|
|
+ log.info(f"{future_code} 检测到跳空开盘,跳空比例: {gap_result['gap_ratio']:.3%}")
|
|
|
|
|
+
|
|
|
# 合并数据并计算均线
|
|
# 合并数据并计算均线
|
|
|
combined_data = combine_and_calculate_ma(historical_data, minute_data)
|
|
combined_data = combine_and_calculate_ma(historical_data, minute_data)
|
|
|
if combined_data is not None:
|
|
if combined_data is not None:
|
|
@@ -379,7 +487,7 @@ def task_4_update_realtime_data(context):
|
|
|
log.warning(f"更新{future_code}实时数据时出错: {str(e)}")
|
|
log.warning(f"更新{future_code}实时数据时出错: {str(e)}")
|
|
|
continue
|
|
continue
|
|
|
|
|
|
|
|
- # log.info(f"实时数据更新完成,共更新 {len(g.ma_data_cache)} 个品种")
|
|
|
|
|
|
|
+ log.info(f"实时数据更新完成,共更新 {len(g.ma_data_cache)} 个品种(可交易: {len(g.tradable_futures) if hasattr(g, 'tradable_futures') else 0},持仓: {len(g.trade_history) if hasattr(g, 'trade_history') else 0})")
|
|
|
|
|
|
|
|
def task_5_analyze_ma_crosses(context):
|
|
def task_5_analyze_ma_crosses(context):
|
|
|
"""任务5: 根据交易品种的今天数据和均线数据,判断是否出现了上穿或者下穿的情况"""
|
|
"""任务5: 根据交易品种的今天数据和均线数据,判断是否出现了上穿或者下穿的情况"""
|
|
@@ -473,7 +581,9 @@ def task_7_execute_trades(context, filtered_signals):
|
|
|
if success:
|
|
if success:
|
|
|
# 获取实际保证金(只有在开仓成功时才能获取)
|
|
# 获取实际保证金(只有在开仓成功时才能获取)
|
|
|
actual_margin = g.trade_history[symbol]['actual_margin']
|
|
actual_margin = g.trade_history[symbol]['actual_margin']
|
|
|
- log.info(f"成功开仓 {symbol} {direction} 金额: {order_value}, 实际保证金: {actual_margin:.0f}")
|
|
|
|
|
|
|
+ # 获取成交价格
|
|
|
|
|
+ actual_price = g.trade_history[symbol]['entry_price']
|
|
|
|
|
+ log.info(f"成功开仓 {symbol} {direction}, 成交价格: {actual_price:.2f}, 金额: {order_value}, 实际保证金: {actual_margin:.0f}")
|
|
|
else:
|
|
else:
|
|
|
log.warning(f"开仓失败 {symbol} {direction}")
|
|
log.warning(f"开仓失败 {symbol} {direction}")
|
|
|
|
|
|
|
@@ -512,14 +622,7 @@ def task_9_check_stop_loss_profit(context):
|
|
|
|
|
|
|
|
def check_has_night_session(underlying_symbol):
|
|
def check_has_night_session(underlying_symbol):
|
|
|
"""检查品种是否有夜盘"""
|
|
"""检查品种是否有夜盘"""
|
|
|
- # 有夜盘的品种列表
|
|
|
|
|
- night_session_symbols = {
|
|
|
|
|
- 'NI', 'CF', 'PF', 'Y', 'M', 'B', 'SN', 'RM', 'RB', 'HC', 'I', 'J', 'JM',
|
|
|
|
|
- 'A', 'AG', 'AL', 'AU', 'BU', 'C', 'CU', 'FU', 'L', 'P', 'PB', 'RU',
|
|
|
|
|
- 'SC', 'SP', 'SS', 'TA', 'V', 'ZC', 'ZN', 'MA', 'SR', 'OI', 'CF', 'RM',
|
|
|
|
|
- 'FG', 'SA', 'UR', 'NR', 'LU', 'BC', 'EC', 'PP', 'EB', 'EG', 'PG'
|
|
|
|
|
- }
|
|
|
|
|
- return underlying_symbol in night_session_symbols
|
|
|
|
|
|
|
+ return get_futures_config(underlying_symbol, 'has_night_session', False)
|
|
|
|
|
|
|
|
def get_today_minute_data(context, future_code):
|
|
def get_today_minute_data(context, future_code):
|
|
|
"""获取今日分钟数据"""
|
|
"""获取今日分钟数据"""
|
|
@@ -540,12 +643,12 @@ def get_today_minute_data(context, future_code):
|
|
|
if minute_data is None or len(minute_data) == 0:
|
|
if minute_data is None or len(minute_data) == 0:
|
|
|
return None
|
|
return None
|
|
|
|
|
|
|
|
- # log.debug(f"原始分钟数据范围: {minute_data.index[0]} 到 {minute_data.index[-1]}")
|
|
|
|
|
|
|
+ log.debug(f"原始分钟数据范围: {minute_data.index[0]} 到 {minute_data.index[-1]}")
|
|
|
|
|
|
|
|
# 提取所有日期(年月日维度)
|
|
# 提取所有日期(年月日维度)
|
|
|
minute_data['date'] = minute_data.index.date
|
|
minute_data['date'] = minute_data.index.date
|
|
|
unique_dates = sorted(minute_data['date'].unique())
|
|
unique_dates = sorted(minute_data['date'].unique())
|
|
|
- # log.debug(f"数据包含的日期: {unique_dates}")
|
|
|
|
|
|
|
+ log.debug(f"数据包含的日期: {unique_dates}")
|
|
|
|
|
|
|
|
if has_night_session:
|
|
if has_night_session:
|
|
|
# 有夜盘的品种:需要找到前一交易日的21:00作为今日开盘起点
|
|
# 有夜盘的品种:需要找到前一交易日的21:00作为今日开盘起点
|
|
@@ -553,13 +656,13 @@ def get_today_minute_data(context, future_code):
|
|
|
|
|
|
|
|
# 找到今天之前的最后一个交易日
|
|
# 找到今天之前的最后一个交易日
|
|
|
previous_trading_dates = [d for d in unique_dates if d < today_date]
|
|
previous_trading_dates = [d for d in unique_dates if d < today_date]
|
|
|
- # log.debug(f"夜盘标的 today_date: {today_date}, previous_trading_dates: {previous_trading_dates}")
|
|
|
|
|
|
|
+ log.debug(f"夜盘标的 today_date: {today_date}, previous_trading_dates: {previous_trading_dates}")
|
|
|
if not previous_trading_dates:
|
|
if not previous_trading_dates:
|
|
|
- # log.warning(f"找不到{future_code}的前一交易日数据")
|
|
|
|
|
|
|
+ log.warning(f"找不到{future_code}的前一交易日数据")
|
|
|
return minute_data
|
|
return minute_data
|
|
|
|
|
|
|
|
previous_trading_date = max(previous_trading_dates)
|
|
previous_trading_date = max(previous_trading_dates)
|
|
|
- # log.debug(f"前一交易日: {previous_trading_date}")
|
|
|
|
|
|
|
+ log.debug(f"前一交易日: {previous_trading_date}")
|
|
|
|
|
|
|
|
# 找到前一交易日21:00:00的数据作为开盘起点
|
|
# 找到前一交易日21:00:00的数据作为开盘起点
|
|
|
previous_day_data = minute_data[minute_data['date'] == previous_trading_date]
|
|
previous_day_data = minute_data[minute_data['date'] == previous_trading_date]
|
|
@@ -569,10 +672,10 @@ def get_today_minute_data(context, future_code):
|
|
|
# 从前一交易日21:00开始的所有数据
|
|
# 从前一交易日21:00开始的所有数据
|
|
|
start_time = night_21_data.index[0] # 21:00:00的时间点
|
|
start_time = night_21_data.index[0] # 21:00:00的时间点
|
|
|
filtered_data = minute_data[minute_data.index >= start_time]
|
|
filtered_data = minute_data[minute_data.index >= start_time]
|
|
|
- # log.debug(f"夜盘品种,从{start_time}开始,数据量: {len(filtered_data)}")
|
|
|
|
|
|
|
+ log.debug(f"夜盘品种,从{start_time}开始,数据量: {len(filtered_data)}")
|
|
|
return filtered_data.drop(columns=['date'])
|
|
return filtered_data.drop(columns=['date'])
|
|
|
else:
|
|
else:
|
|
|
- # log.warning(f"找不到{future_code}前一交易日21:00的数据")
|
|
|
|
|
|
|
+ log.warning(f"找不到{future_code}前一交易日21:00的数据")
|
|
|
# 备选方案:使用今天9:00开始的数据
|
|
# 备选方案:使用今天9:00开始的数据
|
|
|
today_data = minute_data[minute_data['date'] == today_date]
|
|
today_data = minute_data[minute_data['date'] == today_date]
|
|
|
day_9_data = today_data[today_data.index.hour >= 9]
|
|
day_9_data = today_data[today_data.index.hour >= 9]
|
|
@@ -589,10 +692,10 @@ def get_today_minute_data(context, future_code):
|
|
|
day_9_data = today_data[today_data.index.hour >= 9]
|
|
day_9_data = today_data[today_data.index.hour >= 9]
|
|
|
|
|
|
|
|
if len(day_9_data) > 0:
|
|
if len(day_9_data) > 0:
|
|
|
- # log.debug(f"日盘品种,从今天9:00开始,数据量: {len(day_9_data)}")
|
|
|
|
|
|
|
+ log.debug(f"日盘品种,从今天9:00开始,数据量: {len(day_9_data)}")
|
|
|
return day_9_data.drop(columns=['date'])
|
|
return day_9_data.drop(columns=['date'])
|
|
|
else:
|
|
else:
|
|
|
- # log.warning(f"找不到{future_code}今天9:00的数据")
|
|
|
|
|
|
|
+ log.warning(f"找不到{future_code}今天9:00的数据")
|
|
|
return today_data.drop(columns=['date']) if len(today_data) > 0 else minute_data.drop(columns=['date'])
|
|
return today_data.drop(columns=['date']) if len(today_data) > 0 else minute_data.drop(columns=['date'])
|
|
|
|
|
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
@@ -662,6 +765,40 @@ def aggregate_minute_to_daily(minute_data):
|
|
|
log.warning(f"聚合分钟数据时出错: {str(e)}")
|
|
log.warning(f"聚合分钟数据时出错: {str(e)}")
|
|
|
return None
|
|
return None
|
|
|
|
|
|
|
|
|
|
+def check_gap_opening(historical_data, minute_data):
|
|
|
|
|
+ """
|
|
|
|
|
+ 检查开盘是否跳空
|
|
|
|
|
+ :param historical_data: 历史日线数据
|
|
|
|
|
+ :param minute_data: 今日分钟数据
|
|
|
|
|
+ :return: 跳空检查结果字典
|
|
|
|
|
+ """
|
|
|
|
|
+ try:
|
|
|
|
|
+ if historical_data is None or len(historical_data) == 0 or minute_data is None or len(minute_data) == 0:
|
|
|
|
|
+ return {'has_gap': False, 'gap_ratio': 0.0}
|
|
|
|
|
+
|
|
|
|
|
+ # 获取前一交易日收盘价
|
|
|
|
|
+ previous_close = historical_data['close'].iloc[-1]
|
|
|
|
|
+
|
|
|
|
|
+ # 获取今日开盘价
|
|
|
|
|
+ today_open = minute_data['open'].iloc[0]
|
|
|
|
|
+
|
|
|
|
|
+ # 计算跳空比例
|
|
|
|
|
+ gap_ratio = abs(today_open - previous_close) / previous_close
|
|
|
|
|
+
|
|
|
|
|
+ # 判断是否跳空
|
|
|
|
|
+ has_gap = gap_ratio >= g.gap_ratio_threshold
|
|
|
|
|
+
|
|
|
|
|
+ return {
|
|
|
|
|
+ 'has_gap': has_gap,
|
|
|
|
|
+ 'gap_ratio': gap_ratio,
|
|
|
|
|
+ 'previous_close': previous_close,
|
|
|
|
|
+ 'today_open': today_open
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ log.warning(f"检查跳空开盘时出错: {str(e)}")
|
|
|
|
|
+ return {'has_gap': False, 'gap_ratio': 0.0}
|
|
|
|
|
+
|
|
|
############################ 原有函数保持不变 ###################################
|
|
############################ 原有函数保持不变 ###################################
|
|
|
|
|
|
|
|
def check_latest_multi_ma_cross(data, future_code):
|
|
def check_latest_multi_ma_cross(data, future_code):
|
|
@@ -672,8 +809,8 @@ def check_latest_multi_ma_cross(data, future_code):
|
|
|
# 获取最新两天的数据
|
|
# 获取最新两天的数据
|
|
|
today = data.iloc[-1]
|
|
today = data.iloc[-1]
|
|
|
yesterday = data.iloc[-2]
|
|
yesterday = data.iloc[-2]
|
|
|
- # log.info(f"today: {today}")
|
|
|
|
|
- # log.info(f"yesterday: {yesterday}")
|
|
|
|
|
|
|
+ log.debug(f"today: {today}")
|
|
|
|
|
+ log.debug(f"yesterday: {yesterday}")
|
|
|
|
|
|
|
|
# 检查多均线穿越
|
|
# 检查多均线穿越
|
|
|
cross_result = check_multi_ma_cross_single_day(today)
|
|
cross_result = check_multi_ma_cross_single_day(today)
|
|
@@ -717,38 +854,85 @@ def check_multi_ma_cross_single_day(row):
|
|
|
if open_price == close_price:
|
|
if open_price == close_price:
|
|
|
return None
|
|
return None
|
|
|
|
|
|
|
|
- crossed_mas = []
|
|
|
|
|
-
|
|
|
|
|
- # 上涨(阳线),检查上穿
|
|
|
|
|
- if close_price > open_price:
|
|
|
|
|
|
|
+ # 1. 统计开盘价和均线的高低关系
|
|
|
|
|
+ open_above_count = 0 # 开盘价高于均线的数量
|
|
|
|
|
+ open_below_count = 0 # 开盘价低于均线的数量
|
|
|
|
|
+ open_above_mas = [] # 开盘价高于的均线列表
|
|
|
|
|
+ open_below_mas = [] # 开盘价低于的均线列表
|
|
|
|
|
+
|
|
|
|
|
+ for ma_name, ma_value in ma_values:
|
|
|
|
|
+ if open_price > ma_value:
|
|
|
|
|
+ open_above_count += 1
|
|
|
|
|
+ open_above_mas.append((ma_name, ma_value))
|
|
|
|
|
+ elif open_price < ma_value:
|
|
|
|
|
+ open_below_count += 1
|
|
|
|
|
+ open_below_mas.append((ma_name, ma_value))
|
|
|
|
|
+
|
|
|
|
|
+ # 2. 统计收盘价和均线的高低关系
|
|
|
|
|
+ close_above_count = 0 # 收盘价高于均线的数量
|
|
|
|
|
+ close_below_count = 0 # 收盘价低于均线的数量
|
|
|
|
|
+ close_above_mas = [] # 收盘价高于的均线列表
|
|
|
|
|
+ close_below_mas = [] # 收盘价低于的均线列表
|
|
|
|
|
+
|
|
|
|
|
+ for ma_name, ma_value in ma_values:
|
|
|
|
|
+ if close_price > ma_value:
|
|
|
|
|
+ close_above_count += 1
|
|
|
|
|
+ close_above_mas.append((ma_name, ma_value))
|
|
|
|
|
+ elif close_price < ma_value:
|
|
|
|
|
+ close_below_count += 1
|
|
|
|
|
+ close_below_mas.append((ma_name, ma_value))
|
|
|
|
|
+
|
|
|
|
|
+ # 3. 计算穿越情况
|
|
|
|
|
+ # 上穿:收盘价高于的数量比开盘价高于的数量增加了
|
|
|
|
|
+ upward_cross_count = close_above_count - open_above_count
|
|
|
|
|
+ # 下穿:收盘价低于的数量比开盘价低于的数量增加了
|
|
|
|
|
+ downward_cross_count = close_below_count - open_below_count
|
|
|
|
|
+
|
|
|
|
|
+ log.debug(f"开盘价高于均线数量: {open_above_count}, 收盘价高于均线数量: {close_above_count}")
|
|
|
|
|
+ log.debug(f"开盘价低于均线数量: {open_below_count}, 收盘价低于均线数量: {close_below_count}")
|
|
|
|
|
+ log.debug(f"上穿数量: {upward_cross_count}, 下穿数量: {downward_cross_count}")
|
|
|
|
|
+
|
|
|
|
|
+ # 4. 判断是否满足最少穿越条件并找到临界线
|
|
|
|
|
+ if upward_cross_count >= g.min_cross_mas:
|
|
|
|
|
+ # 上穿:找到被穿越的均线中值最大的一条作为临界线
|
|
|
|
|
+ # 被穿越的均线是那些开盘价低于但收盘价高于的均线
|
|
|
|
|
+ crossed_mas = []
|
|
|
|
|
+ crossed_ma_names = []
|
|
|
for ma_name, ma_value in ma_values:
|
|
for ma_name, ma_value in ma_values:
|
|
|
- if open_price < ma_value and close_price > ma_value:
|
|
|
|
|
|
|
+ if open_price <= ma_value and close_price > ma_value:
|
|
|
crossed_mas.append((ma_name, ma_value))
|
|
crossed_mas.append((ma_name, ma_value))
|
|
|
|
|
+ crossed_ma_names.append(ma_name)
|
|
|
|
|
|
|
|
- if len(crossed_mas) >= g.min_cross_mas:
|
|
|
|
|
- # 找到最大的穿越线作为临界线
|
|
|
|
|
|
|
+ if len(crossed_mas) > 0:
|
|
|
|
|
+ # 找到值最大的被穿越均线
|
|
|
critical_ma = max(crossed_mas, key=lambda x: x[1])
|
|
critical_ma = max(crossed_mas, key=lambda x: x[1])
|
|
|
return {
|
|
return {
|
|
|
'direction': 'up',
|
|
'direction': 'up',
|
|
|
'critical_ma_name': critical_ma[0],
|
|
'critical_ma_name': critical_ma[0],
|
|
|
'critical_ma_value': critical_ma[1],
|
|
'critical_ma_value': critical_ma[1],
|
|
|
- 'crossed_count': len(crossed_mas)
|
|
|
|
|
|
|
+ 'crossed_count': upward_cross_count,
|
|
|
|
|
+ 'crossed_ma_names': crossed_ma_names # 添加被穿越的均线名称列表
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- # 下跌(阴线),检查下穿
|
|
|
|
|
- elif open_price > close_price:
|
|
|
|
|
|
|
+ elif downward_cross_count >= g.min_cross_mas:
|
|
|
|
|
+ # 下穿:找到被穿越的均线中值最小的一条作为临界线
|
|
|
|
|
+ # 被穿越的均线是那些开盘价高于但收盘价低于的均线
|
|
|
|
|
+ crossed_mas = []
|
|
|
|
|
+ crossed_ma_names = []
|
|
|
for ma_name, ma_value in ma_values:
|
|
for ma_name, ma_value in ma_values:
|
|
|
- if open_price > ma_value and close_price < ma_value:
|
|
|
|
|
|
|
+ if open_price >= ma_value and close_price < ma_value:
|
|
|
crossed_mas.append((ma_name, ma_value))
|
|
crossed_mas.append((ma_name, ma_value))
|
|
|
|
|
+ crossed_ma_names.append(ma_name)
|
|
|
|
|
|
|
|
- if len(crossed_mas) >= g.min_cross_mas:
|
|
|
|
|
- # 找到最小的穿越线作为临界线
|
|
|
|
|
|
|
+ if len(crossed_mas) > 0:
|
|
|
|
|
+ # 找到值最小的被穿越均线
|
|
|
critical_ma = min(crossed_mas, key=lambda x: x[1])
|
|
critical_ma = min(crossed_mas, key=lambda x: x[1])
|
|
|
return {
|
|
return {
|
|
|
'direction': 'down',
|
|
'direction': 'down',
|
|
|
'critical_ma_name': critical_ma[0],
|
|
'critical_ma_name': critical_ma[0],
|
|
|
'critical_ma_value': critical_ma[1],
|
|
'critical_ma_value': critical_ma[1],
|
|
|
- 'crossed_count': len(crossed_mas)
|
|
|
|
|
|
|
+ 'crossed_count': downward_cross_count,
|
|
|
|
|
+ 'crossed_ma_names': crossed_ma_names # 添加被穿越的均线名称列表
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
return None
|
|
return None
|
|
@@ -804,7 +988,7 @@ def open_position(context, security, value, direction, signal):
|
|
|
cash_before = context.portfolio.available_cash
|
|
cash_before = context.portfolio.available_cash
|
|
|
|
|
|
|
|
order = order_target_value(security, value, side=direction)
|
|
order = order_target_value(security, value, side=direction)
|
|
|
- log.info(f"order: {order}")
|
|
|
|
|
|
|
+ log.debug(f"order: {order}")
|
|
|
|
|
|
|
|
if order is not None and order.filled > 0:
|
|
if order is not None and order.filled > 0:
|
|
|
# 记录交易后的可用资金
|
|
# 记录交易后的可用资金
|
|
@@ -819,7 +1003,7 @@ def open_position(context, security, value, direction, signal):
|
|
|
|
|
|
|
|
# 计算实际保证金比例
|
|
# 计算实际保证金比例
|
|
|
underlying_symbol = security.split('.')[0][:-4]
|
|
underlying_symbol = security.split('.')[0][:-4]
|
|
|
- multiplier = g.multiplier.get(underlying_symbol, 10)
|
|
|
|
|
|
|
+ multiplier = get_multiplier(underlying_symbol)
|
|
|
|
|
|
|
|
# 单笔保证金 = 资金变化 / 数量
|
|
# 单笔保证金 = 资金变化 / 数量
|
|
|
single_margin = cash_change / order_amount if order_amount > 0 else 0
|
|
single_margin = cash_change / order_amount if order_amount > 0 else 0
|
|
@@ -829,7 +1013,7 @@ def open_position(context, security, value, direction, signal):
|
|
|
actual_margin_rate = single_margin / contract_value if contract_value > 0 else 0
|
|
actual_margin_rate = single_margin / contract_value if contract_value > 0 else 0
|
|
|
|
|
|
|
|
# 校准保证金比例(只有变化大于1%时才更新)
|
|
# 校准保证金比例(只有变化大于1%时才更新)
|
|
|
- current_rate = g.margin_rates.get(direction, {}).get(underlying_symbol, 0.10)
|
|
|
|
|
|
|
+ current_rate = get_margin_rate(underlying_symbol, direction)
|
|
|
rate_change = abs(actual_margin_rate - current_rate) / current_rate if current_rate > 0 else 0
|
|
rate_change = abs(actual_margin_rate - current_rate) / current_rate if current_rate > 0 else 0
|
|
|
|
|
|
|
|
if rate_change > 0.01: # 变化大于1%
|
|
if rate_change > 0.01: # 变化大于1%
|
|
@@ -847,35 +1031,36 @@ def open_position(context, security, value, direction, signal):
|
|
|
'security': security
|
|
'security': security
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
- # 直接更新默认保证金比例
|
|
|
|
|
- g.margin_rates[direction][underlying_symbol] = actual_margin_rate
|
|
|
|
|
|
|
+ # 直接更新配置字典中的保证金比例
|
|
|
|
|
+ if underlying_symbol in g.futures_config:
|
|
|
|
|
+ g.futures_config[underlying_symbol]['margin_rate'][direction] = actual_margin_rate
|
|
|
|
|
|
|
|
- log.info(f"保证金比例校准: {underlying_symbol}_{direction} {current_rate:.4f} -> {actual_margin_rate:.4f} (变化{rate_change*100:.1f}%)")
|
|
|
|
|
|
|
+ log.debug(f"保证金比例校准: {underlying_symbol}_{direction} {current_rate:.4f} -> {actual_margin_rate:.4f} (变化{rate_change*100:.1f}%)")
|
|
|
|
|
|
|
|
# 记录当日交易
|
|
# 记录当日交易
|
|
|
g.today_trades.append({
|
|
g.today_trades.append({
|
|
|
- 'security': security,
|
|
|
|
|
- 'underlying_symbol': underlying_symbol,
|
|
|
|
|
- 'direction': direction,
|
|
|
|
|
- 'order_amount': order_amount,
|
|
|
|
|
- 'order_price': order_price,
|
|
|
|
|
- 'cash_change': cash_change,
|
|
|
|
|
- 'actual_margin_rate': actual_margin_rate,
|
|
|
|
|
- 'time': context.current_dt
|
|
|
|
|
|
|
+ 'security': security, # 交易标的
|
|
|
|
|
+ 'underlying_symbol': underlying_symbol, # 标的字母
|
|
|
|
|
+ 'direction': direction, # 方向
|
|
|
|
|
+ 'order_amount': order_amount, # 开仓数量
|
|
|
|
|
+ 'order_price': order_price, # 开仓金额
|
|
|
|
|
+ 'cash_change': cash_change, # 现金变化
|
|
|
|
|
+ 'actual_margin_rate': actual_margin_rate, # 实际保证金率
|
|
|
|
|
+ 'time': context.current_dt # 成交日期
|
|
|
})
|
|
})
|
|
|
|
|
|
|
|
# 记录交易信息
|
|
# 记录交易信息
|
|
|
g.trade_history[security] = {
|
|
g.trade_history[security] = {
|
|
|
- 'entry_price': order_price,
|
|
|
|
|
- 'position_value': value,
|
|
|
|
|
- 'actual_margin': cash_change,
|
|
|
|
|
- 'direction': direction,
|
|
|
|
|
- 'entry_time': context.current_dt,
|
|
|
|
|
- 'signal_info': signal
|
|
|
|
|
|
|
+ 'entry_price': order_price, # 成交价格
|
|
|
|
|
+ 'position_value': value, # 开仓金额
|
|
|
|
|
+ 'actual_margin': cash_change, # 实际保证金
|
|
|
|
|
+ 'direction': direction, # 方向
|
|
|
|
|
+ 'entry_time': context.current_dt, # 开仓时间
|
|
|
|
|
+ 'signal_info': signal # 信号信息
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- log.info(f"开仓成功 - 品种: {underlying_symbol}, 手数: {order_amount}, 订单价格: {order_price:.2f}")
|
|
|
|
|
- log.info(f"资金变化: {cash_change:.0f}, 实际保证金比例: {actual_margin_rate:.4f}")
|
|
|
|
|
|
|
+ log.debug(f"开仓成功 - 品种: {underlying_symbol}, 手数: {order_amount}, 订单价格: {order_price:.2f}")
|
|
|
|
|
+ log.debug(f"资金变化: {cash_change:.0f}, 实际保证金比例: {actual_margin_rate:.4f}")
|
|
|
return True
|
|
return True
|
|
|
|
|
|
|
|
except Exception as e:
|
|
except Exception as e:
|
|
@@ -923,50 +1108,249 @@ def check_stop_loss_profit(context, position):
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
|
trade_info = g.trade_history[security]
|
|
trade_info = g.trade_history[security]
|
|
|
- entry_price = trade_info['entry_price']
|
|
|
|
|
- current_price = position.price
|
|
|
|
|
- direction = trade_info['direction']
|
|
|
|
|
-
|
|
|
|
|
- # 计算盈亏
|
|
|
|
|
- if direction == 'long':
|
|
|
|
|
- pnl_ratio = (current_price - entry_price) / entry_price
|
|
|
|
|
- else:
|
|
|
|
|
- pnl_ratio = (entry_price - current_price) / entry_price
|
|
|
|
|
|
|
|
|
|
- # 止损条件
|
|
|
|
|
- stop_loss_ratio = -0.03 # 3%止损
|
|
|
|
|
- # 止盈条件
|
|
|
|
|
- take_profit_ratio = 0.05 # 5%止盈
|
|
|
|
|
-
|
|
|
|
|
- if pnl_ratio <= stop_loss_ratio:
|
|
|
|
|
- log.info(f"触发止损 {security} 盈亏比例: {pnl_ratio:.2%}")
|
|
|
|
|
- close_position(context, security, direction)
|
|
|
|
|
- return True
|
|
|
|
|
- elif pnl_ratio >= take_profit_ratio:
|
|
|
|
|
- log.info(f"触发止盈 {security} 盈亏比例: {pnl_ratio:.2%}")
|
|
|
|
|
- close_position(context, security, direction)
|
|
|
|
|
|
|
+ # 跟踪均线止损止盈
|
|
|
|
|
+ tracking_stop_result = check_tracking_ma_stop(context, security, position, trade_info)
|
|
|
|
|
+ if tracking_stop_result:
|
|
|
return True
|
|
return True
|
|
|
|
|
|
|
|
return False
|
|
return False
|
|
|
|
|
|
|
|
|
|
+def check_tracking_ma_stop(context, security, position, trade_info):
|
|
|
|
|
+ """
|
|
|
|
|
+ 检查跟踪均线止损止盈
|
|
|
|
|
+ :param context: 上下文对象
|
|
|
|
|
+ :param security: 标的代码
|
|
|
|
|
+ :param position: 持仓对象
|
|
|
|
|
+ :param trade_info: 交易信息
|
|
|
|
|
+ :return: 是否触发止损止盈
|
|
|
|
|
+ """
|
|
|
|
|
+ try:
|
|
|
|
|
+ direction = trade_info['direction']
|
|
|
|
|
+ entry_price = trade_info['entry_price']
|
|
|
|
|
+ current_price = position.price
|
|
|
|
|
+
|
|
|
|
|
+ # 获取最新的均线数据
|
|
|
|
|
+ if security not in g.ma_data_cache:
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
+ ma_data = g.ma_data_cache[security]
|
|
|
|
|
+ if len(ma_data) == 0:
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
+ latest_data = ma_data.iloc[-1]
|
|
|
|
|
+
|
|
|
|
|
+ # 获取四条均线价格和今日最高最低价
|
|
|
|
|
+ ma5 = latest_data['MA5']
|
|
|
|
|
+ ma10 = latest_data['MA10']
|
|
|
|
|
+ ma20 = latest_data['MA20']
|
|
|
|
|
+ ma30 = latest_data['MA30']
|
|
|
|
|
+ today_high = latest_data['high']
|
|
|
|
|
+ today_low = latest_data['low']
|
|
|
|
|
+
|
|
|
|
|
+ # 检查是否有NaN值
|
|
|
|
|
+ if pd.isna(ma5) or pd.isna(ma10) or pd.isna(ma20) or pd.isna(ma30):
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
+ # 获取开仓时被穿越的均线信息
|
|
|
|
|
+ signal_info = trade_info.get('signal_info', {})
|
|
|
|
|
+ crossed_ma_names = signal_info.get('crossed_ma_names', ['MA5', 'MA10', 'MA20', 'MA30'])
|
|
|
|
|
+
|
|
|
|
|
+ # 根据方向确定止损均线,需要过滤掉不符合条件的均线
|
|
|
|
|
+ mas = [ma5, ma10, ma20, ma30]
|
|
|
|
|
+ ma_names = ['MA5', 'MA10', 'MA20', 'MA30']
|
|
|
|
|
+
|
|
|
|
|
+ # 第一步:只考虑被穿越的均线
|
|
|
|
|
+ crossed_mas = []
|
|
|
|
|
+ crossed_ma_prices = []
|
|
|
|
|
+ for i, ma_name in enumerate(ma_names):
|
|
|
|
|
+ if ma_name in crossed_ma_names:
|
|
|
|
|
+ crossed_mas.append(ma_name)
|
|
|
|
|
+ crossed_ma_prices.append(mas[i])
|
|
|
|
|
+
|
|
|
|
|
+ log.debug(f"开仓时被穿越的均线: {crossed_ma_names}")
|
|
|
|
|
+
|
|
|
|
|
+ if direction == 'long':
|
|
|
|
|
+ # 多仓:在被穿越的均线中,选择价格低于今日最高价的均线
|
|
|
|
|
+ valid_mas = []
|
|
|
|
|
+ valid_ma_names = []
|
|
|
|
|
+ for i, ma_name in enumerate(crossed_mas):
|
|
|
|
|
+ ma_price = crossed_ma_prices[i]
|
|
|
|
|
+ if ma_price <= today_high:
|
|
|
|
|
+ valid_mas.append(ma_price)
|
|
|
|
|
+ valid_ma_names.append(ma_name)
|
|
|
|
|
+
|
|
|
|
|
+ if not valid_mas:
|
|
|
|
|
+ # 如果被穿越的均线都高于今日最高价,使用被穿越均线中的最低价
|
|
|
|
|
+ if crossed_ma_prices:
|
|
|
|
|
+ stop_ma_price = min(crossed_ma_prices)
|
|
|
|
|
+ stop_ma_name = crossed_mas[crossed_ma_prices.index(stop_ma_price)]
|
|
|
|
|
+ log.warning(f"多仓被穿越均线都高于今日最高价{today_high:.2f},使用被穿越均线中最低的")
|
|
|
|
|
+ else:
|
|
|
|
|
+ # 如果没有被穿越均线信息,使用所有均线中的最低价
|
|
|
|
|
+ stop_ma_price = min(mas)
|
|
|
|
|
+ stop_ma_name = ma_names[mas.index(stop_ma_price)]
|
|
|
|
|
+ log.warning(f"多仓无被穿越均线信息,使用所有均线中最低的")
|
|
|
|
|
+ else:
|
|
|
|
|
+ # 多仓跟踪符合条件的被穿越均线中价格最高的
|
|
|
|
|
+ stop_ma_price = max(valid_mas)
|
|
|
|
|
+ stop_ma_name = valid_ma_names[valid_mas.index(stop_ma_price)]
|
|
|
|
|
+ else:
|
|
|
|
|
+ # 空仓:在被穿越的均线中,选择价格高于今日最低价的均线
|
|
|
|
|
+ valid_mas = []
|
|
|
|
|
+ valid_ma_names = []
|
|
|
|
|
+ for i, ma_name in enumerate(crossed_mas):
|
|
|
|
|
+ ma_price = crossed_ma_prices[i]
|
|
|
|
|
+ if ma_price >= today_low:
|
|
|
|
|
+ valid_mas.append(ma_price)
|
|
|
|
|
+ valid_ma_names.append(ma_name)
|
|
|
|
|
+
|
|
|
|
|
+ if not valid_mas:
|
|
|
|
|
+ # 如果被穿越的均线都低于今日最低价,使用被穿越均线中的最高价
|
|
|
|
|
+ if crossed_ma_prices:
|
|
|
|
|
+ stop_ma_price = max(crossed_ma_prices)
|
|
|
|
|
+ stop_ma_name = crossed_mas[crossed_ma_prices.index(stop_ma_price)]
|
|
|
|
|
+ log.warning(f"空仓被穿越均线都低于今日最低价{today_low:.2f},使用被穿越均线中最高的")
|
|
|
|
|
+ else:
|
|
|
|
|
+ # 如果没有被穿越均线信息,使用所有均线中的最高价
|
|
|
|
|
+ stop_ma_price = max(mas)
|
|
|
|
|
+ stop_ma_name = ma_names[mas.index(stop_ma_price)]
|
|
|
|
|
+ log.warning(f"空仓无被穿越均线信息,使用所有均线中最高的")
|
|
|
|
|
+ else:
|
|
|
|
|
+ # 空仓跟踪符合条件的被穿越均线中价格最低的
|
|
|
|
|
+ stop_ma_price = min(valid_mas)
|
|
|
|
|
+ stop_ma_name = valid_ma_names[valid_mas.index(stop_ma_price)]
|
|
|
|
|
+
|
|
|
|
|
+ log.debug(f"今日高低价: {today_high:.2f}/{today_low:.2f}, 被穿越均线: {crossed_ma_names}, 选择止损均线: {stop_ma_name}({stop_ma_price:.2f})")
|
|
|
|
|
+ log.debug(f"所有均线: MA5={ma5:.2f}, MA10={ma10:.2f}, MA20={ma20:.2f}, MA30={ma30:.2f}")
|
|
|
|
|
+ # 计算止损均线的收益水平
|
|
|
|
|
+ underlying_symbol = security.split('.')[0][:-4]
|
|
|
|
|
+ multiplier = get_multiplier(underlying_symbol)
|
|
|
|
|
+ position_value = abs(position.total_amount) * stop_ma_price * multiplier
|
|
|
|
|
+
|
|
|
|
|
+ if direction == 'long':
|
|
|
|
|
+ ma_profit = (stop_ma_price - entry_price) * multiplier * abs(position.total_amount)
|
|
|
|
|
+ else:
|
|
|
|
|
+ ma_profit = (entry_price - stop_ma_price) * multiplier * abs(position.total_amount)
|
|
|
|
|
+
|
|
|
|
|
+ # 获取跳空信息
|
|
|
|
|
+ gap_info = g.gap_check_results.get(security, {'has_gap': False})
|
|
|
|
|
+ has_gap = gap_info['has_gap']
|
|
|
|
|
+
|
|
|
|
|
+ # 判断是否盘尾
|
|
|
|
|
+ current_time = context.current_dt.strftime('%H:%M:%S')
|
|
|
|
|
+ is_market_close = current_time in g.market_close_times
|
|
|
|
|
+
|
|
|
|
|
+ # 确定止损比例
|
|
|
|
|
+ stop_ratio = get_tracking_stop_ratio(ma_profit, has_gap, is_market_close)
|
|
|
|
|
+
|
|
|
|
|
+ # 计算止损价格
|
|
|
|
|
+ if direction == 'long':
|
|
|
|
|
+ stop_price = stop_ma_price * (1 - stop_ratio)
|
|
|
|
|
+ should_stop = current_price <= stop_price
|
|
|
|
|
+ else:
|
|
|
|
|
+ stop_price = stop_ma_price * (1 + stop_ratio)
|
|
|
|
|
+ should_stop = current_price >= stop_price
|
|
|
|
|
+
|
|
|
|
|
+ if should_stop:
|
|
|
|
|
+ log.info(f"触发跟踪均线止损 {security} {direction}")
|
|
|
|
|
+ log.info(f"止损均线价格: {stop_ma_price:.2f}, 方向: {direction}, 收益: {ma_profit:.0f}, 跳空: {has_gap}, 盘尾: {is_market_close}")
|
|
|
|
|
+ log.info(f"止损比例: {stop_ratio:.4f}, 止损价格: {stop_price:.2f}, 当前价格: {current_price:.2f}")
|
|
|
|
|
+ close_position(context, security, direction)
|
|
|
|
|
+ return True
|
|
|
|
|
+
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
+ except Exception as e:
|
|
|
|
|
+ log.warning(f"检查跟踪均线止损时出错 {security}: {str(e)}")
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
+def get_tracking_stop_ratio(ma_profit, has_gap, is_market_close):
|
|
|
|
|
+ """
|
|
|
|
|
+ 根据均线收益、跳空情况、盘中盘尾确定止损比例
|
|
|
|
|
+ :param ma_profit: 止损均线的收益水平
|
|
|
|
|
+ :param has_gap: 是否跳空
|
|
|
|
|
+ :param is_market_close: 是否盘尾
|
|
|
|
|
+ :return: 止损比例
|
|
|
|
|
+ """
|
|
|
|
|
+ # 根据收益水平分区
|
|
|
|
|
+ if ma_profit < g.profit_thresholds[0]: # 收益 < 5000
|
|
|
|
|
+ if is_market_close: # 盘尾
|
|
|
|
|
+ if has_gap:
|
|
|
|
|
+ return g.stop_ratios[0] # 0.25%
|
|
|
|
|
+ else:
|
|
|
|
|
+ return g.stop_ratios[1] # 0.5%
|
|
|
|
|
+ else: # 盘中
|
|
|
|
|
+ if has_gap:
|
|
|
|
|
+ return g.stop_ratios[1] # 0.5%
|
|
|
|
|
+ else:
|
|
|
|
|
+ return g.stop_ratios[2] # 1%
|
|
|
|
|
+ elif ma_profit < g.profit_thresholds[1]: # 5000 <= 收益 < 15000
|
|
|
|
|
+ if is_market_close: # 盘尾
|
|
|
|
|
+ return g.stop_ratios[1] # 0.5%
|
|
|
|
|
+ else: # 盘中
|
|
|
|
|
+ return g.stop_ratios[2] # 1%
|
|
|
|
|
+ else: # 收益 >= 15000
|
|
|
|
|
+ if is_market_close: # 盘尾
|
|
|
|
|
+ return g.stop_ratios[2] # 1%
|
|
|
|
|
+ else: # 盘中
|
|
|
|
|
+ return g.stop_ratios[3] # 2%
|
|
|
|
|
+
|
|
|
############################ 辅助函数 ###################################
|
|
############################ 辅助函数 ###################################
|
|
|
|
|
|
|
|
|
|
+def get_futures_config(underlying_symbol, config_key=None, default_value=None):
|
|
|
|
|
+ """
|
|
|
|
|
+ 获取期货品种配置信息的辅助函数
|
|
|
|
|
+ :param underlying_symbol: 品种符号,如 'AU', 'PF' 等
|
|
|
|
|
+ :param config_key: 配置键,如 'multiplier', 'has_night_session' 等
|
|
|
|
|
+ :param default_value: 默认值
|
|
|
|
|
+ :return: 配置值或整个配置字典
|
|
|
|
|
+ """
|
|
|
|
|
+ if underlying_symbol not in g.futures_config:
|
|
|
|
|
+ if config_key and default_value is not None:
|
|
|
|
|
+ return default_value
|
|
|
|
|
+ return {}
|
|
|
|
|
+
|
|
|
|
|
+ if config_key is None:
|
|
|
|
|
+ return g.futures_config[underlying_symbol]
|
|
|
|
|
+
|
|
|
|
|
+ return g.futures_config[underlying_symbol].get(config_key, default_value)
|
|
|
|
|
+
|
|
|
|
|
+def get_margin_rate(underlying_symbol, direction, default_rate=0.10):
|
|
|
|
|
+ """
|
|
|
|
|
+ 获取保证金比例的辅助函数
|
|
|
|
|
+ :param underlying_symbol: 品种符号
|
|
|
|
|
+ :param direction: 方向 'long' 或 'short'
|
|
|
|
|
+ :param default_rate: 默认保证金比例
|
|
|
|
|
+ :return: 保证金比例
|
|
|
|
|
+ """
|
|
|
|
|
+ return g.futures_config.get(underlying_symbol, {}).get('margin_rate', {}).get(direction, default_rate)
|
|
|
|
|
+
|
|
|
|
|
+def get_multiplier(underlying_symbol, default_multiplier=10):
|
|
|
|
|
+ """
|
|
|
|
|
+ 获取合约乘数的辅助函数
|
|
|
|
|
+ :param underlying_symbol: 品种符号
|
|
|
|
|
+ :param default_multiplier: 默认合约乘数
|
|
|
|
|
+ :return: 合约乘数
|
|
|
|
|
+ """
|
|
|
|
|
+ return g.futures_config.get(underlying_symbol, {}).get('multiplier', default_multiplier)
|
|
|
|
|
+
|
|
|
def calculate_order_value(context, security, direction):
|
|
def calculate_order_value(context, security, direction):
|
|
|
"""计算开仓金额"""
|
|
"""计算开仓金额"""
|
|
|
current_price = get_current_data()[security].last_price
|
|
current_price = get_current_data()[security].last_price
|
|
|
underlying_symbol = security.split('.')[0][:-4]
|
|
underlying_symbol = security.split('.')[0][:-4]
|
|
|
|
|
|
|
|
# 使用保证金比例(已经过校准)
|
|
# 使用保证金比例(已经过校准)
|
|
|
- margin_rate = g.margin_rates.get(direction, {}).get(underlying_symbol, 0.10)
|
|
|
|
|
|
|
+ margin_rate = get_margin_rate(underlying_symbol, direction)
|
|
|
|
|
|
|
|
- multiplier = g.multiplier.get(underlying_symbol, 10)
|
|
|
|
|
|
|
+ multiplier = get_multiplier(underlying_symbol)
|
|
|
|
|
|
|
|
# 计算单手保证金
|
|
# 计算单手保证金
|
|
|
single_hand_margin = current_price * multiplier * margin_rate
|
|
single_hand_margin = current_price * multiplier * margin_rate
|
|
|
|
|
|
|
|
# 还要考虑可用资金限制
|
|
# 还要考虑可用资金限制
|
|
|
available_cash = context.portfolio.available_cash * g.usage_percentage
|
|
available_cash = context.portfolio.available_cash * g.usage_percentage
|
|
|
- log.info(f"可用资金: {available_cash:.0f}")
|
|
|
|
|
|
|
+ log.debug(f"可用资金: {available_cash:.0f}")
|
|
|
|
|
|
|
|
# 根据单个标的最大持仓保证金限制计算开仓数量
|
|
# 根据单个标的最大持仓保证金限制计算开仓数量
|
|
|
max_margin = g.max_margin_per_position
|
|
max_margin = g.max_margin_per_position
|
|
@@ -986,7 +1370,7 @@ def calculate_order_value(context, security, direction):
|
|
|
# 计算订单金额
|
|
# 计算订单金额
|
|
|
order_value = actual_margin
|
|
order_value = actual_margin
|
|
|
|
|
|
|
|
- log.info(f"单手保证金: {single_hand_margin:.0f}, 最大手数(保证金限制): {max_hands}, 最大手数(资金限制): {max_hands_by_cash}, 实际开仓手数: {actual_hands}, 实际保证金: {actual_margin:.0f}")
|
|
|
|
|
|
|
+ log.debug(f"单手保证金: {single_hand_margin:.0f}, 最大手数(保证金限制): {max_hands}, 最大手数(资金限制): {max_hands_by_cash}, 实际开仓手数: {actual_hands}, 实际保证金: {actual_margin:.0f}")
|
|
|
else:
|
|
else:
|
|
|
# 如果单手保证金超过最大限制,默认开仓1手
|
|
# 如果单手保证金超过最大限制,默认开仓1手
|
|
|
actual_hands = 1
|
|
actual_hands = 1
|
|
@@ -995,9 +1379,9 @@ def calculate_order_value(context, security, direction):
|
|
|
# 计算订单金额
|
|
# 计算订单金额
|
|
|
order_value = actual_margin
|
|
order_value = actual_margin
|
|
|
|
|
|
|
|
- log.info(f"单手保证金: {single_hand_margin:.0f} 超过最大限制: {max_margin}, 默认开仓1手, 实际保证金: {actual_margin:.0f}")
|
|
|
|
|
|
|
+ log.debug(f"单手保证金: {single_hand_margin:.0f} 超过最大限制: {max_margin}, 默认开仓1手, 实际保证金: {actual_margin:.0f}")
|
|
|
|
|
|
|
|
- log.info(f"计算结果 - 品种: {underlying_symbol}, 开仓手数: {actual_hands}, 订单金额: {order_value:.0f}, 实际保证金: {actual_margin:.0f}")
|
|
|
|
|
|
|
+ log.debug(f"计算结果 - 品种: {underlying_symbol}, 开仓手数: {actual_hands}, 订单金额: {order_value:.0f}, 实际保证金: {actual_margin:.0f}")
|
|
|
|
|
|
|
|
return order_value
|
|
return order_value
|
|
|
|
|
|
|
@@ -1006,8 +1390,8 @@ def calculate_required_margin(context, symbol):
|
|
|
current_price = get_current_data()[symbol].last_price
|
|
current_price = get_current_data()[symbol].last_price
|
|
|
underlying_symbol = symbol.split('.')[0][:-4]
|
|
underlying_symbol = symbol.split('.')[0][:-4]
|
|
|
|
|
|
|
|
- margin_rate = g.margin_rates.get('long', {}).get(underlying_symbol, 0.10)
|
|
|
|
|
- multiplier = g.multiplier.get(underlying_symbol, 10)
|
|
|
|
|
|
|
+ margin_rate = get_margin_rate(underlying_symbol, 'long')
|
|
|
|
|
+ multiplier = get_multiplier(underlying_symbol)
|
|
|
|
|
|
|
|
return current_price * multiplier * margin_rate
|
|
return current_price * multiplier * margin_rate
|
|
|
|
|
|
|
@@ -1033,7 +1417,6 @@ def after_market_close(context):
|
|
|
# 清空当日交易记录
|
|
# 清空当日交易记录
|
|
|
g.today_trades = []
|
|
g.today_trades = []
|
|
|
|
|
|
|
|
- log.info('A day ends')
|
|
|
|
|
log.info('##############################################################')
|
|
log.info('##############################################################')
|
|
|
|
|
|
|
|
def print_daily_trading_summary(context):
|
|
def print_daily_trading_summary(context):
|
|
@@ -1041,34 +1424,34 @@ def print_daily_trading_summary(context):
|
|
|
if not g.today_trades:
|
|
if not g.today_trades:
|
|
|
return
|
|
return
|
|
|
|
|
|
|
|
- log.info("\n=== 当日交易汇总 ===")
|
|
|
|
|
|
|
+ log.debug("\n=== 当日交易汇总 ===")
|
|
|
total_margin = 0
|
|
total_margin = 0
|
|
|
|
|
|
|
|
for trade in g.today_trades:
|
|
for trade in g.today_trades:
|
|
|
if trade['order_amount'] > 0: # 开仓
|
|
if trade['order_amount'] > 0: # 开仓
|
|
|
- log.info(f"开仓 {trade['underlying_symbol']} {trade['direction']} {trade['order_amount']}手 "
|
|
|
|
|
|
|
+ log.debug(f"开仓 {trade['underlying_symbol']} {trade['direction']} {trade['order_amount']}手 "
|
|
|
f"价格:{trade['order_price']:.2f} 保证金:{trade['cash_change']:.0f} "
|
|
f"价格:{trade['order_price']:.2f} 保证金:{trade['cash_change']:.0f} "
|
|
|
f"比例:{trade['actual_margin_rate']:.4f}")
|
|
f"比例:{trade['actual_margin_rate']:.4f}")
|
|
|
total_margin += trade['cash_change']
|
|
total_margin += trade['cash_change']
|
|
|
else: # 平仓
|
|
else: # 平仓
|
|
|
- log.info(f"平仓 {trade['underlying_symbol']} {trade['direction']} {abs(trade['order_amount'])}手 "
|
|
|
|
|
|
|
+ log.debug(f"平仓 {trade['underlying_symbol']} {trade['direction']} {abs(trade['order_amount'])}手 "
|
|
|
f"价格:{trade['order_price']:.2f}")
|
|
f"价格:{trade['order_price']:.2f}")
|
|
|
|
|
|
|
|
- log.info(f"当日保证金占用: {total_margin:.0f}")
|
|
|
|
|
- log.info("==================\n")
|
|
|
|
|
|
|
+ log.debug(f"当日保证金占用: {total_margin:.0f}")
|
|
|
|
|
+ log.debug("==================\n")
|
|
|
|
|
|
|
|
def print_margin_rate_changes(context):
|
|
def print_margin_rate_changes(context):
|
|
|
"""打印保证金比例变化记录"""
|
|
"""打印保证金比例变化记录"""
|
|
|
if not g.margin_rate_history:
|
|
if not g.margin_rate_history:
|
|
|
return
|
|
return
|
|
|
|
|
|
|
|
- log.info("\n=== 保证金比例变化记录 ===")
|
|
|
|
|
|
|
+ log.debug("\n=== 保证金比例变化记录 ===")
|
|
|
for key, history in g.margin_rate_history.items():
|
|
for key, history in g.margin_rate_history.items():
|
|
|
- log.info(f"{key}:")
|
|
|
|
|
|
|
+ log.debug(f"{key}:")
|
|
|
for record in history:
|
|
for record in history:
|
|
|
- log.info(f" {record['date']} {record['time']}: {record['old_rate']:.4f} -> {record['new_rate']:.4f} "
|
|
|
|
|
|
|
+ log.debug(f" {record['date']} {record['time']}: {record['old_rate']:.4f} -> {record['new_rate']:.4f} "
|
|
|
f"(变化{record['change_pct']:.1f}%)")
|
|
f"(变化{record['change_pct']:.1f}%)")
|
|
|
- log.info("========================\n")
|
|
|
|
|
|
|
+ log.debug("========================\n")
|
|
|
|
|
|
|
|
########################## 自动移仓换月函数 #################################
|
|
########################## 自动移仓换月函数 #################################
|
|
|
def position_auto_switch(context,pindex=0,switch_func=None, callback=None):
|
|
def position_auto_switch(context,pindex=0,switch_func=None, callback=None):
|