返回列表 发布新帖

6步选股4个坑5年14倍:"菜场大妈"策略拆解优化实录

16 0

一、开篇:我,被大妈教育了

最近在聚宽社区翻到一个叫"菜场大妈"的策略,名字接地气,回测成绩还挺能打。今天来拆解一下它的核心逻辑,顺便做点优化。

花了不少时间研究量化,看文章、调参数、研究各种多因子模型,每天盯着Alpha信号、因子暴露、信息比率……然后呢?

年化跑不赢沪深300。

有一天我妈跟我说,她闺蜜在菜场摆摊,顺手买了几只"便宜的、但公司还在赚钱的"小股票放着,几年下来收益还挺好。

我当场沉默了三分钟。


大妈炒股的逻辑,其实就三条:

  1. 便宜——贵的我买不起,也不放心
  2. 有肉——不能是只讲故事的空壳子,得有真收入
  3. ——大公司牛人多,哪轮到我捡漏

听起来很土?但这三条,其实就是"小市值低价策略"的核心逻辑。


二、大妈的选菜秘籍

整个策略的核心思路,可以用一套"去菜场买菜"来解释。

第一步:扔掉烂菜叶子(基础过滤)

大妈第一眼看菜,先排除烂掉的、发霉的、一看就没法吃的

股票里的"烂菜叶子"就是:

  • ST股票:名字前面贴了黄色标签,绝对不碰,看着便宜,全是坑
  • 退市边缘股:名字里带"退"的,大妈直接转身走人
  • 停牌股:菜摊今天关门,你掏钱也买不到
# 过滤ST及其他具有退市标签的股票
def filter_st_stock(stock_list):
    current_data = get_current_data()
    return [stock for stock in stock_list
            if not current_data[stock].is_st           # 不是ST
            and 'ST' not in current_data[stock].name   # 名字里没有ST
            and '*' not in current_data[stock].name    # 没有星号(*ST)
            and '退' not in current_data[stock].name]  # 没有退字

另外,科创板和北交所也不去——大妈不去高档精品超市,她就逛老菜市场,接地气,看得懂,买得放心。

# 过滤科创北交股票
def filter_kcbj_stock(stock_list):
    for stock in stock_list[:]:
        if stock[0] == '4' or stock[0] == '8' or stock[:2] == '68' \
           or stock[:3] == '300' or stock[:3] == '301':
            stock_list.remove(stock)
    return stock_list

第二步:检查有没有肉(基本面防雷)

光便宜不行,还得有真材实料

大妈挑肉的标准很简单:

  • 净利润必须大于0——不赚钱的公司,买它干什么?
  • 营业收入大于1亿——连1个"小目标"都没有,别浪费咱的买菜钱
q = query(
    valuation.code,
    valuation.market_cap,
    income.np_parent_company_owners,  # 归母净利润
    income.net_profit,                # 净利润
    income.operating_revenue          # 营业收入
).filter(
    valuation.code.in_(stocks),
    valuation.market_cap.between(g.min_mv, g.max_mv),   # 市值区间
    income.np_parent_company_owners > 0,                 # 有真肉
    income.net_profit > 0,                               # 净利润也得正
    income.operating_revenue > 1e8                       # 营收过亿才算数
).order_by(valuation.market_cap.asc())                   # 市值从小到大排

这个筛选直接干掉了绝大多数"讲故事的仙股"。


第三步:大妈的极简审美——10块以下才考虑

这是大妈最硬核的原则:

超过10块钱的菜,太金贵,不买。

# 过滤股价高于10元的股票
def filter_highprice_stock(context, stock_list):
    last_prices = history(1, unit='1m', field='close', security_list=stock_list)
    return [stock for stock in stock_list
            if stock in context.portfolio.positions.keys()
            or last_prices[stock][-1] <10]

原版策略过滤的是9元以上,我稍微放宽到10元——毕竟通货膨胀嘛,大妈也得跟上时代。


第四步:专挑边角料——市值最小的4只

经过前面三轮筛选,剩下的候选菜已经都是"便宜有肉的好货"了。

接下来怎么选?按市值从小到大排,挑最"边角料"的4只。

捡漏心理:专挑摊位最边角没人注意的那几样,够小、够便宜、胜在没人哄抬价格。

目标市值区间:10亿到100亿之间(不能太小,太小容易跑路;不能太大,太大轮不到散户吃肉)。

g.stock_num = 4       # 最多买4只
g.min_mv = 10         # 最小市值10亿
g.max_mv = 1e8        # 最大市值1000亿(写法是万亿单位,实为100亿)

第五步:隔夜馊了,赶紧处理(卖出逻辑)

大妈有一条铁律:隔夜的菜不留。

