第八章 信号生成模块:技术指标计算与信号合成
信号生成模块,说白了就是做市系统的「大脑」。
前面几章我们搞定了数据清洗、订单簿重建、价格计算。现在,我们要把这些原始数据变成真正的交易信号。我个人习惯把这一步叫做「从数据到决策的最后一公里」。
8.1 技术指标计算:三个核心工具
做市系统里,技术指标不需要太多。我见过不少新手,一上来就堆十几个指标,结果信号互相打架。其实,三个经典指标就够用:移动平均线、RSI、布林带。
8.1.1 移动平均线(MA)
移动平均线是最基础的。它帮你滤掉噪音,看清趋势方向。
我在项目中遇到过一个问题:用简单移动平均(SMA)时,价格突然跳空,均线反应太慢。后来我改用指数移动平均(EMA),效果好了不少。EMA对近期价格更敏感,适合高频做市场景。
核心公式:
EMA = (当前价格 × 平滑因子) + (前一日EMA × (1 - 平滑因子))
平滑因子 = 2 / (周期数 + 1)
def calculate_ema(prices, period=20):
"""
计算指数移动平均线
prices: 价格序列,最新价格在最后
period: 周期,默认20
"""
smoothing_factor = 2 / (period + 1)
ema = [prices[0]] # 初始值用第一个价格
for i in range(1, len(prices)):
ema_value = (prices[i] * smoothing_factor) + (ema[-1] * (1 - smoothing_factor))
ema.append(ema_value)
return ema[-1] # 只返回最新值
我的经验:做市系统里,EMA周期别设太大。5-20周期就够了。周期太长,信号滞后严重,等你反应过来,行情早变了。
8.1.2 RSI(相对强弱指标)
RSI用来判断「超买」和「超卖」。说白了,就是看价格涨得太猛还是跌得太狠。
嗯,这里要注意:RSI的计算方式有很多变种。我建议用 Wilder 的原始版本,简单可靠。
def calculate_rsi(prices, period=14):
"""
计算RSI指标
prices: 价格序列,至少需要period+1个数据点
"""
if len(prices) < period + 1:
return None
# 计算价格变化
deltas = [prices[i] - prices[i-1] for i in range(1, len(prices))]
# 分离上涨和下跌
gains = [d if d > 0 else 0 for d in deltas]
losses = [-d if d < 0 else 0 for d in deltas]
# 计算平均涨幅和平均跌幅
avg_gain = sum(gains[-period:]) / period
avg_loss = sum(losses[-period:]) / period
if avg_loss == 0:
return 100
rs = avg_gain / avg_loss
rsi = 100 - (100 / (1 + rs))
return rsi
避坑指南:我曾经在RSI计算上栽过跟头。当时用了整个历史数据来计算平均涨幅,结果RSI值一直偏低。后来才发现,应该只用最近period个数据点来计算。记住:RSI是「相对」指标,不是「绝对」指标。
8.1.3 布林带(Bollinger Bands)
布林带告诉你价格「应该」在什么范围内波动。它由三条线组成:中轨(MA)、上轨(中轨+2倍标准差)、下轨(中轨-2倍标准差)。
你想想看,当价格碰到上轨时,说明价格偏离均值太多,大概率会回调。这就是做市系统里很好的「反转信号」。
def calculate_bollinger_bands(prices, period=20, num_std=2):
"""
计算布林带
返回: (中轨, 上轨, 下轨)
"""
if len(prices) < period:
return None
# 计算中轨(这里用SMA,你也可以用EMA)
recent_prices = prices[-period:]
middle = sum(recent_prices) / period
# 计算标准差
variance = sum((p - middle) ** 2 for p in recent_prices) / period
std_dev = variance ** 0.5
upper = middle + num_std * std_dev
lower = middle - num_std * std_dev
return middle, upper, lower
8.2 信号合成:把指标变成决策
单个指标容易误判。比如RSI显示超买,但价格还在涨。这时候就需要信号合成——把多个指标的结果综合起来。
我个人习惯用「投票机制」。每个指标投一票,最后看总票数决定方向。
| 指标 | 做多信号 | 做空信号 | 中性信号 |
|---|---|---|---|
| EMA | 价格 > EMA | 价格 < EMA | 价格 ≈ EMA |
| RSI | RSI < 30 | RSI > 70 | 30 ≤ RSI ≤ 70 |
| 布林带 | 价格 < 下轨 | 价格 > 上轨 | 价格在轨内 |
def synthesize_signals(price, ema, rsi, bb_middle, bb_upper, bb_lower):
"""
信号合成函数
返回: 1=做多, -1=做空, 0=观望
"""
score = 0
# EMA信号
if price > ema:
score += 1 # 看多
elif price < ema:
score -= 1 # 看空
# RSI信号
if rsi < 30:
score += 1 # 超卖,看多
elif rsi > 70:
score -= 1 # 超买,看空
# 布林带信号
if price < bb_lower:
score += 1 # 跌破下轨,看多
elif price > bb_upper:
score -= 1 # 突破上轨,看空
# 最终决策
if score >= 2:
return 1 # 强烈做多
elif score <= -2:
return -1 # 强烈做空
else:
return 0 # 观望
关键点:信号合成不是简单的「多数决」。我建议给不同指标分配不同权重。比如在趋势行情中,EMA的权重可以高一些;在震荡行情中,布林带的权重可以高一些。这个权重可以动态调整。
8.3 阈值设定:别让系统太敏感
阈值设定是信号生成模块里最容易出问题的地方。阈值设得太小,系统频繁开仓,手续费吃掉利润。阈值设得太大,信号出不来,错过行情。
我曾经犯过一个错误:把RSI的阈值设成20/80,结果一天下来一个信号都没有。后来改成25/75,信号多了,但假信号也多了。最后我找到个平衡点:30/70,配合布林带一起用,效果不错。
我的建议:阈值不要固定死。可以根据市场波动率动态调整。波动率大的时候,把阈值放宽;波动率小的时候,把阈值收紧。这样系统能自适应市场环境。
8.4 知识体系总览
下面这张图,把信号生成模块的核心逻辑串起来了。你可以看到:原始价格数据经过三个指标计算,然后进入信号合成器,最后输出交易决策。
这张图里,我特意把「阈值判断」放在最后一步。为什么?因为先合成信号,再判断阈值,能减少不必要的计算。如果每个指标都单独设阈值,逻辑会变得很复杂。
最后提醒:信号生成模块一定要做回测。我见过太多人,指标算得漂亮,一上实盘就亏钱。原因往往是参数过拟合。记住:回测是检验信号质量的唯一标准。
无相订单流研究社 微信Lucian808555