基于 Tushare 的简单选股策略验证:一次轻量级回测实践


一、背景

在完成基础筛选和加入均线判断之后,一个更实际的问题是:

👉 筛选出来的股票,到底有没有“效果”?

也就是说,仅仅看到“涨幅 + 均线向上”还不够,
还需要进一步验证:这种筛选方式,在历史数据中是否具备一定参考价值。

因此,这一篇主要做一件事情:

对之前的选股规则做一个简单的收益验证。

不使用复杂回测框架,只做一个轻量级实现。


二、策略说明

沿用上一版的筛选逻辑:

  • 剔除 ST 股票
  • 收盘价 < 20 元
  • 当日涨幅 > 3%
  • MA5 > MA10

在此基础上,加一个验证方式:

👉 假设在筛选日买入,持有 N 天后卖出,计算收益

本次设定:

  • 持有周期:5 个交易日

三、实现思路

整体流程如下:

  1. 获取一段时间的历史数据
  2. 计算均线
  3. 在每个交易日进行筛选
  4. 对筛选结果计算未来收益
  5. 汇总结果

四、数据准备

import tushare as ts
import pandas as pd

ts.set_token('你的token')
pro = ts.pro_api()

获取较长时间区间的数据(用于回测):

df = pro.daily(
    start_date='20240101',
    end_date='20240329'
)

五、计算均线

g = df.groupby("ts_code", group_keys=False)

df["ma5"] = g["close"].transform(lambda x: x.rolling(5).mean())
df["ma10"] = g["close"].transform(lambda x: x.rolling(10).mean())
df["ma20"] = g["close"].transform(lambda x: x.rolling(20).mean())

df["vol_ma5"] = g["vol"].transform(lambda x: x.rolling(5).mean())g(20).mean())


六、构造未来收益

这里是关键步骤。

计算未来 5 天收益:

# 未来收益:持有 HOLD_DAYS 个交易日
df["future_close"] = g["close"].shift(-HOLD_DAYS)
df["future_return"] = (df["future_close"] - df["close"]) / df["close"]

# 下一交易日开盘收益(可选,看起来更接近“次日再买”)
df["next_open"] = g["open"].shift(-1)
df["future_close_from_next_open"] = g["close"].shift(-(HOLD_DAYS + 1))
df["future_return_next_open"] = (
    (df["future_close_from_next_open"] - df["next_open"]) / df["next_open"]
)

解释一下:

  • shift(-n) 表示取未来第n天的价格
  • 用来模拟“持有n天后卖出”

七、逐日筛选并统计结果

results = []

for date in df['trade_date'].unique():
    daily = df[df['trade_date'] == date]
    
    # 筛选条件
    selected = daily[
        (daily['pct_chg'] > 3) &
        (daily['close'] < 20) &
        (daily['ma5'] > daily['ma10'])
    ]
    
    if len(selected) > 0:
        avg_return = selected['return'].mean()
        results.append(avg_return)

八、结果分析

summary = {
    "信号总数": int(len(signals)),
    "有信号交易日数": int(len(daily_result)),
    "单笔平均收益": float(signals["strategy_return"].mean()),
    "单笔收益中位数": float(signals["strategy_return"].median()),
    "单笔胜率": float((signals["strategy_return"] > 0).mean()),
    "按日等权平均收益": float(daily_result["avg_return"].mean()),
    "按日等权胜率": float((daily_result["avg_return"] > 0).mean()),
    "最终净值": float(daily_result["equity"].iloc[-1]),
    "最大单日回撤": float((daily_result["equity"] / daily_result["equity"].cummax() - 1).min())
}

print("\n===== 回测结果汇总 =====")
for k, v in summary.items():
    if isinstance(v, float):
        print(f"{k}: {v:.4f}")
    else:
        print(f"{k}: {v}")

在这里插入图片描述


九、结果可视化

import matplotlib.pyplot as plt

plt.figure(figsize=(10, 5))
plt.plot(daily_result["trade_date"], daily_result["equity"], linewidth=1.5)
plt.xticks(rotation=45)
plt.title("Simple Tushare Strategy Equity Curve")
plt.xlabel("Trade Date")
plt.ylabel("Equity")
plt.tight_layout()
plt.savefig(OUTPUT_DIR / "equity_curve.png", dpi=150)
plt.show()

在这里插入图片描述


十、结果解读

在当前样本区间内,这组条件未取得理想结果。
从回测结果看,策略累计净值持续回落,胜率和平均收益也偏弱,说明仅使用“涨幅 + 均线 + 放量”作为筛选条件,尚不足以形成稳定的短线策略。


十一、方法特点

这个回测方法比较简单,有几个特点:

  • 不依赖复杂框架
  • 计算逻辑清晰
  • 适合快速验证想法

但也存在明显限制:

  • 未考虑交易成本
  • 未考虑滑点
  • 未做仓位管理
  • 未考虑风险控制

因此更适合作为“初步验证”。


十二、可以继续优化的方向

在这个基础上,可以进一步做:

  • 加入止盈止损
  • 引入成交量条件
  • 多策略组合
  • 使用专业回测框架(如 backtrader)

十三、总结

这一步的核心其实不是策略本身,而是完成了一件更重要的事情:

👉 把“筛选结果”转化为“可以量化验证的结果”

相比只看某一天的筛选名单,这种方式更接近实际应用。

虽然当前实现还比较简单,但已经可以作为后续优化策略的基础。

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