☰
量化交易入门:用Python实现双均线策略及回测避坑指南
2026/10/6 3:51:54 网站建设 项目流程

这期播客我反复听了两遍,整理笔记的时候发现很多内容值得单独拿出来细讲。先交代下背景:这是一档讨论量化交易的播客节目,主持人和嘉宾从"普通人能不能做量化"聊到"真金白银进场前必须搞明白的事",信息密度很高。我用一周时间把音频里的要点拆开、补齐代码和参数细节,结合自己这几年跑策略的实操经验,整理成这份从认知到实操的完整笔记。不管你是刚听说量化交易这个词的小白,还是已经在研究python量化交易策略代码但不知道怎么系统推进的入门者,这份笔记都值得收藏。

1. 入门第一课:量化交易不是"写个脚本自动赚钱"

1.1 量化的本质:一套可重复、可验证的决策系统

先把最核心的概念说清楚。很多人第一次接触量化交易,脑子里浮现的画面是:写一套代码,程序自动盯盘、自动下单,躺着赚钱。这个画面不能说全错,但严重偏离了本质。量化交易的本质不是"自动化",而是用规则取代情绪,用可验证的逻辑取代拍脑袋。

传统主观交易是什么状态?凭经验、凭盘感、凭消息。今天觉得茅台要涨就买茅台,明天听说芯片有政策利好就追芯片,止损了又后悔没早卖。这种交易方式最大的问题不是赚不到钱,而是无法复盘、无法迭代。你赚了钱,说不清是运气还是方法;你亏了钱,也说不清是市场变了还是操作变形了。

量化交易把这个问题解决了。它的核心是一套决策系统:输入是市场数据,处理逻辑是明确规则,输出是买卖信号和仓位比例。这套系统具备三个特征——可重复(同样的数据输入永远得到同样的输出)、可验证(可以用历史数据回测)、可改进(发现逻辑缺陷后直接改规则再验证)。这才叫量化。

播客里嘉宾打了一个很好的比方:主观交易像中医靠经验诊断,量化交易像现代医学靠检查指标和循证数据。中医里确实有高手,但经验不可复制;量化把"望闻问切"变成了"化验单",谁拿着这套系统都能执行。这就是量化最大的价值——它让你摆脱对天赋和运气的依赖,把交易变成一项可以工程化推进的工作。

1.2 三个常见误区:回测陷阱、数据依赖、资金门槛

嘉宾在播客里专门花了一段聊"入门的错误姿势",我总结下来是三个典型误区。

**第一个误区:把回测结果当实盘收益。**这是重灾区。很多人拿python写了一个策略,回测年化80%,立刻觉得财务自由近在眼前。实际上回测和实盘之间隔着一道巨大的鸿沟——手续费、滑点、涨跌停买不进卖不出、流动性不足导致的冲击成本,任何一个因素都可能把年化80%打到年化15%。更阴险的是过拟合问题:你在历史数据上反复调参,最终调出来的"完美曲线"其实是把噪音当成了规律。后面我专门用一节讲这个问题。

第二个误区:以为量化必须有庞大的数据和服务器。播客里有个观点我特别认同:个人量化爱好者需要的不是数据越多越好,而是数据可用性。日线级别的数据,几万行就够验证很多经典策略了;你不需要搞什么实时行情服务器,那是有稳定盈利之后才需要考虑的事。起步阶段,免费的公开数据源完全够用,关键是数据要干净、连续、复权正确,而不是"多"。

**第三个误区:资金门槛迷思。**很多人觉得量化是机构玩家的游戏,动辄几百万起步。但入门阶段的核心任务是验证策略逻辑和学习方法论,小资金完全够用。我身边有朋友用两万块起步,跑了两年多,重点根本不是赚了多少钱,而是把"数据获取→策略研究→回测→小资金验证→迭代"整套流程跑通了。流程通了,钱多钱少只是数字问题;流程不通,给你一千万也是送。

2. 技术栈怎么选:Python生态才是新手的最优解

2.1 为什么是Python而不是其他语言

聊到量化交易,绕不开的技术问题就是语言选型。目前市面上的选择主要有Python、R、Matlab,还有一些专业平台(比如聚宽、米筐)。播客嘉宾和我的意见高度一致:入门阶段闭眼选Python,没有之一。

