指数移动平均与一阶低通滤波:本质同源,工程与量化通用

发布时间:2026/9/6 11:21:17
指数移动平均与一阶低通滤波:本质同源,工程与量化通用 做了十几年信号处理和量化数据分析我越来越觉得很多概念其实是一回事只是不同领域叫法不一样。就比如今天要聊的“指数移动平均”(EMAExponential Moving Average)和“一阶低通滤波”搞嵌入式的管它叫滤波器做量化的管它叫均线指标但究其本质它们共享同一个数学内核。这篇文章我会把这两个东西彻底拆开讲透原理、实操和调参经验希望能帮你在工程和量化两个领域之间搭一座桥。我自己最早是在传感器数据清洗里接触一阶低通滤波的后来转去做量化分析发现EMA的递推公式简直和老朋友一样亲切。这个知识点应用面极广从单片机上的陀螺仪姿态解算到股票期货的技术指标计算再到工业控制里的PID前级信号处理哪里都有它的影子。适合刚入门信号处理或量化分析的新手也适合那些想打通两个领域知识的老手。很多讲EMA的文章直接甩一个公式就完事很多讲低通滤波的教材全篇拉普拉斯变换和Z变换看得人头大。我想做的是把中间那层窗户纸捅破为什么EMA的系数是2/(N1)为什么一阶低通滤波的α又等于 Δt/(RCΔt)这两个系数到底有什么联系搞懂了这个你在任何一个场景里都能自由切换视角不会再被公式牵着鼻子走。1. 先认清本质指数移动平均就是离散化的一阶低通滤波我见过太多人在两个领域里重复造轮子其实核心逻辑完全一样。理解这一点你能省下大量学习时间还能在做系统设计时多一种思考角度。1.1 同一个递推式两个领域的不同叫法先从最直观的递推公式说起。指数移动平均的标准写法是EMA_t α * X_t (1 - α) * EMA_{t-1}其中 X_t 是当前时刻的输入值EMA_{t-1} 是上一时刻的平滑值α 是平滑系数取值范围在0到1之间。再看一阶低通滤波的离散形式。模拟RC低通滤波器的传递函数是 Y(s)/X(s) 1/(RCs1)用一阶向后差分法离散化之后得到的递推式长这样Y_t α * X_t (1 - α) * Y_{t-1}其中 α Δt / (RC Δt)Δt 是采样周期。把这两个公式并排放一起你会发现它们完完全全是一个结构。你说它是EMA也好说它是一阶低通滤波也好数学上没有区别唯一的区别只是 α 的取值来源不同。在量化领域大家习惯用 N 表示周期然后用 α 2/(N1) 来定义EMA在工程领域大家习惯用时间常数 τRC 来表示滤波强度然后 α Δt/(τΔt)。本质上都是同一个参数只是标定方式不一样。1.2 为什么叫“指数”移动平均指数移动平均这个名字里的“指数”二字揭示了它的核心特性越久远的数据对当前值的影响呈指数级衰减。展开递推式你就明白了EMA_t α * X_t (1-α) * α * X_{t-1} (1-α)^2 * α * X_{t-2} (1-α)^3 * α * X_{t-3} ...每一项前面的权重是 α·(1-α)^k随着 k 增大权重按指数规律递减。这跟简单移动平均SMA完全不同SMA给窗口内所有数据分配相同的权重1/N而EMA是近期的数据权重大老数据权重呈指数式衰减永远也到不了零。这个特性带来一个非常实用的好处EMA不需要维护一个固定长度的数据窗口每次只需要用到上一个平滑值和当前输入内存占用是O(1)。在嵌入式系统里这意味着不需要开一个数组去存历史数据在量化回测里这意味着遍历千万级K线也只需要一个变量来回滚动。这就是为什么EMA在实时性要求高的场景里如此受欢迎。我在实际做嵌入式项目时曾经为了节省RAM把一个50阶的FIR滤波器换成一阶低通滤波效果上虽然做不到那么陡峭的截止特性但资源占用从200字节降到了8字节电池供电的设备待机时间提升了好几个档次。这就是这个“老朋友”的魅力。2. 参数选择和响应特性α到底该怎么取再高深的原理落到实际工程里最核心的问题就是“参数该设多少”。EMA/一阶低通滤波唯一的参数就是 α但它既可以从时间常数的角度标定也可以从平滑周期的角度标定不同的切入角度会直接影响你的使用体验。2.1 从时间常数理解一阶低通滤波在信号处理里一阶低通滤波的强度最直观的参数是时间常数 τ。它告诉你系统在阶跃输入下输出上升到最终值的63.2%需要多长时间。举个例子如果你的传感器输出是100Hz即Δt0.01s想要滤掉截止频率大约15.9Hz的高频噪声那么令截止频率 f_c 1/(2πτ)解得 τ ≈ 0.01s然后 α Δt/(τΔt) 0.01/(0.010.01) 0.5。这里有个重要的经验公式可以记一下当 α 1 - exp(-Δt/τ) 时这个离散滤波器更接近理想的模拟RC响应。当 Δt 远小于 τ 时α ≈ Δt/τ这时候数值很小滤波效果很强但响应也慢。我个人在做传感器滤波时通常会先把 τ 定在噪声基频的5到10倍周期再根据实测波形微调 α很少直接拍脑袋定 α。参数类型公式特征截止频率f_c α / (2πΔt)α较小时高于此频率的信号衰减明显时间常数τ Δt·(1-α)/α阶跃响应到63.2%所需时间响应速度约3τ达到95%稳态用于评估“多久跟上真实值”噪声衰减每个十倍频程约-20dB一阶特性斜率较温和拿这个表去对照你的实际场景就能在做系统设计时对 α 的量级有一个预判而不是靠玄学调参。2.2 从衰减周期理解EMA在量化场景里大家不聊时间常数而是说“周期N”。A股和期货软件里常见的EMA(12)、EMA(26)就是这种表达方式。EMA周期N和平滑系数 α 的换算关系是α 2 / (N 1)这个公式在国内外技术分析软件里几乎是统一标准。N12时α2/13≈0.1538N26时α2/27≈0.0741。所以为什么EMA(12)比EMA(26)对价格反应更灵敏因为 α 更大新数据占的权重更高。你可能会问这个2/(N1)是怎么来的目的是让EMA和数据窗口长度为N的简单移动平均SMA在“平均年龄”上保持一致。SMA窗口N内每个数据的权重都是1/N它的数据平均年龄是(N-1)/2个周期EMA第k个历史数据的权重是α(1-α)^k它的总权重加权平均年龄是 (1-α)/α。让两者相等得到 (1-α)/α (N-1)/2近似解得 α ≈ 2/(N1)。所以N本质上是“为了让平均延迟和N日SMA相当”而不是真的有N个数据参与计算。这个认识非常重要。你在看行情软件里的MA和EMA时千万别以为EMA(12)只看了最近12根K线其实它理论上看了从上市第一天到现在的所有数据只是老数据的权重小到可以忽略。同样的道理当你把EMA的N设成200它也不是200周期的“平均成本”而是对所有历史数据的加权平均只不过超过200个周期之后的数据权重已经衰减到不足初始权重的1/3可以忽略。2.3 工程上的截止频率、金融上的半衰期我个人更喜欢用“半衰期”来理解EMA的平滑强度。半衰期指的是权重衰减到1/2所需的数据点数h ln(0.5) / ln(1-α)当 α0.2时半衰期约3.1个周期当 α0.05时半衰期约13.5个周期。这意味着一阶低通滤波/EMA会把一个突然的阶跃输入在“半衰期”个采样周期内消化一半之后的响应继续指数逼近。这个概念比周期N更容易建立直觉“我做了个EMA(10)它大约需要3.3个K线才能消化掉一半的跳变。”这样你在预判信号延迟时会更加有底。3. 工程侧实战用一阶低通滤波清洗传感器数据聊完理论必须上硬菜。工程上最常见的一阶低通滤波应用场景就是处理传感器信号比如IMU的加速度计输出、光敏电阻的ADC采样、电机电流反馈。我自己做得最多的就是把这种滤波嵌到单片机中断或定时器循环里。3.1 典型场景ADC采集温度/压力信号假设你有一个温度传感器100ms采集一次10Hz采样率但读数存在随机噪声幅度约±2℃。你希望看到真实的温度趋势又不想被噪声干扰。这时一个一阶低通滤波就够用了。设定时间常数 τ1s代入 αΔt/(τΔt)0.1/(10.1)≈0.0909。放在代码里#define ALPHA 0.0909f static float filtered 0.0f; static int first_run 1; float lowpass(float raw) { if (first_run) { filtered raw; first_run 0; } else { filtered ALPHA * raw (1.0f - ALPHA) * filtered; } return filtered; }这段代码在每次ADC转换完成后调用一次就能在MCU上稳定运行。注意我特意加了 first_run 标志位把第一次采样的原始值直接作为初始值避免从0开始的爬坡过程。这是一个很小的细节但很多新手没加这个标志导致开机后前几秒数据完全没法看。3.2 为什么不用单纯取平均有人会问传感器噪声用简单移动平均SMA不就行了吗确实可以但在实时场景里SMA有个天生的延迟问题SMA需要缓存N个点才能输出还会在窗口滑动时产生滞后。而在嵌入式系统里SMA每计算一次要累加N个数当采样率高时占用MCU算力。EMA只需要一条加法和一条乘法计算量恒定不随平滑强度变化。更关键的区别在于SMA对最近数据点“一视同仁”窗口滑动时一个老数据离开窗口会对结果产生跳跃性影响。而EMA的权重是渐变的输出序列天然平滑到没有台阶感。所以在实时性要求高的控制回路里我基本只用EMA/一阶低通SMA只用在离线处理或采样率极低的场景。3.3 工程上容易踩的坑浮点和噪声放大一阶低通滤波虽然简单但工程上有一个容易忽视的坑当 α 非常小比如0.001时浮点精度会限制滤波效果。在FP32单精度下当 (1-α) 接近1时滤波器的输出实际上很难把细微变化累积起来这时你可能需要用双精度或者把状态变量放大处理。我在做惯性导航时曾经调过一组数据α0.0005结果滤波输出在FP32下完全“死”了数据被舍入误差吞掉肉眼可见地平了。换成FP64之后才正常。另一个直观的坑是一阶低通滤波会对阶跃输入产生明显的“拖尾”。当你需要快速响应真实突变时一阶滤波会滞后好几个时间常数。比如电机堵转检测如果你用低通滤波去平滑电流信号堵转瞬间的电流突变会被滤波削掉导致检测延迟。这时不能简单加大 α而应该做一个“带突跳检测的低通滤波”当相邻采样差超过阈值时临时提高 α甚至直接旁路滤波。我常用的一种实现float adaptive_lowpass(float raw, float prev) { float diff fabsf(raw - prev); float alpha ALPHA; if (diff 50.0f) { alpha 0.8f; // 大跳变时快速跟随 } filtered alpha * raw (1.0f - alpha) * filtered; return filtered; }这种自适应策略在实际工程中非常实用能在平滑和响应速度之间找到一个动态平衡点。虽然文章主题是“一阶低通滤波”但到了实战中死板的单一α往往不够用必须结合业务场景做变体。4. 量化侧实战EMA指标与多尺度叠加工程上做完咱们切换到金融数据分析的视角。EMA在量化领域可以说是技术指标的万金油MACD的双线就是基于EMA(12)和EMA(26)的差与DEA构造出来的布林带的中轨也常用EMA来替代SMA以降低延迟。这里我重点讲EMA在因子计算和择时指标里的实操细节。4.1 在Python/Pandas里写EMA不要用循环很多新手在Python里实现EMA时会用for循环逐条算这在数据量小的时候无所谓但放到回测框架里面对几十万根K线循环就会拖慢速度。正确的做法是用pandas的 ewm 方法import pandas as pd # 模拟收盘价 data pd.Series([10.0, 10.2, 10.15, 10.5, 10.8, 10.6, 10.9, 11.0]) # alpha方法直接指定平滑系数 ema_alpha data.ewm(alpha0.2, adjustFalse).mean() # 周期N方法内部转换成 alpha 2/(N1) ema_span data.ewm(span12, adjustFalse).mean() # 半衰期方法 ema_halflife data.ewm(halflife5, adjustFalse).mean()这里的关键参数 adjustFalse 特别重要。Pandas里默认的 adjustTrue 会对早期数据进行权重修正使得结果更接近“从窗口起点开始的指数加权平均”但这和股票软件里EMA的定义不一样。我在回测时踩过这个坑用默认参数算出的EMA和券商软件对不上后来强制 adjustFalse 才完全一致。4.2 为什么量化里经常用EMA而不是SMA行情软件里的MA均线通常指简单移动平均为什么还有EMA存在因为EMA对最新价格敏感能更快反映趋势变化。在趋势跟踪系统里快线用EMA(5)、慢线用EMA(20)金叉死叉的信号明显比MA(5)/MA(20)更早出现。代价是EMA更容易被短期噪声欺骗产生假信号。在实际使用中我自己更喜欢把EMA和SMA结合用SMA确认大趋势方向用EMA捕捉短期转折点。比如价格站上EMA(20)同时MA(60)仍然向上才考虑做多。这样既利用了EMA的敏感性又有SMA的稳定性来过滤噪声。趋势行情里EMA组合效果很好但在震荡行情里EMA的敏感反而会被噪声反复打脸。这也是为什么单靠均线交叉做量化很容易亏钱的深层原因。4.3 多重EMA叠加的思路现在网络热词里经常出现一些奇怪的EMA写法比如指标4 : HHV(VOL, 3) 3 AND REF(OPEN, 1) EMA(EMA(EMA(CLOSE, 3), 3), 5) * 0.985 AND ...这条公式很有意思它把EMA套了三次形成一种高维平滑的复合指标。它的逻辑是先对收盘价做周期为3的EMA再对这个结果做周期为3的EMA再对结果做周期为5的EMA。实际效果是一条比单次EMA更平滑、延迟更大的曲线对价格的小波动几乎无感。叠加EMA本质上是构造一个高阶IIR滤波器其频率特性比一阶更陡峭。这就回到了信号处理的视角。如果你已经理解了前面的公式很容易推断出它的响应比单个EMA更慢但更稳定。这类“多重EMA”在量化公式中通常被用作大环境的趋势参考比如要求当前开盘价高于三重EMA的0.985倍说明多头格局保持完好。这是一个量价配合的思路成交量连续3日放量HHV(VOL,3)3说明资金活跃价格在一组平滑均线上方说明趋势强势。做量化的时候我建议你把EMA直接当成一种带可调截止频率的低通滤波器来用这能避免很多迷惑。不同的周期N对应不同的截止频率N越小越灵敏N越大越平滑。单个EMA的衰减斜率只有-20dB/dec而双重EMA就是-40dB/dec三重EMA是-60dB/dec。所以当你听到“三重指数移动平均”TEMA这类名词时本质就是用多个一阶滤波串联成更高阶滤波来换取更陡的幅频特性。4.4 用EMA构造简单趋势因子说一个实际的例子。我在做商品期货日线趋势跟踪时经常用EMA(20)和EMA(60)构造一个趋势强度因子trend (EMA20 - EMA60) / EMA60这个因子在多头趋势里为正在空头趋势里为负。然后我会再计算trend自身的EMA作为平滑减少每日波动带来的噪声。进而用这个因子去过滤其他策略方向。这个做法本质上是把价格信息降维成一个缓慢变化的“趋势强度信号”很多成熟的CTA策略都在用类似的框架。在这个框架里需要特别注意EMA的初始值。如果用数据的最初几个点来启动EMA前期的因子值会严重失真。我给量化新手的建议是正式计算时给足“预热期”比如从第200根K线后才开始使用因子值之前的计算只作为内部状态累积。这和工程上第一次滤波取原始值作为初值是同一个道理。5. 两套领域的共性难点延迟、噪声和初始值工程和量化看似相隔千里但一旦把EMA/低通滤波用进去你面对的核心矛盾是一模一样的平滑必然带来延迟灵敏度必然引入噪声。这是我做了这么多年项目最深刻的体会。5.1 延迟-噪声困境用生活的话说如果你对信号打折打得太狠噪声是没了但真实信号也被“揉”得变了形如果你完全信任信号那噪声也跟着进来了。这个矛盾在控制领域叫“响应速度与滤波效果的权衡”在量化领域叫“信号灵敏与假信号的权衡”本质是一回事。怎么破我总结了三层策略第一层根据信号的频率分布选择合适的 α。如果噪声集中在高频段真实信号变化较慢那么α小一点没问题。这在工程上可以通过频谱分析确定在量化上可以通过观察K线波动周期来估计。第二层在关键突变场景使用自适应α。前面说过的突跳检测就是这一类。第三层使用更高阶的滤波结构。单个一阶滤波不够陡可以用多个一阶滤波串联成二阶或三阶也可以用卡尔曼滤波替代它在理论上是更加“聪明”的变系数低通滤波器。我个人并不建议无脑追求高阶滤波毕竟一阶滤波最大的优势是简单、稳定、不会震荡。二阶以上如果参数没配对很容易在阶跃响应中出现超调这对很多系统来说是致命的。5.2 初始值的三种处理方式很多人在用EMA或低通滤波时忽略初始值结果前几十个数据点的输出严重偏离真实。我在项目里总结出三种初始值策略直接赋值法把第一次采样值作为初始值。适合传感器开机时数据已经是稳态的场景最简单。预热法在正式使用前先用前M个数据点走过一遍递推把状态变量“热”起来再开始正式输出。适合离线回测或启动时数据已知的场景。均值启动法取前若干个数据点的平均值作为初始值。适合存在明显波动但均值稳定的场景。我在嵌入式里最常用的是直接赋值法在量化回测里最常用的是预热法。具体用哪种取决于你的系统对初始过渡段的容忍度。如果做的是医疗设备开机后的过渡段不能超过几秒如果做的是量化因子前面的几百个K线可以直接当“burn-in”区间处理。5.3 数值实现要小心的问题工程上 α 太小会导致浮点精度问题量化里同样有。比如计算ETF收益率的EMA时如果收益率值在1e-4量级α0.01那么 new_state α*x (1-α)old_state 里 αx 会非常小在FP32下可能被 (1-α)*old_state 完全吞掉。解决办法是改用FP64或者把状态变量放大10000倍来存输出时再缩回来。还有一个几乎人人都会忽略的问题EMA对NaN值敏感。在信号处理里一个传感器故障可能导致输入变成NaN此时递推输出会永久变成NaN因为NaN和任何数运算的结果都是NaN。工程上要加有效性检查量化里处理行情缺失值时也要先做填充或跳过而不是直接把NaN灌进EMA。6. 常见问题排查速查表写到这里我把这些年最常遇到的问题和排查思路整理成一张表方便你直接对照。这些经验都是实操中验过的不是纸上谈兵。现象可能原因解决办法滤波后输出仍有明显毛刺α 偏大截止频率过高减小 α或增加一阶滤波级数真实变化响应太慢α 偏小延迟过大增大 α或用自适应/突跳检测输出一开始严重偏离真实值初始值设置不当首次直接赋值或增加预热期滤波结果“死”住不动α 太小FP32精度不足改用FP64或状态变量放大和行情软件EMA对不上pandas默认 adjustTrue强制 adjustFalse输入有NaN后输出异常递推被NaN污染做输入有效性检查NaN不参与递推回测中前期因子失真预热期不足丢弃前N根K线的信号反复穿过均线导致频繁换仓均线过于灵敏加大N或用多重EMA更“钝”的曲线这个表有心的话可以直接贴在工位上。我踩过其中至少五条坑尤其是浮点精度和初始值这两个问题排查起来非常烧时间。7. 实操总结两种视角如何统一讲到这里你大概已经看出我的核心思路不要把它们当成两个知识体系去学而是当成一个数学模型在两个领域里的不同马甲。做工程时你可用EMA的“周期N”概念来大概感知平滑程度做量化时你可以用一阶低通滤波的“截止频率”来理解周期N的物理意义。这个视角的切换比背任何公式都管用。最后再分享一个我实际操作中的小技巧。如果你在调α时拿不准先用一个阶跃信号测试滤波器观察输出从10%上升到90%需要多少个采样周期。这个上升时间大约是 2.2τ。然后根据业务上能容忍的最大延迟反推 τ再换算成 α 或 N。用这个方法我第一次给工业压力传感器写滤波时本来要试一整天的参数最后半小时就调好了。这条经验在我后来的量化因子设计里也一直有效任何均线类指标在正式上策略前先画一张阶跃响应图看看它有多“肉”心里对延迟有个数再去做参数优化。很多人做量化盲目网格搜索N不关注延迟的影响最后模型在样本外频繁失效往往就是因为没理解均线的滞后本质。聊了这么多希望这篇拆解能让你对“指数移动平均 一阶低通滤波”有一个整体的认识。不要被公式吓到也不要被各领域的黑话绕晕看清数学本质你就掌握了主动权。