这里对应的是"昨日涨停股"的处理:

这只股票昨天表现亮眼,涨停了!大妈开心,夸它新鲜。 今天一看,没连板,价格缩回来了。 大妈立刻:"隔夜的,赶紧处理,换新鲜的来。"

def check_limit_up(context):
    current_data = get_current_data()
    if g.high_limit_list:
        for stock in g.high_limit_list:         # 昨天涨停的票
            if current_data[stock].last_price \
               <current_data[stock].high_limit:  # 今天没继续涨停
                order_target(stock, 0)            # 清仓
                g.just_sold.append(stock)         # 记录已卖,不二次买入
    # 卖了之后,如果持仓不够,再候补买入
    position_count = len(context.portfolio.positions)
    if g.stock_num > position_count and position_count != 0:
        my_Trader(context)
        psize = context.portfolio.available_cash / (g.stock_num - position_count)
        for s in g.choice:
            if s not in context.portfolio.positions and s not in g.just_sold:
                order_value(s, psize)
                if len(context.portfolio.positions) == g.stock_num:
                    break

注意先卖后买,这是个细节优化——原版是先买后卖,导致卖掉的票要隔天才能补仓,白白空仓一天。


第六步:一周去一次菜场(换仓节奏)

大妈不每天去菜场,那太累了。她每周一早上进一次城,买够就回家

run_weekly(my_Trader, 1, time='13:50')  # 每周第1个交易日,13:50选股
run_weekly(go_Trader, 1, time='14:00')  # 每周第1个交易日,14:00下单

每周换一次仓,频率不算高,也省手续费。大妈的核心竞争力之一,就是不天天折腾


三、翻车现场实录——代码比菜还难伺候

写策略的过程,踩了不少坑,挑几个有代表性的跟大家分享。

坑一:滑点是隐形摊位费

刚开始没设滑点,回测数据漂亮得一塌糊涂。

等我加上 FixedSlippage(0.02) 和真实手续费之后,收益率肉眼可见地往下掉。

set_slippage(FixedSlippage(0.02))
set_order_cost(OrderCost(
    close_tax=0.001,            # 印花税0.1%(卖出才收)
    open_commission=0.0001,     # 买入佣金0.01%
    close_commission=0.0005,    # 卖出佣金0.05%
    min_commission=0.1          # 最低5毛
), type='stock')

这就是菜场的"摊位费":你看着菜很便宜,结果各种税费加起来,利润被切走一大块。

结论:纸面富贵不算数,扣完成本才是真收益。


坑二:提前钦定几只ETF,曲线漂亮到怀疑人生——这叫上帝视角,不叫量化

这个坑不是代码bug,是人的bug。

接手这个策略的时候,它已经配了一段"组合配置"的逻辑——在小市值股票之外,还额外指定了几只ETF:

黄金ETF(518880)、纳斯达克ETF(513100)、芯片ETF(159995)……

配置理由写得头头是道:分散化、降回撤、对冲A股风险……

一跑回测:2016年到2025年,年化亮瞎眼,曲线好看得像PPT配图。

我当时觉得自己是天才。

然后仔细一看,冷汗下来了——

这几只ETF是谁选的?按什么选的?

黄金,2024年大涨;纳斯达克,2023年翻倍反弹;芯片,也有过自己的高光时刻……

这些都是已经发生的历史。策略里写死了"就买这几只",然后用历史回测来"验证"它们表现好——

这不是量化,这是开卷考试然后说自己考满分


把这几只硬编码的ETF全部剔除之后,换成动态筛选的逻辑——

收益率啪啪往下掉。

曲线一下子瘦了一大圈,以前那段"漂亮区间"直接垮掉一半。

这就是过拟合的最朴素版本:用上帝视角挑菜,当然买的全是好菜。但你在真实菜场里没有上帝视角,前一天猪肉涨价了你也不知道。

结论:回测数据越漂亮,越要多问一句"为什么"——是策略真的好,还是你亲手喂了它正确答案?


坑三:9:30下单,大妈两手空空

这个坑藏得很深,或者说——回测系统藏得太好了

策略里有一段逻辑,要在 9:30 开盘第一分钟执行买卖:

run_daily(check_limit_up, time='9:30')

理论上完全合理——越早下单越好,抢先手嘛。

回测跑下来,成交记录里也都有,数据漂漂亮亮。

但如果一上SHIPAN,就尴尬了。

9:30 开盘的第一分钟,实际上是集合竞价刚刚结束的瞬间。行情数据还没稳定下发,API 还没完全就绪,大量股票这一秒钟根本拿不到有效的实时价格——

于是下单,要么直接报错,要么以 0 价格挂出去被拒单,总之:两手空空,什么都没买到。