原因不复杂,三条。第一,量化相关的库生态最成熟。数据获取有akshare、tushare、yfinance,数据处理有pandas、numpy,回测有backtrader、vectorbt,机器学习和因子挖掘有scikit-learn、LightGBM。这些库都是开箱即用,你不需要从零造轮子。第二,社区资料极多。你遇到任何报错,几乎都能在搜索引擎里找到前人踩坑的解决方案。这一点对新手来说是巨大的隐性时间节省。第三,和主流金融科技岗位的技术栈一致。就算你以后不想自己做交易,学了Python做量化,去金融机构做量化研究也有直接竞争力。

也不是说其他语言不行。C++性能确实强,但那是高频交易领域的事,门槛极高;R在统计建模上很优雅,但工程化能力弱;Matlab贵且生态封闭。对一个人要搞定数据、策略、回测全流程的独立开发者来说,Python的综合成本最低。

2.2 数据、回测、实盘:三层工具选型的逻辑

技术栈我建议拆成三层来理解,数据层→回测层→实盘执行层,每一层选型逻辑不同。

**数据层:**起步阶段用akshare或者tushare拿日线数据就够了。akshare是免费开源库,直接pip安装就能用,覆盖A股、港股、美股、期货、币圈等多个市场;tushare需要注册获得token,部分接口有积分要求,但基础的日线行情免费。注意一点:回测前一定要做数据清洗,检查有没有缺失值、异常跳空、复权是否处理。数据的质量直接决定回测结果的可信度,这是数据层最容易被忽略的坑。

回测层:新手我建议先不急着上backtrader这种重量级框架。第一步,用pandas自己手写一个简单的向量化回测,几十行代码就能跑完一个策略的完整流程。这个过程的目的是让你理解回测的内部机制——信号怎么生成、仓位怎么变化、收益怎么计算、最大回撤怎么统计。等理解了底层逻辑,再上手框架,你会发现backtrader那些概念(Strategy、DataFeed、Analyzer)一点不神秘。先用笨办法搞懂原理,再用框架提升效率,这是最优学习路径。

**实盘执行层:**这一步离新手比较远,但心里要有数。国内A股做程序化交易,资金量小的时候通过券商提供的API(比如QMT、Ptrade)可以覆盖大部分需求;资金量大或者涉及多账户管理,就需要更正式的量化接口方案。加密货币市场则可以直接用ccxt库对接交易所。实盘和回测最大的区别就是执行环境和滑点,这一块我在第4节展开讲。

3. 第一个策略从0到1:双均线动量策略的完整实现

3.1 策略逻辑拆解:趋势追踪为什么能长期有效

纸上谈兵差不多了,直接上实战。第一个策略我推荐双均线动量策略,理由有三:逻辑简单(10分钟能讲明白)、代码量小(100行内搞定回测)、思想内核重要(它是趋势追踪类策略的鼻祖,理解了它,后面理解海龟交易法则、唐奇安通道都不费劲)。

策略逻辑一句话:短期均线在长期均线上方时持有资产(做多),在下方时空仓(或做空)。为什么要用均线的金叉死叉来捕捉趋势?因为金融市场的价格波动中,趋势不是随机游走,而是由基本面变化、资金持续流入、投资者情绪扩散等力量驱动的,这些力量的作用有一定持续性。均线本质上是对价格做平滑滤波,金叉意味着短期内价格上涨动能超过了长期平均水平,死叉则相反。

这套逻辑长期有效的根本原因是一种叫做**动量效应(Momentum Effect)**的金融异象:过去一段时间表现好的资产,在未来一段时间内倾向于继续表现好。这个现象从几十年前就被学术界反复验证,至今仍在各大市场存在。当然它也有失效期,震荡市里均线金叉死叉会被反复打脸,这就是趋势追踪的代价——用震荡期的小幅亏损换取趋势期的大幅盈利。

3.2 代码逐段解析:从行情数据到绩效指标

下面给出一个完整的Python回测示例,采用向量化计算,代码简洁且执行效率高。

