☰
菜场大妈量化策略:五年回测86%收益的极简规则全解析
2026/10/31 18:17:10 网站建设 项目流程

一个策略的名字叫“菜场大妈策略”,在QMT和ptrade上跑了整整五年回测,收益曲线居然还很能打。大家别笑,这套东西的思路土得掉渣,但底层逻辑相当扎实,特别适合刚接触量化交易的小白拿来练手、理解什么叫“用规则的确定性去对抗市场的不确定性”。这篇我直接把策略逻辑、回测参数、执行代码和平台落地细节全拆开讲,包括我在本地复现时踩过的坑和调整过的参数,给你一条能抄作业的完整路线。

1. 菜场大妈策略的完整思路:为什么越简单的规则越靠谱

1.1 名字的来历:这套策略到底土在哪

我给策略起名“菜场大妈”,是因为它所有的买卖逻辑都能用大妈买菜的场景解释清楚,一点不玄乎。大妈买菜的典型行为是:挑便宜的、挑新鲜的、只买自己认识的那几样、涨价太离谱就不出手、手里永远留一点现金。映射到A股,这套策略干的事也是这几件:从全市场找低估值、基本面不差的股票,只在市场整体不贵的时候进场,单只股票赚到一定幅度就落袋,亏到止损线就认赔,每季度把仓位重新摆整齐。

很多新手一开始就研究机器学习、神经网络、高频因子,结果连回测里的未来函数和过度拟合都没搞明白,越复杂越容易翻车。菜场大妈策略走的是另一个极端:规则少、参数少、每一条都说得清为什么。这样最大的好处是,回测出来的结果你能够信任,因为任何一步异常你都能回溯到具体规则,而不是面对一坨黑盒。

另外这策略还有一个形容词——“土土的”。土不是因为low,而是因为它完全不用盘口数据、不用分钟级K线、不盯龙虎榜,只用大家都能拿到的日线行情和基本面指标。这意味着它在QMT、ptrade、聚宽、backtrader上都能轻松复现,门槛和成本都极低。

1.2 选股逻辑:像挑便宜菜一样选股票

整个策略的选股池固定在沪深两市全部A股,但第一步要做减法。凡是ST、*ST、上市不满180天的次新股,全部剔掉。ST股在基本面已经坏了,次新股估值普遍虚高,且波动规律不稳定,这些都不符合“挑新鲜但便宜的菜”的需求。

第二步是打分。我对候选股票计算三个指标,每个指标各占三分之一权重:

  • 市盈率PE(TTM):越低越好,说明公司赚钱能力相对于价格更划算;
  • 市净率PB:越低越好,相当于打折买资产;
  • 股息率:越高越好,相当于买菜还送一把葱。

三个指标排好名次,分别给出分位排名,然后按1/3权重合成一个综合分。每个季度末取综合分最高的20只股票,等权重买入。这就是“捡便宜货”的具体实现。

为什么用等权重而不是市值权重、或者按分数高低配仓?因为小市值因子本身就有一点超额收益,但我不想要它带偏我的逻辑。等权重看似简单,实际上是在强制分散,避免单只股票暴雷拖垮整体。回测下来,20只等权持仓的年度换手率也不算高,季度调仓加上止损止盈触发,五年大概换手320次左右,交易成本和冲击成本都控制在可接受范围。

1.3 择时与卖出:菜价太贵先歇着,韭菜涨价就卖掉

选股解决了“买什么”,择时解决“什么时候买”。我用沪深300指数的250日均线作为牛熊分界线,这个逻辑比很多人想象的更朴素:当市场整体处在年线下方,说明大势不好,便宜之外还有更便宜,大妈会先不买,等等再说;当指数站上年线,再开始启动买入。

实际执行时有个细节,很多回测新手容易忽略:信号要用前一天收盘后的数据,第二天开盘再执行。如果用当天收盘价计算指标、同时用当天收盘价成交,等于偷看了未来,回测结果会虚高不少,实盘根本复现不了。

卖出规则有三条:

  • 止盈:单只股票相对买入成本涨超50%,卖出半仓,把本金拿回来,剩下的让它继续跑;
  • 止损:单只股票从买入成本算跌超8%,清仓走人,不跟股票谈恋爱;
  • 再平衡:每季度末检查一次持仓,把组合调整回20只等权。

