止损越紧,收益反而越高——这个结论 90% 的散户不会信。上一篇凯利公式说「最优仓位是 75%,不是满仓」,但仓位只是入场的一半,另一半是出场。这一篇用双均线策略的 91 笔真实交易,实测三种止损方法,结果有一个反直觉的发现:更紧的止损不仅砍了回撤,还涨了收益。
一、先说透本质:止损不是认输,是换资格
散户对止损的最大误解:止损 = 认输 = 亏了。但量化的逻辑是——
止损的本质:用一笔小亏,换一个重新入场的资格。
入场信号是对的,止损后信号再次出现时你会重新进场。你只是把一笔大亏拆成了两笔小亏加一笔盈利。所以止损好不好不能用单笔盈亏判断,要看整体:止损后总收益有没有变好?回撤有没有变小?这必须回测——拍脑袋的回答往往是错的。
二、实验一:固定比例止损,紧的好还是松的好?
数据:沪深300 指数,双均线策略(5 日上穿 20 日线做多,下穿清仓),T+1 执行,单边成本千 1。
import baostock as bs
import pandas as pd
bs.login()
rs = bs.query_history_k_data_plus(
'sh.000300', 'date,open,high,low,close',
start_date='2015-01-01', end_date='2026-09-01',
frequency='d', adjustflag='3')
rows = []
while rs.error_code == '0' and rs.next():
rows.append(rs.get_row_data())
df = pd.DataFrame(rows, columns=rs.fields)
bs.logout()
for c in ['open', 'high', 'low', 'close']:
df[c] = pd.to_numeric(df[c])
df['pct'] = df['close'].pct_change().fillna(0)
df['ma_f'] = df['close'].rolling(5).mean()
df['ma_s'] = df['close'].rolling(20).mean()
df = df.dropna(subset=['ma_s']).reset_index(drop=True)
raw = (df['ma_f'] > df['ma_s']).astype(int)
pos_raw = raw.shift(1).fillna(0) # ★ T日信号,T+1执行
# 固定比例止损:持仓期间从入场价回撤超过 stop_pct 就清仓
def apply_fixed_stop(pos_raw, close, stop_pct):
pos = pos_raw.copy().values
entry = None
for i in range(len(pos)):
if pos[i] == 1 and pos[i-1] == 0:
entry = close[i] # 入场
if pos[i] == 1 and entry is not None and close[i] <= entry * (1 - stop_pct):
pos[i] = 0 # 触发止损,当日清仓
entry = None
if pos[i] == 0:
entry = None
return pd.Series(pos, index=pos_raw.index)
pos = apply_fixed_stop(pos_raw, df['close'].values, 0.05)
trades = pos.diff().abs().fillna(0)
nav = (1 + pos * df['pct'] - trades * 0.001).cumprod()
ann = nav.iloc[-1] ** (252 / len(nav)) - 1
dd = (nav / nav.cummax() - 1).min()
print(f'年化 {ann:.2%} | 最大回撤 {dd:.2%}')
结果(完整扫描含参数网格):
| 止损比例 |
年化收益 |
最大回撤 |
夏普 |
备注 |
| 不止损 |
17.55% |
−32.7% |
1.05 |
基准 |
| −5% |
18.65% |
−19.9% |
1.12 |
全面最优 |
| −8% |
18.34% |
−22.4% |
1.10 |
次优 |
| −10% / −15% |
17.55% |
−32.7% |
1.05 |
从未触发 |
反直觉结论:−5% 止损不仅砍了回撤(−33%→−20%),还涨了收益(17.6%→18.7%)。
原因:策略期望为正,止损后信号再现时你会重新进场——小亏换大赚,整体反而更好。而 −10% 以上的止损从未触发:均线的自然退出比止损更早。
三、实验二:ATR 吊灯止损——动态跟踪的威力
吊灯止损:止损线 = 入场后最高价 − N×ATR。价格涨,止损线跟着上移;跌破即出场。
df['prev_close'] = df['close'].shift(1)
tr = pd.concat([df['high'] - df['low'],
(df['high'] - df['prev_close']).abs(),
(df['low'] - df['prev_close']).abs()], axis=1).max(axis=1)
df['atr'] = tr.rolling(14).mean()
def apply_chandelier(pos_raw, high, close, atr, mult):
pos = pos_raw.copy().values
entry_i = None
for i in range(1, len(pos)):
if pos[i] == 1 and pos[i-1] == 0:
entry_i = i
if pos[i] == 1 and entry_i is not None:
hh = high[entry_i:i+1].max() # 入场以来最高价
if close[i] <= hh - mult * atr[i]: # 跌破吊灯线
pos[i] = 0
entry_i = None
if pos[i] == 0:
entry_i = None
return pd.Series(pos, index=pos_raw.index)
pos = apply_chandelier(pos_raw, df['high'].values, df['close'].values,
df['atr'].values, mult=2.0)
| ATR 倍数 |
年化收益 |
最大回撤 |
夏普 |
| 1.5× |
13.67% |
−11.3% |
1.55 |
| 2.0× |
17.42% |
−11.3% |
1.57 |
| 2.5× |
17.73% |
−17.4% |
1.37 |
| 3.5× |
21.88% |
−19.7% |
1.34 |
2×ATR 是甜点位:回撤只有基准的 1/3,收益几乎不变,夏普 1.57 全实验最高。太紧(1.5×)会被正常波动扫出去,太松(3.5×)回撤控制失效。
四、实验三:时间止损——最不靠谱的一种
固定持有 10/20/30/60 天退出,年化 3.8% / 6.0% / 8.0% / 12.2%,全面劣于不限时退出的基准(17.55%)。原因:趋势的持续时间不可预测,强制退出会打断正在运行的盈利交易。时间止损不适合趋势策略,只适合事件驱动型。
五、成绩单与实操建议
| 方法 |
年化 |
回撤 |
夏普 |
判定 |
| 不止损(基准) |
17.55% |
−32.7% |
1.05 |
基准 |
| 固定 −5% |
18.65% |
−19.9% |
1.12 |
✓ 收益回撤双优 |
| ATR 2.0× |
17.42% |
−11.3% |
1.57 |
✓ 夏普最高 |
| 时间止损 30 天 |
7.99% |
−29.4% |
0.71 |
✗ 劣于基准 |
两个可组合的方案(二选一,不要叠加):① 固定 −5% 止损(简单,双优);② ATR 2× 吊灯(夏普最高,回撤最浅)。推荐半凯利仓位(≈37%,见上一篇)+ ATR 2× 吊灯组合——账户实际回撤约 −4%,普通人的心理承受范围内。
三条边界:止损参数也过拟合(−5% 恰好是全扫描最优,实盘需定期重估);止损不适合所有策略(均值回归越跌越该买,止损反而有害);止损要和入场规则配套(趋势策略配 ATR,均值回归配固定)。
工具箱系列四篇至此闭环:①跑通回测 → ②防坑三件套 → ③凯利公式仓位 → ④止盈止损。全套代码开源可复现(GitHub 搜 quant-toolbox)。
我把量化工具链的实测笔记整理在公众号「晋盈投资」持续更新(不荐股、不承诺收益,只聊工具和方法)。
欢迎讨论:你的止损是怎么设的?被震出去过吗?
风险提示:本文为量化学习方法分享,不构成任何投资建议,不涉及具体标的推荐,亦不承诺收益。止损参数为教学示意,历史回测不代表未来收益。市场有风险,决策需谨慎。