import pandas as pd import numpy as np # 1. 加载数据:假设df是日线行情,包含date, open, high, low, close, volume # 示例中用随机数据代替,实际使用中请用akshare/tushare获取真实行情 np.random.seed(42) dates = pd.date_range('2018-01-01', '2023-12-31', freq='B') df = pd.DataFrame({'date': dates}) df['close'] = 100 + np.cumsum(np.random.randn(len(dates)) * 0.5) + \ np.sin(np.arange(len(dates)) / 50) * 2 # 2. 计算双均线 fast_window = 10 slow_window = 30 df['fast_ma'] = df['close'].rolling(fast_window).mean() df['slow_ma'] = df['close'].rolling(slow_window).mean() # 3. 生成信号:短期均线在长期均线上方 -> 持有(1),否则空仓(0) # 这里用0和1,先不引入做空,新手做多是理解成本最低的 df['signal'] = np.where(df['fast_ma'] > df['slow_ma'], 1, 0) # 4. 计算仓位:信号平移一天,避免用当日收盘信号当日交易的未来函数 df['position'] = df['signal'].shift(1) # 5. 计算策略每日收益率 df['market_ret'] = df['close'].pct_change() df['strategy_ret'] = df['position'] * df['market_ret'] # 6. 计算累计收益和关键绩效指标 df['strategy_cum'] = (1 + df['strategy_ret'].fillna(0)).cumprod() df['market_cum'] = (1 + df['market_ret'].fillna(0)).cumprod() # 年化收益率 annual_ret = df['strategy_cum'].iloc[-1] ** (252 / len(df)) - 1 market_annual_ret = df['market_cum'].iloc[-1] ** (252 / len(df)) - 1 # 年化波动率 annual_vol = df['strategy_ret'].std() * np.sqrt(252) # 夏普比率(无风险利率假设为0) sharpe = annual_ret / annual_vol if annual_vol != 0 else 0 # 最大回撤 cum_max = df['strategy_cum'].cummax() drawdown = df['strategy_cum'] / cum_max - 1 max_drawdown = drawdown.min() print(f"策略年化收益: {annual_ret:.2%}") print(f"基准年化收益(买入持有): {market_annual_ret:.2%}") print(f"策略年化波动率: {annual_vol:.2%}") print(f"夏普比率: {sharpe:.2f}") print(f"最大回撤: {max_drawdown:.2%}")

这段代码虽然短,但有几个关键设计必须说明白。

第一,信号为什么要shift(1)。如果用当天的收盘均线信号、按当天的收盘价成交,就犯了未来函数的错误——你在用当天收盘才知道的信息去交易当天收盘价,这在真实世界里不可能做到。正确做法是:用收盘数据生成信号,下一个交易日才执行。所以position要错开一天。这是新手手写回测最容易犯也最致命的错误,一旦存在未来函数,回测收益率会虚高得离谱。

第二,策略收益率的计算方式。df['position'] * df['market_ret']的意思是:只有position为1的日子,策略才承担市场涨跌;position为0的日子收益为0。这个计算方式隐含了一个假设:空仓时资金闲置,没有利息收入。如果你想更严谨,可以把空仓资金按无风险利率(比如国债逆回购利率)计息,公式改成position * market_ret + (1 - position) * risk_free_rate / 252,更贴近实际。

**第三,绩效指标中最大回撤尤其重要。**它衡量的是账户从最高点回落到最低点的最大幅度。公式里的cummax是累计净值的历史最高点,drawdown是当前净值相对高点的跌幅。回撤直接影响你的资金管理策略——回撤30%需要赚42.86%才能回本,这个数学事实决定了你必须把回撤控制在合理范围内,否则心态先崩了,什么策略都执行不下去。

3.3 回测结果怎么读:收益曲线之外的关键指标

跑完回测,别只盯着年化收益率。我见过太多新手看到策略年化40%就兴奋得直接上实盘,结果三个月后亏损离场。正确姿势是看一组互相配合的指标:

指标含义理想状态
年化收益率策略一年赚多少至少显著跑赢基准
夏普比率每承担一单位风险换来多少超额收益大于1算合格,大于2算优秀
最大回撤最惨的时候亏多少越小越好,结合收益率判断性价比
胜率盈利交易占总交易比例趋势策略通常30%-45%,不追求高胜率
盈亏比平均盈利/平均亏损趋势策略盈亏比通常2:1以上

特别强调胜率和盈亏比的关系。双均线策略在震荡行情里会被反复打脸,可能十次交易里只有三次赚钱,但赚的那三次平均盈利远大于亏的七次平均亏损。这就是趋势追踪的盈利模型——低胜率、高盈亏比。如果你统计出某个策略胜率85%但盈亏比只有0.5,那这个策略的真实期望收益可能是负的,只是靠高胜率的"心理舒适感"在迷惑你。

还有一组必须对比的指标:策略年化收益 vs 基准年化收益。代码里我算了买入持有(buy & hold)的基准收益。如果你的策略跑不出来超额收益,那还不如躺平买指数基金,何必辛苦写代码。策略的存在价值就是创造超额收益(Alpha),否则它只是给券商贡献手续费。

