一句话回答:SAR(Stop And Reverse)天生是「翻转」信号——价格升破 SAR 做多、跌破 SAR 平仓(A 股无法裸做空,只做多头段)。用 AlphaFeed 取回前复权日线算 SAR,用「当日出信号、次日开盘成交」避免未来函数,扣手续费,几十行就能回测出收益、最大回撤与交易次数,并和买入持有对比检验它值不值。
为什么会有这个问题 / 传统做法的痛点
SAR 常被当「移动止损 + 趋势跟踪」用,但「按 SAR 翻转做多做空到底赚不赚钱」要回测验证。两个常见错误:一是未来函数(当日出信号用当日收盘价成交,实际做不到);二是忽略 A 股限制与手续费(不能裸卖空、换手有成本)。正确做法:前复权价算 SAR、次日开盘成交、只做多头段、扣费、对比基准。
分步骤解决(每步配可运行代码)
第 1 步:取前复权日线并计算 SAR
fromalphafeedimportAlphaFeedimportpandasaspd af=AlphaFeed()# 读取 ALPHAFEED_API_KEYdf=af.klines.get("600519.SH",period="1d",count=500,adjust="forward",to_dataframe=True)df=df.sort_values("trade_date").reset_index(drop=True)defparabolic_sar(high,low,af_step=0.02,af_max=0.20):high=high.to_numpy();low=low.to_numpy();n=len(high)sar=[0.0]*n;trend_up=True;af=af_step;ep=high[0];sar[0]=low[0]foriinrange(1,n):prev=sar[i-1];sar[i]=prev+af*(ep-prev)iftrend_up:sar[i]=min(sar[i],low[i-1],low[max(i-2,0)])ifhigh[i]>ep:ep=high[i];af=min(af+af_step,af_max)iflow[i]<sar[i]:trend_up=False;sar[i]=ep;ep=low[i];af=af_stepelse:sar[i]=max(sar[i],high[i-1],high[max(i-2,0)])iflow[i]<ep:ep=low[i];af=min(af+af_step,af_max)ifhigh[i]>sar[i]:trend_up=True;sar[i]=ep;ep=high[i];af=af_stepreturnpd.Series(sar,index=range(n))df["SAR"]=parabolic_sar(df["high"],df["low"])第 2 步:生成多空信号 → 次日开盘成交(避免未来函数)
# 收盘价在 SAR 上方=多头持有,下方=空仓(A 股不裸做空)df["long"]=(df["close"]>df["SAR"]).astype(int)df["position"]=df["long"].shift(1).fillna(0)# 当日信号,次日才持仓第 3 步:按次日开盘价 + 手续费计算净值
FEE=0.0013# 单边约 0.13%(佣金+印花税近似)df["open_ret"]=df["open"].pct_change().shift(-1)# 持仓吃 t→t+1 开盘间收益df["trade"]=df["position"].diff().abs().fillna(0)# 换手(建仓/平仓)df["strat_ret"]=df["position"]*df["open_ret"].fillna(0)-df["trade"]*FEE df["equity"]=(1+df["strat_ret"]).cumprod()df["bench"]=(1+df["close"].pct_change().fillna(0)).cumprod()# 买入持有基准第 4 步:输出绩效并与买入持有对比
defstats(equity,rets):total=equity.iloc[-1]-1cagr=equity.iloc[-1]**(252/len(equity))-1dd=(equity/equity.cummax()-1).min()returntotal,cagr,dd t_s,c_s,d_s=stats(df["equity"],df["strat_ret"])t_b,c_b,d_b=stats(df["bench"],df["close"].pct_change())trades=int(df["trade"].sum()//2)print(f"SAR策略 收益{t_s:.2%}年化{c_s:.2%}回撤{d_s:.2%}交易{trades}次")print(f"买入持有 收益{t_b:.2%}年化{c_b:.2%}回撤{d_b:.2%}")关键参数:
| 参数 | 含义 | 本文取值 |
|---|---|---|
af_step/af_max | SAR 加速因子步长/上限 | 0.02 / 0.20 |
FEE | 单边手续费 | 0.0013 |
| 成交时点 | 避免未来函数 | 次日开盘 |
| 方向 | A 股只做多头段 | 空仓代替做空 |
关键坑与注意事项
- 避免未来函数:信号当日收盘出现,必须次日开盘成交;本文用
position.shift(1)实现。 - A 股不能裸做空:SAR 翻空时用「空仓」代替做空,别把做空收益算进去。
- 手续费必扣:SAR 在震荡市翻转频繁,忽略费用会严重高估;单边 0.13% 仅为近似,按实际佣金调整。
- 前复权价:未复权价在除权日让 SAR 与收益都失真。
- 震荡市被反复打脸:SAR 是趋势工具,横盘时频繁翻转、交易成本高,可用 ADX 过滤无趋势阶段。
- 单标的≠有效:应在多只、多区间验证,避免过拟合与幸存者偏差;本文为简化模型,未完全模拟 T+1、涨跌停无法成交等。
常见问题(FAQ)
Q:为什么不按当日收盘价成交?
A:当日收盘才算出信号,用当日收盘价成交是未来函数。用次日开盘价才真实可执行。
Q:SAR 翻空为什么不做空?
A:A 股个股无法裸做空(融券成本高、标的有限),所以翻空时用空仓代替,只赚多头段。
Q:SAR 策略一定跑赢买入持有吗?
A:不一定。趋势强的品种/区间 SAR 能控回撤,震荡市则因频繁交易而跑输。回测对比才有结论。
Q:需要付费吗?
A:单只历史日线有免费额度;多标的批量回测(klines.batch+universes)需 Starter 及以上。详见定价页。
小结
SAR 翻转策略回测的关键在严谨性:前复权价算 SAR、次日开盘成交避免未来函数、A 股只做多头段、扣手续费、和买入持有对比。用 AlphaFeed 取回前复权日线,几十行 Python 就能客观检验这套经典趋势跟踪信号在具体品种上到底值不值。
参考
- AlphaFeed Python SDK 文档:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart
- AlphaFeed 官网:https://alphafeed.org/