作者:FinTechHi
题图:FinTechHi微信公众号
来自外网一篇文章的翻译,并结合腾讯元宝根据A股实践整理。
一.关于策略
量化研究的核心不是找到 “回测曲线很漂亮” 的策略,
而是找到有经济逻辑支撑、能被检验、可复现的alpha策略,
如果企图在这一步“偷懒”,那后续的量化工作都是一种 “数据挖掘的自我感动”。
所以,我们要构建的是一个:可证伪的交易假设。
普通人的思路:“我有个赚钱的好点子!我要去验证它是对的!”
量化研究员的思路:“我有个可能赚钱的点子。但我先要给自己设置各种‘坑’,看看这个点子会不会掉进这些坑里。”
一个好的交易假设,是策略的根基,必须摆脱笼统表述,满足具体性、可证伪性、经济合理性、有时限性四大核心要求,模糊的假设只会导致后续研究的无方向。(PS:笼统的表述会经常充斥在我们量化研究团队之外的工作中,比如因子好不好,模型准不准,回测收益10%太低了,能不能做到90%盈利等等,每次听到这些问题真不知道怎么回答。)
具体性:拒绝 “动量策略有效” 这类空泛表述,必须明确标的、周期、收益阈值。例如:“沪深 300 成分股中,过去 12 个月(剔除最后 1 个月)收益为正的资产,其未来 1 年收益率比收益为负的资产高出 5%-10%”。
可证伪性:检验前提前定义明确的拒绝标准,避免 “事后找理由”。例如:“若策略扣除交易成本后夏普比率小于0.5,或t统计量小于2.0,则直接拒绝该假设”。
金融逻辑合理性:回答核心问题 “超额收益从何而来”,这是区分 “真正 alpha” 和 “数据噪音” 的关键。需从四大维度寻找逻辑支撑:行为偏差(投资者过度反应、损失厌恶、追涨杀跌)、结构性因素(指数再平衡、监管限制、市场交易规则约束)、风险溢价(套利风险、波动风险、流动性风险溢价)、信息优势(信息扩散速度差异、市场参与者信息不对称)。
有时限性:明确策略有效性的边界,以及优势消失的场景。例如:“该策略的超额收益来自散户对盈利意外的过度反应,若市场中算法交易占比提升、散户参与度下降,这一优势将逐步消失”。
在构建任何因子或信号策略落地前,可以先思考如下 4 个问题,从根源上过滤无逻辑的策略,避免 “为了交易而交易”:
在量化投资中,区分数据挖掘的虚假信号与真正的Alpha至关重要。数据挖掘的预警信号有八大特征:
策略没有合理的经济逻辑支撑,如同不知道为什么赢牌;
参数过度优化,像碰巧打开一个密码锁就宣称掌握了通用密码;
设计过度复杂,违背了“简单策略更稳健”的原则;
只通过样本内测试,如同学生只会做过的题目;
只在特定有利时期有效,无法穿越牛熊;
存在幸存者偏差,只统计存活样本;
依赖无法实现的精确择时;
以及假设不切实际的理想交易条件,忽略了真实的摩擦成本。
与之相对,真正的Alpha策略具备六大核心特征:
具备跨市场的普适性,如同优秀厨师精通多种菜系;
对参数扰动具有鲁棒性,不依赖精确的数字设定;
能适应不同的市场环境,在牛熊市中均能生存;具有可扩展的容量,不会因规模扩大而失效;
拥有清晰且未被充分定价的经济逻辑,而非依赖数据巧合;
以及在充分考虑所有交易成本后仍能持续盈利。
简言之,真正的Alpha源于对市场规律的深刻理解,而非对历史数据的过度拟合。
一个“回测很好”的策略往往是在历史数据里“刻舟求剑”,参数调得刚好,时机抓得完美,但一上实盘就“见光死”。而一个真正能实盘赚钱的策略一定是逻辑清晰,简单稳健,经得起各种考验,实盘扣完成本还能赚钱。
所以在构建一个新策略前,可以先思考几个问题:
同时,也要对自己的策略进行“淘汰更新”
当你管理的策略表现不佳时,首先要看是否触达“五个必须砍”的红线:
实盘跑一年还亏钱(夏普为负)、
实际收益不到回测一半(前向效率<0.5)、
买卖信号完全反了(相关性反转)、
钱一多就赚不到差价(冲击成本吃光利润)、
或者赚钱的逻辑基础已经不存在了(如市场规则变化)。
只要踩中任何一条,必须立即清仓止损,没有商量余地。
如果以上都没问题,但存在以下三种情况,就需要权衡是否放弃了:
策略和现有持仓高度同质(无法分散风险)、
运维成本远超收益(性价比过低)、
或者存在极端尾部风险(平时小赚,危机时巨亏)。
这些属于“可砍可不砍”,取决于基金的整体配置和风险偏好。
记住,淘汰坏策略和发掘好策略同样重要。及时砍掉无效策略,才能释放资源、避免更大的损失。
二.关于数据
量化研究中,数据的质量直接决定策略的成败 —— 前瞻性偏差、幸存者偏差、异常值处理不当,都会让回测结果成为 “空中楼阁”。“数据清理的功夫,藏着最扎实的量化功底”,特别是一些初创私募的数据源往往有限,更需掌握科学的数管理方法,用严谨性弥补数据源的不足。
1.异常值处理
金融数据(尤其是收益数据)天然具有厚尾分布,极端值不是 “噪音”,而是真实的市场行为(如闪崩、盈利意外、公司重大事件),盲目剔除异常值会扭曲数据的真实性,正确的做法是 “标记而非删除,调查而非忽略”。
def detect_and_handle_outliers(df, columns, method='iqr', threshold=3.0):"""检测并处理金融数据中的异常值,核心原则:标记不删除,保留真实市场特征重要提示:切勿盲目删除金融数据中的异常值,极端值是真实市场行为的体现"""import numpy as npimport pandas as pddf_clean = df.copy()outlier_report = {} # 生成异常值报告,便于后续调查for col in columns:if col not in df.columns:continueseries = df[col].dropna()if method == 'iqr':# 四分位距法,适合厚尾分布的金融数据Q1 = series.quantile(0.25)Q3 = series.quantile(0.75)IQR = Q3 - Q1lower = Q1 - threshold * IQRupper = Q3 + threshold * IQRoutliers = (series < lower) | (series > upper)else:# z分数法,适用于近似正态分布的因子数据mean = series.mean()std = series.std()z = np.abs((series - mean) / std)outliers = z > threshold# 标记异常值,不删除df_clean[f'{col}_outlier'] = Falsedf_clean.loc[outliers.index[outliers], f'{col}_outlier'] = True# 生成异常值报告,记录数量、占比、关键日期,便于后续事件调查outlier_report[col] = {'count': outliers.sum(),'pct': outliers.mean() * 100,'dates': series.index[outliers].tolist()[:10]}# 异常值后续处理:结合市场事件调查(如闪崩、财报发布、政策调整)return df_clean, outlier_report
数据缺失不是 “偶然”,往往暗藏市场信号(如公司停牌、业绩披露延迟、退市风险),处理缺失数据的核心是拒绝会引入前瞻偏差的方法,同时重视缺失数据背后的信息。缺失数据处理三大核心规则
可接受的处理方法
比较不科学的做法
2.股票池处理
仅对当前存续的证券(如标普 500、沪深 300 当前成分股)进行测试,会忽略退市、被剔除的标的,这些标的往往具有低收益、高风险特征,会导致策略年收益率被虚高 1-2%,而实盘时这些标的的风险会真实暴露。幸存者偏差四大预防措施建议:
3.时间点数据管理
上市公司财务报表常在首次发布后被重述,若使用重述后的数据进行回测(如4月16日交易却使用了5月20日修订的数据),就构成了严重的“用未来数据决策”。解决方案包括:优先使用特定时间点数据库获取数据原始版本、以实际公布日期而非财报期末日作为纳入策略的时点、并对基本面数据施加保守的滞后(如季度财报后45天再使用),确保策略在实盘中能真正获取到所用数据。
4.特征归一化
需杜绝前瞻性偏差。若使用整个样本期的统计量(如全历史均值、标准差)对因子进行标准化,就等于让策略在回测中“预知”了未来的整体分布,这在实盘中是不可能的。正确做法是严格使用滚动窗口或扩展窗口进行归一化,仅基于历史数据计算统计量,从而完全模拟实盘决策过程。只有守住这两道防线,策略的回测表现才可能真实反映其未来的实盘潜力。
始终使用滚动窗口或扩展窗口进行归一化,仅用历史数据计算统计量,确保与实盘场景一致。参考代码:
def proper_feature_normalization(df, feature_cols, method='zscore', window=252):"""滚动归一化,从根源防止前瞻性偏差关键原则:始终使用扩展窗口或滚动窗口,切勿使用整个样本进行归一化"""import numpy as npimport pandas as pddf_norm = df.copy()for col in feature_cols:if col not in df.columns:continueseries = df[col]if method == 'zscore':# 滚动z分数归一化,消除量纲影响rolling_mean = series.rolling(window, min_periods=window//2).mean()rolling_std = series.rolling(window, min_periods=window//2).std()df_norm[f'{col}_norm'] = (series - rolling_mean) / (rolling_std + 1e-8) # 加小值避免除零elif method == 'rank':# 滚动秩归一化,适用于非正态分布的因子df_norm[f'{col}_norm'] = series.rolling(window).apply(lambda x: (x.iloc[-1] > x[:-1]).mean() if len(x) > 1 else 0.5,raw=False)elif method == 'minmax':# 滚动最大最小值归一化,将因子映射到[0,1]区间rolling_min = series.rolling(window).min()rolling_max = series.rolling(window).max()df_norm[f'{col}_norm'] = (series - rolling_min) / (rolling_max - rolling_min + 1e-8)return df_norm
5.数据平稳性
绝大多数金融时间序列(如价格、交易量)都具有非平稳特性,若直接将其输入模型,极易导致虚假回归——看似显著的拟合效果,实则只是数据趋势的偶然叠加,毫无实盘预测能力。因此,建模前必须通过变换将非平稳序列转换为平稳序列。最核心且常用的变换包括:收益率变换(取价格的对数收益率),这是消除价格水平趋势的根本方法;差分变换(计算序列的差值),适用于移除趋势项;比率变换(计算相对变化),多用于财务指标;以及Z分数变换(标准化处理),适用于多因子整合。一个必须遵守的铁律是:对于任何机器学习模型,原始价格绝不能直接作为输入,必须转换为收益率或其他平稳序列后,才能进行训练和预测。 这是确保模型能够捕捉真实规律、避免虚假相关的数学基础。
6.回溯窗口
回溯窗口的长度选择,本质上是在策略响应速度与信号稳健性之间寻求最优平衡:窗口过短则反应灵敏但易受噪声干扰,窗口过长则趋势平滑但适应滞后。根据A股与美股市场的实践经验,不同策略类型有对应的窗口基准:波动周期策略宜采用20-60天窗口(月度至季度),以捕捉短期波动特征;趋势与动量策略适合60-252天窗口(季度至年度),以平滑噪声、把握长期趋势;均值回归策略则对应5-20天窗口(每周到每月),以便快速反应价格回复;而相关性计算一般需60-126天窗口,以保证统计可靠性。
更重要的是,回溯窗口不应固定不变。一个实用原则是:根据市场波动状态灵活调整——高波动时期缩短窗口以快速适应变化,低波动时期延长窗口以增强稳健性。这种动态调整能力,往往能使策略在不同市场环境中保持效能。
三.关于回测
回测的核心目的不是 “证明策略有效”,而是尽可能还原实盘场景,发现策略的潜在问题—— 初创基金的实盘资金有限,一次失败的策略上线,可能带来不可逆的损失,因此回测的 “严格性”,直接决定策略实盘的存活率。而回测中最容易犯的错误,就是前瞻性偏差,以及对交易成本、执行条件的理想化假设。
1.前瞻性偏差
前瞻性偏差是指使用未来信息做出本应仅基于过去信息的决策,这是导致“回测暴利、实盘亏损”的最常见且隐蔽的原因。其主要来源包括六大方面:使用当日数据生成当日信号(正确做法应对所有特征做shift(1)处理,仅用昨日及更早数据);在全集上拟合并测试模型(应严格划分样本外测试集,并设置训练与测试间的禁运期);使用全样本统计量进行特征归一化(必须采用滚动或扩展窗口,仅基于历史信息);使用重述后的财务数据(应基于时间点数据库,并以实际公布日而非财报期末日为基准,增加合理滞后);样本选择存在幸存者偏差(需使用时点成分列表,包含已退市或剔除的标的);以及因子计算中隐含未来信息(因子值应严格依赖历史价格与成交量计算)。此外,可通过统计方法检测前瞻性偏差:若策略信号与过去收益率呈现异常高的相关性,则极可能存在问题。 只有系统性地预防和检验前瞻性偏差,回测结果才可能转化为真实的盈利能力。
def check_lookahead_bias(df, signal_col, return_col, horizon=1):"""前瞻偏差的统计检验,核心逻辑:存在前瞻偏差的信号,与过去收益相关性异常高"""import numpy as npfrom scipy.stats import spearmanrsignal = df[signal_col].dropna()results = {}# 检测信号与过去收益的相关性(正常情况下应极低)for lag in [1, 2, 5, 10]:past_ret = df[return_col].shift(lag)valid = signal.notna() & past_ret.notna()if valid.sum() > 30: # 保证足够的观测数据corr, pval = spearmanr(signal[valid], past_ret[valid])results[f'corr_lag_{lag}'] = {'corr': corr, 'pval': pval}# 预警:相关性绝对值>0.1且p值<0.05,大概率存在前瞻偏差if abs(corr) > 0.1 and pval < 0.05:print(f"警告:信号与{lag}天的过去收益存在显著相关性!")print(f"相关性:{corr:.4f},p值:{pval:.4f}")# 检测信号与未来收益的相关性(策略有效时应显著为正/负)fwd_ret = df[return_col].shift(-horizon)valid = signal.notna() & fwd_ret.notna()if valid.sum() > 30:corr, pval = spearmanr(signal[valid], fwd_ret[valid])results['corr_forward'] = {'corr': corr, 'pval': pval}return results
2.过拟合
过拟合是指策略过度拟合历史数据中的噪声,而非捕捉真实的市场规律,这会导致回测表现完美但实盘迅速失效。识别过拟合有六大核心信号:样本内外夏普比率差距悬殊;前向行走效率低于0.5;参数微调导致收益断崖下跌;随着测试次数增加策略越发复杂;完美契合历史噪声却在模拟数据中表现差;以及可复现性差,仅限于特定标的或区间。
为了从源头缓解过拟合,可采取六大措施:采用正则化技术(如L1/L2)限制模型复杂度;使用严格的时间序列交叉验证(如前向分析),还原实盘决策场景;主动限制模型复杂度与参数数量;优先选用逻辑清晰、结构简单的信号;坚持进行严格的样本外验证;以及通过参数稳定性分析筛选稳健策略。简言之,真正的阿尔法应源于对市场规律的深刻理解,而非对历史数据的精巧拟合。
3.交易成本模型
即使对于初创私募基金而言,交易规模虽小,但真实的交易成本——包括佣金、买卖价差、市场冲击成本、滑点乃至空头头寸的借贷费用——对策略净收益的影响依然显著。回测中常见的“零成本假设”会严重高估策略表现,导致许多回测盈利的策略在实盘中扣除成本后转为亏损。因此,构建一个贴合实际、包含各主要成本维度的交易成本模型,并将其系统性地纳入回测框架,是确保策略评估结果真实可信、避免“纸上富贵”的关键一步。一个实用的模型应能模拟不同市场条件下的冲击与滑点,并根据交易规模与资产流动性动态调整成本估算,让回测环境最大限度地接近实盘交易场景。
class TransactionCostModel:"""用于回测的实际交易成本模型,全面考虑佣金、点差、市场冲击、滑点、借贷成本适配大多数对冲基金的实盘交易场景,参数可根据券商费率、市场情况调整"""def __init__(self,commission_pct=0.0005, # 佣金比例,默认万5spread_bps=5, # 买卖价差成本,单位:基点,默认5个基点market_impact_bps=2, # 市场冲击成本,每100万美元交易的基点数borrow_cost_annual=0.005, # 空头头寸的年化借贷成本,默认50个基点min_commission=1.0): # 最低佣金,默认1元self.commission_pct = commission_pctself.spread_bps = spread_bps / 10000 # 转换为比例self.market_impact_bps = market_impact_bps / 10000self.borrow_cost_annual = borrow_cost_annualself.min_commission = min_commissiondef compute_cost(self, trade_value, is_short=False, adv=None):"""计算单笔交易的总成本参数:-----------trade_value : float交易的绝对美元/人民币价值is_short : bool是否为空头头寸,空头需计算借贷成本adv : float, optional标的平均每日交易量(美元/人民币),用于计算市场冲击返回值:--------total_cost : float总交易成本(美元/人民币)cost_breakdown : dict交易成本按组成部分分解,便于分析各成本占比"""import numpy as np# 佣金成本,取比例佣金和最低佣金的最大值commission = max(trade_value * self.commission_pct, self.min_commission)# 买卖价差成本spread_cost = trade_value * self.spread_bps# 市场冲击成本,遵循平方根定律(交易规模越大,冲击成本越高)if adv and adv > 0:participation = trade_value / adv # 交易规模占日均交易量的比例market_impact = trade_value * self.market_impact_bps * np.sqrt(participation)else:market_impact = trade_value * self.market_impact_bps# 空头借贷成本,按20个交易日(月均)计算borrow_cost = 0if is_short:borrow_cost = trade_value * self.borrow_cost_annual * (20 / 252)# 总交易成本total_cost = commission + spread_cost + market_impact + borrow_cost# 成本分解cost_breakdown = {'commission': commission,'spread': spread_cost,'market_impact': market_impact,'borrow_cost': borrow_cost}return total_cost, cost_breakdown
4.前向分析
前向分析(Walk Forward Analysis)是最贴近实盘的验证方法,核心逻辑是 “用历史数据逐步训练模型,用后续数据逐步测试模型”,还原实盘中 “无未来信息、逐步迭代” 的场景,能有效检测策略的样本外稳健性,是量化策略从回测走向实盘的 “必经关卡”。
def walk_forward_analysis(df, feature_cols, target_col,min_train_days=252,refit_frequency=20,embargo_days=10,model_class=None):"""具有适当时间结构的前向分析,时间序列验证的黄金标准核心逻辑:训练集→禁运期→测试集,避免信息泄露,还原实盘迭代场景"""import numpy as npimport pandas as pdfrom sklearn.preprocessing import StandardScalerresults = []fold_stats = []# 过滤缺失值,保证数据有效性valid_mask = df[feature_cols + [target_col]].notna().all(axis=1)valid_data = df[valid_mask].copy()train_end_idx = min_train_days # 最小训练天数,保证模型有足够的训练数据fold_num = 0while train_end_idx < len(valid_data) - embargo_days:fold_num += 1# 划分训练集:起始到train_end - 禁运期,避免信息泄露train_data = valid_data.iloc[:train_end_idx - embargo_days]# 划分测试集:train_end到train_end + 重拟合频率test_start = train_end_idxtest_end = min(test_start + refit_frequency, len(valid_data))test_data = valid_data.iloc[test_start:test_end]if len(test_data) == 0:break# 提取特征和标签X_train = train_data[feature_cols].valuesy_train = train_data[target_col].valuesX_test = test_data[feature_cols].valuesy_test = test_data[target_col].values# 特征标准化:仅用训练集数据拟合,避免前瞻偏差scaler = StandardScaler()X_train_scaled = scaler.fit_transform(X_train)X_test_scaled = scaler.transform(X_test)# 模型训练与预测if model_class:model = model_class()model.fit(X_train_scaled, y_train)predictions = model.predict(X_test_scaled)else:predictions = np.zeros(len(y_test)) # 无模型时返回空预测# 记录每一次预测的结果for i, (idx, pred, actual) in enumerate(zip(test_data.index, predictions, y_test)):results.append({'date': idx,'prediction': pred,'actual': actual,'fold': fold_num})# 记录每一轮的折数统计,便于后续分析fold_stats.append({'fold': fold_num,'train_start': train_data.index[0],'train_end': train_data.index[-1],'test_start': test_data.index[0],'test_end': test_data.index[-1],'train_samples': len(train_data),'test_samples': len(test_data)})# 移动训练集终点,进行下一轮前向分析train_end_idx += refit_frequencyreturn pd.DataFrame(results), fold_stats
5.时间序列中未来数据
在时间序列交叉验证中,当预测目标(标签)为多期收益时(例如预测未来5日或10日收益率),标准的随机划分方法会导致一个隐蔽的问题:由于标签数据本身就跨越多个交易日,即使按时间顺序划分数据集,位于训练集末端的样本与测试集前端的样本之间,仍会因标签期的重叠而产生信息泄露,这实质上是一种微妙的前瞻性偏差,会严重夸大策略的预测性能。
为了解决这个问题,必须引入 “清除(Purge)” 和 “禁运(Embargo)” 两项关键技术。清除是指在训练集末端移除一定数量的样本,移除的长度取决于标签的跨越周期(H天),通常移除 H-1 天,以确保训练集最后一个样本的标签计算不会用到任何测试集时段的信息。禁运则是在清除之后,于训练集与测试集之间再设置一段完全空白的间隔期,其长度也建议与标签周期 H 相当,作为额外的缓冲,彻底杜绝因数据依赖关系而产生的任何信息渗透。这两项措施是确保时间序列验证结果无偏、可信的核心保障。
6.综合绩效评价
单一的夏普比率无法全面评估策略的绩效,实盘中更需要关注策略的回撤、风险调整后收益、极端风险等指标。因此,回测中需要计算综合绩效指标,从收益、风险、风险调整后收益、统计显著性等多个维度全面评估策略。综合绩效指标计算代码参考:
def compute_robust_metrics(returns, benchmark_returns=None, rf_rate=0.0):"""计算量化策略的综合性能指标,全面评估收益、风险、回撤、统计显著性等"""import numpy as npimport pandas as pdfrom scipy.stats import skew, kurtosis, tr = returns.dropna()n = len(r)if n < 30: # 保证足够的观测数据,避免指标失真return {'error': '数据不足(需要30个以上观测值)'}# 基础收益指标annual_return = r.mean() * 252 # 年化收益,按252个交易日计算annual_vol = r.std() * np.sqrt(252) # 年化波动率sharpe = annual_return / annual_vol if annual_vol > 0 else 0 # 夏普比率# 回撤相关指标(实盘最关注的风险指标)cum_returns = (1 + r).cumprod() # 累计收益running_max = cum_returns.expanding().max() # 滚动最大值drawdowns = (cum_returns - running_max) / running_max # 回撤序列max_dd = drawdowns.min() # 最大回撤calmar = annual_return / abs(max_dd) if max_dd != 0 else np.nan # 卡玛比率# 高阶矩:偏度和峰度,评估收益分布的极端风险ret_skew = skew(r) # 偏度:<0为左偏,存在极端下跌风险;>0为右偏ret_kurt = kurtosis(r) # 峰度:>0为厚尾,极端行情发生概率更高# 下行风险指标:索提诺比率(仅考虑下行波动率)downside_returns = r[r < 0]downside_vol = downside_returns.std() * np.sqrt(252) if len(downside_returns) > 0 else annual_volsortino = annual_return / downside_vol if downside_vol > 0 else 0# 统计显著性:t统计量和p值,评估收益的统计可靠性t_stat = sharpe * np.sqrt(n / 252)p_value = 2 * (1 - t.cdf(abs(t_stat), df=n-1))# 盈亏比指标:欧米茄比率gains = r[r > 0].sum() # 总盈利loss = abs(r[r < 0].sum()) # 总亏损omega = gains / loss if loss > 0 else np.nan# 基础绩效指标字典metrics = {'annual_return': annual_return,'cumulative_return': cum_returns.iloc[-1] - 1,'annual_volatility': annual_vol,'max_drawdown': max_dd,'avg_drawdown': drawdowns.mean(),'sharpe_ratio': sharpe,'sortino_ratio': sortino,'calmar_ratio': calmar,'omega_ratio': omega,'t_statistic': t_stat,'p_value': p_value,'significant_5pct': p_value < 0.05,'skewness': ret_skew,'kurtosis': ret_kurt,'n_observations': n,'hit_rate': (r > 0).mean(),'profit_factor': gains / loss if loss > 0 else np.nan}# 相对基准的绩效指标(如超额收益、阿尔法、贝塔)if benchmark_returns is not None:bm = benchmark_returns.reindex(r.index).dropna()if len(bm) > 30:excess = r.loc[bm.index] - bm # 超额收益tracking_error = excess.std() * np.sqrt(252) # 跟踪误差info_ratio = excess.mean() * 252 / tracking_error if tracking_error > 0 else 0 # 信息比率# 计算阿尔法和贝塔cov = r.loc[bm.index].cov(bm)bm_var = bm.var()beta = cov / bm_var if bm_var > 0 else 1alpha = (r.loc[bm.index].mean() - rf_rate / 252 - beta * (bm.mean() - rf_rate / 252)) * 252# 更新相对指标metrics.update({'alpha': alpha,'beta': beta,'information_ratio': info_ratio,'tracking_error': tracking_error,'annual_excess_return': excess.mean() * 252})return metrics
7.参数敏感性分析
稳健的量化策略,不应因参数的微小扰动而急剧退化 —— 若策略仅在某一特定参数下有效,微调参数后收益便大幅下跌,说明该策略只是拟合了历史数据的噪音,毫无实盘价值。参数敏感性分析的核心,就是测试策略在参数合理范围内的表现,筛选出参数稳健的策略。参数敏感性分析参考代码:
def parameter_sensitivity_analysis(strategy_func, base_params, param_ranges, df):"""分析策略性能对参数变化的敏感度,评估策略的稳健性核心逻辑:在参数合理范围内微调,观察策略夏普比率的变化"""import numpy as npresults = {}sensitivity_metrics = {}# 遍历每个待测试的参数for param_name, values in param_ranges.items():results[param_name] = []# 遍历参数的每个取值for val in values:test_params = base_params.copy()test_params[param_name] = val # 微调当前参数try:# 运行策略,获取核心绩效指标(夏普比率)sharpe = strategy_func(df, **test_params)results[param_name].append({'value': val, 'sharpe': sharpe})except Exception as e:# 捕获策略运行错误,标记为NaNresults[param_name].append({'value': val, 'sharpe': np.nan, 'error': str(e)})# 计算参数敏感性指标,评估策略对该参数的敏感程度sharpes = [r['sharpe'] for r in results[param_name] if not np.isnan(r['sharpe'])]if len(sharpes) > 1:sensitivity_metrics[param_name] = {'mean_sharpe': np.mean(sharpes),'std_sharpe': np.std(sharpes),'cv': np.std(sharpes) / abs(np.mean(sharpes)) if np.mean(sharpes) != 0 else np.nan, # 变异系数'min_sharpe': np.min(sharpes),'max_sharpe': np.max(sharpes)}return results, sensitivity_metrics
四.实盘
在初创对冲基金,“研究出有效策略” 只是第一步,将策略平稳落地到实盘,实现持续的收益输出,才是量化研究员的核心价值。这一步需要解决研究代码与生产代码的分离、仓位管理、风险控制、alpha 衰变检测等问题,拒绝 “回测与实盘两张皮”。
研究代码和生产代码的核心目标不同,必须严格分离,避免将研究阶段的 “临时代码” 直接投入生产,导致实盘运行的风险。
2.仓位管理
仓位规模调整方法 —— 匹配基金资金规模,控制单笔交易风险
仓位规模调整的核心,是在策略收益和交易风险之间找平衡—— 过高的仓位会放大市场冲击和回撤风险,过低的仓位则无法实现足够的收益。初创基金的资金规模有限,更需要科学的仓位调整方法,适配不同的策略类型和市场环境。
class PositionSizer:"""生产就绪的仓位规模调整方法,支持凯利准则、波动率目标、风险平价三种核心方法适配对冲基金实盘交易场景,可根据策略类型和资金规模灵活选择"""@staticmethoddef kelly_criterion(win_rate, avg_win, avg_loss, fraction=0.25):"""分数凯利准则仓位调整,适用于趋势类、择时类策略核心:完全凯利准则最优但波动率过大,生产中使用1/4-1/2分数凯利,平衡收益和风险参数:win_rate: 策略胜率avg_win: 平均盈利avg_loss: 平均亏损fraction: 分数凯利系数,默认0.25(最稳健)"""if avg_loss == 0:return 0b = avg_win / avg_loss # 盈亏比p = win_rateq = 1 - win_ratekelly = (p * b - q) / b # 标准凯利公式return max(0, min(kelly * fraction, 1.0)) # 仓位限制在0-1之间@staticmethoddef volatility_targeting(returns, target_vol, current_position=1.0,lookback=20, vol_cap=2.0):"""波动率目标仓位调整,适用于多因子、指数增强策略核心:调整仓位规模,使策略的实际波动率始终贴近目标波动率参数:returns: 策略历史收益序列target_vol: 目标年化波动率current_position: 当前仓位lookback: 回溯窗口,默认20天vol_cap: 仓位缩放上限,默认2.0(避免仓位过高)"""import numpy as nprecent_returns = returns.iloc[-lookback:]implemented_vol = recent_returns.std() * np.sqrt(252) # 实际年化波动率if implemented_vol <= 0:return current_positionscale = target_vol / implemented_vol # 仓位缩放比例scale = np.clip(scale, 1/vol_cap, vol_cap) # 限制缩放比例return current_position * scale@staticmethoddef risk_parity_weights(covariance_matrix, target_risk=None):"""风险平价仓位分配,适用于多资产、多策略组合核心:让每个资产/策略对组合的风险贡献相等,实现风险分散参数:covariance_matrix: 资产/策略的协方差矩阵target_risk: 组合的目标年化波动率"""import numpy as npimport pandas as pdfrom scipy.optimize import minimizecov = covariance_matrix.valuesn = len(cov)# 风险平价目标函数:最小化各资产风险贡献的方差def risk_budget_objective(weights):port_vol = np.sqrt(weights @ cov @ weights)marginal_contrib = cov @ weights / port_volrisk_contrib = weights * marginal_contribreturn np.sum((risk_contrib - port_vol/n)**2)# 约束条件:权重和为1,单权重在0-1之间(不做空)constraints = [{'type': 'eq', 'fun': lambda w: np.sum(w) - 1}]bounds = [(0, 1) for _ in range(n)]# 优化求解风险平价权重result = minimize(risk_budget_objective,x0=np.ones(n)/n, # 初始权重:等权重method='SLSQP',bounds=bounds,constraints=constraints)weights = pd.Series(result.x, index=covariance_matrix.columns)# 若设置目标风险,调整权重使组合风险贴近目标风险if target_risk:current_risk = np.sqrt(weights.values @ cov @ weights.values) * np.sqrt(252)weights = weights * (target_risk / current_risk)return weights
量化策略的 alpha 不是永恒的,随着市场结构变化、策略拥挤度上升、核心逻辑失效,alpha 会逐步衰减,甚至消失。实盘中必须建立alpha 衰变探测机制,实时监控策略的表现,及时发现策略失效的信号,避免持续亏损。
alpha 衰变探测实用代码建议:
def detect_alpha_decay(live_returns, backtest_sharpe, lookback=60, warning_threshold=0.5):"""检测策略alpha是否正在衰减,核心对比实盘夏普比率与回测夏普比率的变化参数:live_returns: 策略实盘收益序列backtest_sharpe: 策略回测夏普比率lookback: 滚动回溯窗口,默认60天warning_threshold: 预警阈值,默认0.5(实盘夏普/回测夏普<0.5则预警)"""import numpy as np# 计算滚动实盘夏普比率rolling_sharpe = (live_returns.rolling(lookback).mean() /live_returns.rolling(lookback).std() * np.sqrt(252))# 获取最新的滚动夏普比率recent_sharpe = rolling_sharpe.iloc[-1] if not rolling_sharpe.isna().iloc[-1] else np.nan# 检测夏普比率的趋势:是否持续下降if len(rolling_sharpe.dropna()) >= 60:sharpe_series = rolling_sharpe.dropna()x = np.arange(len(sharpe_series))slope = np.polyfit(x, sharpe_series.values, 1)[0] # 夏普比率趋势斜率is_declining = slope < 0 # 斜率<0则为持续下降else:slope = np.nanis_declining = False# 检测夏普比率的衰减程度if not np.isnan(recent_sharpe):sharpe_ratio = recent_sharpe / backtest_sharpe if backtest_sharpe != 0 else np.nanis_decayed = sharpe_ratio < warning_threshold # 低于预警阈值则为显著衰减else:sharpe_ratio = np.nanis_decayed = False# 生成alpha衰变检测报告和操作建议return {'recent_sharpe': recent_sharpe,'backtest_sharpe': backtest_sharpe,'sharpe_ratio': sharpe_ratio,'sharpe_trend_slope': slope,'
Alpha 衰变的核心探测方法建议:
Alpha 衰变的应对策略
在初创对冲基金做量化研究的一年,最大的收获不是研究出了多少个有效策略,而是学会了用 “实盘思维” 做研究—— 跳出了学术研究的 “唯数据论”,懂得了 “经济逻辑比回测收益更重要”“稳健性比高收益更重要”“风险控制比盈利更重要”。
量化研究的本质,是在市场的非有效性中寻找可复现的 Alpha,并通过严格的风险控制将 Alpha 转化为实盘收益。初创基金的优势是 “船小好调头”,能快速捕捉市场的新机会、快速调整策略;劣势是资源有限、抗风险能力弱。因此,初创基金的量化研究,必须坚持 “简单、稳健、可落地” 的原则,拒绝追求 “高收益、高复杂” 的策略,守住 “研究有逻辑、回测贴实盘、实盘能盈利、风险可控制” 的核心底线,才能在激烈的市场竞争中生存并发展。
而作为量化研究员,最核心的能力不是编写复杂的代码、搭建复杂的模型,而是独立的思考能力、严谨的研究方法、敏锐的市场洞察力,以及果断的止损意识—— 懂得如何提出有价值的假设,如何用严谨的方法验证假设,如何发现策略的潜在问题,如何在实盘中控制风险,如何在 Alpha 衰变时及时止损,这些能力,才是量化研究员最宝贵的财富。
免责声明:
您在阅读本内容或附件时,即表明您已事先接受以下“免责声明”之所载条款:
1、本文内容源于作者对于所获取数据的研究分析,本网站对这些信息的准确性和完整性不作任何保证,对由于该等问题产生的一切责任,本网站概不承担;阅读与私募基金相关内容前,请确认您符合私募基金合格投资者条件。
2、文件中所提供的信息尽可能保证可靠、准确和完整,但并不保证报告所述信息的准确性和完整性;亦不能作为投资决策的依据,不能作为道义的、责任的和法律的依据或者凭证。
3、对于本文以及文件中所提供信息所导致的任何直接的或者间接的投资盈亏后果不承担任何责任;本文以及文件发送对象仅限持有相关产品的客户使用,未经授权,请勿对该材料复制或传播。侵删!
4、所有阅读并从本文相关链接中下载文件的行为,均视为当事人无异议接受上述免责条款,并主动放弃所有与本文和文件中所有相关人员的一切追诉权。