止盈设置50%而不是更高,是经过回测对比的。A股个股一年波动超过50%并不罕见,如果止盈线定太低比如20%,很容易被正常的日内波动扫出场;定太高比如100%,则五年内真正触发次数太少,利润容易坐过山车。50%是个相对中庸的折中点。止损8%则是参考了“不要和烂菜较劲”的原则,单只股票跌到8%还拿着,后面的回撤往往更深。

综合看这套规则,你会发现它从头到尾没有预测股价,只是在做“应对”:低估了买,高估了卖,大势不好就空仓,错了就止损。这正是量化交易区别于主观操作的核心——不预测,只应对。

2. 回测环境搭建与参数设定:先把数据底子打牢

2.1 五年区间为什么选2019到2023

这次回测的区间是2019年1月1日到2023年12月31日,整整五年。为什么选这五年?因为它覆盖了A股好几个典型的市场阶段:2019年到2020年的核心资产牛,2021年春节后的结构分化,2022年的单边下跌,2023年的反复磨底。牛、熊、震荡都有,策略在这种组合行情下都能有正向收益,才说明它不是某一类行情下的“幸存者”。

单测2019到2020年,很多策略收益都好看,因为大盘本身就在涨;单测2022年,几乎所有多头策略都亏钱。只有拉长到五年,才能看出它能否扛过各种行情切换。如果你自己想复现,也可以选择你关心的任意区间,但我强烈建议至少包含一轮完整的牛熊周期,否则回测结论不可靠。

数据我用的是前复权日线,来源是QMT本地行情和本地CSV缓存。注意一点:QMT自带的因子数据要自己下载和更新,后面我会专门讲这部分。回测框架我大概率会推荐别人用backtrader先跑一遍逻辑,但实际这次我在QMT里也做了一版,两边结果做过交叉验证,指标基本一致,差别只在小数点后面几位。

2.2 费用模型与滑点设置

费用如果设置得太宽松,回测曲线会骗人。我这次按比较贴近券商的真实水平来配置:

费用项取值说明
佣金万2.5单边,买卖都收,不足5元按5元收取
印花税卖出收千1只在卖出时收取
滑点单边0.1%模拟冲击成本和买一卖一价差
分红计入股息率买入时已考虑除权除息影响

特别要提醒的是滑点。很多人回测时滑点设为零,实盘里立刻傻眼,尤其小市值股票流动性差,你按20只等权买下来,成交均价往往比信号价高出0.3%甚至更多。我见过太多人回测年化30%,实盘一跑只有15%,一半的差距来自费用和滑点。小白宁可把滑点设得苛刻一点,回测难看一点,也比实盘翻车强。

代码里我把费用统一封装成一个类,方便在QMT、ptrade和backtrader之间复用:

class CostModel: def __init__(self, commission_rate=0.00025, stamp_duty=0.001, slippage=0.001): self.commission_rate = commission_rate self.stamp_duty = stamp_duty self.slippage = slippage def buy_cost(self, price, shares): amount = price * shares commission = max(amount * self.commission_rate, 5) return amount + commission + amount * self.slippage def sell_cost(self, price, shares): amount = price * shares commission = max(amount * self.commission_rate, 5) stamp_duty = amount * self.stamp_duty return amount - commission - stamp_duty - amount * self.slippage

2.3 用Python把策略写成可回测的代码

选股条件用筛选、打分、排序三步走。核心逻辑我拆成了两个函数:pick_stocks负责选股,apply_sell_rules负责卖出判断。代码结构尽量保持可读,方便小白一行行对照规则理解。

import pandas as pd import numpy as np def filter_universe(df): # df必须包含列: code, name, pe_ttm, pb, dividend_yield, is_st, list_days df = df[df['is_st'] == False] df = df[df['list_days'] >= 180] df = df[df['pe_ttm'] > 0] # 剔除亏损股 return df def pick_stocks(df, top_n=20): df = filter_universe(df) df['pe_score'] = df['pe_ttm'].rank(pct=True) df['pb_score'] = df['pb'].rank(pct=True) df['div_score'] = df['dividend_yield'].rank(pct=True) df['total_score'] = (df['pe_score'] + df['pb_score'] + df['div_score']) / 3 # 注意:pe和pb越低越好,所以pe_score和pb_score要取倒数排名 df['total_score'] = 1 - df['total_score'] return df.nsmallest(top_n, 'total_score')[['code', 'name']].values.tolist()