4. 回测跑赢不等于实盘赚钱:成本、过拟合与风控

4.1 成本的真相:滑点与手续费的复利伤害

这是整期播客里我认为最有价值的一段。嘉宾讲了一个真实案例:一个策略在回测中每年换手率800%(一年内持仓总量换手8次),回测时没算交易成本,年化收益45%。加上双边万三的佣金和千一的滑点之后,年化收益直接掉到21%。成本对高频换手策略的杀伤是指数级的,因为它会通过复利效应被放大。

新手容易忽略成本,主要有两个原因:一是回测框架默认不带成本模型,你不主动加它就没有;二是心理上觉得"万几的手续费而已,毛毛雨"。但你要算的是总成本 = 换手率 × 单次成本。如果你一年换手800%,单次综合成本(佣金+滑点+冲击成本)按0.2%算,一年总成本就是1.6%。这笔钱是直接从收益里扣掉的,跑十年就是 (1-1.6%)^10 ≈ 14.9% 的净值损耗。

所以我在自己的回测框架里默认加入成本模型,手续费按万2.5双边算,滑点按固定值(A股我通常按0.1%估算,币圈按0.05%估算),涨停或跌停极端行情再额外加惩罚。你可以直接在3.2的代码里把成本加进去:

# 在计算策略收益时加入成本 cost_rate = 0.001 + 0.0005 # 滑点0.1% + 手续费双边0.1% trade_days = df['position'].diff().fillna(0) != 0 # 持仓变化的日子 df['strategy_ret'] = df['position'] * df['market_ret'] - \ trade_days.astype(int) * cost_rate

position.diff()不为0的日子就是你换仓的日子,每次换仓先扣成本,这样算出来的曲线才是接近实盘的。

4.2 过拟合的识别与规避:参数马拉松没有终点

聊到过拟合,播客里有一句话我深有感触:"当你觉得自己发现了一个完美策略的时候,大概率是你把噪音拟合成了规律。"

什么是过拟合?用双均线策略举例:你用5日均线和20日均线回测,年化25%;改成8日和27日,年化35%;再改成12日和30日,年化28%。然后你心想,那我用8日和27日不就好了。听着合理,但问题在于——你是站在未来看过去。8和27这两个参数是通过反复试凑选出来的,它们恰好抓到了历史数据中某些随机的价格波动形态,而这些形态在未来大概率不会重现。这就是典型的数据窥视偏差(Data Snooping Bias)。

怎么规避?我的实操经验是三个方法叠加。

**第一,限定参数空间。**不要在宽泛的区间里随意试参。双均线策略的有效参数区间通常集中在较短周期(比如5-30日),你就在这个合理区间里测试,不在那些显然不合理的参数(如250日均线和500日均线)上浪费时间。

第二,样本外测试。把数据分成训练集(前80%)和测试集(后20%),只在训练集上调参,调好之后一并放到测试集上验证。如果训练集上表现优异的参数在测试集上大幅失灵,说明你过拟合了;如果表现稳定,策略才相对可信。更进阶的做法是滚动前推(Walk-forward),在多个时间窗口上重复"训练-验证"过程,虽然麻烦,但效果最扎实。

**第三,给策略留出冗余度。**真正稳健的策略不会对参数过于敏感。同样是双均线,你会发现(5,20)、(8,27)、(10,30)跑出来的收益差距不大,最大回撤也相近。如果某个参数组合表现得鹤立鸡群、旁边差一两个数字就崩盘,那就不是发现了圣杯,而是找到了一个美丽的陷阱。

4.3 风险控制的底线:单笔止损与仓位计算

策略负责赚钱,风控负责活下来。很多新手只研究怎么买怎么卖,完全忽略仓位管理,结果就是连续几次判断失误直接亏掉了本金的30%,心态崩了,下一笔交易带着报复心理乱来,彻底进入负循环。

仓位管理最基本的公式是凯利公式的简化版:仓位比例 = 期望单笔盈利 / 亏损幅度限制。更实际的做法是固定比例风险法——每笔交易的风险不超过总资金的1%-2%。假设你账户10万,单笔止损幅度是5%(离场价和入场价差距5%),那你这笔交易的最大仓位 = 10万 × 2% ÷ 5% = 4万。算出来的这笔账是硬约束,超过就是不守纪律。

