跳到正文
进阶指标2026-08-0310分钟阅读

ATR:真实波幅与它在止损中的正确用法

新手学习路线 · 第 3 阶段「控制风险」第 3 / 5 篇

目录(10 节)
  1. 写在前面
  2. 一、定义:ATR 是一把尺子,不是一个信号
  3. 二、公式:为什么要取三个值里的最大值
  4. 三、一个大多数人不知道的细节坑
  5. 四、参数:14 是从哪来的,该不该改
  6. 五、正确用法(三个,按价值排序)
  7. 六、失效场景(这一节比上一节更重要)
  8. 七、代码
  9. 八、三个你该自己跑一遍的实验
  10. 九、结论

写在前面

新手最常问的问题之一:"螺纹钢我该设多少点止损?"

常见的三种回答:

  1. "50 个点吧。"
  2. "亏 2% 就走。"
  3. "跌破支撑位就走。"

这三个答案,错的地方是同一个:它们都默认市场的波动是恒定的。

但市场不是。同一个螺纹钢:

  • 行情平静的时候,一天可能就走三十来个点,来回蹭
  • 行情剧烈的时候,一天走一两百个点很正常

同一个 50 点止损,放在平静期是太宽(亏满了才走,白亏),放在剧烈期是太窄(正常的日内噪音就能把你扫出去,然后行情按你原来的方向走了)。

你以为你在止损,其实你只是在用一个固定的尺子,量一个尺寸一直在变的东西。

ATR 要解决的就是这一件事:给"波动"一个单位,让止损跟着市场一起呼吸。

这是本号《米粒指标》栏目的第一篇。之所以从 ATR 开始,不是因为它最有名(它远不如 MACD 有名),而是因为它是少数几个真正能改善你交易结果的指标——而且它改善的不是"你买在哪儿",是"你亏多少"。


一、定义:ATR 是一把尺子,不是一个信号

ATR(Average True Range,平均真实波幅),由 Welles Wilder 在 1978 年的《New Concepts in Technical Trading Systems》中提出。同一本书里还诞生了 RSI、ADX、抛物线 SAR——这位老兄一个人贡献了半部技术分析教科书。

一句话定义:

ATR 衡量的是"最近这段时间,价格平均每天波动多大"。

请注意这句话里没有方向。

这是全文第一个、也是最重要的一个点:

⚠️ ATR 只回答"多大",从不回答"往哪儿"。

ATR 上升 ≠ 价格上涨,ATR 上升只意味着市场变得更剧烈了——可能是暴涨,也可能是暴跌。

ATR 是一把尺子,不是一个买卖信号。 用它来判断方向,等于拿卷尺测体温。这是它被误用的头号方式。


二、公式:为什么要取三个值里的最大值

2.1 先看"真实波幅" TR

单看某一天的波动幅度,最直觉的算法是「最高价 − 最低价」。

但这个算法有个大漏洞:它测不出跳空。

设想一天:昨天收盘 3800,今天低开在 3600,然后全天在 3600–3620 之间窄幅整理。

  • 按「最高 − 最低」算:波动只有 20 点,看起来风平浪静
  • 但实际上,从昨收到今天,价格已经走了 200 点

对持仓的人来说,这一天的真实杀伤力是 200 点,不是 20 点。

所以 Wilder 定义了真实波幅 True Range,取以下三个值中的最大值:

#计算捕捉的情况
①今日最高 − 今日最低正常的日内波动
② 绝对值今日最高 − 昨日收盘向上跳空
③ 绝对值今日最低 − 昨日收盘向下跳空
TR=max⁡(H−L, ∣H−Cprev∣, ∣L−Cprev∣)TR = \max(H - L,\ |H - C_{prev}|,\ |L - C_{prev}|)

② 和 ③ 里的"昨日收盘",就是专门为跳空准备的。

2.2 这一条对国内期货格外重要

前面几篇讲过两件事,这里正好接上:

  • 夜盘的存在(见《发刊词》)让欧美盘的波动不至于全部堆成第二天早上的一个大缺口,但夜盘收盘到次日日盘开盘之间,缺口依然会发生
  • 涨跌停(见《期货 vs 股票》)意味着极端行情下会连续跳空,而且你平不掉仓

也就是说,在国内期货市场,跳空不是罕见事故,是常规现象。一个测不出跳空的波动指标,在这里是残废的。

TR 这个"取三者最大"的设计,正是为此而生。

2.3 ATR = TR 的平均

