3. 交易信号处理核心算法:移动平均线、MACD、RSI与布林带的硬件实现

做量化交易的硬件加速,说白了就是跟延迟赛跑。我刚开始接触这个领域时,总觉得软件上跑得挺顺的指标,干嘛非要搬到FPGA上?直到有一次,我在实盘环境中看到软件计算MACD的延迟直接把一个套利窗口给关掉了——嗯,从那以后我就铁了心要把这些核心算法全部硬件化。

今天咱们就来聊聊,交易信号处理里最经典的四个算法:移动平均线、MACD、RSI和布林带。它们在FPGA上怎么实现?有哪些坑?我个人习惯怎么处理?咱们一个一个说。

3.1 移动平均线的硬件实现

移动平均线是技术分析的基石。简单移动平均线(SMA)和指数移动平均线(EMA),在FPGA上的实现思路完全不同。

3.1.1 SMA:用滑动窗口做累加

SMA的计算公式很简单:SMA = (P1 + P2 + ... + Pn) / n。但你要是每个周期都重新加一遍所有数据,那资源消耗就太大了。

我建议用「滑动窗口累加器」的思路。说白了就是维护一个累加和,来一个新数据就加进去,同时把最老的那个数据减掉。这样每个周期只需要一次加法和一次减法。

核心思路: 维护一个深度为N的FIFO,配合一个累加器。新数据入FIFO,累加器加上新数据;同时FIFO弹出最老数据,累加器减去它。最后除以N。
// Verilog示例:N=8的SMA核心逻辑
always @(posedge clk or negedge rst_n) begin
    if (!rst_n) begin
        sum <= 0;
        fifo_ptr <= 0;
    end else if (data_valid) begin
        // 减去最老的数据
        sum <= sum - fifo[fifo_ptr] + new_data;
        // 更新FIFO
        fifo[fifo_ptr] <= new_data;
        fifo_ptr <= fifo_ptr + 1;
    end
end

assign sma_out = sum >> 3;  // 除以8,用移位实现

这里有个细节要注意:除法用移位实现的前提是N是2的幂次。如果N不是2的幂次,我一般会用定点数乘法器来做除法,或者干脆用查找表。我在项目中遇到过用N=20的SMA,当时为了省资源,硬是把20改成了16——反正效果差不多,你想想看。

3.1.2 EMA:递归结构更省资源

EMA的公式是:EMA_t = α * P_t + (1-α) * EMA_{t-1}。这个结构天然适合硬件实现,因为它只需要一个乘法器和一个加法器,外加一个寄存器保存上一周期的结果。

α通常取2/(N+1),比如N=12的EMA,α≈0.1538。在FPGA里,我习惯把α量化成定点数,比如用Q1.15格式。

我的经验: α的量化精度很关键。我试过用8位量化,结果EMA的收敛速度跟软件算出来的差了3个周期。后来改成16位量化,误差就控制在1个LSP以内了。
// EMA核心逻辑(α=0.1538,量化成16位定点数)
parameter ALPHA = 16'd10080;  // 0.1538 * 2^16

always @(posedge clk) begin
    if (reset) begin
        ema_reg <= 0;
    end else if (data_valid) begin
        ema_reg <= (ALPHA * new_data) + ((65536 - ALPHA) * ema_reg);
        // 注意:这里需要做右移16位截断
    end
end

assign ema_out = ema_reg >> 16;

为什么会这样?因为EMA的递归结构天然适合流水线。你算完当前周期,结果直接喂给下一周期,中间不需要额外的存储。相比SMA,EMA在FPGA上其实更「友好」。

3.2 MACD指标计算

MACD由三部分组成:快线(12周期EMA)、慢线(26周期EMA)、以及它们的差值(DIF)。最后还要对DIF做9周期EMA得到信号线(DEA)。

你看,这其实就是三个EMA的级联。在FPGA上实现MACD,我建议分三步走:

  1. 第一步: 同时计算12周期EMA和26周期EMA。这两个可以并行,互不干扰。
  2. 第二步: 计算DIF = EMA12 - EMA26。这里注意数据对齐,两个EMA的延迟要一致。
  3. 第三步: 对DIF做9周期EMA,得到DEA。同时输出MACD柱 = 2 * (DIF - DEA)。
我曾经踩过的坑: 三个EMA的初始化问题。软件里EMA的初始值通常取第一个价格,但硬件里如果复位后第一个数据就当成EMA初值,会导致前N个周期的MACD值严重偏离。我的做法是:前N个周期输出无效标志,等EMA稳定后再输出有效数据。

MACD的硬件实现,说白了就是三个EMA模块的拼装。但要注意,这三个EMA的α值不同(12、26、9对应的α分别是2/13、2/27、2/10),所以每个模块的乘法系数要单独配置。

