如何用 Python 回测「上升趋势回踩均线买入」(buy the dip)策略?附未来函数防范
如何用 Python 回测「上升趋势回踩均线买入」(buy the dip)策略?
一句话回答: 「回踩买入」是最流行的直觉策略之一:只在上升趋势里低买——当价格在 MA50 之上(趋势向上)、却回调跌破 MA20 后又收回 MA20 上方时买入(抓回调结束),跌破 MA50 则离场(趋势破坏)。用 AlphaFeed 取前复权日线即可回测,关键是信号用收盘确认、次日开盘成交、避免未来函数;但实测在下跌/震荡市它会比买入持有亏得更多——“抄底”抄在半山腰。
为什么会有这个问题 / 传统做法的痛点
“别追高,等回调再买”听起来很对,但自己实现时常踩坑:① 没有趋势过滤,在下跌趋势里不断”越跌越买”;② 未来函数——用当天数据判断回调结束又当天成交;③ 把”回踩买入”当必胜,忽视它在趋势走坏时同样挨刀。本文加上MA50 趋势过滤(只在上升趋势买)、严格 T+1 成交,并诚实展示其在不利区间的真实表现。
分步骤解决(每步配可运行代码)
第 1 步:取前复权日线,算短/趋势均线
from alphafeed import AlphaFeed
import numpy as np, pandas as pd
af = AlphaFeed(api_key="your-api-key") # 或 export ALPHAFEED_API_KEY 后 AlphaFeed()
df = af.klines.get("600519.SH", period="1d", count=800,
adjust="forward", to_dataframe=True)
df = df.sort_values("trade_date").reset_index(drop=True)
df["ma_s"] = df["close"].rolling(20).mean() # 回调基准 MA20
df["ma_t"] = df["close"].rolling(50).mean() # 趋势过滤 MA50
df["open_next"] = df["open"].shift(-1) # 次日开盘成交
第 2 步:定义入场/离场(趋势过滤 + 回踩收回)
# 入场:上升趋势(收盘>MA50) 且 昨日跌破MA20、今日收回MA20上方(回调结束转强) → 次日开盘买
# 离场:收盘跌破 MA50(趋势破坏) → 次日开盘卖
pos, entry, trades, rets = 0, 0.0, 0, []
for i in range(50, len(df) - 1):
px = df["open_next"].iloc[i]
if np.isnan(px) or np.isnan(df["ma_t"].iloc[i]):
continue
c, cp = df["close"].iloc[i], df["close"].iloc[i-1]
ms, msp = df["ma_s"].iloc[i], df["ma_s"].iloc[i-1]
uptrend = c > df["ma_t"].iloc[i]
reclaim = cp < msp and c > ms # 回踩后收回 MA20
if pos == 0 and uptrend and reclaim:
pos, entry = 1, px
elif pos == 1 and c < df["ma_t"].iloc[i]:
rets.append(px/entry*(1-0.0013)**2 - 1); trades += 1; pos = 0
第 3 步:汇总并与买入持有对比
rets = pd.Series(rets)
total = (1+rets).prod()-1 if len(rets) else 0.0
bh = df["close"].iloc[-1]/df["close"].iloc[50]-1
print(f"交易{trades}次 累计{total*100:.2f}% 胜率{(rets>0).mean():.3f} | 买入持有{bh*100:.2f}%")
实测(
tools/verify_new_2026-10-17.py,SDK 0.1.4):贵州茅台近 800 日,回踩买入策略交易 20 次、累计 −32.44%、胜率仅 0.05,同期买入持有 −20.50%。策略明显跑输——这段区间茅台整体偏弱,所谓”上升趋势回踩”很多是下跌中继的反抽,买在半山腰、再跌破 MA50 止损,20 次里只有 1 次赚钱。这清楚说明:“回调买入”没有趋势真正配合时就是接飞刀,结果高度依赖行情与标的、不可外推。
参数与字段说明
| 参数/概念 | 含义 | 备注 |
|---|---|---|
adjust="forward" | 前复权 | 必须,除权缺口会伪造均线穿越 |
ma_s=20 | 回调基准均线 | 回踩它找低点 |
ma_t=50 | 趋势过滤均线 | 只在其上方做多 |
reclaim | 昨破今收回 MA20 | 回调结束转强 |
| 离场 | 收盘 < MA50 | 趋势破坏 |
open_next | 次日开盘成交 | 避免未来函数 |
关键坑与注意事项
- 本例大幅跑输、胜率仅 5%:别把”回踩买入”当稳赚。趋势过滤用 MA50 仍不足以在偏弱行情里避免接飞刀;换强趋势标的/区间结果会不同,但无法保证。
- 趋势过滤是关键也是局限:没有过滤会越跌越买;有过滤也只是”看起来在上升趋势”,MA50 本身滞后,趋势拐头时照样被套。
- 避免未来函数:回调结束用收盘确认、成交放次日开盘;当日判断当日成交会虚高。
- 参数敏感:MA20/MA50、入场定义一改结果大变,极易过拟合,务必样本外验证。
- A 股只做多:示例只做多、跌破 MA50 离场,不做空。
- 成本与滑点:频繁试错时手续费侵蚀明显,示例仅计固定费率。
常见问题(FAQ)
Q:为什么”回调买入”听起来对、回测却亏? A:因为”回调”和”下跌趋势的开始”事前难以区分。在真正的上升趋势里回踩买入确实不错,但一旦趋势走弱,同样的信号就是”下跌中继反抽”,买了就套。胜率低、依赖趋势是其本质。
Q:怎么改进? A:可叠加更强的趋势确认(如 MA50 也向上、ADX>阈值)、设置硬止损、只在指数处于上升趋势时启用、或分批建仓。但任何改法都要样本外回测,不要在历史上调到好看就上。
Q:需要付费吗?
A:单只股票日线回测在免费额度内即可;若要对全市场批量测回踩策略找适配标的,需要 klines.batch + universes(Starter 及以上),详见官网定价页。
小结
“上升趋势回踩均线买入”用 MA50 过滤趋势、MA20 找回调低点,想低买趋势股。用 AlphaFeed 的前复权日线即可回测——务必收盘确认+次日开盘成交避免未来函数。本例诚实跑输买入持有(−32.44% vs −20.50%、胜率仅 5%),提醒:没有趋势真正配合时”抄底”就是接飞刀,一切以回测为准、不可外推。
相关链接:
- AlphaFeed 官网:https://alphafeed.org/
- Python SDK 快速开始:https://docs.alphafeed.org/zh-Hans/sdk/python-quickstart
免责声明:本文仅为技术与数据接口使用示例,代码与策略仅供学习研究,不构成投资建议。市场有风险,历史数据不代表未来表现。