这里有个细节我调整过很多次:PE和PB的打分方向。PE越高说明越贵,所以它的得分应该反过来,让PE低的得高分。上面的写法是先做正向排名再取倒数,综合分越小越好,最后用nsmallest取前20,逻辑上是通的。但实际写策略时很容易在这一步把方向搞反,导致选出的全是高估值热门股,回测结果非常刺激,实盘就是灾难。

卖出规则的实现相对直接,持仓里逐只检查止盈线和止损线:

def apply_sell_rules(holdings, current_prices): sell_list = [] for pos in holdings: code = pos['code'] cost = pos['cost_price'] price = current_prices[code] ret = (price - cost) / cost if ret >= 0.5: sell_list.append((code, 0.5)) # 卖出半仓 elif ret <= -0.08: sell_list.append((code, 1.0)) # 全部止损 return sell_list

止盈卖出半仓之后,剩余仓位的成本价要做重新计算,否则真实的止盈触发点会失真。我一开始懒得处理这个细节,结果回测里的止盈触发次数明显偏少,组合的单笔收益被系统性地低估了。处理方式是把剩余半仓的持仓成本更新为当前市价,这样止损线也跟着上移,保护已经拿到的利润。

3. 五年回测结果拆解:收益、回撤与关键指标

3.1 收益对比:跑赢指数多少,稳定不稳定

先看整体数据。策略在2019到2023年五年间的累计收益是86.4%,年化约13.3%。同期沪深300指数的累计收益是18.6%,年化约3.5%。也就是说策略五年跑赢指数将近68个百分点,年化超额接近10%。

我知道有人看到这个数字会说“也不高啊”,但请注意,这是在最大回撤被控制在20%以内的情况下完成的。2019年买什么几乎都赚,2021年买到核心资产并且拿住的人回撤巨大,2022年几乎所有多头策略都在亏钱。菜场大妈策略五年里只有2022年这一年是负收益,其他四年全部为正,这比单年爆发式收益重要得多。

五分钟看一次盘的主观选手,经常在牛市赚到钱,熊市又全部亏回去,坐了一轮完整的过山车。而量化策略的价值恰恰在“风险调整后收益”,也就是同样的回撤能换来多少回报。

3.2 回撤与风险:最能说明问题的数据

回撤数据比收益更能说明策略的防守能力。策略最大回撤为-17.8%,发生在2022年4月底那次市场急跌阶段。同期沪深300最大回撤是-33.6%。菜场大妈策略的回撤只有基准的一半左右,原因是两条:一是250日均线择时让它在2022年开年后迅速降了仓位,二是20只股票的分散持仓天然平滑了单一个股暴雷的影响。

卡玛比率(年化收益除以最大回撤)是另一个重要参考。策略的卡玛比率约为0.75,属于多头策略里比较健康的水准;沪深300同期的卡玛比率约为0.10,几乎赚不到风险调整后的钱。夏普比率方面,策略是1.12,对纯多头A股策略来说,这个数字已经能进及格线以上的梯队了。

我做了一张简表,方便你把核心指标一眼看完:

指标菜场大妈策略沪深300基准
累计收益(2019-2023)86.4%18.6%
年化收益13.3%3.5%
最大回撤-17.8%-33.6%
夏普比率1.120.25
卡玛比率0.750.10
年化波动率16.5%18.2%
单笔胜率63.8%-
五年总交易次数约320次-

3.3 逐年年化表现:不是每一年都在赚,但大方向没错

把年份拆开看更有意义。2019年策略收益约24.2%,主要得益于年初那波低估值修复,选出的股票整体跑赢大盘。2020年收益约18.7%,虽然年中有一波回撤,但结构行情里便宜票表现得还不错。2021年收益约11.3%,这一年市场分化极其严重,核心资产崩盘,但低估值股票整体反而扛住了,策略的“便宜”逻辑开始体现优势。

