说明 :这是一篇个人学习记录,不构成任何形式的实盘建议。
文章中的所有实验都是在「已知长期负期望」的前提下进行的,目的只有一个:
用工程化的方法,把"为什么赢不了"这件事搞清楚
1. 引子:动机与设定
这一年,我把很多业余时间都砸在了 PK10 上。
不是为了「一夜暴富」,也不是相信什么「绝技」,而是想用工程化的方式回答一个简单又直白的问题:
在一个开奖几乎完全随机、赔率又让玩家天然负期望 的游戏里,
还能不能通过策略和模型,找到哪怕一丁点「可利用的规律」?
如果只看理论,答案很残酷:不行。
------ 随机独立 + 负期望,本来就意味着「长期必输」。
但我并不满足于"相信结论",而是想自己动手,用代码和数据走一遍,从另一个角度把这件事证明给自己看。同时,也借这个过程练习数据工程、统计、机器学习、深度学习、强化学习这些工具。
一年下来,结果基本可以先写在前面:
- 从统计分布到相关性检验,PK10 的历史开奖非常接近一个独立同分布的随机过程;
- 在这种前提下,不论是规则策略、传统机器学习、深度网络还是强化学习,
都没办法稳定地打破负期望,只能短期「好看一下」,最终回归亏损。
但这段折腾的过程本身很有价值:
我一步步把自己最初零散的脚本,整理成了一个相对完整的「个人量化实验室」,也在实践中理解了很多书本上的概念。
这篇文章就是对这一阶段的总结:
整理我做过的事情、用过的方法、得到的结论,以及接下来我打算怎么继续玩下去。
2. 游戏与数据:PK10 作为一个随机过程
2.1 游戏规则简述
PK10 的基本规则(这里只保留和本文相关的部分):
-
每期从 1~10 这 10 个数字中,产生一个全排列;
-
10 个位置分别称为「第 1 名」到「第 10 名」;
-
常见玩法包括:
- 某个位置的大小(1-5 为小,6-10 为大);
- 某个位置的单双(奇数为单,偶数为双);
- 冠亚和及其大小、单双等。
这意味着:
- 每一期开奖中,「大/小」各自恰好出现 5 个,「单/双」也各自恰好是 5 个;
- 真正随机的部分在于:每个数字出现在每个位置上的概率是否均匀,彼此是否独立。
这恰好非常适合作为一个「随机序列实验室」。
2.2 数据规模与采集
为了做长期的统计与回测,我从公开接口抓取了 PK10 的历史开奖数据,并存到本地数据库和 CSV。
大致数据量级是:
- 每天 1152 期;
- 一个阶段的数据大约覆盖数十万期;
- 每期有 10 个位置的号码,以及我额外派生出来的「大/小、单/双、冠亚和值」等字段。
数据抓取与清洗主要由一组脚本完成,比如:
- 从接口拉原始 JSON,转成统一格式;
- 保存为 CSV/SQL,方便做批量统计和回测;
- 建立简单索引,支持按时间窗口检索数据。
这一块虽然没什么花哨算法,却是后面所有研究的基础。
2.3 初步统计:接近完美的均匀性
拿到数据之后,第一个问题其实很朴素:
各种简单统计量,是不是如理论所说那样「非常平均」?
我对每个位置、每种属性做了大样本统计,包括:
- 各位置的大/小比例;
- 各位置的单/双比例;
- 数字 1~10 在各位置出现的频率;
- 各种组合(比如「第 1 名为大且单」)的频率。
结果非常接近直觉上的「完美随机」:
- 每个位置上,大/小出现的比例都在 49.x% ~ 50.x% 之间;
- 每个数字在各位置出现的频率,基本都在 接近 10% 的范围内波动;
- 随着样本量增大,这些比例整体会缓慢往 50% / 10% 靠拢。
这些数字并不惊艳,却构成了一个关键信号:
如果系统存在某种偏差,它要么非常非常小,要么在我现有的数据量和方法下检测不到。
2.4 相关性检验:前后期几乎独立
均匀性只是第一步,更关键的问题是:
开奖期与期之间,有没有可利用的相关性或结构?
我做了一些简单而直接的检验:
- 计算「某个位置当前期是大/小」与「下一期是大/小」之间的条件概率;
- 计算不同位置之间在同一期内的相关性;
- 做类似 NIST 随机性测试的游程检验、区块检验等。
结论可以一句话概括:
在统计误差范围内,前后期几乎可以当作独立事件。
比如,对于某个固定位置:
- P(下一期是大∣当前期是大) 与
P(下一期是大∣当前期是小) 非常接近,都围绕 50% 左右小幅波动。
这意味着:
- 我们肉眼看到的「连大」「补偿」「走势」,
很大程度上只是随机序列中自然会出现的模式; - 用这些模式作为下注依据,从数学上讲是站不住脚的。
也正是在这样的背景下,我开始了后面的各种策略与模型实验:
一方面是「不甘心」,另一方面也想用实际回测来印证这些理论。
3. 我到底在找什么:研究问题拆解
如果把各种尝试抽象一下,其实就围绕着三个核心问题:
Q1:有没有稳定的统计偏差?
也就是说:
- 在长期数据上,是否存在某些玩法,其真实胜率 大于 赔率隐含的胜率?
如果存在这样的偏差,那么哪怕只高出 1%,只要回测和统计显著,就有理论上的「正期望策略」可以挖。
Q2:在短期窗口内,能不能预测走势?
即使长期完全均匀,短期区间内仍然可能出现偏离:
- 未来 5 期、第 7 名是不是更有可能开「大」?
- 未来 10 期里,某个位置出现某个数字的次数是否会「多于平均」?
很多人日常下注时,直觉其实就是在做这种「短期预测」。
我想用模型把这种直觉形式化:看看在现实数据上,能否在未来 5~10 期这种尺度上,获得哪怕稍微高于 50% 的胜率。
Q3:在负期望前提下,还能做什么?
假设前两个问题的答案都是悲观的,那第三个问题就是:
-
在负期望已成定局的前提下,是否可以通过:
- 资金管理;
- bandit 或强化学习;
- 过滤下注时机;
来让:
- 短期内盈利的概率更高一点,或者
- 总体亏损的速度更慢一些?
这个问题已经从「如何赢钱」转成了「如何在注定输的游戏里,控制自己的下落方式」。
这种思路在真实的金融风险管理里反而非常常见,也算是我继续研究下去的一个动机。
4. 方法路线 1:特征工程 + 传统机器学习
最系统的一条线,是把 PK10 的某个玩法,直接建成一个标准二分类问题,然后用传统机器学习来做预测。
4.1 特征设计:滚动窗口下的各种统计量
以「某个和值是否为小」为例,我构建了大量基于滚动窗口的特征,包括:
-
过去 24 / 48 / 96 / 192 / 384 期的:
- 平均值、标准差、最大值、最小值;
- 最近几期的平均数(前 4、8 期);
- 指数加权平均(EWMA);
-
衍生的逻辑特征:
- 当前和值与历史均值的偏差(z-score);
- 过去窗口中「小」出现的频率、连开情况等。
这些特征的目的是:把我们脑子里那些「走势」「偏离均值」「该回调了」之类的感觉,转成明确的数值指标。
4.2 模型:Logistic + GBDT + Stacking
在模型端,我主要做了两种组合:
-
单模型版本:
- 以 Logistic 回归为主,简单直接,可解释性强;
-
集成版本:
- LightGBM + XGBoost + Logistic 的 stacking;
- 使用 KFold 做 out-of-fold 训练,尽量避免过拟合。
这两类模型都是在严格的时间顺序下训练与验证:
用早期数据训练,用中期数据做验证调参,最后再留出一段数据做真正的测试。
4.3 信号生成与下注规则
模型给出的是「下一期是小的概率 ppp」,
我并不是在每一期都下注,而是做了阈值过滤:
- 当 ppp 非常高(比如进入 top 15%)时,只押「小」;
- 当 ppp 非常低(比如进入 bottom 15%)时,只押「大」;
- 其他时候跳过,不下注。
这样做的逻辑是:
只在模型「最有把握」的时候出手,宁可少下注,也要避免完全随机的情况。
4.4 回测与现实约束:从「暴利」到「认输」
在最早期的回测版本里,我用的是一系列非常理想化的假设:
- 下注金额固定;
- 不考虑限注和其他现实限制;
- 可以随意在任意多盘口上下单。
在这些假设下,某些模型的回测曲线很好看,甚至出现了持续盈利 的阶段。
但随着我逐步把现实约束加回来(这也是这条线真正有意思的地方):
- 限制单期、单玩法的下注金额;
- 限制每天的总下注次数;
- 计入佣金、滑点等因素;
我计算了多个版本的每日收益:
daily_profits.csv:理想假设下的收益;daily_profits_corrected.csv:加入部分现实约束后的收益;daily_profits_realistic.csv:较完整考虑现实条件后的收益。
最终,在「现实版」的语境下,
这套看起来颇有技术含量的 stacking 策略,
在长样本期上总体亏损 约几万单位 ,
和简单的固定押大/押小相比,本质上只是曲线更曲折了一些。
这一条线让我非常直观地意识到:
在一个独立随机 + 负期望的环境里,
即使模型在训练集/验证集上捕捉到了某些模式,
一旦放回到「完整的时间序列 + 现实交易约束」中,最终还是会被负期望吃掉。
5. 方法路线 2:深度学习序列模型
传统机器学习之后,我把思路推进到了深度学习:
既然 PK10 可以看作一个时间序列,那用 CNN / LSTM / Transformer 来挖更复杂的时序模式,会不会有惊喜?
5.1 输入与标签设计
为了充分利用「序列信息」,我对数据做了这样的编码:
-
取最近 64 期或 128 期;
-
对每一期的 10 个位置,分别编码为:
- 是否为大(0/1);
- 是否为单(0/1);
-
得到一个类似 [时间步 × 特征通道] 的张量,
例如:
64 × (10 位 × 2 属性) = 64 × 20。
在标签上,我尝试过多种设计:
- 预测下一期某个位置是大/小;
- 预测未来 6 期内某种组合(如大偶、大小交替等)是否出现;
- 预测未来一个小窗口内,大/小的数量是否偏离均值。
这些标签本质上都是对「短期走势」的不同刻画方式。
5.2 模型结构:ResNet、CNN、Transformer、LSTM
我实现并尝试了多套架构:
- 基于 1D CNN 的残差网络(ResNet Tiny);
- 在 CNN 上叠加简单的 Transformer 模块,试图捕捉更长距离依赖;
- 传统的 LSTM / GRU 序列模型;
- 以及多个模型的 ensemble。
这些模型在工程上都是标准套路:
卷积 + 残差块 / 自注意力 + 全连接输出,
训练时用交叉熵损失、Adam 优化器、学习率衰减等。
5.3 训练表现与过拟合问题
在训练集和验证集上,这些深度模型的表现往往会比纯随机略好:
- 准确率比 50% 高出几个百分点;
- AUC 有时能到 0.55 或更高;
- 损失函数相对纯随机基线明显下降。
如果只看这些数字,很容易产生错觉:
「模型学到东西了!」
然而,当我把模型放到严格按时间分割的测试集上,并用真实回测方式去检验时,情况就不那么好看了:
- 准确率迅速向 50% 靠拢;
- AUC 在误差范围内徘徊;
- 不同随机种子 / 不同训练区间得到的模型,在测试集上的表现差异很大,稳定性很差。
这其实正是「在白噪声上做深度学习」的典型症状:
模型会在训练集上努力拟合各种随机噪声模式,但这些模式在未来完全不复存在。
5.4 从深度学习的角度再认识一次「随机」
这条路线给我的最大收获是认知上的:
只要底层过程真的是「独立随机」,
再强大的函数近似器(CNN、Transformer......)也只能努力拟合过去的噪声,
却无法在未来创造出稳定的 edge。
这听起来像是常识,但当你在代码和 GPU 上亲自体验一遍之后,这个结论会刻进骨子里:
模型并不是无所不能,信息边界之外的东西,它也无从下手。
6. 方法路线 3:强化学习与 Bandit
尝试完「先预测,再下注」的思路后,我也玩了一些更"直接"的方法------让强化学习模型自己在仿真环境里学习下注策略。
6.1 环境建模
我把 PK10 简化成一个 RL 环境:
-
状态:最近 N 期的开奖特征(大小、单双、和值、滚动统计等);
-
动作:
- 押大、押小、押单、押双,或者不下注;
-
奖励:
- 单期押中的赢利,押错的亏损,不下注则 0。
在这个环境里,智能体可以自由探索「什么时候下注」「怎么下注」「压哪一边」。
6.2 算法与实现
我做过的尝试包括:
- 基于 Q 表的小规模 Q-learning;
- 线性近似 / 简单网络逼近 Q 函数;
- 多臂 bandit 算法(ε-greedy 等),
用来在线调整一些策略参数,比如返现比例、下注规模。
这些实验更多是在验证一个直觉:
如果不给它任何"未来信息",只让它在历史数据上反复玩,
强化学习能否自主摸索出一套比人类直觉更好的下注策略?
6.3 结果:学到的只是「如何更体面地输」
现实很有趣,也很诚实:
- 有些 Q-learning 策略,最终学出来的行为是几乎不下注,只在极少数情况下出手;
- 有些 bandit 在长时间仿真后,调整出一套「亏得稍微慢一点」的参数;
- 如果强行规定「每期必须下注」,策略的长期收益仍然明显为负。
这与理论是一致的:
在一个没有任何信息 edge 的赌场游戏里,强化学习无法凭空创造正期望,只能在负期望的框架内调整路径------要么减少参与,要么在局部看起来更顺滑。
7. 全局回顾:从「想赢」到「学会认输」
回头看这一年,其实可以分成三个阶段:
7.1 阶段一:带着幻想上路
最开始,我和很多普通玩家没有本质差别:
- 觉得走势里可能有「某种规律」;
- 相信足够聪明的策略、足够复杂的模型,
能在某些阶段捕捉到高于 50% 的胜率。
于是我写了很多「看起来有道理」的策略:
- 跨路挑出出现最少的号码;
- 押「最长没出的那一边」;
- 盯着连开次数、K 线走势、窗口偏离均值......
短期内,这些策略确实时不时会「表演」一下------
但一旦拉长时间、引入更多数据,所有曲线都逐步滑回负区间。
7.2 阶段二:用模型验证「真的没门」
后来我开始系统地用模型和回测框架来验证这些想法:
-
统计检验告诉我:
- 各位置、各数字的分布几乎完美均匀;
- 前后期之间几乎独立;
-
Logistic / GBDT / CNN / Transformer 告诉我:
- 在严格的时间切分下,
模型在未来的表现很难稳定超过随机;
- 在严格的时间切分下,
-
强化学习告诉我:
- 在没有 edge 的游戏里,
最稳妥的策略往往是降低参与度。
- 在没有 edge 的游戏里,
这些都是同一个事实,从不同角度的重复证明。
我也逐渐从「想法不够好」「模型不够强」的自我怀疑中走出来,改成:
信息边界就在那里,在给定的条件下,这个问题本身就没有可行解。
7.3 阶段三:把失败变成方法论
当我接受「靠走势和模型赢不了」这一点之后,这个项目的意义反而清晰了:
-
我做了一整套数据抓取与清洗的流水线;
-
搭建了一个支持各种策略与模型的回测框架;
-
实践了如何:
- 设计特征;
- 评估模型;
- 避免数据泄漏和过拟合;
- 把看起来漂亮的回测,逐步修正成现实可执行的策略;
-
也在 RL、bandit 的实验里,
深刻感受到了「在没有 edge 的环境里,算法的能与不能」。
这些经验完全可以迁移到别的领域:
无论是金融量化,还是对其他随机过程的建模,它们都能发挥作用。
8. 下一阶段:从 PK10 走向更广泛的量化与随机性研究
这一阶段的结论并不浪漫:
在目前的假设和信息下,我很难通过任何走势或模型击败 PK10。
但我并不打算就此停下,而是把它当成一个好的起点,
下一阶段,我大概会往这几个方向继续探索:
8.1 把现有代码整理成一个「随机序列体检框架」
把这一年积累的脚本系统化:
-
输入:任意时间序列(不一定是 PK10,可以是价格、指标、实验数据);
-
输出:
- 基本分布统计;
- 随机性检验(游程、区块、NIST 风格);
- 简单 ML / DL 预测基线;
- 如果连这些模型都学不到东西,就可以大胆假设:这个序列在给定信息下几乎是「白噪声」。
这套工具可以作为以后做量化或实验时的「第一层 sanity check」。
8.2 把回测框架迁移到更有结构的信息源
既然在真正的随机序列上「无计可施」,那不妨把这套 pipeline 用到别的地方:
- 使用公开金融数据(如指数 ETF、期货连续合约),
练习最基础的因子选股 / CTA 策略; - 用宏观数据、网络数据、业务指标做一些简单预测任务,
观察在这些有真实结构的场景里,
模型是否真的能学到可复现的模式。
这样既能保持工程上的连续性,又能把视野扩展到 PK10 之外。
8.3 保持几个原则:少一些幻想,多一些理性
不管下一步研究对象是什么,我大概会坚持这几个原则:
-
先算期望,再谈策略
- 了解规则与成本,搞清楚理论上是不是负期望;
-
严格时间切分,拒绝数据泄漏
- 训练、验证、测试分明,避免自嗨式回测;
-
任何「太好看」的曲线,都值得怀疑
- 优先检查假设、数据质量、样本外表现;
-
模型不是魔法,只能在信息允许的范围内工作
- 不要指望它在真正的白噪声上创造奇迹。
结语
PK10 最终没有给我一套赢钱策略,这一点我现在可以非常坦然地承认。
但它确实给了我很多别的东西:
一套从数据抓取到回测评估的工程流程、一堆亲手踩过的坑、一堆被证伪的念头,以及一个更稳固的概率与随机性直觉。
如果说这一年的研究有什么价值,大概就是:
把「赢不了」这三个字,从别人嘴里的结论,
变成了一套由我自己动手、用数据和模型反复验证过的、
足够让我信服的 个人答案。
接下来,我会带着这套答案,去挑战一些不是完全随机 的问题。
至于能走多远,那就是下一阶段故事里的事了。