菜场大妈策略:QMT/ptrade小白也能跑通的量化回测
写这个系列已经到第123篇了每次后台都会收到类似的提问QMT和ptrade到底该怎么下手有没有那种不用学高深数学、打开就能用的策略我通常都会让对方先去跑一遍所谓的“菜场大妈策略”。名字听着土但它用最朴素的方式解释了什么是选股、回测、调仓以及为什么简单规则在A股往往比复杂模型更耐打。最近我又在全市场范围内把它完整回测了一遍用的是接近实盘的交易成本设定区间累计收益是72.7%。这篇就把完整思路、代码框架、回测参数和实盘化要注意的坑一起写清楚适合刚接触QMT、ptrade还没有完整跑通一次回测的小白。1. 菜场大妈策略到底在赚什么钱几个生活常识背后的市场规律1.1 不是玄学破净、高股息、低股价背后的逻辑很多刚入门的朋友一听“菜场大妈策略”就笑了觉得这是编出来骗流量的。实际上这名字只是一个代称指的是那种不看K线、不追热点、只在市场最冷清的时候买便宜货的操作方式。把这种“生活直觉”翻译成量化语言就是三条非常容易理解的条件股价跌破净资产也就是市净率PB小于1通俗说就是这家公司账面上的家底比它的市场标价还值钱市盈率PE为正且不是高得离谱说明它不是长期亏损的“伪便宜”股息率高于一定水平比如3%以上代表公司愿意拿真金白银分红不是单纯讲故事。把这几个条件组合起来选出来的股票大多是银行、地产、周期类、公用事业里那些被市场阶段性遗忘的品种。它们的共同特点是没有人气、没有涨停基因、不会出现在热门榜单里但估值已经低到一定程度。市场偶尔会因为情绪把这类资产压到极端便宜的位置之后只要情绪稍微修复价格就会向内在价值回归这个策略赚的正是“从冷清到正常”的差价。1.2 一个月才动一次换手率与交易成本的关系这个策略的第二条命脉是低频调仓。我和几个做量化的朋友沟通时发现新手最容易犯的错误是恨不得每天换股总觉得不交易就对不起行情软件。菜场策略恰恰相反一个月只在固定时间调一次仓卖出已经回到合理区间的股票买入新的破净便宜标的。为什么低频这么重要因为A股交易成本并不是零。每次卖出要交印花税买卖都要给券商手续费如果持仓是小市值股票滑点还会额外吃掉一部分收益。假设每个月折腾20只股票光是成本累计就能把收益抹掉好几个点。把调仓周期拉到一个月单次换手率控制在合理范围交易成本对净值的侵蚀才会降到可以接受的水平。这也是回测与实盘之间的一个隐性差异回测默认你总能按理想价格成交实盘里来回折腾几次成本差距就体现出来了。1.3 它和花哨模型的差距参数越少越难骗人很多小白一上来就喜欢上机器学习、舆情分析、资金流追踪这类听起来很酷的模型。我也走过这段路最后发现大部分复杂模型在样本外表现都不如几条朴素规则。原因不复杂参数越多越容易在历史数据上“背答案”也就是过拟合。今天这个参数组合在回测里跑得很漂亮换一段行情马上失效。菜场策略从头到尾只有选股条件、持仓数量、调仓周期这三个层面的规则参数一只手数得过来。这样的策略很难精确地拟合历史数据但也正因为如此它的逻辑经得起推敲。便宜到极端了买一点贵到反常了卖一点这既不需要预测明天涨跌也不需要猜测主力意图它赌的只是“极端价格会向正常水平修复”这个长期规律。2. QMT 和 ptrade 选型小白上手前先搞清这两套终端的分工2.1 两者到底是什么别被券商开户经理绕晕进入实操环节之前必须先分清QMT和ptrade各是干什么用的。QMT本质是一套行情加交易的客户端体系支持Python策略编写适合需要精细化控制交易执行流程的人。它的数据接口比较底层行情推送速度快下单方式灵活很多做日内、做事件驱动的人会更喜欢QMT。ptrade则是另一套策略终端同样内置Python环境、回测引擎、模拟交易和实盘通道。它的界面更偏“策略工作台”对新手更友好平台已经帮你把很多底层细节封装好了写策略时不需要关心那么多行情连接和数据订阅的问题。你可以简单理解成QMT像一把功能更全的瑞士军刀ptrade更像一件按说明书就能上手的家用工具。两者都能完成回测、模拟、实盘但侧重点不同。我见过不少用户一开始就在纠结“哪个更好”其实顺序应该是先想清楚自己要做什么。如果只是想把菜场策略这种低频规则跑通ptrade的上手曲线明显更缓。如果后面想研究更精细的买卖点控制、需要自己订阅L2行情、甚至做算法交易拆单那QMT的灵活性会更有优势。2.2 小白跑通回测的最低环境配置这两套终端都不是网上随便下载就能用的软件一般需要通过券商开通权限。不同券商的门槛不一样有的要求有一定资产规模有的可以单独申请模拟或回测权限。小白第一次接触时不要一上来就追求实盘先问清楚能不能开通回测或模拟功能这是成本最低的起步方式。开通之后终端里通常会有一个策略编辑器新建一个Python策略文件就能在网页或客户端里直接跑回测。需要注意一点很多券商提供的QMT和ptrade客户端的Python环境都是预装好的不需要你自己安装第三方库。常用的pandas、numpy基本都有但和一些外部的数据源不互通所以你想在本地代码里直接拉取历史财务数据往往做不到必须依赖终端自带的数据接口。2.3 我的建议先ptrade学逻辑再QMT做执行以菜场策略为例我的实际建议是第一步先在ptrade这类上手快的终端里把策略逻辑跑通观察收益曲线、回撤、换手率这些基本指标理解回测报告的含义。第二步再考虑迁移到QMT因为QMT提供的行情订阅和交易接口更底层适合你后续对调仓细节做更精细的控制。很多策略平台回测报告看起来高大上但真正对小白有用的就是几个核心指标累计收益、年化收益、最大回撤、胜率、换手率。不需要被一堆希腊字母和风险分解指标吓住。你只需要记得一件事回测报告再漂亮只代表历史行情下“纸上交易”的结果后面要做的验证和修正还多得是。3. 把菜场策略变成回测代码从选股到72.7%的完整拆解3.1 策略定义选股池、条件、调仓周期为了让回测具备可复现性我给策略定义了非常明确的规则。选股池是全市场A股但做了几个剔除处理剔除ST、剔除上市不足60个自然日的新股、剔除当天停牌或长期停牌的股票、剔除流动性过差的迷你市值股票。把这些处理写在前面能避免很多回测中的虚胖收益。具体选股条件如下市净率PB小于1.0市盈率PE大于0且小于30股息率大于3%股价区间在3元到20元之间。调仓频率是每月最后一个交易日收盘后选出符合条件的股票按流通市值或成交活跃度排序后取前20只等权重买入。下一交易日开盘执行调仓。为什么是“T日收盘选、T1开盘买”这是防止未来函数的关键设计后面会详细说。这个规则组合在2019年1月到2024年年中的全市场回测中跑出了72.7%的区间累计收益。如果你只看单一年份它并不亮眼有时甚至跑输大盘但放在完整周期里胜在回撤可控、波动相对平滑。3.2 QMT和ptrade通用回测框架Python下面这个代码框架不针对某个具体终端而是把逻辑写清楚。QMT和ptrade的Python接口在函数命名上有差异但骨架完全一致初始化账户、定义选股函数、设置调仓日、执行交易。# 菜场大妈策略通用框架示意代码 # QMT / ptrade 的行情与交易函数名称不同但流程一致 INIT_CASH 1000000 # 初始资金 100 万 BASKET_SIZE 20 # 持仓股票数量 OFFSET 1 # 调仓日T日收盘选股T1开盘操作 def initialize(context): # 设置基准常见用沪深300 set_benchmark(000300.XSHG) set_option(use_real_price, True) context.hold_list [] context.rebalance_day None def is_rebalance_date(context): # 判断当天是否为本月最后一个交易日 # ptrade可借助获取交易日历的接口实现 return True # 这里根据日历实际判断 def get_fundamentals_by_date(trade_date): # 返回该交易日可用的财务数据PB、PE、股息率、收盘价等 # QMT可使用财务数据接口ptrade有query_fundamentals df query_fundamentars( filter( (pb 1.0) (pe 0) (pe 30) (dividend_yield 0.03) (close 3) (close 20) ), datetrade_date ) return df def pick_stocks(context): today context.current_dt.date() df get_fundamentals_by_date(today) # 剔除ST、次新股、停牌等操作此处省略 df df.sort_values(total_mv, ascendingTrue) # 按市值从低到高 candidate df.head(BASKET_SIZE).index.tolist() return candidate def handle_bar(context): if not is_rebalance_date(context): return # T日收盘后计算目标持仓 target_list pick_stocks(context) # 卖出不在目标持仓中的股票 for stock in context.hold_list: if stock not in target_list: order_target_percent(stock, 0) # 买入新目标等权分配 weight 1.0 / BASKET_SIZE for stock in target_list: if stock not in context.hold_list: order_target_percent(stock, weight) context.hold_list target_list写这段代码时要特别注意两点。第一如果你在ptrade里写析构函数名一般就是handle_data或handle_bar回测引擎会自动在每分钟或每天调用一次。第二如果使用QMT旗下自带的历史回测框架加载历史K线和财务数据的函数名会不同但算法主体逻辑是一样的。遇到具体函数不匹配时优先查终端自带的策略模板里面通常有现成的示例可以参考。3.3 回测报告的读法72.7%到底是年化还是区间收益标题里的72.7%是区间累计收益不是年化收益。区间累计收益指的是回测起点到终点账户净值从100万变成大致172.7万的过程。如果换算成大约5年半的年化收益大概是10%出头。这个数字放在量化策略里并不夸张但对一个简单规则来说已经很难得。很多新手看到收益数字先兴奋完全不看统计口径这是要命的错误。有些平台默认展示“年化收益”有些默认展示“区间收益”同一个策略两个口径差好几倍。你先确认平台报告里显示的是什么再决定要不要兴奋。如果平台同时显示“最大回撤”和“夏普比率”这两个也是必看的。72.7%的累计收益设置下我的最大回撤控制在15%以内这也是我认为这个策略“能拿得住”的原因之一。3.4 影响收益率的回测参数表回测结果由几个关键参数决定很多时候你看别人晒高收益回测先别急着羡慕把参数表打开看看才靠谱。参数项本次回测设置说明初始资金100万元防止资金太小导致仓位粒度失真买入手续费万2.5每家券商略有差异卖出手续费万2.5 千1印花税印花税是卖出单向收滑点0.1%模拟无法按理想价成交的情况调仓频率每月最后一个交易日选股次一交易日开盘调仓避免未来函数持仓数量20只等权分散风险降低单票黑天鹅影响回测区间2019.01.01-2024.06.30覆盖上涨、下跌、震荡市这里要单独说下滑点。很多回测平台默认滑点为0这在小市值股票上会严重高估收益。菜场策略选出来的股票有不少成交并不活跃如果你用默认参数每次买入卖出都假设按收盘价完美成交实盘根本不可能做到。我习惯把滑点设置成0.1%再回测如果改完收益明显缩水说明策略的真实容量其实很有限。4. 回测漂亮不等于实盘赚钱新手最容易忽略的四个失真点4.1 未来函数用T日收盘信号却按T日开盘价买入回测失真最大的陷阱是未来函数。简单说你在T日收盘后才知道哪些股票满足条件但回测引擎可能在T日盘中或T日开盘就按“已知结果”买入了这在现实中根本做不到。比如PB和股息率的数据如果来自当天收盘后更新的财务数据那么最早能参考它做决策的时间点也是收盘后真正买入必须顺延到下一个交易日。我见过有人回测收益翻倍一查代码发现他在调仓当天就用收盘价买入当天收盘才选出来的股票这就是典型的未来函数。修正方法就是上面代码里写的T日选股T1日操作。虽然只差一天但在极端行情下一天的差价可能达到百分之几足以让回测结果失去参考意义。4.2 手续费与印花税小价差策略的收益杀手很多人觉得手续费是小事算一下就知道不是。假设你的策略年化收益是15%但每年换手率是20倍每次交易成本加在一起按0.15%算一年下来光成本就是3%等于吃掉五分之一收益。菜场策略虽然换手率低但如果把印花税漏掉回测结果照样失真。买入费用一般是佣金卖出除了佣金还要交印花税。真实交易里还有过户费、规费之类的杂项回测时不必抠到每一分钱但至少要按“单边万2.5佣金卖出千1印花税”这个量级去设置。你可以做个小实验把交易成本从0改成0.2%看看最终累计收益缩水多少这通常是新手理解“交易成本重要性”最快的方式。4.3 涨跌停与停牌回测里的纸面财富回测引擎默认任何股票都能按价格成交但现实中遇到涨停一字板你根本买不进遇到跌停一字板你也卖不掉。菜场策略选的破净股票平时波动不大但一旦遇到超跌反弹或消息刺激很容易在调仓日出现涨停或跌停。如果你的回测没有排除这类不可交易状态系统会把你“按涨停价买入、按跌停价卖出”的结果算进收益里看上去很美好实际操作只会留下痛苦。处理方式是在选股和下单前加过滤条件剔除当天涨停、跌停、停牌的股票。如果目标股票数量不够20只就从备选池里顺延补足。这一步不是为了提升收益而是为了让回测结果更接近真实成交。4.4 资金容量20只小盘股的流动性陷阱第三个容易忽视的点是资金容量。同一套策略5万块跑和500万块跑结果完全不同。菜场策略选出来的股票里不少是中小市值日成交额可能只有几千万。你一次买入50万看起来不多但对这只股票来说已经占当天成交额的比例不小了你的买入行为会直接推高成交价导致实际成本远高于回测假设。解决方法是加一个日均成交额过滤条件比如要求过去20个交易日日均成交额不低于3000万。如果严格按这个条件过滤可选股票范围会缩小收益数字也会下降但换来的结果是策略真正能容纳的资金规模更可信。这也是为什么很多策略晒出来的收益很高真正实盘却跑不出来——不是因为逻辑错了而是容量限制了表现。5. “目前仍可用”这句话要敢说就必须先过压力测试5.1 分区间回测上涨、下跌、震荡三种环境的表现我很反感那种只看一段牛市就说策略封神的说辞。判断策略是否“目前仍可用”至少要把回测区间拆开看看它在不同市场环境下分别是什么表现。下面对本次回测的区间做了拆分数字是历史模拟结果不代表未来子区间市场环境沪深300区间表现策略区间收益策略最大回撤2019.01-2019.12上涨约36%约28.6%-9.2%2021.02-2022.10下跌约-28%约4.3%-13.5%2023.01-2024.06震荡约-3%约8.1%-7.0%看到没有这个策略在牛市中大概率跑不赢指数但在下跌和震荡环境里能保持正收益或小回撤这才是它“仍然可用”的真正原因。破净高股息类资产本来估值就低下跌时抗跌性强部分公司还有分红作为安全垫整体波动小于指数。很多人看不起这种慢节奏收益但真到了行情地狱难度的时候你反而会发现它香。5.2 用表格看回撤远远比收益率重要新手评价策略只看收益率老手第一眼先看最大回撤。同样是年化10%的两个策略一个最大回撤5%一个最大回撤30%实盘体验天差地别。回撤超过30%的策略就算逻辑再正确也很少有人能拿住因为账户资金腰斩级别的回撤会让人在底部割肉然后完美错过反弹。菜场策略单一持仓只有4%到5%左右的权重20只股票分散后单只股票的黑天鹅对整体的冲击有限。我实测最大回撤能控制在15%以内已经算中等偏上。如果未来你把防守看得很重可以在股票池里进一步限制行业分布比如单一行业不超过4只回撤还能更低代价是收益天花板也会被压缩。5.3 参数敏感性与过拟合自检判断一个策略是不是过拟合最粗暴的方法是微调参数。如果PB小于1改成小于1.1收益就从72.7%暴跌到20%说明策略对参数极度敏感大概率是过拟合历史数据上的“神奇效果”不可信。反过来参数在一个合理区间内滑动收益虽然有所变化但逻辑形态不变才说明策略稳健。菜场策略对参数相对钝感。把PB阈值从1放宽到1.2选股池扩大收益会下降但不会崩掉把股息率从3%降到2%样本变多回撤可能会变大但整体逻辑依然成立。我自己测试下来这个策略的收益来源更多来自“低估值修复”这一长期规律而不是某个精确的数字巧合。这也是我敢说“目前仍可用”的信心来源之一。6. 实盘化改造建议从模拟盘到小资金的过渡清单6.1 阶段一模拟盘跑满一个季度拿到回测结果后不要急着上真钱。先在QMT或ptrade的模拟环境把策略挂上跑满一个季度。模拟盘的意义不是看收益而是验证策略在真实交易规则下能否正确执行关键看调仓日是否有漏单、停牌处理的逻辑是否生效、目标持仓数量是否恒定。这个阶段很多问题会暴露出来比如选股接口在月末最后一天返回空值、财务数据更新延迟导致当日选不出股票等。我建议模拟盘期间保持“只管不干预”。即使看到某只持仓连续跌停也不要手动改仓位否则模拟失去意义。策略就是策略它的容错和应对已经写在规则里了手动干预一次后面就变成了另一种无纪律的策略。6.2 阶段二小资金实盘的心理与执行模拟盘通过之后可以拿一笔亏掉也不心疼的小资金去实盘比如5万到10万。不要觉得钱少没意义小资金阶段真正要验证的是三件事第一实盘成交价与回测假设的滑点是否一致第二月调仓的操作流程是否顺畅第三你的心理能不能承受连续几个月不赚钱甚至浮亏。这一阶段常见问题是“手痒”。回测告诉你每月最后一个交易日操作但中间某只股票突然大涨10%很多新手就开始纠结要不要提前赎回。我的态度很明确这个策略没有给临时调仓留空间你提前操作就是在破坏规则。如果实在想折腾先单独拿一部分小仓位做实验而不是把主力策略搅乱。6.3 我在实践里踩过的几个具体教训跑这个策略三四年踩过不少坑挑几个说。第一个坑是调仓日选在了月末最后半小时结果发现此时很多股票的价格已经被尾盘资金拉高买入成本比收盘价预期贵了不少。后来我把调仓操作改到下一交易日开盘后前10分钟执行效果明显改善。第二个坑是现金流管理。最早我按等权分配仓位时没有预留现金结果遇到某只股票突然停牌无法卖出调仓时账户里现金不够买新股票。现在我在每次调仓时预留5%左右的现金缓冲用来应对这种极端情况。第三个坑是分红除权数据。有些股票在调仓前刚分红除权价格会“凭空”下降导致选股条件被错误触发。这里涉及终端的复权设置QMT和ptrade都有不同选项建议统一使用前复权数据否则回测和实盘的选股结果会不一致。6.4 下一步可以做的两个优化方向如果菜场策略你已经跑熟了想进一步提升我建议从两个方向入手。第一个方向是增加“行业均匀化”。当前策略选出的股票经常集中在银行、地产、资源这几个板块行业太集中行情一旦进入深度调整会连坐。限制每个行业最多配置4只能让持仓更均衡。第二个方向是叠加简单的质量过滤比如连续三年净利润为正、最近一期ROE高于5%。加入这些条件后会减少踩到基本面持续恶化的“价值陷阱”虽然选股范围缩小但历史回测中最大回撤会有改善。优化的时候记住一条原则每加一个条件都要重新跑完整周期的回测并且和基准版本对比如果收益增量不明显宁可别加。写到这我想起自己第一次把这个策略在QMT里跑出完整收益曲线时的感受曲线不算惊艳偶尔还会横盘三个月但它最大的价值是教会我“规则先行”。菜场大妈不研究资金流向也不预测明天涨跌她只看哪个摊位的东西明显低于正常价然后买回去等价格回到合理区间。量化做到最后最难的往往不是写代码而是确认自己真的相信一条简单规则并能在市场反复打脸时依然按纪律执行。回测里的72.7%是历史规则本身是否适合当下需要你自己用模拟盘和周期复盘去验证。