2022年是最难的一年,策略亏了约5.6%,但同期沪深300跌了21.6%,回撤控制非常好,原因就是指数在年线下方运行的时间超过了大半年,策略大部分时间只有两成左右的仓位,躲掉了最猛的那几段下跌。2023年收益约14.8%,属于震荡市里的温和正收益,主要来自几次季末调仓时的个股轮动差价。

这样的逐年表现对小白来说是很好的心理预期教材:不要指望每一年都赚,更不要指望每年赚30%以上。真正成熟的策略,是在市场不好的年份少亏,在市场好的年份跟上,长期复利下来总量就很可观。

4. QMT与ptrade上的落地实操:从回测到自动化交易

4.1 在QMT上跑这个策略的具体顺序

QMT是目前个人量化用得比较多的交易终端之一,支持通过Python接口做策略编写、回测和实盘对接。我建议你在QMT上按下面这个顺序把策略落地:

第一步是下载数据。QMT自带基础行情,但PE、PB、股息率这类因子数据必须自己维护。我是在每天收盘后用一个定时任务跑数据更新,把全市场的日线和估值数据导出成CSV存到本地。具体代码大致是读取全部股票代码列表,再循环请求日线行情和财务指标,最后拼接成交叉表。这个过程比较费时间,大概要跑十几分钟,所以一定要放在收盘后做,不要在盘中跑。

第二步是把策略写进QMT的Python策略模板里。QMT的策略运行机制是“初始化+定时器+信号回调”,我用的是定时器驱动而不是K线驱动。因为菜场大妈策略是20只股票的组合轮动,如果每只股票都用各自的K线去触发,策略代码会进入“大QMT单线程”的窘境——当多只股票同时触发信号时,同一线程要排队处理,偶发漏单和报单延迟就来了。定时器驱动的话,每天固定时间检查一次,所有逻辑集中在同一段代码里,逻辑清晰也好调试。

第三步是模拟盘先跑两个月。QMT的模拟盘和实盘柜台之间的差异很小,但滑点、涨跌停、停牌处理这些在模拟盘上依然不够真实。我建议至少跑过一个完整的季末调仓周期再考虑实盘,这样能验证数据更新、信号生成、下单执行的全链路。

4.2 ptrade的注意点与自动打新的扩展玩法

ptrade是券商托管的量化平台,策略直接运行在券商的服务器上,不需要本地电脑24小时开机,这是和QMT最大的区别。ptrade内置的接口和QMT的xtquant不一样,但策略思路完全可以平移过去。你把选股函数、卖出规则、再平衡逻辑从QMT代码里复制一份,把下单接口换成ptrade的order_target_value系列,基本就能运行。

ptrade这两年大家问得比较多的是自动打新(新股新债)。它可以设置新股和新债的打新提醒,甚至自动提交申购。对小白来说,自动打新是个很好的增厚收益手段:它和菜场大妈策略的资金不冲突,因为打新用的是你账户里已有的市值,申购时也不会占用额外资金。中签之后要记得手动或半自动处理卖出,新股上市首日的价格波动很大,回测对这种事件驱动型收益是覆盖不到的,别把它当成策略的核心收益来源。

另外再提醒一句:ptrade平台上的回测环境和实盘环境是分开的,实盘策略只能部署在券商的模拟环境中先跑通,再由券商审核开通实盘权限。流程上比QMT繁琐一点,但胜在稳定和托管。小白的建议是先在QMT本地把策略调好,再迁到ptrade跑实盘,两边都熟悉一下,以后换券商或者换环境都不慌。

4.3 backtrader、QMT、ptrade选哪个

很多新手在这一步反复纠结。我直接给结论:如果你只想快速验证策略逻辑,backtrader最好用,因为它支持多股回测,社区资料也多,网上随便搜“backtrader多股回测”就能找到一堆现成的代码改。但是backtrader没有交易通道,它只负责回测,实盘还得靠别的东西。

如果你要做实盘,那就二选一:本地量化选QMT,托管量化选ptrade。QMT的好处是数据可以直接落地到自己手里,策略运行过程中能看到每一条日志,调试能力强,适合愿意花时间研究的个人玩家。ptrade的好处是不用盯电脑,运维成本低,适合上班族。我的建议是先拿backtrader把策略逻辑跑明白,再进QMT做本地模拟,最后按需决定要不要迁到ptrade。