回测里的 9:30 是"模拟的9:30",数据是现成的,下单当然成功。 **里的 9:30 是"真实的9:30",数据还在飞,根本接不住。


改成 9:319:35 之后,下单终于正常了。

但收益往下掉了。

因为策略里有依赖"开盘价"的买卖判断,哪怕晚了1~5分钟,成交价就不一样了,有些单子该买的没买上,该卖的滑了点。

看起来只差几分钟,代入收益一算,差距比想象中大。

结论:回测的时间刻度是理想化的,****的第一分钟是混沌的。9:30 下单,在这个系统里就是一个幻觉。**


坑四:大妈不止损,一套套到认命

这是原版策略最让我不安的一个设计——它没有止损。

大妈的哲学是:买来的菜,就算有点蔫,也不扔。泡泡水,明天还能吃。

策略里只有两种卖出情形:

  1. 股票掉出候选池(市值涨太大、财务变差、变ST了)
  2. 昨天涨停今天没连板

跌了10%?不管。跌了20%?继续拿着。只要它还符合小市值条件,就一直持有。

极端行情一来,这个策略会被套得很难看。2020年春节后复市那天,我看着日志:

[止损] 002112 三变科技 成本=6.94 现价=5.83 亏损=-16.0%,强制清仓
[止损] 600099 林海股份 成本=6.74 现价=5.65 亏损=-16.2%,强制清仓
[止损] 600493 凤竹纺织 成本=5.67 现价=4.77 亏损=-15.9%,强制清仓

等等,这是我加了止损之后的日志,亏损还是达到了15%+。

原因很简单:跌停股票卖不掉。

止损单挂出去,市场全是卖盘没有买盘,当天根本成交不了。第二天继续跌停,继续挂单,继续成交不了—— 这就是"跳空"的残酷现实:止损线写的是8%,真正止损的时候可能已经亏了15%。

最后加了两个补丁才勉强解决:

  • g.stop_loss_set 记录已发止损单的股票,跌停时静默重试,不重复打日志
  • 止损后的股票本周不再买回,防止刚割肉就被自动补仓
g.stop_loss_ratio = 0.08    # 止损线8%
g.stop_loss_set = set()     # 已发止损单,跌停未成交则次日重试

结论:止损写进代码只是第一步,跌停穿越才是真正的硬伤。极端行情面前,8%的止损线可能保不住你,但有和没有,差距还是很大。


四、最后说一句

折腾一圈之后,我发现我最初看不上的东西,反而是最难做到的。

量化圈有个通病:喜欢追求复杂。因子越多越好,模型越深越好,参数越精细越好。

但菜场大妈的策略只有三个筛选条件,逻辑三句话说完,参数五个以内,任何人看懂之后都能在脑子里复现一遍。

这就是它最宝贵的地方:逻辑清晰。

你知道它为什么买,你知道它为什么卖,你知道它会在什么情况下亏钱,你不会因为"这个信号我也说不清楚为什么"而在极端行情裂开。


说到稳定性,我的实际体感是这样的:

  • 不会让你一夜暴富——4只小市值股票,赶上行情好也就是稳稳地跟上指数
  • 也不容易把你打趴下——每周换仓、市值过滤、加了止损,大的单边亏损会被强制截断
  • 回撤控制不完美,极端行情(比如2020年春节复市)照样被打,但不会持续失血

这种感觉就像买的不是最贵的全熟牛排,而是一碗好熬的老火靓汤——慢,但真实,喝完不反胃。


当然它有很多问题没解决,还需要继续努力:

  • 没有趋势判断,熊市里也一样买买买
  • 止损是硬止损,碰到跌停排队卖不掉的情况还是会穿越
  • 换仓频率固定,遇到消息面剧变反应慢

回测这么漂亮的策略,拿到全市场里到底能排第几?

我把这个大妈策略丢进了一个叫 9db智能体交易竞技场 的地方,那里可以上传交割单,跟别人的策略一起按实时收益PK排名,跑了才发现,大佬们的有多稳


如果这个策略对你有帮助,点个赞就行。如果你发现了什么Bug或者有更好的改法,欢迎评论区指教——毕竟,大妈选菜也需要老街坊互相提醒。

回复

您需要登录后才可以回帖 登录 | 立即注册

客服专线

400-080-8112

用思考的速度交易,用真诚的态度合作,我们是认真的!
  • 关注公众号
  • 添加微信客服
Copyright © 2001-2026 迅投QMT社区 版权所有 All Rights Reserved. 京ICP备2025122616号-3
关灯 快速发帖
扫一扫添加微信客服
QQ客服返回顶部
快速回复 返回顶部 返回列表