写在前面
新手最常问的问题之一:"螺纹钢我该设多少点止损?"
常见的三种回答:
- "50 个点吧。"
- "亏 2% 就走。"
- "跌破支撑位就走。"
这三个答案,错的地方是同一个:它们都默认市场的波动是恒定的。
但市场不是。同一个螺纹钢:
- 行情平静的时候,一天可能就走三十来个点,来回蹭
- 行情剧烈的时候,一天走一两百个点很正常
同一个 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,取以下三个值中的最大值:
| # | 计算 | 捕捉的情况 |
|---|---|---|
| ① | 今日最高 − 今日最低 | 正常的日内波动 |
| ② 绝对值 | 今日最高 − 昨日收盘 | 向上跳空 |
| ③ 绝对值 | 今日最低 − 昨日收盘 | 向下跳空 |
② 和 ③ 里的"昨日收盘",就是专门为跳空准备的。
2.2 这一条对国内期货格外重要
前面几篇讲过两件事,这里正好接上:
- 夜盘的存在(见《发刊词》)让欧美盘的波动不至于全部堆成第二天早上的一个大缺口,但夜盘收盘到次日日盘开盘之间,缺口依然会发生
- 涨跌停(见《期货 vs 股票》)意味着极端行情下会连续跳空,而且你平不掉仓
也就是说,在国内期货市场,跳空不是罕见事故,是常规现象。一个测不出跳空的波动指标,在这里是残废的。
TR 这个"取三者最大"的设计,正是为此而生。
2.3 ATR = TR 的平均
单日 TR 噪音太大,做个平均就平滑了。
三、一个大多数人不知道的细节坑
"平均"这两个字,藏着一个坑。
Wilder 用的不是简单移动平均(SMA),也不是我们通常说的那个 EMA。他用的是自己的一套平滑(后人称 Wilder's Smoothing / RMA):
它等价于平滑系数 α = 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 通常取 2–3。 海龟交易法则用的是 2N(即 2 倍的 20 日 ATR)。
k 该怎么选? 这是一个明确的取舍,没有最优解:
| k 值 | 效果 | 代价 |
|---|---|---|
| 小(1–1.5) | 单笔亏损小、资金效率高 | 胜率显著下降,经常被噪音扫出去 |
| 大(3–4) | 不容易被噪音打掉,能拿住趋势 | 单笔亏损大,同样资金下只能开更小的仓 |
关键认知:把 k 调大并不会降低你的总风险。 因为止损放宽之后,为了保持单笔风险不变,你的手数必须相应减少。 k 决定的不是"你亏多少",而是"你用什么姿势去亏"——是被频繁小额扫损,还是少数几次大额止损。
5.2 用法二:ATR 仓位管理(这才是它真正的杀手锏)
这一条知道的人少得多,但价值远高于止损。
先看一个问题:
你有 20 万,同时想做螺纹钢和黄金。每个品种该开几手?
如果你的答案是"一样多"或者"随便",那你的账户风险是完全失控的——因为这两个品种的波动天差地别。一手螺纹钢波动一天可能亏几百,一手黄金可能亏几千。
ATR 让你可以用同一把尺子量所有品种:
算一遍(数字仅为演示,实际请以交易所合约规格与实时行情为准):
- 账户权益: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:
在移仓换月那一天,新旧合约之间存在价差,会被算成一根巨大的 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. 它救不了重仓。 涨跌停面前,再精确的止损位也只是屏幕上的一个数字。能保护你的从来不是止损单,是仓位。
下一篇:《保证金制度:杠杆是怎么来的,又是怎么要命的》——把各品种的真实资金门槛算给你看。
⚠️ 风险提示 本文仅为期货知识分享与研究交流,不构成任何投资建议。文中所有参数、算例与代码均为原理演示,不构成任何交易参数推荐,历史数据与回测结果不代表未来表现。期货交易具有高杠杆特性,可能导致本金全部损失甚至产生穿仓负债。合约乘数、涨跌停等规则以各期货交易所官方最新公告为准。请通过正规期货公司开户,理性参与、独立决策、风险自担。