ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

Python量化投资全流程:从数据清洗到策略回测的完整知识地图

Python量化投资全流程:从数据清洗到策略回测的完整知识地图 简介这份Python股票量化投资课程配套课件定位为零基础到进阶的量化交易学习资料面向想用Python进行金融数据分析、策略构建与自动化交易的学员可系统补齐编程、数据、策略、回测、风控与机器学习全链路能力。资源压缩包共142个文件、约56.91MB核心是85个.py可运行脚本与21个.pyc编译文件配合9个.csv股票行情数据、12个.xml配置、3个.zip依赖包及若干说明文档形成从数据到代码的完整配套。目前已有2393人学习下载是被量化初学者反复参考的一套课件。内容覆盖Pandas/NumPy数据处理、Matplotlib可视化、Yahoo Finance等数据获取、移动均线与布林带指标、Backtrader回测、经纪商API自动交易、止损与资金管理以及机器学习预测实战压缩包内还提供A股历史行情CSV与选股数据详细说明便于直接运行演练从数据读取到策略回测一气呵成。1. 量化投资的知识地图Python、股票与策略怎么接起来很多人学 Python 股票量化投资会遇到同一个卡点语法书看了两遍但不知道整条路怎么走。课程配套课件的价值不是把某个指标公式贴出来而是把量化交易从头到尾拆成一条可执行的生产链路数据获取与清洗、因子计算、策略信号、回测验证、绩效归因、风险控制。这条链路里数据是地基因子是武器回测是照妖镜风险控制则是活下来的前提。这份课件适合两类人一是已经掌握 Python 基础、想系统进入量化交易方向的学习者二是做过简单回测但总被“回测很漂亮、实盘很惨淡”困扰的复习者。复习时不需要逐条重读公式只要抓住主线就能判断自己漏在哪一环。把整门课想象成一条流水线位置越靠前错误越致命数据一旦带错后面所有因子和回测都是空中楼阁。知识地图的顺序就是复习的顺序。2. 数据是第一关行情、财务与预处理怎么做进入量化学习后的第一个“劝退点”通常是数据。很多人以为拿到收盘价就能开始算信号结果在处理复权、停牌、公告日期时不断翻车。梳理下来会发现数据层解决的问题就三个拿什么数据、数据长什么样、哪些数据不能直接用于回测。2.1 先想清楚行情数据要什么样的列结构和频率规范的行情表不是 Excel 那样的宽表格而是长表。每一行对应一个股票在一个交易日的记录核心字段包括字段含义注意事项symbol股票代码统一格式不要混用带前缀和不带前缀date交易日期必须是datetime类型不要用字符串open / high / low / close开高低收复权前或复权后要统一volume成交量确认单位是“股”还是“手”amount成交额做流动性过滤时很有用拿到数据后的第一步不是画 K 线而是做排序、去重、类型修正import pandas as pd df pd.read_csv(daily.csv, parse_dates[date]) df df.sort_values([symbol, date]).reset_index(dropTrue) df df.drop_duplicates(subset[symbol, date], keeplast) df[ret] df.groupby(symbol)[close].pct_change()代码里最关键的是pct_change()它计算的是相邻两行之间的收益率。若中间有缺失交易日它不会自动跳过缺失行而是在出现 NaN 的位置直接给到缺失收益。更严谨的做法是先用交易日历对齐再决定填充策略。频率上日线足够覆盖选股和低频择时分钟线适合日内策略但初期不建议直接上分钟级先跑通日线再说。2.2 复权口径前复权、后复权与回测的对应关系复权问题是数据层最大的坑。除权除息会让价格出现人为跳空如果不复权收益率的计算就会失真。前复权以最近价格为基准调整历史价格适合看图后复权以最早日期为基准适合计算真实收益率。回测中计算信号和收益率应该统一使用后复权价格否则分红次数多的股票会被系统误判为暴跌。代码里通常这样处理df[adj_close] df[close] * df[adj_factor] df[ret_adj] df.groupby(symbol)[adj_close].pct_change()adj_factor是行情源提供的复权因子它通常是累乘的拿来直接乘原始收盘价即可。最容易漏的一点是前复权价格会随着新行情发布而整体变化今天算出的前复权历史值和下周算出的不一样。因此研究过程中的数据快照要固定版本不要每次回测都重新下载否则回测结果无法复现。2.3 预处理三坑缺失值、停牌、零成交缺失值不能无脑删除也不能无脑前向填充。停牌期间没有成交本来就该没有价格记录如果你用ffill()把上一交易日的价格延续到停牌日回测引擎就会在停牌日“假装”成交这是典型的隐性未来函数。正确处理是保留交易状态信息df[tradable] df[volume] 0 df[can_buy] df[tradable] (df[ret] 0.095) df[can_sell] df[tradable] (df[ret] -0.095)这里用涨跌幅近似过滤涨跌停无法买入卖出的情况涨幅接近 10% 时买单大概率无法成交跌幅接近 10% 时卖单大概率无法成交。阈值是示意创业板、科创板需要换挡。更准确的方案是让行情源直接提供涨跌停价格字段计算前先判断当日能否成交。2.4 财务数据不能用报告期对齐用公告日期年报、季报发布存在明显滞后。如果只按“报告期”把财务数据和行情合并等于在 4 月末就使用了 3 月末才发布的季报数据这在回测里是未来函数。正确做法是以公告日期为信息生效日让因子在公告发布之后才“看见”数据。fin fin.sort_values(anno_date) price price.sort_values(date) merged pd.merge_asof( price.sort_values(date), fin, left_ondate, right_onanno_date, bysymbol, directionbackward, )merge_asof会为每个交易日找到最近一次已经公告的财务数据directionbackward表示只取早于或等于当前日期的记录。这个方向一旦写反就变成了未来数据。数据层最后应该有一份自查脚本检查每个股票的最短历史长度、缺失值比例、复权因子是否单调以及merge_asof之后是否有空值。数据没有问题后面的策略才有讨论意义。3. 指标与因子从均线到多因子选股的转化策略层是另一个学习分水岭。很多人以为把 MACD、RSI、布林带凑在一起就是量化策略实际上技术指标只是信号的一种来源。更系统的做法是先理解指标的计算口径再从单因子到多因子一步步建立选股逻辑。3.1 技术指标的计算口径均线、MACD、布林带指标计算在 Pandas 里几乎是模板化操作但有一个共性要求按股票分组后计算不能把不同股票混在一个序列里滚动。df df.sort_values([symbol, date]) df[ma5] df.groupby(symbol)[adj_close].transform( lambda x: x.rolling(5).mean() ) ema12 df.groupby(symbol)[adj_close].transform( lambda x: x.ewm(span12).mean() ) ema26 df.groupby(symbol)[adj_close].transform( lambda x: x.ewm(span26).mean() ) df[macd] ema12 - ema26 df[macd_signal] df.groupby(symbol)[macd].transform( lambda x: x.ewm(span9).mean() )transform的作用是让计算结果保持原索引对齐不会因为分组而打乱行序。滚动窗口的前 N 行一定会产生 NaN后续生成信号前必须dropna()否则 NaN 会被当成空仓或买入信号。布林带就是均线加减两倍标准差核心不是指标本身而是“突破”和“回归”两种状态分别对应趋势策略和均值回归策略。同一个指标放在不同市场状态下含义完全不同。3.2 因子与未来收益IC、分组回测与因子方向因子的本质是排序工具。它不直接告诉你买什么而是告诉你哪些股票应该排在前面。判断一个因子有没有效不能只看收益曲线要看因子值和未来收益之间的单调关系。信息系数 IC 是因子与未来 N 期收益的相关系数通常用 Spearman 秩相关计算from scipy import stats def factor_ic(df, factor_col, fwd_ret_col): ic_by_date df.groupby(date).apply( lambda g: stats.spearmanr(g[factor_col], g[fwd_ret_col])[0] ) return ic_by_date.mean(), ic_by_date.std() ic_mean, ic_std factor_ic(df, zscore, fwd_ret_5) icir ic_mean / ic_std * np.sqrt(52)IC 均值为正且稳定说明因子有预测力ICIR 是经波动调整后的指标用来判断因子稳定性。只看 IC 均值不够一个因子可能均值很高但方向经常反转实盘根本无法信任。更直观的验证是分组回测把每日股票按因子值分成 5 组每组建仓后持有 N 天观察收益是否随因子值单调变化。如果第 3 组收益最高、第 1 组次之、第 5 组最低因子很可能只是噪声而不是有效排序。3.3 多因子合成标准化、中性化与合成单因子容易失效多因子合成可以分散风险但前提是合成方法正确。不同因子的量纲不同直接相加等于给量纲大的因子更高权重。正确做法是先在每个交易日截面上做标准化。df[zscore] df.groupby(date)[factor].transform( lambda x: (x - x.mean()) / x.std() )标准化之后还要做行业中性化。如果一个因子只是选出了某个行业的一堆股票那它捕捉的可能不是 alpha而是行业 beta。最简单的中性化是减去行业均值df[factor_neutral] ( df.groupby([date, industry])[zscore] .transform(lambda x: x - x.mean()) )行业分组股票数量太少时组内均值会不稳定这时可以考虑行业哑变量回归取残差。市值中性化思路类似把因子值对市值做回归用残差代替原始因子值。中性化不是可选项而是多因子研究的标准动作。跳过这一步后面所有分析结果都会带上一层隐性的行业或市值风格。3.4 从因子到策略打分、选股与择时的配合单因子研究最终要落到一篮子股票。常见的落地方案是每个调仓日按因子 zscore或中性化后分值排序取前 N 只等权买入持有到下一个调仓日。这里面有两个容易被忽略的量调仓频率和换手成本。signal df.sort_values([date, zscore], ascending[True, False]) signal signal.groupby(date).head(20) targets signal[[date, symbol]].copy()每天调仓会让手续费和滑点吃掉大量收益更常见的是周频或月频调仓。技术指标如指数均线可以作为择时过滤条件市场整体处于下跌趋势时降低仓位或空仓这比单独使用某一种指标更稳健。到这里一个完整的策略已经从“数据”走到了“信号”下一步是回测验证。4. 回测引擎与绩效分析别让图表骗了你回测是量化学习中最容易被“自欺”的环节。初学者看到一段漂亮的净值曲线就以为策略有效实际上回测框架的一个小错误可能让结果严重失真。这一章从最小回测框架开始把绩效指标和常见偏差一起理清。4.1 最小可用的回测框架逐笔结算还是逐 bar 结算最简单的回测逻辑是在每个交易日先结算持仓市值再按信号决定买卖。from dataclasses import dataclass dataclass class Position: symbol: str shares: int def run_backtest(daily, select_func, initial_cash1_000_000): cash initial_cash positions [] timeline sorted(daily[date].unique()) for date in timeline: today daily[daily[date] date] for pos in positions: px today.loc[today[symbol] pos.symbol, close].values if len(px) 0: pos.last_price px[0] targets select_func(today) # 先卖后买简化处理但要有滑点与手续费 for pos in list(positions): if pos.symbol not in targets: cash pos.shares * today.loc[ today[symbol] pos.symbol, close ].iloc[0] positions.remove(pos) for symbol in targets: if symbol not in [p.symbol for p in positions]: price today.loc[today[symbol] symbol, close].iloc[0] shares int(cash * 0.1 // price) cash - shares * price positions.append(Position(symbolsymbol, sharesshares)) equity cash sum(p.shares * p.last_price for p in positions) yield date, equity这个框架里至少有两个问题需要明确第一这里用“当日收盘信号、当日收盘价成交”在严格回测中属于前视偏差相当于你看到了收盘价才决定买入却又按收盘价成交第二没有手续费和滑点收益会虚高。更稳妥的做法是信号在收盘后产生成交放在次日开盘价。框架可以先简后严但心里要清楚它偏乐观。4.2 绩效指标的计算口径年化、最大回撤、夏普比率净值曲线出来后第一件事不是画图而是算四个核心指标。equity pd.Series([e for _, e in results], index[d for d, _ in results]) daily_ret equity.pct_change().dropna() total_ret equity.iloc[-1] / equity.iloc[0] - 1 days len(equity) annual_ret (1 total_ret) ** (252 / days) - 1 roll_max equity.cummax() drawdown equity / roll_max - 1 max_dd drawdown.min() excess daily_ret - 0.02 / 252 # 假设无风险年化2% sharpe np.sqrt(252) * excess.mean() / excess.std()年化收益率不是简单地把总收益除以年数而是按 252 个交易日做复利折算。最大回撤要看净值峰值到谷底的跌幅而不是只看某一段收益波动。夏普比率对异常值非常敏感日收益标准差偏小时夏普会被算得异常高最好同时看最大回撤和收益回撤比。指标计算口径常见错误年化收益率对总收益做 252 日复利折算直接总收益/年份最大回撤净值峰值到后续最低点的最大跌幅用收益率序列代替净值夏普比率日超额收益均值/标准差 再年化忘记减无风险利率胜率盈利交易次数/总交易次数胜率高但盈亏比很低盈亏比平均盈利/平均亏损单独使用容易误判4.3 回测偏差自查清单回测结果异常漂亮时先别高兴按下面的清单逐一排查。第一幸存者偏差。股票池只包含“今天还活着”的股票历史上已经退市的股票被排除在外回测自然会偏向乐观。复盘时要加入历史退市股或者用全量历史成分股。第二前视偏差。未来数据进入了信号计算比如用当日收盘价生成当日信号再按当日收盘价成交。排查方式是检查所有信号计算中使用的数据是否都在信号生成时刻之前。第三过拟合。策略参数反复调整到与历史完美匹配换一段样本外数据立刻失效。应对方法是做滚动训练测试或用参数敏感性分析观察邻近参数下绩效是否剧烈波动。第四成本缺失。双边手续费、印花税、滑点全部设为 0高频调仓策略在回测里会显得异常优秀。实际回测至少应计入双边万三到千一的手续费以及一个最小滑点。5. 最该警惕的易错点量化交易里的“黑匣子”陷阱学完数据、因子和回测之后靠前的坑大多能避开但有一些错误藏在代码细节里属于“看着没问题跑起来全错”的类型。梳理整个课程内容我总结出五个最容易漏掉、也最值得反复检查的易错点。5.1 未来函数回测悄悄用了未来的数据易漏点信号生成时没有注意数据的时间边界最常见的是“用当日收盘价计算信号再用当日收盘价成交”。为什么漏因为逐行处理时同一行里既有价格又有信号代码不会报错逻辑上也浑然不觉。尤其是在计算因子时如果因子值来自未来 N 天的收益那整个回测都是自欺欺人。怎么补写一个统一的工具函数把所有信号统一到“信号生成日”的次日处理。每次回测前做一次数据泄漏检查把因子值随机打乱如果绩效不变差说明原有因子可能是噪声如果把因子滞后一天后绩效显著变差就要警惕未来函数。简单有效。5.2 复权口径混用前复权数据算收益率后复权数据算信号易漏点同一套代码里历史价格有的来自前复权接口有的来自后复权接口两者混用会导致除权日附近的信号和收益无法对齐。为什么漏因为前复权价格会随时间变化今天下载的历史前复权价格和下周下载的可能不同。如果研究过程跨越多次数据更新复现结果就会悄悄改变。怎么补建立研究数据快照整个实验周期内固定使用同一份数据计算收益统一用复权因子看行情图才用前复权。如果条件允许直接在后复权价格上完成全部计算。5.3 手续费和滑点设为零低换手策略也会“虚胖”易漏点回测里把交易成本设为 0策略年化收益看着很高实盘一执行就缩水。为什么漏因为初学者通常先用简单框架验证思路忽略了每笔交易的摩擦成本。尤其是多因子周频调仓策略每次调仓一篮子股票换手率很容易超过 100%成本会被快速放大。怎么补回测框架至少内置一个cost_model参数买入收万二佣金卖出收万二佣金加千一印花税滑点按固定金额或固定比例计入。调仓频率越高成本模型越要精细。在策略筛选阶段可以直接看“扣除成本后的收益”不要看毛收益。5.4 股票池里的幸存者偏差退市股被悄悄丢掉了易漏点从某个“当前存活股票列表”里取股票池历史回测自然只包含现在还在的公司退市股票全部缺失。为什么漏因为大多数行情源默认只提供当前可交易的股票历史停牌和退市记录需要额外接口补齐。很多人不知道这一点导致回测结果天然偏高。怎么补回测的股票池要使用历史时点的成分股全集至少包含当时存在过的所有股票代码。可以用退市股列表回填数据源或者在研究报告中明确标注“仅包含当前存续股票”并接受这个偏差。复习时这一条直接决定回测结论是否可信。5.5 时间切分错误随机划分训练集和测试集易漏点做因子测试时把历史数据随机切分一部分当训练集、一部分当测试集导致同一时间段的数据既参与训练又参与验证。为什么漏因为通用机器学习的交叉验证习惯是随机划分但金融数据是强序列相关的时间序列随机切分会让模型“偷看”到未来数据的信息。怎么补金融场景必须按时间顺序切分前 70% 做训练后 30% 做样本外测试更严格的做法是滚动训练每次只用过去一段窗口训练然后预测下一段。课程里反复强调这一点因为它直接决定了因子测试结论能不能外推到实盘。6. 复习清单从头到尾复盘量化知识树的 12 个问题把课件重新过一遍时最有效的不是重读全部代码而是按清单逐个回答“你能不能做到”。以下这 12 个问题覆盖了数据、因子、回测三个阶段的关键能力。模块复习问题完成标准数据能否把原始数据整理成规范长表排序、去重、类型修正无报错数据能否说明前复权与后复权的区别能说清回测用哪一档、实盘看哪一档数据能否正确处理停牌与缺失值回测中不会出现停牌日成交数据能否用公告日期合并财务数据merge_asof方向正确且无未来信息因子能否独立计算均线、MACD、布林带分组计算、不混股票因子能否计算 IC 和 ICIR能解释因子是否稳定有效因子能否做截面标准化和行业中性化知道为什么要先标准化再合成策略能否把因子变成一个调仓方案有明确的调仓频率和持仓数量回测能否写出最小回测框架信号与成交分离回测能否计算年化、回撤、夏普公式口径正确回测能否识别未来函数能说出至少三个泄漏场景回测是否计入手续费和滑点回测绩效为扣除成本后的结果查漏补缺的方法也很直接先跑一遍数据层自查脚本再跑一个单因子 IC 分析最后跑一次完整回测。哪个环节报错或结果异常就回到对应模块复习。最容易漏的不是代码而是“数据口径”和“时间对齐”这类看不见的逻辑。从那以后我每次拿到新数据都会强制走一遍这套流程先检查列结构和缺失值再确认复权口径最后用滞后一天的信号跑一次对照回测。如果对照结果和原结果差距过大说明一定有未来函数藏在代码里。这套习惯帮我避免了很多次“回测很爽、实盘翻车”的尴尬。希望帮到你。本文还有配套的精品资源点击获取
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表