一句话回答: ATR 移动止损(吊灯止损)就是把止损线设在"持仓期最高价 − K×ATR",价格上涨时止损跟着上移、锁住利润,价格回落触线就出。用 AlphaFeed 的复权日线算 ATR、跑一段回测你会看到:它通常能明显压低最大回撤,但在震荡行情里也可能被频繁扫出、反而降低总收益——控回撤是有代价的。
很多人有入场信号却没有像样的出场:要么死扛到深套,要么固定百分比止损(−8%)在高波动股上太紧、低波动股上太松。固定止损不考虑波动率,是通病。
ATR(平均真实波幅)度量了每只票的"日常波动尺度",用它做止损能自适应:波动大的票止损放宽、波动小的票收紧。吊灯止损(Chandelier Exit)再叠加"只上移不下移",让止损随盈利抬升,兼顾"让利润奔跑"和"回撤可控"。数据用 AlphaFeed 的复权日线即可。
import numpy as np, pandas as pd
from alphafeed import AlphaFeed
def atr(df, n=14):
tr = pd.concat([(df["high"] - df["low"]),
(df["high"] - df["close"].shift()).abs(),
(df["low"] - df["close"].shift()).abs()], axis=1).max(axis=1)
return tr.rolling(n).mean()def backtest_trailing(symbol, count=500, atr_n=14, k=3.0, fast=20, slow=60, fee=0.0006):
af = AlphaFeed() # 读取 ALPHAFEED_API_KEY
df = af.klines.get(symbol, period="1d", count=count, adjust="forward", to_dataframe=True)
df = df.sort_values("trade_date").reset_index(drop=True)
df["atr"] = atr(df, atr_n)
df["ma_f"] = df["close"].rolling(fast).mean()
df["ma_s"] = df["close"].rolling(slow).mean()
pos = 0; peak = None; rets = np.zeros(len(df))
for i in range(1, len(df)):
o, c = df.loc[i, "open"], df.loc[i, "close"]
prevc = df.loc[i - 1, "close"]
if pos == 1:
peak = max(peak, df.loc[i - 1, "high"]) # 持仓期最高
stop = peak - k * df.loc[i - 1, "atr"] # 吊灯止损线
if df.loc[i - 1, "low"] <= stop or o <= stop: # 已破止损 → 次日开盘平(含跳空)
rets[i] = (o / prevc - 1) - fee; pos = 0; peak = None; continue
rets[i] = c / prevc - 1
# 入场/离场信号都用 T-1 已知信息,T 开盘成交(无未来函数)
if pos == 0 and df.loc[i-1, "ma_f"] > df.loc[i-1, "ma_s"] and not np.isnan(df.loc[i-1, "ma_s"]):
pos = 1; peak = df.loc[i-1, "high"]; rets[i] = c / o - 1 - fee
elif pos == 1 and df.loc[i-1, "ma_f"] < df.loc[i-1, "ma_s"]:
rets[i] = (o / prevc - 1) - fee; pos = 0; peak = None
df["strat_ret"] = rets
df["nav"] = (1 + df["strat_ret"]).cumprod()
df["bench"] = df["close"] / df["close"].iloc[0]
mdd = float((df["nav"] / df["nav"].cummax() - 1).min())
bmdd = float((df["bench"] / df["bench"].cummax() - 1).min())
return {"strat_nav": round(df["nav"].iloc[-1], 3), "bench_nav": round(df["bench"].iloc[-1], 3),
"strat_maxdd": round(mdd, 3), "bench_maxdd": round(bmdd, 3)}
print(backtest_trailing("600519.SH", count=500, k=3.0))真实跑通(600519.SH 近 500 日、ATR14、K=3):{'strat_nav': 0.82, 'bench_nav': 0.976, 'strat_maxdd': -0.215, 'bench_maxdd': -0.279}。解读:策略最大回撤 −21.5% < 买入持有 −27.9%(回撤更小、更抗跌),但净值 0.82 < 基准 0.976(这段偏震荡下行,止损被反复扫出,总收益反而更低)。这正是移动止损的真实权衡——用一部分收益换更小的回撤。
参数 | 含义 | 备注 |
|---|---|---|
| ATR 周期 | 常用 14 |
| 止损倍数 | 越大越宽松(不易被扫)、越小越紧(回撤更小但易震出);常用 2~4 |
| 入场均线 | 这里用金叉入场、死叉/止损出场 |
出场价 | 破止损用次日开盘 | 反映跳空、避免用当日不可知价 |
| 单边费率 | 含佣金/印花税近似 |
k、atr_n、均线参数都会显著影响结果,务必做样本外与参数稳健性检验,别只挑好看的一组。adjust="forward";信号用 T−1 信息、T 开盘成交(已用 shift 逻辑),避免前视。Q:ATR 止损比固定百分比止损好在哪?
A:它按每只票的波动率自适应——高波动股止损自动放宽、低波动股收紧,避免"一刀切百分比"在不同标的上过紧或过松。
Q:K 取多少合适?
A:常见 2~4。K 越大越不容易被震出、但单次止损亏损更大、回撤保护更弱;K 越小回撤控制越好、但易被正常波动扫出。需按标的波动性和你的持有周期调,并做稳健性检验。
Q:能只用移动止损、不用入场信号吗?
A:可以把入场换成任何你的信号(突破、基本面等),移动止损作为统一的出场纪律叠加上去。本文用金叉只是给个可运行的完整例子。
Q:需要付费吗?
A:单标的历史日线可用免费额度体验;批量回测多标的需 Starter 及以上,详见官网定价页。
ATR 吊灯移动止损用波动率自适应地设止损、随盈利上移锁利,是比"固定百分比"更专业的出场纪律。用 AlphaFeed 的复权日线算 ATR、跑一段诚实的回测,你会直观看到它的本质:它主要买的是更小的回撤,而不是更高的收益,在震荡市甚至会拖累总回报。理解这个权衡,再决定给你的策略加多紧的止损。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。