3. 交易策略基础:策略设计原则、策略类型与信号生成
做量化交易这几年,我越来越觉得策略设计就像盖房子。地基没打好,后面装修得再漂亮也没用。今天咱们就聊聊策略设计的基本原则、几种主流策略类型,以及信号是怎么生成的。
3.1 策略设计原则:别让策略死在起跑线上
我个人习惯把策略设计原则归纳为三条。这三条看着简单,但我在项目中踩过的坑,十有八九都是违背了其中一条。
3.1.1 可解释性原则
说白了,你的策略得能说清楚「为什么赚钱」。我见过有人把50个技术指标扔进神经网络,回测曲线漂亮得不行。结果实盘一周就亏了20%。为什么?因为那些指标组合根本就是过拟合,没有任何逻辑支撑。
嗯,这里要注意:如果你自己都解释不清策略的逻辑,市场一定会用亏损给你解释清楚。
3.1.2 稳健性原则
策略不能只在某个特定行情下有效。你想想看,如果一个策略在2015年股灾里赚了钱,但在2016年震荡市里亏成狗,那它就不是好策略。
我曾经做过一个趋势跟踪策略,在牛市里年化收益80%。我兴奋得不行,直接上了实盘。结果熊市一来,三个月回撤了40%。后来我学乖了,任何策略都要在多种市场环境下测试——趋势市、震荡市、高波动、低波动,一个都不能少。
3.1.3 简洁性原则
参数越少越好。为什么?因为参数越多,过拟合的风险就越大。我见过一个策略有20多个参数,每个参数都优化得恰到好处。但实盘呢?一塌糊涂。
3.2 策略类型:三大主流流派
交易策略千千万,但核心逻辑逃不出这三类。咱们一个一个看。
3.2.1 趋势跟踪策略
趋势跟踪,说白了就是「追涨杀跌」。它的核心假设是:趋势一旦形成,就会持续一段时间。
最常见的实现方式就是均线交叉。比如5日均线上穿20日均线时买入,下穿时卖出。代码很简单:
def moving_average_crossover(data, short_window=5, long_window=20):
data['MA_short'] = data['close'].rolling(window=short_window).mean()
data['MA_long'] = data['close'].rolling(window=long_window).mean()
# 生成信号:1表示买入,-1表示卖出,0表示持有
data['signal'] = 0
data.loc[data['MA_short'] > data['MA_long'], 'signal'] = 1
data.loc[data['MA_short'] < data['MA_long'], 'signal'] = -1
return data
我在项目中遇到过一个问题:均线策略在强趋势行情里赚得盆满钵满,但在震荡行情里会被反复打脸。后来我加了ATR(平均真实波幅)作为过滤器——只有波动率足够大时才开仓。效果好了不少。
3.2.2 均值回归策略
均值回归和趋势跟踪正好相反。它赌的是「价格偏离均值后一定会回来」。说白了就是「低买高卖」的数学版本。
最经典的实现是布林带策略。当价格触及下轨时买入,触及上轨时卖出。
def bollinger_band_strategy(data, window=20, num_std=2):
data['MA'] = data['close'].rolling(window=window).mean()
data['std'] = data['close'].rolling(window=window).std()
data['upper'] = data['MA'] + num_std * data['std']
data['lower'] = data['MA'] - num_std * data['std']
data['signal'] = 0
data.loc[data['close'] < data['lower'], 'signal'] = 1 # 买入
data.loc[data['close'] > data['upper'], 'signal'] = -1 # 卖出
return data
嗯,这里要注意:均值回归策略在单边行情里会死得很惨。我记得有一次做股指期货的均值回归,遇到了一波连续上涨,我一路逆势做空,结果爆仓了。后来我加了趋势过滤器——只有市场处于震荡状态时才用均值回归。
3.2.3 统计套利策略
统计套利,说白了就是「找亲戚」。找两个相关性高的品种,当它们的价差偏离正常范围时,做多一个、做空另一个,赌价差会回归。
最经典的是配对交易。比如茅台和五粮液,历史上它们的价差一直在某个范围内波动。当价差扩大到2倍标准差时,就开仓。
def pairs_trading_strategy(price_a, price_b, window=60, entry_z=2):
# 计算价差
spread = price_a - price_b
spread_mean = spread.rolling(window=window).mean()
spread_std = spread.rolling(window=window).std()
z_score = (spread - spread_mean) / spread_std
# 生成信号
signal_a = 0
signal_b = 0
if z_score > entry_z:
signal_a = -1 # 做空A
signal_b = 1 # 做多B
elif z_score < -entry_z:
signal_a = 1 # 做多A
signal_b = -1 # 做空B
return signal_a, signal_b
3.3 策略参数与信号生成
参数和信号,是策略的「灵魂」和「手脚」。参数决定了策略的性格,信号决定了策略的行动。
3.3.1 参数选择:别过度优化
参数优化是门学问。我见过有人用网格搜索把参数优化到小数点后两位,结果实盘一塌糊涂。为什么?因为过度优化等于在拟合噪音。
我个人习惯的做法是:
- 先定范围:比如均线周期,我一般只试5-60之间的整数
- 再定步长:步长不要太小,5或10就够
- 最后验证:用样本外数据验证,如果样本外表现和样本内差距很大,说明过拟合了
3.3.2 信号生成:从数据到决策
信号生成的过程,其实就是把策略逻辑翻译成「买、卖、持有」三个动作。我一般用-1、0、1来表示。
信号生成有几个关键点:
- 信号要明确:不能模棱两可。是买就是买,是卖就是卖
- 信号要可执行:比如收盘价信号,你只能在收盘时交易
- 信号要防未来函数:绝对不能用到未来的数据
3.4 知识体系总览
为了让你更直观地理解这一章的内容,我画了一张结构图。它把策略设计、策略类型、参数和信号的关系串起来了。
这张图把咱们这一章的核心内容串起来了。你想想看,策略设计原则是「道」,策略类型是「法」,参数和信号是「术」。道、法、术三者结合,才能做出靠谱的策略。
好了,这一章就聊到这儿。记住:好的策略不是靠参数堆出来的,而是靠逻辑赢出来的。下一章咱们聊聊回测系统的搭建,到时候我会分享一些我在实际项目中踩过的坑。