ATRn=TR 的 n 期平均ATR_n = \text{TR 的 } n \text{ 期平均}

单日 TR 噪音太大,做个平均就平滑了。


三、一个大多数人不知道的细节坑

"平均"这两个字,藏着一个坑。

Wilder 用的不是简单移动平均(SMA),也不是我们通常说的那个 EMA。他用的是自己的一套平滑(后人称 Wilder's Smoothing / RMA):

ATRt=ATRt−1×(n−1)+TRtnATR_t = \frac{ATR_{t-1} \times (n-1) + TR_t}{n}

它等价于平滑系数 α = 1/n 的指数移动平均。

而大多数人默认的 EMA 用的是 α = 2/(n+1)。

两者差多少? 一个 14 期的 Wilder 平滑,其平滑强度大致相当于 27 期左右的常规 EMA——差了将近一倍。

后果:不同软件、不同库算出来的 "ATR(14)",数值可能明显不同。 你在 A 平台调好的 2 × ATR 止损参数,搬到 B 平台可能就完全不是那个宽度了。

这不是学究式的抠字眼,这是实盘会亏钱的差别。第七节的代码里我把三种算法都写出来了,你可以自己跑一遍看看差多少。

建议:搞清楚你用的平台是哪种算法,然后始终用同一种。用哪种其实没那么重要,前后不一致才要命。


四、参数:14 是从哪来的,该不该改

Wilder 的默认值是 14。 这个数字没有什么神圣的数学依据,就是他当年在日线上用着顺手。

  • 调小(7–10):反应更快,能更早察觉波动率的抬升;代价是噪音大,止损宽度会跟着日内情绪频繁抖动
  • 调大(20–30):更稳定,止损宽度不会一惊一乍;代价是行情突然变剧烈时,它反应慢半拍
  • 海龟交易法则用的是 20 日(他们管它叫 N)

我的建议:别在这个参数上花太多时间。

ATR 是这一类指标里参数敏感度相对最低的一个——因为它测的是波动率,而波动率本身具有很强的持续性(大波动之后往往跟着大波动,这在学术上叫波动率聚集)。14 和 20 算出来的结果,趋势方向基本一致。

真正会显著改变你结果的,不是 n,而是下一节里那个 k。


五、正确用法(三个,按价值排序)

5.1 用法一:ATR 止损(最常见)

多单止损=入场价−k×ATR\text{多单止损} = \text{入场价} - k \times ATR 空单止损=入场价+k×ATR\text{空单止损} = \text{入场价} + k \times ATR

k 通常取 2–3。 海龟交易法则用的是 2N(即 2 倍的 20 日 ATR)。

k 该怎么选? 这是一个明确的取舍,没有最优解:

k 值效果代价
小(1–1.5)单笔亏损小、资金效率高胜率显著下降,经常被噪音扫出去
大(3–4)不容易被噪音打掉,能拿住趋势单笔亏损大,同样资金下只能开更小的仓

关键认知:把 k 调大并不会降低你的总风险。 因为止损放宽之后,为了保持单笔风险不变,你的手数必须相应减少。 k 决定的不是"你亏多少",而是"你用什么姿势去亏"——是被频繁小额扫损,还是少数几次大额止损。

5.2 用法二:ATR 仓位管理(这才是它真正的杀手锏)

这一条知道的人少得多,但价值远高于止损。

先看一个问题:

你有 20 万,同时想做螺纹钢和黄金。每个品种该开几手?

如果你的答案是"一样多"或者"随便",那你的账户风险是完全失控的——因为这两个品种的波动天差地别。一手螺纹钢波动一天可能亏几百,一手黄金可能亏几千。

ATR 让你可以用同一把尺子量所有品种:

手数=账户权益×单笔风险比例k×ATR×合约乘数\text{手数} = \frac{\text{账户权益} \times \text{单笔风险比例}}{k \times ATR \times \text{合约乘数}}

算一遍(数字仅为演示,实际请以交易所合约规格与实时行情为准):

  • 账户权益:20 万
  • 单笔愿意承担的风险:1% = 2000 元
  • 螺纹钢合约乘数:10 吨/手
  • 当前 ATR(14):假设 60 元/吨
  • k = 2

每手的风险敞口 = 2 × 60 × 10 = 1200 元

手数 = 2000 ÷ 1200 ≈ 1.67 → 开 1 手

这个公式的威力在于:

不管你做什么品种、不管市场是平静还是剧烈,每一笔交易亏掉的钱都是一样的(约账户的 1%)。

  • 波动大的品种 → ATR 大 → 自动开小仓
  • 波动小的品种 → ATR 小 → 自动开大仓
  • 同一品种进入剧烈期 → ATR 变大 → 自动降仓

风险被归一化了。 你的账户不再被某个恰好在暴动的品种绑架。

这就是海龟交易法则里"一个 Unit"的核心思想,也是绝大部分专业 CTA 做仓位管理的基础思路。

如果这篇文章你只带走一样东西,请带走这个公式,而不是止损那个。

5.3 用法三:行情状态过滤

ATR 处于极低水平 → 市场缩量窄幅 → 突破类策略在这种环境下容易被反复打脸(假突破多)。

反过来,ATR 从低位快速抬升,往往对应着方向选择的开始。

注意:这只是个粗糙的环境判断,不是入场信号。它告诉你"现在适不适合用某类策略",不告诉你"该买还是该卖"。


六、失效场景(这一节比上一节更重要)

任何只讲用法不讲失效的指标文章,都是在害人。ATR 有五个明确的失效边界:

① 它没有方向——头号误用

再说一遍:ATR 上升只说明波动变大了,不说明要涨。 拿 ATR 当买卖信号,是最常见也最昂贵的错误。

② 它是滞后的

ATR 描述的是已经发生的波动。

黑天鹅来临的那一刻,ATR 还停留在"昨天很平静"的水平上,于是它给出的止损宽度恰恰在最需要宽的时候是最窄的。

ATR 帮你适应波动率的缓慢变化,救不了你于突变。

③ 涨跌停时,ATR 止损直接失效

这是国内期货特有的、也是最致命的一条。

你算得再精确的止损位,如果价格封死在跌停板上没有对手盘,你就是平不掉。

止损是一个需要成交才能兑现的承诺。ATR 只能帮你算价位,不能帮你保证成交。

唯一的对策不是更好的止损,是更轻的仓位。

④ 不同品种的 ATR 绝对值不能横向比较

螺纹钢的 ATR 是"多少元/吨",黄金的 ATR 是"多少元/克",单位都不一样,比大小毫无意义。

要横向比较,必须用相对波幅:

ATR%=ATR收盘价×100%ATR\% = \frac{ATR}{\text{收盘价}} \times 100\%

这个数才是可比的。它回答的是"这个品种一天大约波动百分之几"。

⑤ 主力连续合约的陷阱(量化的人尤其注意)

如果你用未经价差调整的主力连续合约数据算 ATR:

在移仓换月那一天,新旧合约之间存在价差,会被算成一根巨大的 TR——一个纯粹由拼接方式造成的、市场上根本没发生过的"暴动"。

后果:那几天的 ATR 被严重高估,你的止损宽度和仓位计算全部失真,而回测里你根本看不出来。

务必使用做过价差调整的连续合约,或者直接在单个合约上计算。 (主力连续合约怎么拼接,值得单开一篇,我放在后面的排期里。)


七、代码

ATR 的计算逻辑写成纯函数,不绑定任何数据源——你从哪儿拿的数据都能用,只要有 high / low / close 三列、按时间升序排列。

import pandas as pd

def true_range(df: pd.DataFrame) -> pd.Series:
    """真实波幅 TR = max(H-L, |H-PrevC|, |L-PrevC|)"""
    prev_close = df["close"].shift(1)
    return pd.concat([
        df["high"] - df["low"],
        (df["high"] - prev_close).abs(),
        (df["low"]  - prev_close).abs(),
    ], axis=1).max(axis=1)

def atr_wilder(df: pd.DataFrame, n: int = 14) -> pd.Series:
    """Wilder 原版平滑(RMA),alpha = 1/n —— 这是"正统"的 ATR"""
    return true_range(df).ewm(alpha=1/n, adjust=False, min_periods=n).mean()

def atr_sma(df: pd.DataFrame, n: int = 14) -> pd.Series:
    """简单移动平均版本"""
    return true_range(df).rolling(n).mean()

def atr_ema(df: pd.DataFrame, n: int = 14) -> pd.Series:
    """常规 EMA 版本,alpha = 2/(n+1) —— 不少平台默认用这个"""
    return true_range(df).ewm(span=n, adjust=False, min_periods=n).mean()

def atr_pct(df: pd.DataFrame, n: int = 14) -> pd.Series:
    """相对波幅,唯一能跨品种横向比较的口径"""
    return atr_wilder(df, n) / df["close"] * 100

一个实现细节:Wilder 原始算法的种子值是前 n 根 TR 的简单平均,而 ewm 是从第一个有效值开始递推。两者在最开始的几十根 K 线上会有差异,之后会收敛。做长周期回测可以忽略;如果你的样本很短,就自己写循环实现原版递推。

仓位管理函数(5.2 那个公式):

def position_size(equity: float, risk_pct: float,
                  atr: float, k: float, multiplier: float) -> int:
    """
    equity     : 账户权益
    risk_pct   : 单笔愿意承担的风险比例,如 0.01 表示 1%
    atr        : 当前 ATR(价格单位/手的计价单位)
    k          : 止损倍数,常用 2~3
    multiplier : 合约乘数(如螺纹钢 10 吨/手)
    """
    risk_per_lot = k * atr * multiplier
    if risk_per_lot <= 0:
        return 0
    return int(equity * risk_pct / risk_per_lot)   # 向下取整,宁少勿多

注意最后一行的 int():向下取整,不是四舍五入。在仓位这件事上,永远往少了取。

取数据(以 AKShare 为例,接口和列名可能随版本变化,以官方文档为准):

import akshare as ak

df = ak.futures_main_sina(symbol="RB0")          # 螺纹钢主力连续
df = df.rename(columns={
    "日期": "date", "最高价": "high",
    "最低价": "low", "收盘价": "close",
})
df = df[["date", "high", "low", "close"]].sort_values("date").reset_index(drop=True)

df["atr"]  = atr_wilder(df, 14)
df["atr%"] = atr_pct(df, 14)

⚠️ 回看第六节第 ⑤ 条:这样直接取到的主力连续合约大概率没做价差调整,移仓日的 ATR 会失真。拿它做演示可以,拿它做实盘参数不行。


八、三个你该自己跑一遍的实验

指标文章最没价值的写法,就是作者给一堆数字让你信。这三个实验代码就在上面,跑一遍你会记一辈子。

实验一:三种平滑算法差多少

把 atr_wilder / atr_sma / atr_ema 在同一个品种上画在一张图里。

你会看到:三条线明显分离,Wilder 那条最平滑、位置通常也最不一样。

结论:你在别人文章里看到的"2 倍 ATR 止损",必须先问清楚是哪种 ATR。

实验二:不同品种的 ATR% 差多少

挑五六个品种(比如螺纹钢、铜、黄金、豆粕、PTA),各算一条 atr%,放在一张图上。

你会看到:各品种的日均相对波幅存在明显差距,而且这个差距长期稳定存在。

结论:"每个品种都开 2 手"是一种未经计算的风险敞口。 5.2 那个公式不是锦上添花,是必需品。

实验三:固定点数止损 vs ATR 止损

同一套入场规则,一组用固定点数止损,一组用 2 × ATR,比较两者的最大回撤和单笔亏损的离散程度(标准差)。

你重点要看的不是谁赚得多(那可能只是运气),而是:ATR 止损那一组,每一笔亏损的金额是不是明显更一致、更可控。

这才是 ATR 的真正价值:它不提高你的胜率,它让你的亏损变得可预测。


九、结论

用三句话收尾:

1. ATR 是尺子,不是信号。 它只回答"多大",永远不回答"往哪儿"。指望它告诉你买卖方向,是拿卷尺测体温。

2. 它最大的价值在仓位,不在止损。 用它把不同品种、不同市况下的单笔风险归一化——这一条的收益,远大于把止损从固定点数换成 ATR。

3. 它救不了重仓。 涨跌停面前,再精确的止损位也只是屏幕上的一个数字。能保护你的从来不是止损单,是仓位。


下一篇:《保证金制度:杠杆是怎么来的,又是怎么要命的》——把各品种的真实资金门槛算给你看。


⚠️ 风险提示 本文仅为期货知识分享与研究交流,不构成任何投资建议。文中所有参数、算例与代码均为原理演示,不构成任何交易参数推荐,历史数据与回测结果不代表未来表现。期货交易具有高杠杆特性,可能导致本金全部损失甚至产生穿仓负债。合约乘数、涨跌停等规则以各期货交易所官方最新公告为准。请通过正规期货公司开户,理性参与、独立决策、风险自担。

本文首发于微信公众号「米粒论期」 · 阅读原文

免责声明:本文仅供学习参考,不构成任何投资建议。期货交易存在风险,入市需谨慎。

本文涉及

文中提到的概念、品种和书目,都能在站内查到。

沪ICP备2026050790号-1