《选股因子系列研究(六十四)——基于直观逻辑和机器学习的高频数据低频化应用》 报告核心内容总结
这份 Python 代码提供了报告中核心因子计算思路的实现框架。在实际应用中,需要根据具体的数据源、精确的指标定义以及计算效率要求进行调整和优化。这里我们根据报告表10中的公式,用Pandas实现其中几个Alpha因子的计算逻辑。这需要日度聚合后的分钟数据和滚动窗口操作。ML因子的计算(尤其是Alpha2等涉及乘积波动的)可能更复杂,并且。这个指标可能需要从TICK级委托数据计算得到,然后聚合到分
报告核心内容总结 (Report Core Summary)
这份报告的核心目标是探索如何 融合不同类型的高频数据(分钟级成交、TICK级委托、逐笔成交),并将其 低频化(日度/月度),以构建有效的选股因子。报告主要从两个角度出发:
- 直观逻辑驱动 (Intuitive Logic Driven): 结合委**托挂单(未实现意愿)和逐笔成交(已实现行为)**信息,构建“买入意愿”类因子,旨在更全面地刻画投资者的交易意愿。
- 机器学习驱动 (Machine Learning Driven): 利用机器学习(特别是特征工程)方法,从融合后的高频数据中自动挖掘新的选股因子 (Alpha因子)。
主要发现 (Key Findings):
- 买入意愿因子有效性:
- 结合了委托变动和主动买入的“买入意愿”指标被提出。
- 基于此构建的“买入意愿占比”和“日内买入意愿强度”因子(尤其是在开盘时段计算)表现出显著的月度选股能力 (IC > 0.03, ICIR > 2.5/3.0, 胜率 > 80%)。
- 与仅使用逐笔成交构建的“净主买”类因子相比,“买入意愿”类因子在选股效果上有明显提升 (IC、ICIR、多空收益、多头收益均改善)。
- 机器学习挖掘因子有效性:
- 报告通过机器学习挖掘出多个Alpha因子 (Alpha1-Alpha6),这些因子即使在剔除常规低频因子影响后仍有显著选股能力。
- 进一步剔除其他已知高频因子的影响后,这些ML因子的选股能力更为突出 (IC > 0.04, ICIR 接近 3.0, 多空收益 > 1.5%)。
- 部分ML因子具有一定的逻辑可解释性(如Alpha1衡量买入额波动率,Alpha4/Alpha6衡量特定时段成交额的相对强弱)。
- 因子组合改进:
- 将新构建的“买入意愿”类因子或机器学习挖掘的Alpha因子加入传统多因子模型(如中证500增强),能够带来组合表现的改善(提升超额收益)。
- 部分因子(如开盘后日内买入意愿强度、Alpha_2、Alpha_4)带来的收益提升更显著。
- 同时引入逻辑因子和ML因子(利用它们之间较低的相关性)可能进一步提升模型的稳健性。
目录 (Table of Contents)
- 报告核心总结 (Report Core Summary)
- 主要发现 (Key Findings)
- 数据准备与假设 (Data Preparation & Assumptions)
- Python 实现: 逻辑驱动因子 (Python Implementation: Logic-Driven Factors)
- 4.1 计算分钟级“买入意愿” (Calculating Minute-level “Purchase Intention”)
- 4.2 计算日度“买入意愿占比” (Calculating Daily “Purchase Intention Ratio”)
- 4.3 计算日度“日内买入意愿强度” (Calculating Daily “Intraday Purchase Intention Strength”)
- 4.4 因子计算主函数 (Main Factor Calculation Function)
- Python 实现: 机器学习因子示例 (Python Implementation: Machine Learning Factor Examples)
- 5.1 Alpha_1 因子计算 (Alpha_1 Factor Calculation)
- 5.2 Alpha_4 因子计算 (Alpha_4 Factor Calculation)
- 5.3 Alpha_6 因子计算 (Alpha_6 Factor Calculation)
- 5.4 因子计算主函数 (Main Factor Calculation Function)
- 因子评估与应用 (Factor Evaluation & Application)
- 6.1 IC/ICIR 计算 (IC/ICIR Calculation)
- 6.2 因子正交化 (Factor Orthogonalization)
- 6.3 因子组合 (Factor Combination)
- 风险提示 (Risk Warning)
- 开源库选择 (Choice of Open-Source Libraries)
3. 数据准备与假设 (Data Preparation & Assumptions)
- 输入数据: 需要分钟级别的行情和交易数据。理想情况下,应包含:
- 时间戳 (Timestamp)
- 股票代码 (Stock Code)
- 成交额 (Turnover / Amount)
- 主动买入额 (Active Buy Amount)
- 主动卖出额 (Active Sell Amount)
- 净委买变动额 (Net Order Change Amount) - 这个指标可能需要从TICK级委托数据计算得到,然后聚合到分钟
- 大单买入额 (Large Order Buy Amount) - 定义可能需要明确
- 数据格式: 使用 Pandas DataFrame 进行处理。建议使用 MultiIndex (date, stock_code, minute_timestamp)。
- 时间段定义 (根据报告):
- 全天 (Full Day): 09:30 - 14:56 (或根据实际交易时间调整)
- 开盘后 (Opening): 09:30 - 09:59
- 盘中 (Mid-day): 10:00 - 14:26
- 收盘前 (Closing): 14:27 - 14:56
- 假设:
- 所需的高频字段已预处理并聚合至分钟级别。
- “净委买变动额” 指的是特定分钟内买一到买五(或更多档位)委托量变动对应的金额估算,减去卖一到卖五委托量变动金额估算。其精确计算依赖TICK数据。
- “净主动买入额” = 主动买入额 - 主动卖出额。
- 因子计算的回看期 (如rolling操作) 根据报告设定(例如ML因子中的20天)。
4. Python 实现: 逻辑驱动因子 (Python Implementation: Logic-Driven Factors)
import pandas as pd
import numpy as np
# 假设 df_minute 是包含分钟数据的 DataFrame
# 列应包括: 'timestamp', 'stock_code', 'turnover', 'net_order_change', 'net_active_buy_value'
# timestamp 应为 datetime 对象
# --- 4.1 计算分钟级“买入意愿” ---
def calculate_minute_purchase_intention(df_minute):
"""计算每分钟的买入意愿"""
# 报告公式:买入意愿 = 净委买变化额 + 净主动买入额
# 假设 'net_order_change' 和 'net_active_buy_value' 列已存在
df_minute['purchase_intention'] = df_minute['net_order_change'].fillna(0) + \
df_minute['net_active_buy_value'].fillna(0)
return df_minute
# --- 4.2 计算日度“买入意愿占比” ---
def calculate_daily_intention_ratio(daily_group):
"""计算单个股票单日的买入意愿占比"""
total_intention = daily_group['purchase_intention'].sum()
total_turnover = daily_group['turnover'].sum()
if total_turnover > 0:
return total_intention / total_turnover
else:
return np.nan
# --- 4.3 计算日度“日内买入意愿强度” ---
def calculate_daily_intention_strength(daily_group):
"""计算单个股票单日的日内买入意愿强度"""
mean_intention = daily_group['purchase_intention'].mean()
std_intention = daily_group['purchase_intention'].std()
# 防止除以零或接近零的标准差
if std_intention is not None and std_intention > 1e-6:
return mean_intention / std_intention
else:
return np.nan
# --- 4.4 因子计算主函数 ---
def calculate_logical_factors_daily(df_minute, period='full_day'):
"""
计算指定时间段内的逻辑因子日度值
Args:
df_minute (pd.DataFrame): 包含 'timestamp', 'stock_code', 'turnover',
'net_order_change', 'net_active_buy_value' 的分钟数据.
period (str): 时间段 ('full_day', 'opening', 'mid_day', 'closing').
Returns:
pd.DataFrame: 包含 'date', 'stock_code', 'intention_ratio', 'intention_strength' 的日度因子.
"""
df = df_minute.copy()
df['date'] = df['timestamp'].dt.date
df['time'] = df['timestamp'].dt.time
# 根据时间段筛选数据
if period == 'opening':
df = df[df['time'] <= pd.Timestamp('09:59:00').time()]
elif period == 'mid_day':
df = df[(df['time'] >= pd.Timestamp('10:00:00').time()) &
(df['time'] <= pd.Timestamp('14:26:00').time())]
elif period == 'closing':
df = df[df['time'] >= pd.Timestamp('14:27:00').time()]
elif period != 'full_day':
raise ValueError("Invalid period specified")
# 计算分钟意愿
df = calculate_minute_purchase_intention(df)
# 按日期和股票代码分组计算日度因子
grouped = df.groupby(['date', 'stock_code'])
daily_factors = pd.DataFrame({
f'intention_ratio_{period}': grouped.apply(calculate_daily_intention_ratio),
f'intention_strength_{period}': grouped.apply(calculate_daily_intention_strength)
}).reset_index()
return daily_factors
# --- 示例 ---
# 假设已有分钟数据 df_minute_data
# factor_opening = calculate_logical_factors_daily(df_minute_data, period='opening')
# factor_full_day = calculate_logical_factors_daily(df_minute_data, period='full_day')
# ... 合并不同周期的因子 ...
5. Python 实现: 机器学习因子示例 (Python Implementation: Machine Learning Factor Examples)
这里我们根据报告表10中的公式,用Pandas实现其中几个Alpha因子的计算逻辑。这需要日度聚合后的分钟数据和滚动窗口操作。
import pandas as pd
import numpy as np
# 假设 df_daily_features 是包含日度聚合特征的 DataFrame
# 需要 MultiIndex (date, stock_code)
# 列应包括: 'active_buy_amount_sum_mid', 'turnover_sum_med',
# 'net_active_buy_value_sum_end', 'turnover_sum_full',
# 'active_buy_amount_sum_open', 'active_buy_amount_sum_end',
# 'turnover_sum_open', 'turnover_sum_end'
# 这些日度特征需要先从分钟数据按不同时间段聚合得到 (sum 或其他聚合方式)
# --- 辅助函数:获取不同时间段的日度聚合值 ---
def get_daily_agg_features(df_minute):
"""从分钟数据计算所需的日度聚合特征"""
df = df_minute.copy()
df['date'] = df['timestamp'].dt.date
df['time'] = df['timestamp'].dt.time
# 定义时间段
is_opening = (df['time'] <= pd.Timestamp('09:59:00').time())
is_mid_day = (df['time'] >= pd.Timestamp('10:00:00').time()) & \
(df['time'] <= pd.Timestamp('14:26:00').time())
is_closing = (df['time'] >= pd.Timestamp('14:27:00').time())
# 确保必要的列存在,若不存在则设为0(根据实际情况调整)
required_cols = ['turnover', 'active_buy_amount', 'net_active_buy_value', 'large_order_buy_amount']
for col in required_cols:
if col not in df.columns:
df[col] = 0 # 或者根据实际数据来源处理
grouped = df.groupby(['date', 'stock_code'])
# 聚合计算 - 注意'主买额'可能指'active_buy_amount'或'net_active_buy_value',根据实际定义
# '成交额' 指 'turnover'
# 这里以 sum 为例进行聚合,具体聚合方式需核对因子原始定义
features = pd.DataFrame({
'turnover_sum_full': grouped['turnover'].sum(),
'active_buy_amount_sum_mid': grouped.apply(lambda x: x.loc[is_mid_day[x.index], 'active_buy_amount'].sum()),
'turnover_sum_med': grouped.apply(lambda x: x.loc[is_mid_day[x.index], 'turnover'].sum()),
'net_active_buy_value_sum_end': grouped.apply(lambda x: x.loc[is_closing[x.index], 'net_active_buy_value'].sum()),
'turnover_sum_open': grouped.apply(lambda x: x.loc[is_opening[x.index], 'turnover'].sum()),
'turnover_sum_end': grouped.apply(lambda x: x.loc[is_closing[x.index], 'turnover'].sum()),
# Alpha2 需要 大单买入额 * 开盘成交额 的波动率,这个计算更复杂,需要先算分钟乘积再日聚合
# 'large_buy_X_turnover_open_minute_product': df['large_order_buy_amount'] * df['turnover'],
# ... 然后聚合这个乘积序列 ...
})
# 为了简化后续因子计算,将列名对应报告中的含义
features = features.rename(columns={
'active_buy_amount_sum_mid': 'period_med(主买额)',
'turnover_sum_med': 'period_med(成交额)',
'net_active_buy_value_sum_end': 'period_end(净主买额)',
'turnover_sum_full': '成交额', # 假设 Alpha4 的成交额指全天
'turnover_sum_open': 'period_beg(成交额)',
'turnover_sum_end': 'period_end(成交额)',
})
return features
# --- 5.1 Alpha_1 因子计算 ---
# Alpha1 = neg(log(roll_std(period_med(主买额))))
def calculate_alpha1(daily_features, window=20):
"""计算 Alpha1 因子"""
# 计算 period_med(主买额) 的 20 日滚动标准差
# groupby().rolling() 需要时间序列索引是单调递增的
# 需要先 unstack,计算 rolling,再 stack 回来
feat = daily_features['period_med(主买额)'].unstack()
rolling_std = feat.rolling(window=window, min_periods=max(1, window // 2)).std() # 设置最小期数
alpha1_unstacked = -np.log(rolling_std + 1e-9) # 加小量防止log(0)
alpha1 = alpha1_unstacked.stack().rename('Alpha_1')
return alpha1
# --- 5.2 Alpha_4 因子计算 ---
# Alpha4 = neg(log(roll_mean(成交额 - period_med(成交额))))
def calculate_alpha4(daily_features, window=20):
"""计算 Alpha4 因子"""
# 计算 (全天成交额 - 盘中成交额)
diff = daily_features['成交额'] - daily_features['period_med(成交额)']
# 计算 diff 的 20 日滚动均值
diff_unstacked = diff.unstack()
rolling_mean = diff_unstacked.rolling(window=window, min_periods=max(1, window // 2)).mean()
alpha4_unstacked = -np.log(rolling_mean + 1e-9) # 加小量防止负数或零
# 注意:如果 rolling_mean 可能为负,log 会出错,需要处理,例如取绝对值或符号处理
# alpha4_unstacked = -np.sign(rolling_mean) * np.log(np.abs(rolling_mean) + 1e-9)
alpha4 = alpha4_unstacked.stack().rename('Alpha_4')
return alpha4
# --- 5.3 Alpha_6 因子计算 ---
# Alpha6 = neg(roll_sum(period_beg(成交额) + period_end(成交额)) / roll_sum(period_med(成交额)))
def calculate_alpha6(daily_features, window=20):
"""计算 Alpha6 因子"""
# 开盘+收盘成交额
open_close_turnover = daily_features['period_beg(成交额)'] + daily_features['period_end(成交额)']
mid_turnover = daily_features['period_med(成交额)']
# 计算滚动和
open_close_unstacked = open_close_turnover.unstack()
mid_unstacked = mid_turnover.unstack()
roll_sum_open_close = open_close_unstacked.rolling(window=window, min_periods=max(1, window // 2)).sum()
roll_sum_mid = mid_unstacked.rolling(window=window, min_periods=max(1, window // 2)).sum()
# 计算比率并取负
ratio_unstacked = roll_sum_open_close / (roll_sum_mid + 1e-9) # 防止除零
alpha6_unstacked = -ratio_unstacked
alpha6 = alpha6_unstacked.stack().rename('Alpha_6')
return alpha6
# --- 5.4 因子计算主函数 ---
def calculate_ml_factors_daily(df_minute, window=20):
"""计算所有(示例)ML因子的日度值"""
print("Aggregating daily features from minute data...")
daily_features = get_daily_agg_features(df_minute)
daily_features = daily_features.sort_index(level='date') # 确保按时间排序
print("Calculating Alpha_1...")
alpha1 = calculate_alpha1(daily_features, window)
print("Calculating Alpha_4...")
alpha4 = calculate_alpha4(daily_features, window)
print("Calculating Alpha_6...")
alpha6 = calculate_alpha6(daily_features, window)
# ... 计算其他 Alpha 因子 ...
# 合并所有因子
all_ml_factors = pd.concat([alpha1, alpha4, alpha6], axis=1) # 添加其他计算出的alpha因子
return all_ml_factors.reset_index()
# --- 示例 ---
# 假设已有分钟数据 df_minute_data
# ml_factors = calculate_ml_factors_daily(df_minute_data, window=20)
注意: ML因子的计算(尤其是Alpha2等涉及乘积波动的)可能更复杂,并且rolling
操作在大型数据集上可能较慢,需要优化。unstack/stack
是处理面板数据滚动计算的常用方法。
6. 因子评估与应用 (Factor Evaluation & Application)
- 6.1 IC/ICIR 计算:
- 需要股票的未来收益率数据 (e.g., T+1日收益率, T+20日收益率)。
- 计算每个截面期 (通常是每天或每月) 因子值与未来收益率的 Spearmann 或 Pearson 相关系数 (即 IC)。
- ICIR =
mean(IC) / std(IC)
。 - 可以使用
scipy.stats.spearmanr
或pandas.Series.corr
。
- 6.2 因子正交化:
- 需要其他基准因子(市值、行业、估值等)的同期因子值。
- 对每个截面期,将待正交化的因子 Y 对基准因子 X1, X2, … 做线性回归:
Y = a + b1*X1 + b2*X2 + ... + residual
。 - 正交化后的因子即为回归的残差 (residual)。
- 可以使用
statsmodels.api.OLS
。
- 6.3 因子组合:
- 将计算得到的因子值(可能经过标准化和正交化处理)输入到多因子模型框架中(如 Barra CNE5 或自定义模型)。
- 根据因子暴露度和预期收益(通常与IC/ICIR相关)进行打分或优化,构建投资组合。
- 回测组合表现,评估超额收益、信息比率、最大回撤等指标。
- 常用的回测框架有
zipline
(略旧但经典),backtrader
,QuantConnect LEAN
, 或商业平台。
7. 风险提示 (Risk Warning)
- 过拟合风险: 机器学习挖掘的因子可能存在过拟合风险,样本外表现可能不及回测。
- 数据质量: 高频数据的清洗和处理至关重要,错误或缺失数据会严重影响因子质量。
- 计算成本: 处理高频数据和进行复杂因子计算(尤其是滚动计算和ML挖掘)需要较高的计算资源。
- 模型衰减: 因子有效性可能随市场环境变化而衰减。
- 逻辑理解: 部分ML因子缺乏直观逻辑,增加了理解和风控的难度。
- 报告中的回测结果是基于特定时期和参数设置,未来表现不确定。
8. 开源库选择 (Choice of Open-Source Libraries)
- 数据处理:
Pandas
,NumPy
(核心库) - 统计与评估:
SciPy
(相关性计算),Statsmodels
(回归/正交化) - 机器学习因子挖掘 (可选):
scikit-learn
(用于特征工程、回归等辅助任务)gplearn
(可用于遗传规划,类似报告中可能使用的挖掘方法之一,但使用较复杂)
- 回测框架 (可选):
backtrader
,Zipline
(维护可能滞后),QuantConnect LEAN
这份 Python 代码提供了报告中核心因子计算思路的实现框架。在实际应用中,需要根据具体的数据源、精确的指标定义以及计算效率要求进行调整和优化。
更多推荐
所有评论(0)