4. 信号生成进阶:多因子信号融合、信号权重动态调整、信号延迟与衰减模型、信号置信度评估
做市引擎的核心,说白了就是信号处理。
上一章我们聊了基础信号怎么生成,但真实战场哪有这么简单?你想想看,一个信号源往往不够可靠,多个信号之间还可能打架。我早期做的一个项目,就因为信号融合没处理好,回测曲线漂亮得像假的一样,实盘一跑直接崩了。
今天咱们就深入聊聊信号生成的进阶玩法。我会把我在实战中踩过的坑、总结的经验,都摊开来跟你讲。
4.1 多因子信号融合:别让好信号被埋没
多因子融合,不是简单地把几个信号加起来就完事。我见过太多人这么干,结果就是信号质量反而下降。
为什么?因为不同信号之间可能存在共线性,或者量纲不统一。你一个波动率信号可能取值在0.01到0.1之间,另一个动量信号可能取值在-100到100之间,直接相加?那波动率信号基本被淹没了。
核心思路:先标准化,再融合。标准化方法我推荐用Z-score或者分位数归一化。
我个人习惯用分位数归一化,因为它对极端值更鲁棒。举个例子:
def quantile_normalize(signal):
"""分位数归一化,将信号映射到[0,1]区间"""
from scipy.stats import rankdata
ranks = rankdata(signal)
return ranks / len(signal)
# 假设我们有三个信号
momentum = get_momentum_signal(price_data)
volatility = get_vol_signal(price_data)
orderflow = get_orderflow_signal(orderbook_data)
# 分别归一化
mom_norm = quantile_normalize(momentum)
vol_norm = quantile_normalize(volatility)
of_norm = quantile_normalize(orderflow)
# 等权融合
combined_signal = (mom_norm + vol_norm + of_norm) / 3
这里有个坑:千万不要在回测时用全量数据做归一化。我曾经犯过这个错,回测时用了未来数据,结果实盘信号完全不对。正确的做法是用滚动窗口,或者用历史数据拟合好参数再应用。
注意:信号融合前一定要做相关性分析。如果两个信号相关性超过0.8,建议只保留一个,或者做PCA降维后再融合。
4.2 信号权重动态调整:市场在变,你的权重也得变
固定权重?那是静态思维。市场环境在变,不同信号的有效性也在变。比如趋势行情里,动量信号表现好;震荡行情里,反转信号更靠谱。
我建议用滚动窗口的夏普比率来动态调整权重。具体做法:
- 计算每个信号在过去N个交易日的夏普比率
- 根据夏普比率大小分配权重
- 设置权重上下限,防止某个信号被完全抛弃
def dynamic_weight_adjust(signals, returns, window=20):
"""
基于滚动夏普比率动态调整信号权重
signals: DataFrame, 每列是一个信号
returns: 实际收益序列
"""
# 计算每个信号的滚动夏普
rolling_sharpe = signals.rolling(window).apply(
lambda x: np.mean(x * returns) / np.std(x * returns + 1e-8)
)
# 用softmax转换为权重
weights = np.exp(rolling_sharpe) / np.exp(rolling_sharpe).sum(axis=1)
# 限制权重范围 [0.1, 0.6]
weights = weights.clip(0.1, 0.6)
weights = weights.div(weights.sum(axis=1), axis=0)
return weights
小技巧:权重调整不要太频繁。我一般用20个交易日作为滚动窗口,每5个交易日更新一次权重。太频繁容易过拟合,太迟钝又跟不上市场变化。
4.3 信号延迟与衰减模型:时间是有成本的
信号不是永恒的。一个信号产生后,它的价值会随时间衰减。你想想看,5分钟前的买卖压力信号,跟5秒前的,能一样吗?
我做过一个统计:在股指期货上,订单流不平衡信号的半衰期大约是3秒。也就是说,3秒后这个信号的价值就只剩一半了。
常用的衰减模型有两种:
| 模型 | 公式 | 适用场景 |
|---|---|---|
| 指数衰减 | w(t) = exp(-λ * t) | 高频信号,衰减快 |
| 线性衰减 | w(t) = max(0, 1 - t/T) | 低频信号,衰减慢 |
def exponential_decay(signal_time, current_time, half_life=3.0):
"""
指数衰减模型
half_life: 半衰期,单位秒
"""
time_diff = current_time - signal_time
decay_factor = np.exp(-np.log(2) * time_diff / half_life)
return decay_factor
def linear_decay(signal_time, current_time, max_life=60.0):
"""
线性衰减模型
max_life: 最大存活时间,单位秒
"""
time_diff = current_time - signal_time
decay_factor = max(0, 1 - time_diff / max_life)
return decay_factor
实战经验:我建议把衰减模型和信号延迟结合起来。信号从产生到被系统处理,本身就有延迟。比如网络延迟、计算延迟。我曾经遇到过一个情况,信号延迟了200毫秒,但衰减模型没考虑这个,导致信号权重被高估。后来我加了一个延迟补偿:
def decay_with_lag_compensation(signal_time, process_time, current_time, half_life=3.0):
"""
考虑处理延迟的衰减模型
process_time: 信号被处理的时间戳
"""
# 实际延迟 = 处理时间 - 信号产生时间
lag = process_time - signal_time
# 有效时间差 = 当前时间 - 处理时间 + 延迟补偿
effective_diff = current_time - process_time + lag * 0.5 # 补偿一半的延迟
decay_factor = np.exp(-np.log(2) * effective_diff / half_life)
return decay_factor
4.4 信号置信度评估:不是所有信号都值得信任
信号有强弱之分,但更重要的是置信度。一个强信号如果置信度低,可能还不如一个弱信号但置信度高的。
我一般从三个维度评估置信度:
- 统计显著性:信号是否通过了统计检验?比如t检验、信息比率
- 样本稳定性:信号在不同时间段的稳定性如何?用滚动窗口的方差来衡量
- 市场环境匹配度:当前市场环境是否适合这个信号?比如趋势信号在震荡市里置信度就低
def confidence_score(signal, returns, market_regime):
"""
综合置信度评分
"""
# 1. 统计显著性:用信息比率
ir = np.mean(signal * returns) / np.std(signal * returns + 1e-8)
stat_score = min(1, max(0, (ir + 2) / 4)) # 映射到[0,1]
# 2. 样本稳定性:用滚动窗口的变异系数
rolling_cv = signal.rolling(20).std() / signal.rolling(20).mean().abs()
stability_score = 1 - min(1, rolling_cv.iloc[-1])
# 3. 市场环境匹配度
# 假设market_regime: 0=震荡, 1=趋势
if market_regime == 1: # 趋势市
regime_score = 0.8 if signal.name == 'momentum' else 0.3
else: # 震荡市
regime_score = 0.8 if signal.name == 'mean_reversion' else 0.3
# 综合评分
final_score = 0.4 * stat_score + 0.3 * stability_score + 0.3 * regime_score
return final_score
注意:置信度评估不能只看历史表现。我吃过一次亏:某个信号在回测中表现极好,置信度评分很高,结果实盘时市场结构变了,信号直接失效。后来我加了一个在线学习机制,实时更新置信度。
4.5 实战框架:把这些串起来
好了,上面讲了四个核心模块。现在咱们把它们整合成一个完整的信号处理流水线。
下面这张图展示了整个流程:
这个流水线我用了很久,效果还不错。但你要记住:没有银弹。每个市场、每个品种都需要微调参数。
我的建议:先跑一个简单的版本,比如等权融合+固定衰减。跑通之后,再逐步加入动态权重和置信度评估。一步到位容易出问题,迭代优化才是正道。
嗯,信号生成进阶这块就聊到这儿。内容不少,但都是实战中摸爬滚打出来的经验。你回去可以试着把代码跑一跑,看看不同参数对信号质量的影响。有什么问题,咱们随时交流。
公众号:蓝海资料掘金营,微信deep3321