资讯详情

A股回测第一天:用 Python 拉行情、验数据、跑通双均线(完整代码)

📅 2026/10/14 16:57:08 | 华诺云谱 👁 阅读
A股回测第一天:用 Python 拉行情、验数据、跑通双均线(完整代码)
很多量化新手的经历都一样照着教程写了个双均线回测夏普 2.0兴冲冲上实盘一个月后收益是负的。问题往往不在策略而在回测系统本身口径混用、量纲错误、停牌日数据缺失、撮合约束缺失——这四个坑每一个都足以让回测数字变成好看的故事。本文用一个完整可运行的脚本把这四件事一次处理掉你拿到的不是又一个教程级回测而是一个可以直接往上加策略的骨架。完整脚本# -*- coding: utf-8 -*-A股双均线回测骨架口径 - 量纲 - 停牌 - 撮合 - 绩效importrequestsimportpandasaspd# ---------- 1. 拉数据趋势策略用前复权与后复权收益率序列相同 ----------deffetch(symbol,adjustqfq,start20150101,end20251231):urlhttps://api.cxdy.vip/api/lsjyparams{apiKey:你的key,symbol:symbol,adjust:adjust,start_date:start,end_date:end}rowsrequests.get(url,paramsparams,timeout30).json()dfpd.DataFrame(rows)df[date]pd.to_datetime(df[date])returndf.sort_values(date).reset_index(dropTrue)dffetch(sh600036)# ---------- 2. 量纲校验成交额 ≈ 成交量 × 均价量级一对单位现形 ----------# ratio amount / (volume(股) * close)# ≈1 额是元、量是股 ≈1/100 额是元、量是手# ≈1e4 额是万元、量是股 ≈100 额是万元、量是手# 判定后把换算固化在接入层下游统一股 元# ---------- 3. 停牌/缺失检测日期缺口 舍弃型空值 空值型 ----------fullpd.bdate_range(df[date].min(),df[date].max())gapsset(full)-set(df[date])print(f[检查] 日期缺口{len(gaps)}个停牌舍弃型源会在此现形)dfdf.dropna(subset[open,close,high,low]).reset_index(dropTrue)# ---------- 4. 信号5/20 双均线T 日收盘确认、T1 开盘执行 ----------df[ma5],df[ma20]df[close].rolling(5).mean(),df[close].rolling(20).mean()golden(df[ma5]df[ma20])(df[ma5].shift(1)df[ma20].shift(1))dead(df[ma5]df[ma20])(df[ma5].shift(1)df[ma20].shift(1))df.loc[golden,signal],df.loc[dead,signal]1,-1df[target]df[signal].ffill().fillna(0)# ---------- 5. 撮合T1 不可当日卖、100 股整数倍、无未来函数 ----------cash,shares,buy_idx,capital100000.0,0,-10,100000.0foriinrange(1,len(df)):odf.loc[i,open]wantdf.loc[i-1,target]# 昨日信号 - 今日开盘执行ifwant1andshares0andcasho*100:sharesint(cash//(o*100))*100# 主板申报100 股整数倍cash,buy_idxcash-shares*o,i# 记买入日当日买不可当日卖elifwant0andshares0andibuy_idx:cashshares*o shares0df[nav](cashshares*df[close])/capital# ---------- 6. 绩效 ----------retdf[nav].iloc[-1]-1anndf[nav].iloc[-1]**(252/len(df))-1dd(df[nav]/df[nav].cummax()-1).min()dailydf[nav].pct_change().dropna()shpdaily.mean()/daily.std()*252**0.5ifdaily.std()0else0benchdf[close].iloc[-1]/df[close].iloc[0]-1print(f总收益{ret:.1%}| 年化{ann:.1%}| 最大回撤{dd:.1%}| 夏普{shp:.2f}| 买入持有{bench:.1%})整段直接能跑。下面拆四个关键点——每一个都对应一种回测很美、实盘翻车。关键点 1复权口径——先想清楚你算的是什么复权数据答的是收益率不复权数据答的是真实成交价。均线这类趋势信号用 qfq/hfq 都行两者逐日收益率完全相同但如果你要算涨停价、判断可成交性必须切回不复权——涨停价只能用真实昨收乘涨跌幅限制用复权价算出来的涨停价在盘口根本不存在。新手最典型的事故是前半段不复权、后半段 qfq 拼接拼接点出现一个不存在的跳空之后所有收益率全是错的。一个回测系统里每个模块用什么口径要写成死规则不能凭感觉。关键点 2量纲——差一个数量级回测就是另一个故事交易所行情的成交量口径是手1 手 100 股成交额是元但各家数据接口返回的量、额字段单位五花八门股、手、万手元、千元、万元、亿元都有人用。把手当股用换手率直接虚高 100 倍所有依赖流动性的过滤条件形同虚设。解法是脚本第 2 节的交叉校验amount / (volume × close)的量级直接暴露单位组合多抽几天、多抽几只票取中位数结论写死在数据接入层。别信文档信物理关系。关键点 3停牌——那个洞里没有价格只有你的假设停牌日没有成交交易所不生成 K 线。数据源对停牌的处理分三类整行舍弃日期缺口、保留日期但价格为 NaN、用前收盘自动填充。先检测你的数据源属于哪类脚本第 3 节三行代码再决定填充策略。最危险的做法是把 NaN 当 0回测净值会画出一个不存在的 -100%或者反过来——策略在历史里完美避开了所有暴跌日实盘复牌补跌全接住了。通用做法是前向填充收盘价 加一列停牌标记撮合层读到标记就禁止申报。关键点 4T1 与申报数量——实盘做不到的回测就不能做A 股当日买入的股票次日才能卖沪深北全市场如此卖出资金当日可用于再买入。撮合层必须落实三条持仓记买入日sell_index buy_index才允许卖主板/创业板 100 股整数倍科创板 ≥200 股、1 股递增小资金的可投标的池直接受此约束信号次日执行顺便杜绝未来函数。自查一条就够了在成交记录里搜buy_date sell_date的股票交易。搜出来一堆你的回测收益要先打个对折再说话。输出怎么读跑完你会看到类似总收益 31.4% | 年化 4.9% | 最大回撤 -18.2% | 夏普 0.41 | 买入持有 58.7%示例数字非收益承诺双均线大概率跑不赢买入持有——这是正常的它的教学价值在于用一个人尽皆知优缺点的策略验证回测系统本身是对的。骨架验证过了往上换策略、加止损、加仓位管理出来的数字才有讨论价值。下一步加止损模块和波动率仓位管理骨架不用动多标的池回测时注意幸存者偏差退市股要留在池子里需要分钟级执行模拟的话把日线接口换成对应的分时接口即可口径纪律完全相同数据源说明文中行情接口为迪雅数据diyadata.cn的历史行情 API注册即有免费额度adjust参数支持前复权/后复权/不复权三种口径官网文档有完整的字段说明。
📝

华诺云谱内容团队

资深建站顾问 · 行业研究员

10年+企业数字化服务经验,专注智能建站、SEO优化与品牌营销,持续输出建站技巧、行业洞察与营销干货,已帮助5000+企业实现数字化增长。

你可能需要的服务

订阅华诺云谱资讯周报

每周一封,精选建站技巧、SEO与营销干货,直达邮箱。已有 8,000+ 企业主订阅,助你少走弯路。

↑