3.3 RSI指标计算

RSI的计算稍微复杂一点。公式是:RSI = 100 - 100 / (1 + RS),其中RS = 平均上涨幅度 / 平均下跌幅度。

这里的关键是「平均上涨幅度」和「平均下跌幅度」怎么算。通常用SMA或EMA来平滑。我个人习惯用EMA,因为递归结构在硬件里更省事。

具体实现步骤:

  1. 每个周期计算当前价格与上一周期价格的差值ΔP。
  2. 如果ΔP > 0,则U = ΔP,D = 0;否则U = 0,D = -ΔP。
  3. 对U和D分别做N周期EMA(通常N=14),得到AvgU和AvgD。
  4. 计算RS = AvgU / AvgD,然后算RSI。

这里最头疼的是除法。AvgU / AvgD 这个除法在FPGA里很吃资源。我建议用查找表或者CORDIC算法来做除法。如果精度要求不高,也可以用移位近似。

避坑指南: 当AvgD接近0时,RS会趋向无穷大,RSI会直接跳到100。我在项目中遇到过这种情况,导致交易系统误判超买。后来我加了一个保护逻辑:如果AvgD小于某个阈值,直接输出RSI=100。
// RSI计算中的除法保护逻辑
if (avg_d < THRESHOLD) begin
    rsi <= 16'd65535;  // 对应100.0
end else begin
    rs <= (avg_u << 16) / avg_d;  // 定点数除法
    rsi <= 100 - (100 << 16) / (rs + (1<<16));
end

3.4 布林带实现

布林带由三根线组成:中轨(SMA)、上轨(中轨 + k倍标准差)、下轨(中轨 - k倍标准差)。k通常取2。

实现布林带的核心难点在于标准差的计算。标准差公式是:σ = sqrt( Σ(Pi - SMA)^2 / N )。这里涉及三个运算:平方、累加、开方。

我的实现方案是这样的:

  1. 用滑动窗口累加器计算SMA(跟3.1.1一样)。
  2. 同时计算每个价格与SMA的差值,然后平方。
  3. 对平方值再做滑动窗口累加,得到方差。
  4. 对方差做开方,得到标准差。
  5. 最后计算上下轨:SMA ± 2 * σ。

开方运算在FPGA里可以用CORDIC算法,也可以用牛顿迭代法。我个人习惯用CORDIC,因为它没有除法,全是移位和加减,流水线深度可控。

一个小技巧: 布林带的窗口长度N通常取20。如果你用SMA,那就要维护两个滑动窗口(一个算均值,一个算方差)。但如果你用EMA来算均值和方差,就可以省掉FIFO,只用递归结构。代价是EMA算出来的布林带跟标准布林带略有偏差,但实盘交易中影响不大。

3.5 本章小结:四个算法的硬件资源对比

说了这么多,咱们来做个总结。这四个算法在FPGA上的资源消耗和延迟特性,我整理了一张表:

算法 核心运算 DSP资源 BRAM资源 延迟(周期) 我的评价
SMA 加法、减法、移位 0-1 1个FIFO 1 简单粗暴,但N大时BRAM消耗大
EMA 乘法、加法 1-2 0 1 递归结构,省资源,我的首选
MACD 3个EMA + 减法 3-6 0 3 EMA的级联,注意初始化
RSI EMA + 除法 2-4 + 除法器 0 2 + 除法延迟 除法是瓶颈,建议用查找表
布林带 SMA + 平方 + 开方 2-3 + CORDIC 2个FIFO 2 + 开方延迟 资源消耗最大,但值得做

你想想看,这些算法在软件里跑可能就几行代码,但在硬件里每一步都要精打细算。不过一旦在FPGA上跑通了,延迟从微秒级降到纳秒级,那种成就感——嗯,只有做过的人才知道。

最后说一句: 以上所有算法,我都建议加上数据有效标志(valid信号)和流水线级数对齐。别问我为什么——我曾经因为忘记对齐流水线,在MACD的DIF和DEA之间差了2个周期,查了整整一个下午的波形。
交易信号处理核心算法硬件实现框架 价格数据流 SMA 滑动窗口累加器 EMA 递归乘法器 MACD 3级EMA级联 RSI EMA + 除法器 布林带 SMA + 方差 + CORDIC开方 交易信号输出 图例: 移动平均线 MACD RSI 布林带 数据流

好了,这一章的内容就到这里。记住,硬件实现的核心思路是:能并行就并行,能递归就递归,能用移位就别用除法。下一章咱们聊聊更高级的信号处理算法——你猜猜是什么?


公众号:蓝海资料掘金营,微信deep3321