我在量化策略里会把止损和仓位写进规则里,比如双均线策略的变体:当价格跌破慢速均线3%时无条件离场,不等到死叉。这叫移动止损,目的是在趋势已经明显走坏的早期就控制损失,避免死叉确认时已经多亏了好几天。你可以在信号生成逻辑里加一行:

# 移动止损:持有时,若收盘价低于慢线3%,强制离场 df['stop_signal'] = ((df['position'] == 1) & (df['close'] < df['slow_ma'] * 0.97)) df['signal'] = np.where(df['stop_signal'], 0, df['signal'])

风控的本质不是预测行情,而是预设极端情况下的应对方案。行情不可能按你的剧本走,但你的亏损可以按你的剧本封顶。

5. 后面怎么进阶:策略池思维与学习路线图

5.1 从单策略到策略池:让收益曲线更平滑

当你在某一个策略上稳定获利一段时间后,下一步不是把仓位加倍,而是横向扩展策略池。原因很简单:单一策略有周期性,趋势策略在震荡市里天然亏损,如果只靠一个策略,你就被迫在它表现差的阶段硬扛——要么扛着亏损,要么空仓踏空。

多策略组合的目的就是用不相关的策略互相补齐收益曲线。比如你有一个趋势追踪策略(适合单边行情),再加一个均值回归策略(适合震荡行情),两个策略的相关性越低,组合后的净值和单边波动就越小。这才是量化交易从"小打小闹"走向"可持续"的关键一步。

实操上,不用急着追求复杂策略。我建议按"低相关、同逻辑复杂度"的标准慢慢扩充:趋势类(双均线、唐奇安通道)、动能类(相对强弱、动量排名)、套利类(跨期、跨品种统计套利)、事件类(财报发布、指数调仓)。每个策略独立回测、独立风控,在策略池层统一做资金分配。

5.2 一条可复制的学习路线图

播客末尾,嘉宾给了一套学习路线,我结合自己走过来的经验整理成下面这条路径,按顺序推进即可。

第一阶段(1-2个月):打基础。学Python基础语法、pandas数据处理,搞懂DataFrame和Series的常用操作。目标不是当程序员,而是能读懂行情数据、能算均线和收益率。资源方面看廖雪峰Python教程入门,再用pandas官方文档的十分钟入门速览。每天两小时,一个月足够。

**第二阶段(2-3个月):手写回测。**按第3节的方法,从零写一个能跑的双均线策略回测。重点不是你赚了多少,而是把信号生成、仓位平移、成本计算、绩效统计这套逻辑彻底想明白。这一步如果囫囵吞枣,后面用框架也会被各种抽象概念绕晕。

**第三阶段(1-2个月):用框架提升效率。**上手backtrader或vectorbt,把你手写的策略迁移到框架里。你会遇到各种关于指标、定单、滑点配置的问题——遇到问题不要急着搜答案,先想想框架的设计者为什么要这样设计。工具的抽象层次,就是你对量化交易理解的层次。

**第四阶段(长期):迭代和研究。**开始涉足因子挖掘(用Alpha因子库分析数据的预测能力)、统计学习(用LightGBM等模型做收益预测)、多策略组合(如第5.1节所说)。这个阶段没有终点,但你已经从"入门者"毕业,进入"研究者"的状态了。

5.3 面对失败的心态:量化交易是长跑

最后说点务虚但很重要的话。播客里有个听众提问环节,有人问:"我实盘半年一直在亏,是不是该放弃?"嘉宾的回答我记了很久:"量化交易入门阶段的亏损,本质上是你为自己的认知盲区买的单。每一笔亏损都在提醒你一个还没发现的漏洞,关键是你能不能把漏洞变成规则。"

我自己也经历过这个阶段。早期有个策略回测数据好看得不得了,上实盘之后连续三个月跑输基准,最后发现是没算滑点,每次下单的成交价都比回测里贵了几个tick。找到原因的那天晚上我甚至有点高兴——不是高兴亏损,而是高兴真相大白的感觉,回测和实盘的差异终于有了合理的解释。后面我养成了记录实盘交易日志的习惯,每个策略都认真记录信号、实际成交价、预期成交价的偏差。半年之后翻日志,你会发现实盘与回测的偏差模式是那么清晰可见,每一条都能映射到一个具体的技术环节。把偏差修掉一个,你的系统就结实一分。

量化交易这条路,比拼的不是谁短期赚得多,而是谁能把系统打磨得更完善、让自己活得更久。这个道理,和所有手艺活是相通的。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询