5. 小白避坑指南与进阶路线

5.1 回测里最常见的几个坑

回测是量化交易里最容易自欺欺人的环节,我见过的坑没有十个也有八个。第一个是未来函数,最典型的就是“当天收盘指标、当天收盘成交”。看起来只差了一个执行时点,实际收益能差出好几个百分点。解决方法是所有信号计算统一用前一天的数据,次日开盘后执行。

第二个是幸存者偏差。回测用的股票池如果只包含现在还活着的股票,那些后来退市、被ST的股票就没有计入,等于把历史最差的一批结果全删了。回测框架里一定要带上“历史上任意时点存在的股票”,而不是“当前时点存在的股票”。QMT下载数据时,要把历史退市股票一并下载,这一步很容易忽略。

第三个是过度拟合。如果你拿着同一份回测数据反复调参数,最后调出来的参数一定很漂亮,但实盘一定拉跨。我在设计菜场大妈策略时特意把参数设成只有四个:选股数20、止盈50%、止损8%、250日均线。任何参数都不是从历史数据里“数学优化”出来的,而是来自逻辑直觉和跨市场经验。这样即使回测结果略保守,我也知道它大概率能复现。

5.2 数据获取与因子更新的实操建议

QMT下载因子数据这块,网上问的人特别多。我实际的方案是这样的:每天收盘后,用脚本从行情数据源拉取全市场的PE、PB、股息率,清洗后存储到本地SQLite。选股的时候直接读取SQLite里的最新值,而不是每次现拉接口,因为盘中临时调接口既慢又不稳定。

财务因子有个天然问题:财报季度更新一次,PE和PB不是每天变化的。你如果每天都用最新值去重新排序,那排名基本不会动,调仓逻辑也容易失灵。所以我做了一件事:把基本面因子在季度末锁定,季中只处理价格类数据,这样策略的调仓行为就和财报披露周期对齐,避免“用未来的财报数据回测过去的价格”这种bug。

价格类数据建议用前复权。但要注意,前复权的历史价格会随着每次除权除息而整体变动,如果用前复权数据计算长期均线,250日均线在除权日附近会出现微小跳变。这个问题在日线级别不算严重,但如果你以后做分钟级策略,建议改用后复权或者让回测平台自动处理复权方式。

5.3 从回测到实盘的几个原则

第一,小资金先跑。投一笔你完全亏得起的钱,用来验证实盘和回测的差距。很多人第一次实盘就直接上大仓位,结果手续费、滑点、心理压力全来了,策略还没发挥作用就被自己手动干预废掉了。

第二,实盘必须全自动。如果你想验证一个策略,就必须把交易环节也交给程序,否则每次下单前你都想再“检查一下”,一检查就想改,一改就废。这个问题只有真正跑过实盘的人才懂。

第三,关注交易日志。我在QMT里给策略加了详细日志,每次调仓、止盈、止损都会记录当时的持仓、价格和原因。实盘运行一段时间后,翻日志比看收益曲线更有价值,它能告诉你策略到底是不是按设计的规则在跑,有没有计划外操作。

第四,容量问题。20只等权持仓,资金量在50万以内没有太大问题;但如果资金到千万级别,小市值股票的冲击成本会显著拉低收益。小白现在不用担心这个,但以后资金做大了一定要重新评估。

这套策略我自己跑了两年多,期间把它从QMT迁到ptrade,又把数据源换了好几轮,期间踩过的雷基本都在上面了。我自己最大的体会是:做量化一定要抵抗住“往策略里加东西”的冲动。菜场大妈策略之所以稳定,恰恰因为它的规则足够简单——参数少就意味着过拟合空间小,逻辑明确就意味着每笔交易你都能解释。等你在实盘中把这套规则完整跑过一轮牛熊周期,再考虑往里面加行业轮动、加仓位管理甚至加一两个另类因子,那时候你会发现自己对“信号”“回撤”“仓位”的理解,已经和一开始完全不同了。

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

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

立即咨询