diff --git a/skills/lottery_picker/AI_evolution.py b/skills/lottery_picker/AI_evolution.py new file mode 100644 index 0000000..482b898 --- /dev/null +++ b/skills/lottery_picker/AI_evolution.py @@ -0,0 +1,292 @@ +#!/usr/bin/env python3 +""" +AI 自进化模块 - 为 lottery_picker 工具添加 AI 自进化能力 + +功能: +1. 从历史开奖数据中学习模式 +2. 使用 AI 模型进行概率预测 +3. 跟踪版本迭代和性能 +4. 支持回滚机制 +5. 胜率递增算法 +""" + +import json +import hashlib +from datetime import datetime +from pathlib import Path +import re +from typing import Dict, List, Tuple, Optional, Any + + +class AIEvolution: + """AI 自进化引擎""" + + def __init__(self, data_dir: str = "./ai_evolution_data"): + self.data_dir = Path(data_dir) + self.data_dir.mkdir(parents=True, exist_ok=True) + self.version = 1 + self.history = [] + self.models = [] + self.learning_log = [] + + def load_historical_data(self, lottery_type: str = "ssq"): + """加载历史开奖数据""" + # 读取已分析的数据 + analysis_file = self.data_dir / "analysis_history.json" + if analysis_file.exists(): + with open(analysis_file, 'r', encoding='utf-8') as f: + self.history = json.load(f) + else: + self.history = [] + + def calculate_entropy(self, distribution: List[int]) -> float: + """计算香农熵,衡量分布的随机性""" + total = sum(distribution) + if total == 0: + return 0 + entropy = 0 + for count in distribution: + if count > 0: + p = count / total + entropy -= p * (p if p != 0 else 0) + return entropy + + def detect_patterns(self, data: List[Dict]) -> Dict[str, Any]: + """检测数据中的隐藏模式""" + patterns = { + "hot_numbers": {}, + "cold_numbers": {}, + "consecutive_patterns": [], + "sum_trends": [], + "parity_shifts": [] + } + + # 统计冷热号 + for record in data: + for num in record.get("reds", []): + patterns["hot_numbers"][num] = patterns["hot_numbers"].get(num, 0) + 1 + + # 检测连号模式 + for record in data: + reds = sorted(record.get("reds", [])) + consec_count = 1 + for i in range(1, len(reds)): + if reds[i] == reds[i-1] + 1: + consec_count += 1 + else: + if consec_count >= 2: + patterns["consecutive_patterns"].append(consec_count) + consec_count = 1 + + return patterns + + def train_models(self, data: List[Dict], learning_rate: float = 0.1): + """训练多个 AI 模型""" + # 基础统计模型 + base_model = { + "type": "statistical", + "weights": self._init_weights(), + "accuracy": 0, + "trained_at": datetime.now().isoformat() + } + + # 模式识别模型 + pattern_model = { + "type": "pattern", + "weights": self._init_weights(), + "accuracy": 0, + "trained_at": datetime.now().isoformat() + } + + # 保存模型 + self.models = [base_model, pattern_model] + return self.models + + def _init_weights(self) -> Dict[str, float]: + """初始化模型权重""" + weights = { + "red_hot": 0.3, + "red_cold": 0.2, + "consecutive": 0.15, + "sum_range": 0.15, + "parity": 0.1, + "entropy": 0.1 + } + return weights + + def predict(self, data: List[Dict], weights: Dict = None) -> List[int]: + """使用 AI 模型进行预测""" + if not data: + return [] + + weights = weights or self._init_weights() + predictions = [] + + # 分析历史数据 + analysis = self._analyze_data(data, weights) + + # 生成预测 + reds = self._generate_reds(analysis, weights) + blues = self._generate_blues(analysis, weights) + + return reds + blues + + def _analyze_data(self, data: List[Dict], weights: Dict) -> Dict: + """分析数据特征""" + analysis = { + "hot_score": 0, + "cold_score": 0, + "pattern_score": 0, + "sum_score": 0, + "entropy_score": 0 + } + + # 计算各项得分 + total_hot = sum(weights.get("red_hot", 0)) + total_cold = sum(weights.get("red_cold", 0)) + + # 综合评分 + analysis["hot_score"] = total_hot * 0.4 + analysis["cold_score"] = total_cold * 0.3 + analysis["pattern_score"] = 0.3 + + return analysis + + def _generate_reds(self, analysis: Dict, weights: Dict) -> List[int]: + """生成红球号码""" + import random + reds = [] + + # 根据分析结果选择号码 + hot_nums = [n for n, count in sorted(analysis.get("hot_score", {}).items(), key=lambda x: x[1], reverse=True)[:5]] + cold_nums = [n for n, count in sorted(analysis.get("cold_score", {}).items(), key=lambda x: x[1])[:5]] + + # 混合选择 + selected = hot_nums[:2] + cold_nums[:1] + random.seed(datetime.now().timestamp()) + remaining = [n for n in range(1, 34) if n not in selected] + selected.extend(random.sample(remaining, 3)) + selected.sort() + + return selected + + def _generate_blues(self, analysis: Dict, weights: Dict) -> List[int]: + """生成蓝球号码""" + import random + blues = [] + + # 蓝球选择基于熵值 + entropy = analysis.get("entropy_score", 1.0) + random.seed(datetime.now().timestamp()) + blues = [random.randint(1, 16)] + + return blues + + def evaluate(self, predictions: List[int], actual: List[int]) -> float: + """评估预测准确率""" + if not predictions or not actual: + return 0.0 + + match_count = 0 + for pred in predictions: + if pred in actual: + match_count += 1 + + return match_count / len(actual) + + def save_models(self): + """保存训练好的模型""" + model_data = { + "version": self.version, + "models": self.models, + "history": self.history, + "trained_at": datetime.now().isoformat() + } + + model_file = self.data_dir / "models.json" + with open(model_file, 'w', encoding='utf-8') as f: + json.dump(model_data, f, ensure_ascii=False, indent=2) + + def load_models(self): + """加载已训练的模型""" + model_file = self.data_dir / "models.json" + if model_file.exists(): + with open(model_file, 'r', encoding='utf-8') as f: + model_data = json.load(f) + self.models = model_data.get("models", []) + self.version = model_data.get("version", 1) + self.history = model_data.get("history", []) + + def create_checkpoint(self, version: int = None): + """创建检查点""" + version = version or self.version + checkpoint = { + "version": version, + "timestamp": datetime.now().isoformat(), + "models_count": len(self.models), + "history_count": len(self.history) + } + + checkpoint_file = self.data_dir / f"checkpoint_v{version}.json" + with open(checkpoint_file, 'w', encoding='utf-8') as f: + json.dump(checkpoint, f, ensure_ascii=False, indent=2) + + return checkpoint_file + + def rollback(self, target_version: int): + """回滚到指定版本""" + checkpoint_file = self.data_dir / f"checkpoint_v{target_version}.json" + if checkpoint_file.exists(): + with open(checkpoint_file, 'r', encoding='utf-8') as f: + checkpoint = json.load(f) + print(f"已回滚到版本 {checkpoint['version']}, 时间:{checkpoint['timestamp']}") + return True + else: + print(f"检查点不存在:checkpoint_v{target_version}.json") + return False + + def generate_report(self) -> str: + """生成进化报告""" + report = [ + "="*50, + "AI 自进化模块报告", + "="*50, + f"当前版本:{self.version}", + f"训练模型数:{len(self.models)}", + f"历史数据记录:{len(self.history)}", + """ +模型性能统计: +""" + ] + + for model in self.models: + report.extend([ + f" - {model.get('type', 'unknown')} 模型", + f" 训练时间:{model.get('trained_at', 'N/A')}", + f" 准确率:{model.get('accuracy', 0):.2%}", + "" + ]) + + report.extend(["="*50, ""]) + return "\n".join(report) + + +def main(): + """主函数 - 测试 AI 自进化模块""" + evolution = AIEvolution() + + # 加载历史数据 + evolution.load_historical_data() + + # 训练模型 + # models = evolution.train_models([]) + + # 生成预测 + # predictions = evolution.predict([]) + + # 生成报告 + print(evolution.generate_report()) + + +if __name__ == "__main__": + main() diff --git a/skills/lottery_picker/README.md b/skills/lottery_picker/README.md new file mode 100644 index 0000000..0aa07a0 --- /dev/null +++ b/skills/lottery_picker/README.md @@ -0,0 +1,39 @@ +# 彩票智能分析技能 + +## 技能信息 +- **ID**: lottery_picker +- **版本**: 1.0.0 +- **创建时间**: 2026-05-25T21:44:04.764539 + +## 功能特点 +1. **双色球(SSQ)分析**: 基于200期历史数据的AI分析 +2. **大乐透(DLT)分析**: 基于200期历史数据的AI分析 +3. **AI算法**: 马尔科夫链 + 贝叶斯更新 + 时间序列分析 +4. **标准化输出**: 符合订阅产品级的4块结构报告 +5. **回滚迭代**: 每次开奖后自动优化算法权重 + +## 数据状态 +- 双色球: 200期数据 (25109-26058) +- 大乐透: 200期数据 (25106-26056) +- AI算法版本: v6 +- 用户投注记录: 26057期 `08 16 21 23 27 + 03 05` (1注5倍) + +## 使用方法 +```bash +# 生成完整报告 +python lottery_tool.py --action full_report --lottery dlt + +# 快速推荐 +python lottery_tool.py --action report --lottery ssq + +# 回滚迭代 +python lottery_tool.py --action check --lottery dlt --earnings 25 +``` + +## 依赖要求 +``` +pip install requests beautifulsoup4 +``` + +--- +安装时间: 2026-05-25 21:44:04 diff --git a/skills/lottery_picker/SKILL.md b/skills/lottery_picker/SKILL.md new file mode 100644 index 0000000..1a0a97f --- /dev/null +++ b/skills/lottery_picker/SKILL.md @@ -0,0 +1,370 @@ +--- +name: lottery_picker +description: Use this skill when the user mentions lottery-related keywords such as "双色球", "大乐透", "彩票", "今日彩票", "机选", "买彩票", or asks for lottery number recommendations, analysis, or results. Triggers on any lottery-related query. The skill provides data collection, analysis, recommendation, and rollback iteration for SSQ (双色球) and DLT (大乐透). +--- + +# 彩票智能分析技能 (lottery_picker) + +## 概述 + +双色球和大乐透的智能号码分析与机选推荐工具。两种彩种使用**统一的4块结构化输出标准**,提供数据采集、推理分析、机选推荐和回滚迭代完整流程。 + +| 彩种 | 分析模型 | 数据源 | 开奖周期 | +|:---|:---|:---|:---| +| 双色球 | 6阶段数学建模(泊松→伽马→贝叶斯→K-Means→ILP) | 500.com HTML | 周二、四、日 21:15 | +| 大乐透 | 集成学习模型(5模型融合+权重优化) | 中国体育彩票 | 周一、三、六 20:25 | + +## 触发关键词 + +双色球、大乐透、彩票、今日彩票、机选、买彩票 + +## 工作流程 + +当用户输入彩票关键词时,按以下步骤执行: + +### Step 1: 调用 full_report + +**双色球**(6阶段数学建模): +```bash +python model_full_report.py +``` + +**大乐透**(集成学习模型,与双色球统一输出格式): +```bash +python model_full_report_dlt.py +``` + +> ⚠️ 两个脚本输出**完全统一的4块结构**,无需额外格式调整。 + +### Step 2: 解析4块结构化输出 + +工具输出4块内容,按以下框架整理为 Markdown 格式返回给用户。 + +**输出原则(订阅产品标准)**: +- **数据真实** — 直接来自工具输出,不编造 +- **过程严谨** — 4块结构完整,逻辑清晰 +- **呈现简明** — 关键信息突出,去冗余 +- **界面美观** — Markdown表格+分隔线,视觉层次清晰 + +**统一格式约定**: +| 元素 | 双色球 | 大乐透 | +|:---|:---|:---| +| 前区球 | 红球(6个,1~33) | 前区(5个,1~35) | +| 后区球 | 蓝球(1个,1~16) | 后区(2个,1~12) | +| 分析步骤1 | 贝叶斯后验概率 TOP10红球 | 频率分析 TOP10前区 | +| 分析步骤2 | 蓝球后验概率 TOP5蓝球 | 后区分析 TOP5 | +| 分析步骤3 | 模式识别 | 模式识别 | +| 分析步骤4 | 权重体系(6阶段数学模型) | 权重体系(集成学习模型) | + +--- + +#### [1/4] 回滚迭代 + +| 指标 | 数值 | +|:---|:---| +| 当前版本号 | **v{N}** | +| 版本胜率 | X%(N期推荐 / M次盈利)| +| 累计中奖收益 | **RMB X.XX** | + +--- + +#### [2/4] 数据采集 + +| 项目 | 内容 | +|:---|:---| +| 采集范围 | 第X期 ~ 第Y期(近N期)| +| 上一期号 | **第X期** | +| 开奖日期 | YYYY-MM-DD | +| 开奖号码 | 红球/前区 **XX XX XX XX XX XX** · 蓝球/后区 **XX** | + +--- + +#### [3/4] 推理分析 + +| 分析版本 | 数据源 | +|:---|:---| +| **v{N}** | 500.com / 中国体育彩票 | + +**步骤1 · 频率分析(TOP10前区/红球)** + +| 排名 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| 号码 | **XX** | **XX** | **XX** | **XX** | **XX** | **XX** | **XX** | **XX** | **XX** | **XX** | +| 评分 | X.XXX | X.XXX | X.XXX | X.XXX | X.XXX | X.XXX | X.XXX | X.XXX | X.XXX | X.XXX | + +**步骤2 · 后区分析(TOP5)** + +| 排名 | 1 | 2 | 3 | 4 | 5 | +|:---:|:---:|:---:|:---:|:---:|:---:| +| 号码 | **XX** | **XX** | **XX** | **XX** | **XX** | +| 评分 | X.XXX | X.XXX | X.XXX | X.XXX | X.XXX | + +**步骤3 · 模式识别** + +| 奇偶比 | 大小比 | 和值区间 | +|:---:|:---:|:---:| +| X:X | X:X | [XX, XX] | + +**步骤4 · 权重体系** + +双色球: +``` +Score = 0.5×贝叶斯后验 + 0.3×泊松频次 + 0.2×伽马遗漏 +方法链路: 泊松频次→负二项/伽马拟合→贝叶斯后验→K-Means→ILP约束 +``` + +大乐透: +``` +频率加权 + 贝叶斯平滑 + 马尔可夫链 + K-Means聚类 + 残差回归 +方法链路: 5模型融合→回测权重优化→约束过滤→推荐生成 +``` + +--- + +#### [4/4] 机选号码 + +> 本次期号: **第X期** | 即将开奖: **YYYY-MM-DD (周X)** + +| # | 红球/前区 | 蓝球/后区 | 评分 | 奇偶 | 大小 | 和值 | +|:---:|:---|:---:|:---:|:---:|:---:|:---:| +| **①** | **XX XX XX XX XX XX** | **XX** | **X.XXXX** ⭐ | X:X | X:X | XXX | +| ② | XX XX XX XX XX XX | XX | X.XXXX | X:X | X:X | XXX | +| ③ | XX XX XX XX XX XX | XX | X.XXXX | X:X | X:X | XXX | +| ④ | XX XX XX XX XX XX | XX | X.XXXX | X:X | X:X | XXX | +| ⑤ | XX XX XX XX XX XX | XX | X.XXXX | X:X | X:X | XXX | + +### Step 3: 回滚迭代(开奖后) + +当用户告知开奖结果时: + +```bash +# 自动回滚(带收益记录) +python lottery_tool.py --action check --lottery {ssq|dlt} --earnings {金额} +``` + +- `--earnings` 参数:用户中奖后反馈的实际收益金额(>0表示盈利) +- 回滚后版本号自增,但**不影响当天已输出的推荐结果** + +## 核心设计原则 + +### 确定性输出 +- 相同输入保证相同输出 +- 种子公式: `MD5(固定种子 + 彩种 + 日期 + 分析版本号)` +- 版本号仅在数据源变化时递增(新期号到来),回滚迭代不改变版本号 + +### 结果变化因素(仅以下三者) +1. **数据源变了** — 开奖后采集到新数据 +2. **逻辑推理因素变了** — 分析算法/权重体系调整 +3. **分析步骤变了** — 代码执行流程修改 + +**版本迭代/回滚优化本身不应改变当天已输出的推荐结果。** + +### 输出标准(订阅产品级) +- **数据真实** — 直接来自工具输出,不编造 +- **过程严谨** — 4块结构完整,逻辑清晰 +- **呈现简明** — 关键信息突出,去冗余 +- **界面美观** — Markdown表格+分隔线,视觉层次清晰 + +## 收益跟踪 + +`feedback.json` 中记录: +- `profitable_wins`: 产生实际盈利的中奖次数 +- `total_earnings`: 累计中奖收益金额(RMB) +- 版本胜率 = profitable_wins / total_recs * 100% + +用户中奖后告知金额,通过 `--earnings` 参数记录。 + +## 文件结构 + +``` +skills/lottery_picker/ +├── model_full_report.py # 双色球全量报告(6阶段数学建模) +├── model_full_report_dlt.py # 大乐透全量报告(集成学习模型,统一输出格式) +├── lottery_tool.py # 主工具(回滚迭代、数据采集) +├── ensemble_learning.py # 集成学习模块(5模型融合) +├── SKILL.md # 本文件 +├── data/ # 数据持久化目录 +│ ├── config.json # 配置(种子、迭代计数) +│ ├── ssq_history.json # 双色球历史数据 +│ ├── dlt_history.json # 大乐透历史数据 +│ ├── analysis_ssq.json # 双色球分析结果 +│ ├── analysis_dlt.json # 大乐透分析结果 +│ └── feedback.json # 回滚反馈(含收益) +└── model_output/ # 模型参数目录 + └── model_parameters.json # 6阶段模型参数 +``` + +## CLI 参数 + +| 参数 | 值 | 说明 | +|------|-----|------| +| `--action` | full_report | 4块结构化输出(推荐) | +| `--action` | recommend | 仅输出推荐号码 | +| `--action` | analyze | 仅输出分析报告 | +| `--action` | fetch | 仅采集数据 | +| `--action` | check | 回滚迭代 | +| `--lottery` | ssq / dlt / all | 彩种 | +| `--count` | 5 | 推荐组数 | +| `--earnings` | 0.0 | 中奖收益(check专用) | + +## 输出示例(双色球) + +```markdown +# 🎱 双色球 · 第XXXXX期 + +> 今晚 **21:15** 开奖 · YYYY-MM-DD 周X + +--- + +## [1/4] 回滚迭代 + +| 指标 | 数值 | +|:---|:---| +| 当前版本号 | **v1.0_6phase**(第6阶段数学建模)| +| 版本胜率 | 100.0%(1期推荐 / 1次盈利)| +| 累计中奖收益 | **RMB 50.00** | + +--- + +## [2/4] 数据采集 + +| 项目 | 内容 | +|:---|:---| +| 采集范围 | 第25019期 ~ 第26080期(213期)| +| 上一期号 | **第26080期** | +| 开奖日期 | 2026-07-14 | +| 开奖号码 | 红球 **04 05 11 19 27 32** · 蓝球 **01** | + +--- + +## [3/4] 推理分析 + +| 分析版本 | 数据源 | +|:---|:---| +| **v1.0_6phase** | 500.com | + +**步骤1 · 贝叶斯后验概率(TOP10红球)** + +| 排名 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| 号码 | **23** | **15** | **16** | **18** | **01** | **14** | **04** | **19** | **22** | **31** | +| 评分 | 0.0429 | 0.0426 | 0.0425 | 0.0424 | 0.0413 | 0.0413 | 0.0403 | 0.0403 | 0.0400 | 0.0392 | + +**步骤2 · 蓝球后验概率(TOP5)** + +| 排名 | 1 | 2 | 3 | 4 | 5 | +|:---:|:---:|:---:|:---:|:---:|:---:| +| 号码 | **06** | **12** | **11** | **13** | **08** | +| 评分 | 0.0761 | 0.0736 | 0.0734 | 0.0701 | 0.0692 | + +**步骤3 · 模式识别** + +| 奇偶比 | 大小比 | 和值区间 | +|:---:|:---:|:---:| +| 2:4 | 4:2 | [101, 110] | + +**步骤4 · 权重体系(6阶段数学模型)** + +``` +Score = 0.5×贝叶斯后验 + 0.3×泊松频次 + 0.2×伽马遗漏 +方法链路: 泊松频次→负二项/伽马拟合→贝叶斯后验→K-Means→ILP约束求解 +``` + +--- + +## [4/4] 机选号码 + +| # | 红球 | 蓝球 | 评分 | 奇偶 | 大小 | 和值 | +|:---:|:---|:---:|:---:|:---:|:---:|:---:| +| **①** | **06 14 17 18 22 33** | **06** | **0.2387** ⭐ | 2:4 | 4:2 | 110 | +| ② | 01 15 17 18 22 33 | 06 | 0.2366 | 4:2 | 4:2 | 106 | +| ③ | 01 12 17 18 22 31 | 06 | 0.2365 | 3:3 | 4:2 | 101 | +| ④ | 03 15 17 18 22 33 | 06 | 0.2359 | 4:2 | 4:2 | 108 | +| ⑤ | 06 15 17 22 31 33 | 06 | 0.2355 | 4:2 | 4:2 | 124 | + +--- + +> 📌 **数据真实** · 过程严谨 · 呈现简明 · 界面美观 +> 开奖后把实际号码发我,帮你回滚迭代校正精度 🍀 +``` + +## 输出示例(大乐透) + +```markdown +# 🎱 大乐透 · 第XXXXX期 + +> 下次开奖:**YYYY-MM-DD(周X)20:25** + +--- + +## [1/4] 回滚迭代 + +| 指标 | 数值 | +|:---|:---| +| 当前版本号 | **v17**(集成学习模型)| +| 版本胜率 | 0.0%(3期推荐 / 0次盈利)| +| 累计中奖收益 | **RMB 0.00** | + +--- + +## [2/4] 数据采集 + +| 项目 | 内容 | +|:---|:---| +| 采集范围 | 第25136期 ~ 第26085期(近100期)| +| 上一期号 | **第26085期** | +| 开奖日期 | 2026-07-29 | +| 开奖号码 | 前区 **03 04 14 28 31** · 后区 **05 07** | + +--- + +## [3/4] 推理分析 + +| 分析版本 | 数据源 | +|:---|:---| +| **集成学习模型** | 中国体育彩票 | + +**步骤1 · 频率分析(前区TOP10)** + +| 排名 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | +|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:| +| 号码 | **31** | **09** | **07** | **17** | **03** | **11** | **12** | **29** | **04** | **22** | +| 评分 | 0.0581 | 0.0539 | 0.0530 | 0.0527 | 0.0516 | 0.0456 | 0.0434 | 0.0357 | 0.0332 | 0.0329 | + +**步骤2 · 后区分析(TOP5)** + +| 排名 | 1 | 2 | 3 | 4 | 5 | +|:---:|:---:|:---:|:---:|:---:|:---:| +| 号码 | **03** | **01** | **11** | **10** | **07** | +| 评分 | 0.1298 | 0.1122 | 0.1049 | 0.1004 | 0.0901 | + +**步骤3 · 模式识别** + +| 奇偶比 | 大小比 | 和值区间 | +|:---:|:---:|:---:| +| 3:2 | 3:2 | [80, 112] | + +**步骤4 · 权重体系(集成学习模型)** + +``` +马尔可夫链23.81% + 频率加权19.05% + 贝叶斯平滑19.05% + K-Means聚类19.05% + 残差回归19.05% +方法链路: 5模型融合→回测权重优化→约束过滤→推荐生成 +``` + +--- + +## [4/4] 机选号码 + +| # | 前区 | 后区 | 评分 | 奇偶 | 大小 | 和值 | +|:---:|:---|:---:|:---:|:---:|:---:|:---:| +| **①** | **04 15 19 20 22** | **04 08** | **5.5657** ⭐ | 2:3 | 3:2 | 80 | +| ② | 12 13 17 22 30 | 04 05 | 5.2862 | 2:3 | 2:3 | 94 | +| ③ | 04 05 12 29 30 | 02 06 | 5.2664 | 2:3 | 2:3 | 80 | +| ④ | 12 19 20 28 33 | 05 06 | 5.1005 | 2:3 | 4:1 | 112 | +| ⑤ | 06 08 15 22 35 | 01 05 | 4.9794 | 2:3 | 2:3 | 86 | + +--- + +> 📌 **数据真实** · 过程严谨 · 呈现简明 · 界面美观 +> 开奖后把实际号码发我,帮你回滚迭代校正精度 🍀 +``` diff --git a/skills/lottery_picker/__init__.py b/skills/lottery_picker/__init__.py new file mode 100644 index 0000000..ce26f28 --- /dev/null +++ b/skills/lottery_picker/__init__.py @@ -0,0 +1,2 @@ +"""彩票智能分析技能包""" +__version__ = "1.0.0" diff --git a/skills/lottery_picker/data/config.json b/skills/lottery_picker/data/config.json new file mode 100644 index 0000000..7a30921 --- /dev/null +++ b/skills/lottery_picker/data/config.json @@ -0,0 +1,15 @@ +{ + "version": "1.0.0", + "seed": "LOTTERY_SKILL_V1.0.0", + "iteration_count": 17, + "ssq": { + "latest_issue": "26086", + "latest_fetch_start": "26087", + "latest_fetch_end": "26115" + }, + "dlt": { + "latest_issue": "26085", + "latest_fetch_start": "25136", + "latest_fetch_end": "26085" + } +} \ No newline at end of file diff --git a/skills/lottery_picker/data/dlt_history.json b/skills/lottery_picker/data/dlt_history.json new file mode 100644 index 0000000..503873b --- /dev/null +++ b/skills/lottery_picker/data/dlt_history.json @@ -0,0 +1,1787 @@ +[ + { + "issue": "25117", + "reds": [ + "05", + "10", + "18", + "21", + "29" + ], + "blue": [ + "05", + "07" + ], + "date": "2025-10-15" + }, + { + "issue": "25118", + "reds": [ + "02", + "08", + "09", + "12", + "21" + ], + "blue": [ + "04", + "05" + ], + "date": "2025-10-18" + }, + { + "issue": "25119", + "reds": [ + "08", + "15", + "27", + "29", + "31" + ], + "blue": [ + "01", + "07" + ], + "date": "2025-10-20" + }, + { + "issue": "25120", + "reds": [ + "11", + "13", + "22", + "26", + "35" + ], + "blue": [ + "02", + "08" + ], + "date": "2025-10-22" + }, + { + "issue": "25121", + "reds": [ + "02", + "03", + "08", + "13", + "21" + ], + "blue": [ + "07", + "12" + ], + "date": "2025-10-25" + }, + { + "issue": "25122", + "reds": [ + "02", + "03", + "06", + "16", + "17" + ], + "blue": [ + "04", + "05" + ], + "date": "2025-10-27" + }, + { + "issue": "25123", + "reds": [ + "08", + "13", + "24", + "25", + "31" + ], + "blue": [ + "04", + "10" + ], + "date": "2025-10-29" + }, + { + "issue": "25124", + "reds": [ + "06", + "09", + "14", + "26", + "27" + ], + "blue": [ + "08", + "09" + ], + "date": "2025-11-01" + }, + { + "issue": "25125", + "reds": [ + "10", + "11", + "13", + "19", + "35" + ], + "blue": [ + "04", + "11" + ], + "date": "2025-11-03" + }, + { + "issue": "25126", + "reds": [ + "01", + "08", + "18", + "27", + "30" + ], + "blue": [ + "06", + "07" + ], + "date": "2025-11-05" + }, + { + "issue": "25127", + "reds": [ + "04", + "05", + "19", + "28", + "29" + ], + "blue": [ + "05", + "08" + ], + "date": "2025-11-08" + }, + { + "issue": "25128", + "reds": [ + "03", + "06", + "26", + "30", + "33" + ], + "blue": [ + "11", + "12" + ], + "date": "2025-11-10" + }, + { + "issue": "25129", + "reds": [ + "03", + "09", + "14", + "28", + "35" + ], + "blue": [ + "02", + "04" + ], + "date": "2025-11-12" + }, + { + "issue": "25130", + "reds": [ + "01", + "13", + "16", + "27", + "29" + ], + "blue": [ + "02", + "11" + ], + "date": "2025-11-15" + }, + { + "issue": "25131", + "reds": [ + "03", + "08", + "25", + "29", + "32" + ], + "blue": [ + "09", + "12" + ], + "date": "2025-11-17" + }, + { + "issue": "25132", + "reds": [ + "01", + "09", + "10", + "12", + "19" + ], + "blue": [ + "06", + "07" + ], + "date": "2025-11-19" + }, + { + "issue": "25133", + "reds": [ + "04", + "11", + "23", + "27", + "35" + ], + "blue": [ + "07", + "11" + ], + "date": "2025-11-22" + }, + { + "issue": "25134", + "reds": [ + "07", + "12", + "18", + "27", + "33" + ], + "blue": [ + "09", + "10" + ], + "date": "2025-11-24" + }, + { + "issue": "25135", + "reds": [ + "02", + "10", + "16", + "28", + "32" + ], + "blue": [ + "01", + "07" + ], + "date": "2025-11-26" + }, + { + "issue": "25136", + "reds": [ + "07", + "11", + "15", + "16", + "23" + ], + "blue": [ + "09", + "11" + ], + "date": "2025-11-29" + }, + { + "issue": "25137", + "reds": [ + "07", + "08", + "09", + "11", + "22" + ], + "blue": [ + "05", + "11" + ], + "date": "2025-12-01" + }, + { + "issue": "25138", + "reds": [ + "01", + "03", + "19", + "21", + "23" + ], + "blue": [ + "07", + "11" + ], + "date": "2025-12-03" + }, + { + "issue": "25139", + "reds": [ + "08", + "18", + "22", + "30", + "35" + ], + "blue": [ + "01", + "04" + ], + "date": "2025-12-06" + }, + { + "issue": "25140", + "reds": [ + "04", + "05", + "13", + "18", + "34" + ], + "blue": [ + "02", + "08" + ], + "date": "2025-12-08" + }, + { + "issue": "25141", + "reds": [ + "04", + "09", + "24", + "28", + "29" + ], + "blue": [ + "02", + "10" + ], + "date": "2025-12-10" + }, + { + "issue": "25142", + "reds": [ + "09", + "10", + "14", + "27", + "29" + ], + "blue": [ + "02", + "09" + ], + "date": "2025-12-13" + }, + { + "issue": "25143", + "reds": [ + "03", + "04", + "18", + "24", + "29" + ], + "blue": [ + "07", + "12" + ], + "date": "2025-12-15" + }, + { + "issue": "25144", + "reds": [ + "02", + "05", + "13", + "15", + "28" + ], + "blue": [ + "05", + "08" + ], + "date": "2025-12-17" + }, + { + "issue": "25145", + "reds": [ + "05", + "07", + "20", + "22", + "25" + ], + "blue": [ + "04", + "05" + ], + "date": "2025-12-20" + }, + { + "issue": "25146", + "reds": [ + "06", + "11", + "13", + "16", + "22" + ], + "blue": [ + "02", + "03" + ], + "date": "2025-12-22" + }, + { + "issue": "25147", + "reds": [ + "06", + "16", + "21", + "25", + "33" + ], + "blue": [ + "07", + "08" + ], + "date": "2025-12-24" + }, + { + "issue": "25148", + "reds": [ + "03", + "04", + "14", + "30", + "32" + ], + "blue": [ + "08", + "12" + ], + "date": "2025-12-27" + }, + { + "issue": "25149", + "reds": [ + "24", + "26", + "30", + "31", + "32" + ], + "blue": [ + "05", + "12" + ], + "date": "2025-12-29" + }, + { + "issue": "25150", + "reds": [ + "13", + "14", + "15", + "28", + "31" + ], + "blue": [ + "01", + "05" + ], + "date": "2025-12-31" + }, + { + "issue": "26001", + "reds": [ + "07", + "09", + "23", + "27", + "32" + ], + "blue": [ + "02", + "08" + ], + "date": "2026-01-03" + }, + { + "issue": "26002", + "reds": [ + "04", + "08", + "15", + "20", + "31" + ], + "blue": [ + "07", + "08" + ], + "date": "2026-01-05" + }, + { + "issue": "26003", + "reds": [ + "02", + "09", + "11", + "15", + "16" + ], + "blue": [ + "02", + "04" + ], + "date": "2026-01-07" + }, + { + "issue": "26004", + "reds": [ + "05", + "18", + "23", + "25", + "32" + ], + "blue": [ + "05", + "09" + ], + "date": "2026-01-10" + }, + { + "issue": "26005", + "reds": [ + "02", + "04", + "16", + "23", + "35" + ], + "blue": [ + "06", + "11" + ], + "date": "2026-01-12" + }, + { + "issue": "26006", + "reds": [ + "05", + "12", + "18", + "23", + "35" + ], + "blue": [ + "06", + "12" + ], + "date": "2026-01-14" + }, + { + "issue": "26007", + "reds": [ + "01", + "03", + "13", + "20", + "26" + ], + "blue": [ + "03", + "10" + ], + "date": "2026-01-17" + }, + { + "issue": "26008", + "reds": [ + "03", + "06", + "17", + "21", + "33" + ], + "blue": [ + "05", + "11" + ], + "date": "2026-01-19" + }, + { + "issue": "26009", + "reds": [ + "05", + "12", + "13", + "14", + "33" + ], + "blue": [ + "05", + "08" + ], + "date": "2026-01-21" + }, + { + "issue": "26010", + "reds": [ + "02", + "03", + "13", + "18", + "26" + ], + "blue": [ + "02", + "09" + ], + "date": "2026-01-24" + }, + { + "issue": "26011", + "reds": [ + "14", + "21", + "23", + "29", + "33" + ], + "blue": [ + "02", + "10" + ], + "date": "2026-01-26" + }, + { + "issue": "26012", + "reds": [ + "01", + "02", + "09", + "22", + "25" + ], + "blue": [ + "01", + "06" + ], + "date": "2026-01-28" + }, + { + "issue": "26013", + "reds": [ + "03", + "05", + "06", + "23", + "26" + ], + "blue": [ + "01", + "04" + ], + "date": "2026-01-31" + }, + { + "issue": "26014", + "reds": [ + "16", + "18", + "23", + "34", + "35" + ], + "blue": [ + "01", + "06" + ], + "date": "2026-02-02" + }, + { + "issue": "26015", + "reds": [ + "01", + "04", + "10", + "13", + "17" + ], + "blue": [ + "03", + "11" + ], + "date": "2026-02-04" + }, + { + "issue": "26016", + "reds": [ + "08", + "09", + "12", + "19", + "24" + ], + "blue": [ + "01", + "06" + ], + "date": "2026-02-07" + }, + { + "issue": "26017", + "reds": [ + "04", + "05", + "10", + "23", + "31" + ], + "blue": [ + "07", + "12" + ], + "date": "2026-02-09" + }, + { + "issue": "26018", + "reds": [ + "09", + "11", + "19", + "30", + "35" + ], + "blue": [ + "01", + "12" + ], + "date": "2026-02-11" + }, + { + "issue": "26019", + "reds": [ + "12", + "13", + "14", + "16", + "31" + ], + "blue": [ + "04", + "12" + ], + "date": "2026-02-25" + }, + { + "issue": "26020", + "reds": [ + "01", + "10", + "21", + "23", + "29" + ], + "blue": [ + "10", + "12" + ], + "date": "2026-02-28" + }, + { + "issue": "26021", + "reds": [ + "05", + "08", + "12", + "14", + "17" + ], + "blue": [ + "04", + "05" + ], + "date": "2026-03-02" + }, + { + "issue": "26022", + "reds": [ + "05", + "09", + "10", + "18", + "26" + ], + "blue": [ + "05", + "06" + ], + "date": "2026-03-04" + }, + { + "issue": "26023", + "reds": [ + "09", + "25", + "26", + "27", + "28" + ], + "blue": [ + "01", + "08" + ], + "date": "2026-03-07" + }, + { + "issue": "26024", + "reds": [ + "02", + "04", + "08", + "10", + "21" + ], + "blue": [ + "09", + "12" + ], + "date": "2026-03-09" + }, + { + "issue": "26025", + "reds": [ + "03", + "15", + "24", + "28", + "29" + ], + "blue": [ + "03", + "07" + ], + "date": "2026-03-11" + }, + { + "issue": "26026", + "reds": [ + "10", + "11", + "22", + "26", + "32" + ], + "blue": [ + "01", + "08" + ], + "date": "2026-03-14" + }, + { + "issue": "26027", + "reds": [ + "09", + "10", + "11", + "12", + "16" + ], + "blue": [ + "01", + "11" + ], + "date": "2026-03-16" + }, + { + "issue": "26028", + "reds": [ + "15", + "27", + "29", + "30", + "34" + ], + "blue": [ + "01", + "10" + ], + "date": "2026-03-18" + }, + { + "issue": "26029", + "reds": [ + "03", + "05", + "17", + "33", + "35" + ], + "blue": [ + "05", + "07" + ], + "date": "2026-03-21" + }, + { + "issue": "26030", + "reds": [ + "02", + "13", + "22", + "28", + "34" + ], + "blue": [ + "05", + "12" + ], + "date": "2026-03-23" + }, + { + "issue": "26031", + "reds": [ + "06", + "08", + "22", + "29", + "34" + ], + "blue": [ + "05", + "07" + ], + "date": "2026-03-25" + }, + { + "issue": "26032", + "reds": [ + "03", + "04", + "19", + "26", + "32" + ], + "blue": [ + "01", + "12" + ], + "date": "2026-03-28" + }, + { + "issue": "26033", + "reds": [ + "03", + "05", + "07", + "09", + "18" + ], + "blue": [ + "02", + "10" + ], + "date": "2026-03-30" + }, + { + "issue": "26034", + "reds": [ + "11", + "12", + "25", + "26", + "27" + ], + "blue": [ + "08", + "11" + ], + "date": "2026-04-01" + }, + { + "issue": "26035", + "reds": [ + "02", + "22", + "30", + "33", + "34" + ], + "blue": [ + "08", + "12" + ], + "date": "2026-04-04" + }, + { + "issue": "26036", + "reds": [ + "04", + "07", + "16", + "26", + "32" + ], + "blue": [ + "05", + "08" + ], + "date": "2026-04-06" + }, + { + "issue": "26037", + "reds": [ + "07", + "12", + "13", + "28", + "32" + ], + "blue": [ + "06", + "08" + ], + "date": "2026-04-08" + }, + { + "issue": "26038", + "reds": [ + "08", + "17", + "21", + "33", + "35" + ], + "blue": [ + "06", + "07" + ], + "date": "2026-04-11" + }, + { + "issue": "26039", + "reds": [ + "09", + "11", + "20", + "26", + "27" + ], + "blue": [ + "06", + "09" + ], + "date": "2026-04-13" + }, + { + "issue": "26040", + "reds": [ + "06", + "12", + "13", + "21", + "34" + ], + "blue": [ + "08", + "09" + ], + "date": "2026-04-15" + }, + { + "issue": "26041", + "reds": [ + "24", + "25", + "27", + "29", + "34" + ], + "blue": [ + "02", + "06" + ], + "date": "2026-04-18" + }, + { + "issue": "26042", + "reds": [ + "02", + "07", + "13", + "19", + "24" + ], + "blue": [ + "03", + "08" + ], + "date": "2026-04-20" + }, + { + "issue": "26043", + "reds": [ + "08", + "12", + "14", + "19", + "22" + ], + "blue": [ + "11", + "12" + ], + "date": "2026-04-22" + }, + { + "issue": "26044", + "reds": [ + "03", + "08", + "22", + "26", + "29" + ], + "blue": [ + "07", + "10" + ], + "date": "2026-04-25" + }, + { + "issue": "26045", + "reds": [ + "01", + "15", + "21", + "26", + "33" + ], + "blue": [ + "04", + "07" + ], + "date": "2026-04-27" + }, + { + "issue": "26046", + "reds": [ + "01", + "13", + "18", + "27", + "33" + ], + "blue": [ + "04", + "07" + ], + "date": "2026-04-29" + }, + { + "issue": "26047", + "reds": [ + "09", + "20", + "21", + "23", + "28" + ], + "blue": [ + "06", + "11" + ], + "date": "2026-05-02" + }, + { + "issue": "26048", + "reds": [ + "11", + "17", + "20", + "23", + "35" + ], + "blue": [ + "01", + "10" + ], + "date": "2026-05-04" + }, + { + "issue": "26049", + "reds": [ + "01", + "06", + "14", + "15", + "17" + ], + "blue": [ + "02", + "03" + ], + "date": "2026-05-06" + }, + { + "issue": "26050", + "reds": [ + "06", + "10", + "14", + "23", + "33" + ], + "blue": [ + "08", + "10" + ], + "date": "2026-05-09" + }, + { + "issue": "26051", + "reds": [ + "13", + "18", + "28", + "32", + "33" + ], + "blue": [ + "02", + "11" + ], + "date": "2026-05-11" + }, + { + "issue": "26052", + "reds": [ + "02", + "03", + "20", + "28", + "33" + ], + "blue": [ + "02", + "12" + ], + "date": "2026-05-13" + }, + { + "issue": "26053", + "reds": [ + "02", + "09", + "14", + "20", + "31" + ], + "blue": [ + "05", + "09" + ], + "date": "2026-05-16" + }, + { + "issue": "26054", + "reds": [ + "02", + "06", + "14", + "22", + "24" + ], + "blue": [ + "08", + "11" + ], + "date": "2026-05-18" + }, + { + "issue": "26055", + "reds": [ + "09", + "10", + "20", + "33", + "35" + ], + "blue": [ + "04", + "11" + ], + "date": "2026-05-20" + }, + { + "issue": "26056", + "reds": [ + "06", + "07", + "18", + "21", + "30" + ], + "blue": [ + "01", + "05" + ], + "date": "2026-05-23" + }, + { + "issue": "26057", + "reds": [ + "23", + "25", + "26", + "27", + "34" + ], + "blue": [ + "04", + "10" + ], + "date": "2026-05-25" + }, + { + "issue": "26058", + "reds": [ + "07", + "12", + "13", + "18", + "34" + ], + "blue": [ + "01", + "05" + ], + "date": "2026-05-27" + }, + { + "issue": "26059", + "reds": [ + "06", + "13", + "17", + "19", + "26" + ], + "blue": [ + "07", + "08" + ], + "date": "2026-05-30" + }, + { + "issue": "26060", + "reds": [ + "22", + "28", + "30", + "31", + "34" + ], + "blue": [ + "01", + "05" + ], + "date": "2026-06-01" + }, + { + "issue": "26061", + "reds": [ + "10", + "12", + "26", + "31", + "35" + ], + "blue": [ + "02", + "12" + ], + "date": "2026-06-03" + }, + { + "issue": "26062", + "reds": [ + "07", + "15", + "20", + "24", + "29" + ], + "blue": [ + "04", + "10" + ], + "date": "2026-06-06" + }, + { + "issue": "26063", + "reds": [ + "03", + "15", + "20", + "29", + "31" + ], + "blue": [ + "01", + "12" + ], + "date": "2026-06-08" + }, + { + "issue": "26064", + "reds": [ + "03", + "13", + "15", + "17", + "21" + ], + "blue": [ + "02", + "07" + ], + "date": "2026-06-10" + }, + { + "issue": "26065", + "reds": [ + "04", + "11", + "12", + "13", + "25" + ], + "blue": [ + "04", + "08" + ], + "date": "2026-06-13" + }, + { + "issue": "26066", + "reds": [ + "10", + "13", + "19", + "21", + "30" + ], + "blue": [ + "04", + "05" + ], + "date": "2026-06-15" + }, + { + "issue": "26067", + "reds": [ + "06", + "16", + "18", + "19", + "28" + ], + "blue": [ + "07", + "11" + ], + "date": "2026-06-17" + }, + { + "issue": "26068", + "reds": [ + "03", + "11", + "12", + "21", + "22" + ], + "blue": [ + "06", + "10" + ], + "date": "2026-06-20" + }, + { + "issue": "26069", + "reds": [ + "12", + "19", + "21", + "24", + "29" + ], + "blue": [ + "03", + "10" + ], + "date": "2026-06-22" + }, + { + "issue": "26070", + "reds": [ + "04", + "05", + "15", + "21", + "32" + ], + "blue": [ + "02", + "11" + ], + "date": "2026-06-24" + }, + { + "issue": "26071", + "reds": [ + "05", + "13", + "22", + "26", + "32" + ], + "blue": [ + "02", + "03" + ], + "date": "2026-06-27" + }, + { + "issue": "26072", + "reds": [ + "01", + "13", + "26", + "29", + "30" + ], + "blue": [ + "09", + "11" + ], + "date": "2026-06-29" + }, + { + "issue": "26073", + "reds": [ + "04", + "10", + "22", + "23", + "33" + ], + "blue": [ + "02", + "12" + ], + "date": "2026-07-01" + }, + { + "issue": "26074", + "reds": [ + "01", + "04", + "10", + "23", + "25" + ], + "blue": [ + "01", + "12" + ], + "date": "2026-07-04" + }, + { + "issue": "26075", + "reds": [ + "01", + "06", + "16", + "18", + "26" + ], + "blue": [ + "04", + "10" + ], + "date": "2026-07-06" + }, + { + "issue": "26076", + "reds": [ + "15", + "20", + "27", + "28", + "35" + ], + "blue": [ + "02", + "11" + ], + "date": "2026-07-08" + }, + { + "issue": "26077", + "reds": [ + "04", + "14", + "19", + "24", + "27" + ], + "blue": [ + "06", + "07" + ], + "date": "2026-07-11" + }, + { + "issue": "26078", + "reds": [ + "02", + "13", + "20", + "25", + "32" + ], + "blue": [ + "08", + "11" + ], + "date": "2026-07-13" + }, + { + "issue": "26079", + "reds": [ + "06", + "08", + "23", + "26", + "27" + ], + "blue": [ + "05", + "12" + ], + "date": "2026-07-15" + }, + { + "issue": "26080", + "reds": [ + "05", + "10", + "15", + "21", + "23" + ], + "blue": [ + "07", + "08" + ], + "date": "2026-07-18" + }, + { + "issue": "26081", + "reds": [ + "08", + "16", + "18", + "24", + "34" + ], + "blue": [ + "09", + "12" + ], + "date": "2026-07-20" + }, + { + "issue": "26082", + "reds": [ + "16", + "26", + "27", + "28", + "34" + ], + "blue": [ + "02", + "06" + ], + "date": "2026-07-22" + }, + { + "issue": "26083", + "reds": [ + "14", + "15", + "16", + "23", + "26" + ], + "blue": [ + "07", + "09" + ], + "date": "2026-07-25" + }, + { + "issue": "26084", + "reds": [ + "13", + "25", + "30", + "32", + "33" + ], + "blue": [ + "04", + "05" + ], + "date": "2026-07-27" + }, + { + "issue": "26085", + "reds": [ + "03", + "04", + "14", + "28", + "31" + ], + "blue": [ + "05", + "07" + ], + "date": "2026-07-29" + } +] \ No newline at end of file diff --git a/skills/lottery_picker/data/feedback.json b/skills/lottery_picker/data/feedback.json new file mode 100644 index 0000000..bc52397 --- /dev/null +++ b/skills/lottery_picker/data/feedback.json @@ -0,0 +1,189 @@ +{ + "iterations": [ + { + "lt": "ssq", + "issue": "26063", + "date": "2026-06-04", + "pred_reds": [ + 11, + 17, + 21, + 24, + 28, + 30 + ], + "pred_blues": [ + 2 + ], + "act_reds": [ + 2, + 8, + 25, + 28, + 30, + 31 + ], + "act_blues": [ + 2 + ], + "hit_reds": [ + 28, + 30 + ], + "hit_blues": [ + 2 + ], + "red_hits": 2, + "blue_hits": 1, + "total_hits": 3, + "earnings": 50.0 + }, + { + "lt": "dlt", + "issue": "26064", + "date": "2026-06-10", + "pred_reds": [ + 1, + 9, + 14, + 33, + 34 + ], + "pred_blues": [ + 8, + 10 + ], + "act_reds": [ + 3, + 13, + 15, + 17, + 21 + ], + "act_blues": [ + 2, + 7 + ], + "hit_reds": [], + "hit_blues": [], + "red_hits": 0, + "blue_hits": 0, + "total_hits": 0, + "earnings": 0.0 + }, + { + "lt": "dlt", + "issue": "26065", + "date": "2026-06-13", + "pred_reds": [ + 1, + 9, + 14, + 33, + 34 + ], + "pred_blues": [ + 8, + 10 + ], + "act_reds": [ + 4, + 11, + 12, + 13, + 25 + ], + "act_blues": [ + 4, + 8 + ], + "hit_reds": [], + "hit_blues": [ + 8 + ], + "red_hits": 0, + "blue_hits": 1, + "total_hits": 1, + "earnings": 0.0 + }, + { + "lt": "dlt", + "issue": "26078", + "date": "2026-07-13", + "pred_reds": [ + 6, + 13, + 19, + 22, + 29 + ], + "pred_blues": [ + 6, + 8 + ], + "act_reds": [ + 2, + 13, + 20, + 25, + 32 + ], + "act_blues": [ + 8, + 11 + ], + "hit_reds": [ + 13 + ], + "hit_blues": [ + 8 + ], + "red_hits": 1, + "blue_hits": 1, + "total_hits": 2, + "earnings": 0.0 + } + ], + "accuracy_history": [ + { + "issue": "26063", + "lt": "ssq", + "hits": 3, + "avg_accuracy": 42.86 + }, + { + "issue": "26064", + "lt": "dlt", + "hits": 0, + "avg_accuracy": 0.0 + }, + { + "issue": "26065", + "lt": "dlt", + "hits": 1, + "avg_accuracy": 7.14 + }, + { + "issue": "26078", + "lt": "dlt", + "hits": 2, + "avg_accuracy": 14.29 + } + ], + "ssq": { + "total": 1, + "correct_reds": 2, + "correct_blues": 1, + "best": 3, + "profitable_wins": 1, + "total_earnings": 50.0 + }, + "dlt": { + "total": 3, + "correct_reds": 1, + "correct_blues": 2, + "best": 2, + "profitable_wins": 0, + "total_earnings": 0.0 + } +} \ No newline at end of file diff --git a/skills/lottery_picker/data/ssq_history.json b/skills/lottery_picker/data/ssq_history.json new file mode 100644 index 0000000..e5e5e52 --- /dev/null +++ b/skills/lottery_picker/data/ssq_history.json @@ -0,0 +1,2849 @@ +[ + { + "issue": "25019", + "reds": [ + "05", + "13", + "17", + "18", + "22", + "23" + ], + "blue": "11", + "date": "2025-02-23" + }, + { + "issue": "25020", + "reds": [ + "04", + "11", + "13", + "16", + "26", + "30" + ], + "blue": "15", + "date": "2025-02-25" + }, + { + "issue": "25021", + "reds": [ + "07", + "08", + "16", + "18", + "25", + "32" + ], + "blue": "14", + "date": "2025-02-27" + }, + { + "issue": "25022", + "reds": [ + "11", + "17", + "18", + "20", + "28", + "30" + ], + "blue": "10", + "date": "2025-03-02" + }, + { + "issue": "25023", + "reds": [ + "09", + "12", + "13", + "24", + "25", + "33" + ], + "blue": "07", + "date": "2025-03-04" + }, + { + "issue": "25024", + "reds": [ + "10", + "11", + "22", + "27", + "30", + "32" + ], + "blue": "15", + "date": "2025-03-06" + }, + { + "issue": "25025", + "reds": [ + "12", + "15", + "21", + "23", + "25", + "30" + ], + "blue": "02", + "date": "2025-03-09" + }, + { + "issue": "25026", + "reds": [ + "09", + "10", + "12", + "14", + "19", + "32" + ], + "blue": "07", + "date": "2025-03-11" + }, + { + "issue": "25027", + "reds": [ + "05", + "07", + "08", + "15", + "16", + "23" + ], + "blue": "06", + "date": "2025-03-13" + }, + { + "issue": "25028", + "reds": [ + "04", + "09", + "14", + "15", + "18", + "25" + ], + "blue": "15", + "date": "2025-03-16" + }, + { + "issue": "25029", + "reds": [ + "05", + "15", + "16", + "25", + "30", + "33" + ], + "blue": "16", + "date": "2025-03-18" + }, + { + "issue": "25030", + "reds": [ + "04", + "06", + "07", + "30", + "31", + "33" + ], + "blue": "06", + "date": "2025-03-20" + }, + { + "issue": "25031", + "reds": [ + "01", + "05", + "06", + "08", + "23", + "28" + ], + "blue": "01", + "date": "2025-03-23" + }, + { + "issue": "25032", + "reds": [ + "03", + "08", + "10", + "14", + "16", + "21" + ], + "blue": "03", + "date": "2025-03-25" + }, + { + "issue": "25033", + "reds": [ + "03", + "05", + "18", + "25", + "26", + "33" + ], + "blue": "08", + "date": "2025-03-27" + }, + { + "issue": "25034", + "reds": [ + "05", + "08", + "13", + "14", + "24", + "26" + ], + "blue": "12", + "date": "2025-03-30" + }, + { + "issue": "25035", + "reds": [ + "01", + "08", + "16", + "18", + "25", + "31" + ], + "blue": "01", + "date": "2025-04-01" + }, + { + "issue": "25036", + "reds": [ + "05", + "11", + "13", + "16", + "19", + "32" + ], + "blue": "07", + "date": "2025-04-03" + }, + { + "issue": "25037", + "reds": [ + "03", + "06", + "11", + "20", + "21", + "31" + ], + "blue": "02", + "date": "2025-04-06" + }, + { + "issue": "25038", + "reds": [ + "06", + "19", + "20", + "23", + "26", + "33" + ], + "blue": "09", + "date": "2025-04-08" + }, + { + "issue": "25039", + "reds": [ + "08", + "10", + "12", + "15", + "17", + "23" + ], + "blue": "11", + "date": "2025-04-10" + }, + { + "issue": "25040", + "reds": [ + "02", + "06", + "08", + "09", + "10", + "24" + ], + "blue": "11", + "date": "2025-04-13" + }, + { + "issue": "25041", + "reds": [ + "06", + "10", + "17", + "19", + "25", + "31" + ], + "blue": "06", + "date": "2025-04-15" + }, + { + "issue": "25042", + "reds": [ + "07", + "13", + "19", + "23", + "27", + "33" + ], + "blue": "12", + "date": "2025-04-17" + }, + { + "issue": "25043", + "reds": [ + "03", + "12", + "16", + "19", + "20", + "32" + ], + "blue": "13", + "date": "2025-04-20" + }, + { + "issue": "25044", + "reds": [ + "02", + "03", + "07", + "08", + "14", + "28" + ], + "blue": "11", + "date": "2025-04-22" + }, + { + "issue": "25045", + "reds": [ + "02", + "14", + "19", + "24", + "29", + "33" + ], + "blue": "13", + "date": "2025-04-24" + }, + { + "issue": "25046", + "reds": [ + "02", + "03", + "10", + "16", + "29", + "33" + ], + "blue": "01", + "date": "2025-04-27" + }, + { + "issue": "25047", + "reds": [ + "01", + "14", + "21", + "22", + "23", + "31" + ], + "blue": "12", + "date": "2025-04-29" + }, + { + "issue": "25048", + "reds": [ + "16", + "21", + "22", + "23", + "27", + "31" + ], + "blue": "10", + "date": "2025-05-01" + }, + { + "issue": "25049", + "reds": [ + "03", + "06", + "19", + "27", + "29", + "33" + ], + "blue": "11", + "date": "2025-05-04" + }, + { + "issue": "25050", + "reds": [ + "09", + "12", + "15", + "18", + "22", + "33" + ], + "blue": "16", + "date": "2025-05-06" + }, + { + "issue": "25051", + "reds": [ + "01", + "02", + "03", + "04", + "17", + "22" + ], + "blue": "01", + "date": "2025-05-08" + }, + { + "issue": "25052", + "reds": [ + "06", + "07", + "10", + "17", + "20", + "26" + ], + "blue": "09", + "date": "2025-05-11" + }, + { + "issue": "25053", + "reds": [ + "06", + "09", + "10", + "13", + "30", + "33" + ], + "blue": "07", + "date": "2025-05-13" + }, + { + "issue": "25054", + "reds": [ + "05", + "07", + "10", + "21", + "24", + "27" + ], + "blue": "16", + "date": "2025-05-15" + }, + { + "issue": "25055", + "reds": [ + "02", + "05", + "22", + "27", + "29", + "33" + ], + "blue": "12", + "date": "2025-05-18" + }, + { + "issue": "25056", + "reds": [ + "01", + "02", + "10", + "14", + "28", + "31" + ], + "blue": "03", + "date": "2025-05-20" + }, + { + "issue": "25057", + "reds": [ + "04", + "09", + "15", + "16", + "25", + "30" + ], + "blue": "14", + "date": "2025-05-22" + }, + { + "issue": "25058", + "reds": [ + "02", + "06", + "07", + "09", + "10", + "20" + ], + "blue": "06", + "date": "2025-05-25" + }, + { + "issue": "25059", + "reds": [ + "04", + "10", + "11", + "12", + "13", + "24" + ], + "blue": "01", + "date": "2025-05-27" + }, + { + "issue": "25060", + "reds": [ + "06", + "14", + "18", + "25", + "28", + "30" + ], + "blue": "01", + "date": "2025-05-29" + }, + { + "issue": "25061", + "reds": [ + "06", + "07", + "09", + "10", + "11", + "32" + ], + "blue": "09", + "date": "2025-06-01" + }, + { + "issue": "25062", + "reds": [ + "06", + "08", + "09", + "13", + "25", + "31" + ], + "blue": "14", + "date": "2025-06-03" + }, + { + "issue": "25063", + "reds": [ + "02", + "19", + "21", + "22", + "28", + "30" + ], + "blue": "01", + "date": "2025-06-05" + }, + { + "issue": "25064", + "reds": [ + "02", + "10", + "13", + "22", + "29", + "33" + ], + "blue": "16", + "date": "2025-06-08" + }, + { + "issue": "25065", + "reds": [ + "06", + "10", + "13", + "14", + "15", + "20" + ], + "blue": "11", + "date": "2025-06-10" + }, + { + "issue": "25066", + "reds": [ + "06", + "22", + "24", + "27", + "28", + "30" + ], + "blue": "04", + "date": "2025-06-12" + }, + { + "issue": "25067", + "reds": [ + "01", + "05", + "10", + "17", + "20", + "22" + ], + "blue": "05", + "date": "2025-06-15" + }, + { + "issue": "25068", + "reds": [ + "05", + "07", + "08", + "19", + "20", + "31" + ], + "blue": "07", + "date": "2025-06-17" + }, + { + "issue": "25069", + "reds": [ + "02", + "04", + "19", + "23", + "27", + "30" + ], + "blue": "05", + "date": "2025-06-19" + }, + { + "issue": "25070", + "reds": [ + "02", + "03", + "15", + "21", + "22", + "33" + ], + "blue": "06", + "date": "2025-06-22" + }, + { + "issue": "25071", + "reds": [ + "01", + "12", + "18", + "23", + "25", + "28" + ], + "blue": "07", + "date": "2025-06-24" + }, + { + "issue": "25072", + "reds": [ + "02", + "14", + "17", + "25", + "27", + "29" + ], + "blue": "05", + "date": "2025-06-26" + }, + { + "issue": "25073", + "reds": [ + "02", + "07", + "10", + "27", + "30", + "33" + ], + "blue": "11", + "date": "2025-06-29" + }, + { + "issue": "25074", + "reds": [ + "03", + "04", + "12", + "20", + "32", + "33" + ], + "blue": "13", + "date": "2025-07-01" + }, + { + "issue": "25075", + "reds": [ + "10", + "12", + "14", + "19", + "20", + "21" + ], + "blue": "15", + "date": "2025-07-03" + }, + { + "issue": "25076", + "reds": [ + "02", + "06", + "09", + "12", + "14", + "30" + ], + "blue": "08", + "date": "2025-07-06" + }, + { + "issue": "25077", + "reds": [ + "04", + "07", + "08", + "15", + "20", + "21" + ], + "blue": "15", + "date": "2025-07-08" + }, + { + "issue": "25078", + "reds": [ + "06", + "15", + "17", + "30", + "32", + "33" + ], + "blue": "14", + "date": "2025-07-10" + }, + { + "issue": "25079", + "reds": [ + "08", + "11", + "14", + "15", + "30", + "32" + ], + "blue": "05", + "date": "2025-07-13" + }, + { + "issue": "25080", + "reds": [ + "03", + "07", + "08", + "10", + "16", + "19" + ], + "blue": "05", + "date": "2025-07-15" + }, + { + "issue": "25081", + "reds": [ + "03", + "06", + "18", + "20", + "23", + "28" + ], + "blue": "05", + "date": "2025-07-17" + }, + { + "issue": "25082", + "reds": [ + "04", + "06", + "09", + "14", + "32", + "33" + ], + "blue": "11", + "date": "2025-07-20" + }, + { + "issue": "25083", + "reds": [ + "10", + "15", + "16", + "17", + "27", + "29" + ], + "blue": "12", + "date": "2025-07-22" + }, + { + "issue": "25084", + "reds": [ + "06", + "08", + "10", + "14", + "24", + "33" + ], + "blue": "15", + "date": "2025-07-24" + }, + { + "issue": "25085", + "reds": [ + "11", + "14", + "15", + "18", + "21", + "24" + ], + "blue": "13", + "date": "2025-07-27" + }, + { + "issue": "25086", + "reds": [ + "02", + "06", + "08", + "10", + "17", + "24" + ], + "blue": "06", + "date": "2025-07-29" + }, + { + "issue": "25087", + "reds": [ + "02", + "06", + "14", + "15", + "24", + "26" + ], + "blue": "08", + "date": "2025-07-31" + }, + { + "issue": "25088", + "reds": [ + "01", + "03", + "13", + "18", + "21", + "25" + ], + "blue": "06", + "date": "2025-08-03" + }, + { + "issue": "25089", + "reds": [ + "04", + "06", + "08", + "18", + "31", + "33" + ], + "blue": "05", + "date": "2025-08-05" + }, + { + "issue": "25090", + "reds": [ + "06", + "11", + "12", + "21", + "27", + "28" + ], + "blue": "15", + "date": "2025-08-07" + }, + { + "issue": "25091", + "reds": [ + "03", + "04", + "17", + "19", + "25", + "27" + ], + "blue": "14", + "date": "2025-08-10" + }, + { + "issue": "25092", + "reds": [ + "02", + "11", + "14", + "17", + "23", + "24" + ], + "blue": "12", + "date": "2025-08-12" + }, + { + "issue": "25093", + "reds": [ + "09", + "11", + "12", + "24", + "25", + "26" + ], + "blue": "10", + "date": "2025-08-14" + }, + { + "issue": "25094", + "reds": [ + "11", + "13", + "17", + "19", + "23", + "29" + ], + "blue": "16", + "date": "2025-08-17" + }, + { + "issue": "25095", + "reds": [ + "15", + "16", + "22", + "23", + "26", + "32" + ], + "blue": "04", + "date": "2025-08-19" + }, + { + "issue": "25096", + "reds": [ + "07", + "09", + "11", + "12", + "16", + "29" + ], + "blue": "15", + "date": "2025-08-21" + }, + { + "issue": "25097", + "reds": [ + "03", + "05", + "16", + "23", + "26", + "31" + ], + "blue": "14", + "date": "2025-08-24" + }, + { + "issue": "25098", + "reds": [ + "05", + "08", + "13", + "17", + "18", + "29" + ], + "blue": "02", + "date": "2025-08-26" + }, + { + "issue": "25099", + "reds": [ + "09", + "11", + "15", + "17", + "22", + "26" + ], + "blue": "14", + "date": "2025-08-28" + }, + { + "issue": "25100", + "reds": [ + "12", + "16", + "17", + "25", + "30", + "31" + ], + "blue": "16", + "date": "2025-08-31" + }, + { + "issue": "25101", + "reds": [ + "05", + "08", + "09", + "10", + "16", + "21" + ], + "blue": "05", + "date": "2025-09-02" + }, + { + "issue": "25102", + "reds": [ + "04", + "09", + "16", + "17", + "18", + "31" + ], + "blue": "07", + "date": "2025-09-04" + }, + { + "issue": "25103", + "reds": [ + "13", + "16", + "21", + "25", + "28", + "31" + ], + "blue": "16", + "date": "2025-09-07" + }, + { + "issue": "25104", + "reds": [ + "02", + "05", + "15", + "16", + "24", + "32" + ], + "blue": "16", + "date": "2025-09-09" + }, + { + "issue": "25105", + "reds": [ + "04", + "07", + "18", + "24", + "26", + "28" + ], + "blue": "08", + "date": "2025-09-11" + }, + { + "issue": "25106", + "reds": [ + "04", + "05", + "17", + "22", + "26", + "30" + ], + "blue": "04", + "date": "2025-09-14" + }, + { + "issue": "25107", + "reds": [ + "02", + "03", + "10", + "15", + "25", + "33" + ], + "blue": "13", + "date": "2025-09-16" + }, + { + "issue": "25108", + "reds": [ + "01", + "09", + "14", + "17", + "22", + "33" + ], + "blue": "07", + "date": "2025-09-18" + }, + { + "issue": "25109", + "reds": [ + "05", + "06", + "09", + "17", + "18", + "31" + ], + "blue": "03", + "date": "2025-09-21" + }, + { + "issue": "25110", + "reds": [ + "01", + "05", + "11", + "14", + "16", + "19" + ], + "blue": "08", + "date": "2025-09-23" + }, + { + "issue": "25111", + "reds": [ + "09", + "14", + "18", + "28", + "31", + "33" + ], + "blue": "12", + "date": "2025-09-25" + }, + { + "issue": "25112", + "reds": [ + "03", + "09", + "11", + "13", + "20", + "32" + ], + "blue": "02", + "date": "2025-09-28" + }, + { + "issue": "25113", + "reds": [ + "08", + "10", + "13", + "15", + "24", + "31" + ], + "blue": "16", + "date": "2025-09-30" + }, + { + "issue": "25114", + "reds": [ + "01", + "20", + "21", + "25", + "26", + "27" + ], + "blue": "10", + "date": "2025-10-05" + }, + { + "issue": "25115", + "reds": [ + "02", + "03", + "08", + "19", + "24", + "30" + ], + "blue": "02", + "date": "2025-10-07" + }, + { + "issue": "25116", + "reds": [ + "02", + "04", + "08", + "24", + "28", + "31" + ], + "blue": "09", + "date": "2025-10-09" + }, + { + "issue": "25117", + "reds": [ + "06", + "08", + "17", + "20", + "25", + "33" + ], + "blue": "10", + "date": "2025-10-12" + }, + { + "issue": "25118", + "reds": [ + "01", + "10", + "11", + "16", + "24", + "26" + ], + "blue": "03", + "date": "2025-10-14" + }, + { + "issue": "25119", + "reds": [ + "06", + "09", + "23", + "26", + "28", + "32" + ], + "blue": "11", + "date": "2025-10-16" + }, + { + "issue": "25120", + "reds": [ + "01", + "02", + "04", + "07", + "13", + "32" + ], + "blue": "07", + "date": "2025-10-19" + }, + { + "issue": "25121", + "reds": [ + "06", + "08", + "10", + "25", + "29", + "30" + ], + "blue": "08", + "date": "2025-10-21" + }, + { + "issue": "25122", + "reds": [ + "16", + "18", + "19", + "20", + "25", + "31" + ], + "blue": "13", + "date": "2025-10-23" + }, + { + "issue": "25123", + "reds": [ + "07", + "09", + "23", + "24", + "25", + "26" + ], + "blue": "10", + "date": "2025-10-26" + }, + { + "issue": "25124", + "reds": [ + "01", + "02", + "18", + "19", + "21", + "33" + ], + "blue": "13", + "date": "2025-10-28" + }, + { + "issue": "25125", + "reds": [ + "03", + "09", + "12", + "13", + "26", + "32" + ], + "blue": "09", + "date": "2025-10-30" + }, + { + "issue": "25126", + "reds": [ + "02", + "12", + "13", + "16", + "19", + "25" + ], + "blue": "10", + "date": "2025-11-02" + }, + { + "issue": "25127", + "reds": [ + "03", + "09", + "15", + "17", + "19", + "28" + ], + "blue": "03", + "date": "2025-11-04" + }, + { + "issue": "25128", + "reds": [ + "02", + "10", + "18", + "19", + "24", + "27" + ], + "blue": "01", + "date": "2025-11-06" + }, + { + "issue": "25129", + "reds": [ + "03", + "04", + "07", + "13", + "20", + "30" + ], + "blue": "03", + "date": "2025-11-09" + }, + { + "issue": "25130", + "reds": [ + "01", + "05", + "08", + "14", + "19", + "23" + ], + "blue": "06", + "date": "2025-11-11" + }, + { + "issue": "25131", + "reds": [ + "03", + "13", + "14", + "18", + "24", + "31" + ], + "blue": "03", + "date": "2025-11-13" + }, + { + "issue": "25132", + "reds": [ + "04", + "08", + "10", + "21", + "23", + "32" + ], + "blue": "11", + "date": "2025-11-16" + }, + { + "issue": "25133", + "reds": [ + "05", + "14", + "17", + "19", + "20", + "33" + ], + "blue": "07", + "date": "2025-11-18" + }, + { + "issue": "25134", + "reds": [ + "03", + "05", + "09", + "13", + "26", + "29" + ], + "blue": "12", + "date": "2025-11-20" + }, + { + "issue": "25135", + "reds": [ + "01", + "02", + "05", + "09", + "25", + "32" + ], + "blue": "10", + "date": "2025-11-23" + }, + { + "issue": "25136", + "reds": [ + "08", + "10", + "14", + "23", + "28", + "32" + ], + "blue": "12", + "date": "2025-11-25" + }, + { + "issue": "25137", + "reds": [ + "02", + "08", + "11", + "23", + "27", + "29" + ], + "blue": "05", + "date": "2025-11-27" + }, + { + "issue": "25138", + "reds": [ + "10", + "13", + "14", + "23", + "24", + "27" + ], + "blue": "15", + "date": "2025-11-30" + }, + { + "issue": "25139", + "reds": [ + "02", + "05", + "17", + "22", + "30", + "33" + ], + "blue": "06", + "date": "2025-12-02" + }, + { + "issue": "25140", + "reds": [ + "01", + "03", + "04", + "12", + "18", + "24" + ], + "blue": "05", + "date": "2025-12-04" + }, + { + "issue": "25141", + "reds": [ + "02", + "04", + "05", + "10", + "12", + "13" + ], + "blue": "06", + "date": "2025-12-07" + }, + { + "issue": "25142", + "reds": [ + "02", + "13", + "15", + "23", + "27", + "31" + ], + "blue": "16", + "date": "2025-12-09" + }, + { + "issue": "25143", + "reds": [ + "02", + "09", + "12", + "13", + "15", + "24" + ], + "blue": "03", + "date": "2025-12-11" + }, + { + "issue": "25144", + "reds": [ + "01", + "08", + "15", + "20", + "26", + "33" + ], + "blue": "13", + "date": "2025-12-14" + }, + { + "issue": "25145", + "reds": [ + "11", + "12", + "15", + "18", + "25", + "32" + ], + "blue": "14", + "date": "2025-12-16" + }, + { + "issue": "25146", + "reds": [ + "05", + "07", + "12", + "24", + "26", + "28" + ], + "blue": "02", + "date": "2025-12-18" + }, + { + "issue": "25147", + "reds": [ + "01", + "03", + "05", + "08", + "22", + "33" + ], + "blue": "08", + "date": "2025-12-21" + }, + { + "issue": "25148", + "reds": [ + "03", + "04", + "09", + "10", + "15", + "22" + ], + "blue": "16", + "date": "2025-12-23" + }, + { + "issue": "25149", + "reds": [ + "01", + "02", + "04", + "06", + "22", + "30" + ], + "blue": "10", + "date": "2025-12-25" + }, + { + "issue": "25150", + "reds": [ + "06", + "13", + "17", + "19", + "24", + "31" + ], + "blue": "08", + "date": "2025-12-28" + }, + { + "issue": "25151", + "reds": [ + "08", + "09", + "14", + "22", + "28", + "30" + ], + "blue": "04", + "date": "2025-12-30" + }, + { + "issue": "26001", + "reds": [ + "02", + "06", + "11", + "12", + "13", + "33" + ], + "blue": "15", + "date": "2026-01-01" + }, + { + "issue": "26002", + "reds": [ + "01", + "05", + "07", + "18", + "30", + "32" + ], + "blue": "02", + "date": "2026-01-04" + }, + { + "issue": "26003", + "reds": [ + "05", + "06", + "09", + "21", + "28", + "30" + ], + "blue": "16", + "date": "2026-01-06" + }, + { + "issue": "26004", + "reds": [ + "03", + "07", + "08", + "09", + "18", + "32" + ], + "blue": "10", + "date": "2026-01-08" + }, + { + "issue": "26005", + "reds": [ + "01", + "20", + "22", + "27", + "30", + "33" + ], + "blue": "10", + "date": "2026-01-11" + }, + { + "issue": "26006", + "reds": [ + "02", + "06", + "22", + "23", + "24", + "28" + ], + "blue": "15", + "date": "2026-01-13" + }, + { + "issue": "26007", + "reds": [ + "09", + "13", + "19", + "27", + "29", + "30" + ], + "blue": "01", + "date": "2026-01-15" + }, + { + "issue": "26008", + "reds": [ + "06", + "09", + "16", + "27", + "31", + "33" + ], + "blue": "10", + "date": "2026-01-18" + }, + { + "issue": "26009", + "reds": [ + "03", + "06", + "13", + "19", + "23", + "25" + ], + "blue": "10", + "date": "2026-01-20" + }, + { + "issue": "26010", + "reds": [ + "04", + "09", + "10", + "15", + "19", + "26" + ], + "blue": "12", + "date": "2026-01-22" + }, + { + "issue": "26011", + "reds": [ + "02", + "03", + "04", + "20", + "31", + "32" + ], + "blue": "04", + "date": "2026-01-25" + }, + { + "issue": "26012", + "reds": [ + "03", + "05", + "07", + "16", + "20", + "24" + ], + "blue": "08", + "date": "2026-01-27" + }, + { + "issue": "26013", + "reds": [ + "04", + "09", + "12", + "13", + "16", + "20" + ], + "blue": "01", + "date": "2026-01-29" + }, + { + "issue": "26014", + "reds": [ + "07", + "13", + "19", + "22", + "26", + "32" + ], + "blue": "01", + "date": "2026-02-01" + }, + { + "issue": "26015", + "reds": [ + "07", + "10", + "13", + "22", + "27", + "31" + ], + "blue": "12", + "date": "2026-02-03" + }, + { + "issue": "26016", + "reds": [ + "04", + "05", + "09", + "10", + "27", + "30" + ], + "blue": "13", + "date": "2026-02-05" + }, + { + "issue": "26017", + "reds": [ + "01", + "03", + "05", + "18", + "29", + "32" + ], + "blue": "04", + "date": "2026-02-08" + }, + { + "issue": "26018", + "reds": [ + "11", + "15", + "17", + "22", + "25", + "30" + ], + "blue": "07", + "date": "2026-02-10" + }, + { + "issue": "26019", + "reds": [ + "07", + "08", + "16", + "17", + "18", + "30" + ], + "blue": "01", + "date": "2026-02-12" + }, + { + "issue": "26020", + "reds": [ + "01", + "13", + "14", + "21", + "24", + "30" + ], + "blue": "02", + "date": "2026-02-24" + }, + { + "issue": "26021", + "reds": [ + "03", + "13", + "25", + "26", + "30", + "31" + ], + "blue": "04", + "date": "2026-02-26" + }, + { + "issue": "26022", + "reds": [ + "15", + "18", + "23", + "25", + "28", + "32" + ], + "blue": "11", + "date": "2026-03-01" + }, + { + "issue": "26023", + "reds": [ + "01", + "03", + "08", + "10", + "23", + "29" + ], + "blue": "06", + "date": "2026-03-03" + }, + { + "issue": "26024", + "reds": [ + "01", + "02", + "13", + "21", + "23", + "29" + ], + "blue": "14", + "date": "2026-03-05" + }, + { + "issue": "26025", + "reds": [ + "02", + "03", + "15", + "20", + "23", + "24" + ], + "blue": "10", + "date": "2026-03-08" + }, + { + "issue": "26026", + "reds": [ + "02", + "09", + "16", + "22", + "25", + "29" + ], + "blue": "03", + "date": "2026-03-10" + }, + { + "issue": "26027", + "reds": [ + "02", + "13", + "17", + "18", + "25", + "26" + ], + "blue": "13", + "date": "2026-03-12" + }, + { + "issue": "26028", + "reds": [ + "02", + "06", + "09", + "17", + "25", + "28" + ], + "blue": "15", + "date": "2026-03-15" + }, + { + "issue": "26029", + "reds": [ + "06", + "19", + "22", + "23", + "28", + "31" + ], + "blue": "05", + "date": "2026-03-17" + }, + { + "issue": "26030", + "reds": [ + "10", + "11", + "14", + "19", + "22", + "24" + ], + "blue": "04", + "date": "2026-03-19" + }, + { + "issue": "26031", + "reds": [ + "03", + "10", + "12", + "13", + "18", + "33" + ], + "blue": "08", + "date": "2026-03-22" + }, + { + "issue": "26032", + "reds": [ + "01", + "03", + "11", + "18", + "31", + "33" + ], + "blue": "02", + "date": "2026-03-24" + }, + { + "issue": "26033", + "reds": [ + "03", + "06", + "13", + "21", + "28", + "29" + ], + "blue": "06", + "date": "2026-03-26" + }, + { + "issue": "26034", + "reds": [ + "01", + "03", + "07", + "13", + "22", + "23" + ], + "blue": "07", + "date": "2026-03-29" + }, + { + "issue": "26035", + "reds": [ + "02", + "06", + "12", + "24", + "25", + "32" + ], + "blue": "02", + "date": "2026-03-31" + }, + { + "issue": "26036", + "reds": [ + "06", + "10", + "12", + "15", + "22", + "28" + ], + "blue": "08", + "date": "2026-04-02" + }, + { + "issue": "26037", + "reds": [ + "11", + "22", + "27", + "29", + "31", + "33" + ], + "blue": "12", + "date": "2026-04-05" + }, + { + "issue": "26038", + "reds": [ + "01", + "02", + "13", + "23", + "25", + "27" + ], + "blue": "05", + "date": "2026-04-07" + }, + { + "issue": "26039", + "reds": [ + "08", + "17", + "18", + "21", + "25", + "30" + ], + "blue": "05", + "date": "2026-04-09" + }, + { + "issue": "26040", + "reds": [ + "03", + "04", + "14", + "22", + "23", + "33" + ], + "blue": "04", + "date": "2026-04-12" + }, + { + "issue": "26041", + "reds": [ + "02", + "08", + "10", + "17", + "19", + "24" + ], + "blue": "13", + "date": "2026-04-14" + }, + { + "issue": "26042", + "reds": [ + "02", + "07", + "12", + "19", + "24", + "31" + ], + "blue": "10", + "date": "2026-04-16" + }, + { + "issue": "26043", + "reds": [ + "06", + "09", + "14", + "16", + "25", + "32" + ], + "blue": "16", + "date": "2026-04-19" + }, + { + "issue": "26044", + "reds": [ + "02", + "14", + "17", + "18", + "22", + "30" + ], + "blue": "01", + "date": "2026-04-21" + }, + { + "issue": "26045", + "reds": [ + "04", + "11", + "15", + "17", + "24", + "30" + ], + "blue": "15", + "date": "2026-04-23" + }, + { + "issue": "26046", + "reds": [ + "02", + "09", + "10", + "24", + "31", + "33" + ], + "blue": "16", + "date": "2026-04-26" + }, + { + "issue": "26047", + "reds": [ + "07", + "16", + "21", + "24", + "27", + "30" + ], + "blue": "07", + "date": "2026-04-28" + }, + { + "issue": "26048", + "reds": [ + "09", + "15", + "18", + "24", + "28", + "33" + ], + "blue": "01", + "date": "2026-04-30" + }, + { + "issue": "26049", + "reds": [ + "03", + "04", + "14", + "15", + "18", + "20" + ], + "blue": "02", + "date": "2026-05-03" + }, + { + "issue": "26050", + "reds": [ + "06", + "09", + "25", + "27", + "28", + "30" + ], + "blue": "03", + "date": "2026-05-05" + }, + { + "issue": "26051", + "reds": [ + "09", + "14", + "15", + "16", + "29", + "30" + ], + "blue": "10", + "date": "2026-05-07" + }, + { + "issue": "26052", + "reds": [ + "01", + "03", + "11", + "22", + "26", + "31" + ], + "blue": "11", + "date": "2026-05-10" + }, + { + "issue": "26053", + "reds": [ + "01", + "02", + "03", + "08", + "13", + "14" + ], + "blue": "02", + "date": "2026-05-12" + }, + { + "issue": "26054", + "reds": [ + "13", + "20", + "25", + "29", + "30", + "33" + ], + "blue": "02", + "date": "2026-05-14" + }, + { + "issue": "26055", + "reds": [ + "04", + "11", + "24", + "25", + "32", + "33" + ], + "blue": "13", + "date": "2026-05-17" + }, + { + "issue": "26056", + "reds": [ + "10", + "19", + "21", + "22", + "31", + "33" + ], + "blue": "05", + "date": "2026-05-19" + }, + { + "issue": "26057", + "reds": [ + "01", + "10", + "22", + "24", + "28", + "30" + ], + "blue": "07", + "date": "2026-05-21" + }, + { + "issue": "26058", + "reds": [ + "01", + "04", + "07", + "21", + "29", + "30" + ], + "blue": "01", + "date": "2026-05-24" + }, + { + "issue": "26059", + "reds": [ + "08", + "16", + "26", + "28", + "29", + "30" + ], + "blue": "15", + "date": "2026-05-26" + }, + { + "issue": "26060", + "reds": [ + "07", + "09", + "10", + "16", + "22", + "27" + ], + "blue": "11", + "date": "2026-05-28" + }, + { + "issue": "26061", + "reds": [ + "01", + "04", + "05", + "15", + "23", + "28" + ], + "blue": "07", + "date": "2026-05-31" + }, + { + "issue": "26062", + "reds": [ + "02", + "04", + "07", + "14", + "28", + "29" + ], + "blue": "09", + "date": "2026-06-02" + }, + { + "issue": "26063", + "reds": [ + "02", + "08", + "25", + "28", + "30", + "31" + ], + "blue": "02", + "date": "2026-06-04" + }, + { + "issue": "26064", + "reds": [ + "01", + "09", + "15", + "18", + "29", + "33" + ], + "blue": "15", + "date": "2026-06-07" + }, + { + "issue": "26065", + "reds": [ + "07", + "08", + "16", + "24", + "30", + "32" + ], + "blue": "02", + "date": "2026-06-09" + }, + { + "issue": "26066", + "reds": [ + "05", + "11", + "21", + "23", + "24", + "29" + ], + "blue": "16", + "date": "2026-06-11" + }, + { + "issue": "26067", + "reds": [ + "04", + "19", + "27", + "29", + "30", + "32" + ], + "blue": "13", + "date": "2026-06-14" + }, + { + "issue": "26068", + "reds": [ + "03", + "05", + "16", + "18", + "29", + "32" + ], + "blue": "04", + "date": "2026-06-16" + }, + { + "issue": "26069", + "reds": [ + "12", + "14", + "16", + "17", + "18", + "32" + ], + "blue": "08", + "date": "2026-06-18" + }, + { + "issue": "26070", + "reds": [ + "03", + "06", + "08", + "14", + "26", + "27" + ], + "blue": "08", + "date": "2026-06-21" + }, + { + "issue": "26071", + "reds": [ + "03", + "08", + "19", + "25", + "31", + "33" + ], + "blue": "05", + "date": "2026-06-23" + }, + { + "issue": "26072", + "reds": [ + "07", + "08", + "12", + "15", + "17", + "21" + ], + "blue": "01", + "date": "2026-06-25" + }, + { + "issue": "26073", + "reds": [ + "09", + "10", + "13", + "16", + "19", + "21" + ], + "blue": "08", + "date": "2026-06-28" + }, + { + "issue": "26074", + "reds": [ + "02", + "23", + "24", + "26", + "28", + "32" + ], + "blue": "04", + "date": "2026-06-30" + }, + { + "issue": "26075", + "reds": [ + "08", + "12", + "18", + "21", + "24", + "30" + ], + "blue": "01", + "date": "2026-07-02" + }, + { + "issue": "26076", + "reds": [ + "01", + "03", + "19", + "20", + "24", + "25" + ], + "blue": "07", + "date": "2026-07-05" + }, + { + "issue": "26077", + "reds": [ + "01", + "04", + "05", + "14", + "18", + "25" + ], + "blue": "04", + "date": "2026-07-07" + }, + { + "issue": "26078", + "reds": [ + "07", + "11", + "14", + "16", + "27", + "28" + ], + "blue": "06", + "date": "2026-07-09" + }, + { + "issue": "26079", + "reds": [ + "01", + "11", + "17", + "22", + "24", + "29" + ], + "blue": "04", + "date": "2026-07-12" + }, + { + "issue": "26080", + "reds": [ + "04", + "05", + "11", + "19", + "27", + "32" + ], + "blue": "01", + "date": "2026-07-14" + }, + { + "issue": "26081", + "reds": [ + "06", + "10", + "12", + "15", + "24", + "27" + ], + "blue": "12", + "date": "2026-07-16" + }, + { + "issue": "26082", + "reds": [ + "05", + "07", + "10", + "14", + "21", + "28" + ], + "blue": "04", + "date": "2026-07-19" + }, + { + "issue": "26083", + "reds": [ + "07", + "14", + "15", + "23", + "28", + "33" + ], + "blue": "03", + "date": "2026-07-21" + }, + { + "issue": "26084", + "reds": [ + "01", + "05", + "06", + "10", + "12", + "16" + ], + "blue": "05", + "date": "2026-07-23" + }, + { + "issue": "26085", + "reds": [ + "06", + "09", + "13", + "17", + "24", + "28" + ], + "blue": "15", + "date": "2026-07-26" + }, + { + "issue": "26086", + "reds": [ + "02", + "05", + "14", + "25", + "30", + "32" + ], + "blue": "05", + "date": "2026-07-28" + } +] \ No newline at end of file diff --git a/skills/lottery_picker/ensemble_learning.py b/skills/lottery_picker/ensemble_learning.py new file mode 100644 index 0000000..626a219 --- /dev/null +++ b/skills/lottery_picker/ensemble_learning.py @@ -0,0 +1,725 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +大乐透/双色球 集成学习模块 +方案3:多模型集成 + 权重优化训练 +================================== +包含5种分析模型: +1. 频率加权模型 (Frequency Model) +2. 贝叶斯平滑模型 (Bayesian Smoothing Model) +3. 马尔可夫链模型 (Markov Chain Model) +4. K-Means聚类模型 (K-Means Clustering Model) +5. 残差回归模型 (Residual Regression Model) + +通过历史数据回测学习各模型的最优权重 +""" + +import json, math, random, hashlib +from collections import Counter, defaultdict + +# ==================== 基础工具函数 ==================== + +def sigmoid(x): + """Sigmoid激活函数""" + return 1 / (1 + math.exp(-max(min(x, 500), -500))) + +def normalize(scores): + """归一化分数,使总和为1""" + total = sum(scores.values()) + if total == 0: + return {k: 1.0/len(scores) for k in scores} + return {k: v/total for k, v in scores.items()} + +def weighted_choice(weights, count=1): + """从权重分布中无放回选择指定数量的元素""" + items = list(weights.keys()) + if len(items) == 0: + return [] + if count >= len(items): + return items[:] + result = [] + remaining = items[:] + remaining_weights = weights.copy() + for _ in range(count): + total = sum(remaining_weights.values()) + if total <= 0: + choice = random.choice(remaining) + else: + r = random.random() * total + cum = 0 + for item in remaining: + cum += remaining_weights[item] + if r <= cum: + choice = item + break + result.append(choice) + remaining.remove(choice) + del remaining_weights[choice] + return result + +# ==================== 模型1:频率加权模型 ==================== + +class FrequencyModel: + """基于历史频率的加权模型""" + def __init__(self, lt, data, rule): + self.lt = lt + self.data = data + self.rule = rule + self.rcnt = rule["red_count"] + self.rmax = rule["red_max"] + self.bcnt = rule["blue_count"] + self.bmax = rule["blue_max"] + self.red_score = {} + self.blue_score = {} + self.analyze() + + def analyze(self): + rule, rmax, bmax, rcnt, bcnt = self.rule, self.rule["red_max"], self.rule["blue_max"], self.rule["red_count"], self.rule["blue_count"] + all_r, all_b = [], [] + for rec in self.data: + all_r.extend(int(x) for x in rec["reds"]) + bl = rec["blue"] + # 修复:蓝球处理 - 支持整数和字符串两种格式 + if isinstance(bl, str): + all_b.append(int(bl)) + elif isinstance(bl, list): + all_b.extend(bl) + else: + all_b.append(bl) + + # 频率统计 + rf = Counter(all_r) + bf = Counter(all_b) + + max_rf = max(rf.values()) if rf else 1 + max_bf = max(bf.values()) if bf else 1 + + # 归一化 + self.red_score = {i: rf.get(i, 0) / max_rf for i in range(1, rmax + 1)} + self.blue_score = {i: bf.get(i, 0) / max_bf for i in range(1, bmax + 1)} + + # 平滑:避免0分 + for i in range(1, rmax + 1): + if self.red_score[i] == 0: + self.red_score[i] = 0.01 + for i in range(1, bmax + 1): + if self.blue_score[i] == 0: + self.blue_score[i] = 0.01 + + # 归一化到[0,1] + max_r = max(self.red_score.values()) + max_b = max(self.blue_score.values()) + if max_r > 0: + for i in self.red_score: + self.red_score[i] /= max_r + if max_b > 0: + for i in self.blue_score: + self.blue_score[i] /= max_b + + +# ==================== 模型2:贝叶斯平滑模型 ==================== + +class BayesianModel: + """贝叶斯先验 + 似然平滑模型""" + def __init__(self, lt, data, rule): + self.lt = lt + self.data = data + self.rule = rule + self.rcnt = rule["red_count"] + self.rmax = rule["red_max"] + self.bcnt = rule["blue_count"] + self.bmax = rule["blue_max"] + self.prior = {i: 1.0 / rule["red_max"] for i in range(1, rule["red_max"] + 1)} + self.red_score = {} + self.blue_score = {} + self.analyze() + + def analyze(self): + rule, rmax, bmax, rcnt, bcnt = self.rule, self.rule["red_max"], self.rule["blue_max"], self.rule["red_count"], self.rule["blue_count"] + all_r, all_b = [], [] + for rec in self.data: + all_r.extend(int(x) for x in rec["reds"]) + bl = rec["blue"] + # 修复:蓝球处理 - 支持整数和字符串两种格式 + if isinstance(bl, str): + all_b.append(int(bl)) + elif isinstance(bl, list): + all_b.extend(bl) + else: + all_b.append(bl) + + # 似然 + rf = Counter(all_r) + bf = Counter(all_b) + + # 贝叶斯后验:后验 ∝ 先验 × 似然 + # 使用伪计数:先验+1,似然+1,避免0 + alpha = 1 # 先验伪计数 + beta = 1 # 似然伪计数 + total_alpha = rmax * alpha + total_beta = len(all_r) + beta * rmax + + self.red_score = {} + for i in range(1, rmax + 1): + # 后验均值 = (α + count) / (α + β * rmax) + posterior = (alpha + rf.get(i, 0)) / (total_alpha + beta) + self.red_score[i] = posterior + + total_blue = len(all_b) + beta * bmax + self.blue_score = {} + for i in range(1, bmax + 1): + posterior = (alpha + bf.get(i, 0)) / (total_alpha + beta) + self.blue_score[i] = posterior + + # 归一化 + max_r = max(self.red_score.values()) + max_b = max(self.blue_score.values()) + if max_r > 0: + for i in self.red_score: + self.red_score[i] /= max_r + if max_b > 0: + for i in self.blue_score: + self.blue_score[i] /= max_b + + +# ==================== 模型3:马尔可夫链模型 ==================== + +class MarkovModel: + """基于马尔可夫链的状态转移模型""" + def __init__(self, lt, data, rule): + self.lt = lt + self.data = data + self.rule = rule + self.rcnt = rule["red_count"] + self.rmax = rule["red_max"] + self.bcnt = rule["blue_count"] + self.bmax = rule["blue_max"] + self.red_score = {} + self.blue_score = {} + self.analyze() + + def analyze(self): + rule, rmax, bmax, rcnt, bcnt = self.rule, self.rule["red_max"], self.rule["blue_max"], self.rule["red_count"], self.rule["blue_count"] + + # 构建状态转移矩阵(考虑最近5期作为状态) + # 这里简化为:计算每个号码在历史数据中的"惯性" + # 即:一个号码出现后,后续几期再次出现的概率 + + # 使用frozenset作为不可变集合作为字典键 + transition = defaultdict(lambda: defaultdict(int)) + for i in range(1, len(self.data)): + prev = frozenset(int(x) for x in self.data[i-1]["reds"]) + curr = set(int(x) for x in self.data[i]["reds"]) + for num in curr: + transition[prev][num] += 1 + + # 计算每个号码的"热度"(结合近期出现频率和转移概率) + recent_data = self.data[:20] if len(self.data) >= 20 else self.data + recent_reds = [] + for rec in recent_data: + recent_reds.extend(int(x) for x in rec["reds"]) + + rf = Counter(recent_reds) + total_recent = len(recent_reds) + + # 综合评分:近期频率 + 转移惯性 + self.red_score = {} + for i in range(1, rmax + 1): + freq_score = rf.get(i, 0) / total_recent if total_recent > 0 else 0 + # 惯性得分:如果上一期出现过,给予额外权重 + last_reds = set(int(x) for x in self.data[0]["reds"]) if self.data else set() + inertia = 1.5 if i in last_reds else 1.0 + self.red_score[i] = (freq_score * inertia + 0.01) # 加0.01平滑 + + # 蓝球简化:直接用近期频率 + recent_blues = [] + for rec in recent_data: + bl = rec["blue"] + recent_blues.append(int(bl) if isinstance(bl, str) else (bl[0] if isinstance(bl, list) else bl)) + + bf = Counter(recent_blues) + total_blue = len(recent_blues) + self.blue_score = {} + for i in range(1, bmax + 1): + self.blue_score[i] = (bf.get(i, 0) / total_blue * 0.8 + 1.0 / bmax * 0.2) if total_blue > 0 else (1.0 / bmax) + + # 归一化 + max_r = max(self.red_score.values()) if self.red_score else 1 + max_b = max(self.blue_score.values()) if self.blue_score else 1 + for i in self.red_score: + self.red_score[i] /= max_r + for i in self.blue_score: + self.blue_score[i] /= max_b + + +# ==================== 模型4:K-Means聚类模型 ==================== + +class KMeansModel: + """基于历史聚类中心的推荐模型""" + def __init__(self, lt, data, rule): + self.lt = lt + self.data = data + self.rule = rule + self.rcnt = rule["red_count"] + self.rmax = rule["red_max"] + self.bcnt = rule["blue_count"] + self.bmax = rule["blue_max"] + self.red_score = {} + self.blue_score = {} + self.analyze() + + def analyze(self): + rule, rmax, bmax, rcnt, bcnt = self.rule, self.rule["red_max"], self.rule["blue_max"], self.rule["red_count"], self.rule["blue_max"] + + # 提取所有历史红球组合 + clusters = [] + for rec in self.data: + nums = sorted(int(x) for x in rec["reds"]) + if len(nums) == rcnt: + clusters.append(nums) + + if len(clusters) < 3: + # 数据不足,退化为频率模型 + rf = Counter() + for nums in clusters: + for n in nums: + rf[n] += 1 + max_f = max(rf.values()) if rf else 1 + self.red_score = {i: rf.get(i, 0) / max_f for i in range(1, rmax + 1)} + return + + # 简化版K-Means:使用质心法 + # 计算每个位置的平均值(排序后) + sorted_clusters = [sorted(c) for c in clusters] + n_clusters = min(5, len(sorted_clusters)) + + # 计算每个位置的聚类中心 + position_means = [[] for _ in range(rcnt)] + for cluster in sorted_clusters: + for pos in range(rcnt): + position_means[pos].append(cluster[pos]) + + # 计算每个号码的"接近中心"得分 + position_avg = [sum(p) / len(p) for p in position_means if p] + + self.red_score = {} + for i in range(1, rmax + 1): + # 计算该号码在所有位置上偏离平均值的程度 + deviations = [] + for pos, avg in enumerate(position_avg): + if len(sorted_clusters) > pos: + cluster_at_pos = [c[pos] for c in sorted_clusters if len(c) > pos] + if cluster_at_pos: + deviations.append(abs(i - avg)) + avg_dev = sum(deviations) / len(deviations) if deviations else rmax / 2 + # 得分:偏离越小得分越高(越接近典型组合) + score = max(0, 1 - avg_dev / (rmax * 0.5)) + self.red_score[i] = max(score, 0.01) # 最小0.01 + + # 蓝球:直接用频率 + all_blues = [] + for rec in self.data: + bl = rec["blue"] + all_blues.append(int(bl) if isinstance(bl, str) else (bl[0] if isinstance(bl, list) else bl)) + bf = Counter(all_blues) + max_bf = max(bf.values()) if bf else 1 + self.blue_score = {i: bf.get(i, 0) / max_bf for i in range(1, bmax + 1)} + for i in range(1, bmax + 1): + if self.blue_score[i] == 0: + self.blue_score[i] = 0.01 + + # 归一化 + max_r = max(self.red_score.values()) if self.red_score else 1 + max_b = max(self.blue_score.values()) if self.blue_score else 1 + for i in self.red_score: + self.red_score[i] /= max_r + for i in self.blue_score: + self.blue_score[i] /= max_b + + +# ==================== 模型5:残差回归模型 ==================== + +class ResidualModel: + """残差分析 + 回归预测模型""" + def __init__(self, lt, data, rule): + self.lt = lt + self.data = data + self.rule = rule + self.rcnt = rule["red_count"] + self.rmax = rule["red_max"] + self.bcnt = rule["blue_count"] + self.bmax = rule["blue_max"] + self.red_score = {} + self.blue_score = {} + self.analyze() + + def analyze(self): + rule, rmax, bmax, rcnt, bcnt = self.rule, self.rule["red_max"], self.rule["blue_max"], self.rule["red_count"], self.rule["blue_count"] + + # 计算每期的和值、奇偶比、大小比作为特征 + sums = [] + for rec in self.data: + s = sum(int(x) for x in rec["reds"]) + sums.append(s) + + if len(sums) < 10: + # 数据不足,退化为均匀分布 + self.red_score = {i: 1.0 / rmax for i in range(1, rmax + 1)} + self.blue_score = {i: 1.0 / bmax for i in range(1, bmax + 1)} + return + + # 计算和值的移动平均(最近10期) + window = min(10, len(sums)) + recent_sums = sums[-window:] + avg_sum = sum(recent_sums) / len(recent_sums) + std_sum = math.sqrt(sum((s - avg_sum) ** 2 for s in recent_sums) / len(recent_sums)) + + # 计算每个号码在"和值附近"组合中的出现频率 + # 思路:哪些号码更常出现在和值接近均值的组合中 + score_by_num = {i: 0.0 for i in range(1, rmax + 1)} + count_by_num = {i: 0 for i in range(1, rmax + 1)} + + for rec in self.data: + nums = [int(x) for x in rec["reds"]] + s = sum(nums) + # 计算该组合和值与均值的距离 + dist = abs(s - avg_sum) + if dist <= std_sum * 2: # 落在2个标准差内 + for n in nums: + score_by_num[n] += 1 / (dist + 1) # 距离越近得分越高 + count_by_num[n] += 1 + + # 归一化 + max_score = max(score_by_num.values()) if max(score_by_num.values()) > 0 else 1 + self.red_score = {} + for i in range(1, rmax + 1): + if count_by_num[i] > 0: + self.red_score[i] = (score_by_num[i] / count_by_num[i]) / max_score + else: + self.red_score[i] = 1.0 / rmax # 均匀分布 + + # 蓝球:和值相关性分析 + blues_by_sum = defaultdict(list) + for rec in self.data: + bl = rec["blue"] + bl_val = int(bl) if isinstance(bl, str) else (bl[0] if isinstance(bl, list) else bl) + s = sum(int(x) for x in rec["reds"]) + blues_by_sum[s].append(bl_val) + + self.blue_score = {} + for i in range(1, bmax + 1): + # 计算该蓝球在"和值附近"出现的频率 + total = 0 + for s, blues in blues_by_sum.items(): + dist = abs(s - avg_sum) + if dist <= std_sum * 2: + total += len([b for b in blues if b == i]) / (dist + 1) + self.blue_score[i] = max(total / 100, 1.0 / bmax) + + # 归一化 + max_r = max(self.red_score.values()) if self.red_score else 1 + max_b = max(self.blue_score.values()) if self.blue_score else 1 + for i in self.red_score: + self.red_score[i] /= max_r + for i in self.blue_score: + self.blue_score[i] /= max_b + + +# ==================== 集成学习引擎 ==================== + +class EnsembleLearner: + """集成学习引擎:多模型加权 + 回测优化权重""" + + def __init__(self, lt, data, rule): + self.lt = lt + self.data = data + self.rule = rule + self.rcnt = rule["red_count"] + self.rmax = rule["red_max"] + self.bcnt = rule["blue_count"] + self.bmax = rule["blue_max"] + + # 初始化5个模型 + self.models = { + 'freq': FrequencyModel(lt, data, rule), + 'bayes': BayesianModel(lt, data, rule), + 'markov': MarkovModel(lt, data, rule), + 'kmeans': KMeansModel(lt, data, rule), + 'residual': ResidualModel(lt, data, rule) + } + + # 初始权重(等权) + self.weights = {m: 1.0 / len(self.models) for m in self.models} + self.initial_weights = self.weights.copy() + + # 回测结果 + self.backtest_results = None + + def get_combined_score(self): + """获取集成后的综合评分""" + red_combined = {} + blue_combined = {} + + for model_name, model in self.models.items(): + w = self.weights[model_name] + for i in range(1, self.rmax + 1): + red_combined[i] = red_combined.get(i, 0) + w * model.red_score[i] + for i in range(1, self.bmax + 1): + blue_combined[i] = blue_combined.get(i, 0) + w * model.blue_score[i] + + # 归一化 + max_r = max(red_combined.values()) if red_combined else 1 + max_b = max(blue_combined.values()) if blue_combined else 1 + for i in red_combined: + red_combined[i] /= max_r + for i in blue_combined: + blue_combined[i] /= max_b + + return red_combined, blue_combined + + def _get_combined_for_weights(self, weights): + """根据给定权重获取组合评分""" + red_c = {} + blue_c = {} + for model_name, model in self.models.items(): + ww = weights[model_name] + for i in range(1, self.rmax + 1): + red_c[i] = red_c.get(i, 0) + ww * model.red_score[i] + for i in range(1, self.bmax + 1): + blue_c[i] = blue_c.get(i, 0) + ww * model.blue_score[i] + return red_c, blue_c + + def _predict_from_scores(self, red_c, blue_c, rmax, bmax, rcnt, bcnt): + """根据评分预测号码""" + sorted_r = sorted(red_c.items(), key=lambda x: x[1], reverse=True) + top_r = [n for n, s in sorted_r[:rmax]] + sorted_b = sorted(blue_c.items(), key=lambda x: x[1], reverse=True) + top_b = [n for n, s in sorted_b[:bmax]] + + sel = [] + cand = top_r[:] + weights_r = [red_c.get(n, 0.5) for n in cand] + while len(sel) < rcnt and cand: + tw = sum(weights_r) + if tw <= 0: pk = random.choice(cand) + else: + rv = random.random() * tw + cum = 0 + for i, ww in enumerate(weights_r): + cum += ww + if rv <= cum: + pk = cand[i] + break + if pk not in sel: sel.append(pk) + idx = cand.index(pk); cand.pop(idx); weights_r.pop(idx) + sel.sort() + + bls = [] + cand_b = top_b[:] + weights_b = [blue_c.get(n, 0.5) for n in cand_b] + while len(bls) < bcnt and cand_b: + tw = sum(weights_b) + if tw <= 0: pk = random.choice(cand_b) + else: + rv = random.random() * tw + cum = 0 + for i, ww in enumerate(weights_b): + cum += ww + if rv <= cum: + pk = cand_b[i] + break + if pk not in bls: bls.append(pk) + idx = cand_b.index(pk); cand_b.pop(idx); weights_b.pop(idx) + bls.sort() + + return sel, bls + + def backtest_optimize(self, test_periods=30): + """ + 通过回测优化各模型权重 + 使用简单网格搜索 + 梯度上升混合方法 + """ + rule = self.rule + rmax, bmax = self.rule["red_max"], self.rule["blue_max"] + + print(f"\n{'='*60}") + print(f"🧠 集成学习权重优化 | 彩种: {rule['name']} | 数据期数: {len(self.data)}") + print(f"{'='*60}") + + # 准备回测数据(按时间顺序) + sorted_data = sorted(self.data, key=lambda x: int(x["issue"])) + if len(sorted_data) < test_periods + 10: + test_periods = max(10, len(sorted_data) - 10) + + # 评分函数:计算推荐与实际匹配的分数 + def evaluate(weights, data_slice): + red_comp, blue_comp = self._get_combined_for_weights(weights) + pred_r, pred_b = self._predict_from_scores(red_comp, blue_comp, rmax, bmax, rule["red_count"], rule["blue_count"]) + + actual = data_slice + act_r = [int(x) for x in actual["reds"]] + act_b = actual["blue"] if isinstance(actual["blue"], list) else [int(actual["blue"])] + + # 评分:命中红球数*2 + 命中蓝球数 + hit_r = sum(1 for n in pred_r if n in act_r) + hit_b = sum(1 for n in pred_b if n in act_b if (isinstance(act_b, list) and n in act_b) or (isinstance(act_b, int) and n == act_b)) + return hit_r * 2 + hit_b + + # 简单网格搜索优化权重 + best_weights = self.weights.copy() + best_score = float('-inf') + + # 初始评估 + current_score = 0 + for i in range(test_periods): + if i + 10 < len(sorted_data): + slice_data = sorted_data[i + 10:i + 11] # 下一期作为测试 + current_score += evaluate(self.weights, slice_data[0]) + + print(f" 初始等权总分: {current_score:.1f}") + + # 简单梯度上升(每个模型权重微调) + improved = True + iterations = 0 + max_iterations = 20 + + while improved and iterations < max_iterations: + improved = False + iterations += 1 + + for model_name in self.weights: + for delta in [-0.05, 0.05]: # 微调权重 + test_w = self.weights.copy() + test_w[model_name] = max(0.01, min(0.9, test_w[model_name] + delta)) + + # 归一化 + total = sum(test_w.values()) + test_w = {k: v/total for k, v in test_w.items()} + + score = 0 + for i in range(test_periods): + if i + 10 < len(sorted_data): + slice_data = sorted_data[i + 10:i + 11] + score += evaluate(test_w, slice_data[0]) + + if score > best_score: + best_score = score + best_weights = test_w.copy() + improved = True + + self.weights = best_weights + self.backtest_results = { + 'iterations': iterations, + 'best_score': best_score, + 'test_periods': test_periods, + 'model_weights': best_weights + } + + # 输出各模型权重 + print(f" 优化后权重:") + for m, w in sorted(self.weights.items(), key=lambda x: -x[1]): + model_names = { + 'freq': '频率加权', + 'bayes': '贝叶斯平滑', + 'markov': '马尔可夫链', + 'kmeans': 'K-Means聚类', + 'residual': '残差回归' + } + print(f" {model_names.get(m, m):<12s}: {w:.4f}") + + print(f" 最终集成总分: {best_score:.1f} (测试{test_periods}期)") + + # 返回综合评分 + return self.get_combined_score() + + def get_recommendations(self, count=5): + """基于集成评分生成推荐""" + red_comp, blue_comp = self.get_combined_score() + rmax, bmax = self.rule["red_max"], self.rule["blue_max"] + rcnt, bcnt = self.rule["red_count"], self.rule["blue_count"] + + sorted_r = sorted(red_comp.items(), key=lambda x: x[1], reverse=True) + top_r = [n for n, s in sorted_r[:rmax]] + sorted_b = sorted(blue_comp.items(), key=lambda x: x[1], reverse=True) + top_b = [n for n, s in sorted_b[:bmax]] + + seed = hashlib.md5(f"{self.lt}_ensemble".encode()).hexdigest() + res = [] + for ci in range(count): + cs = hashlib.md5(f"ensemble_c{ci}_{seed}".encode()).hexdigest() + cr = random.Random(cs) + + sel = [] + cand = top_r[:] + w = [red_comp.get(n, 0.5) for n in cand] + while len(sel) < rcnt and cand: + tw = sum(w) + if tw <= 0: pk = cr.choice(cand) + else: + rv = cr.random() * tw + cum = 0 + for i, ww in enumerate(w): + cum += ww + if rv <= cum: + pk = cand[i] + break + if pk not in sel: sel.append(pk) + idx = cand.index(pk); cand.pop(idx); w.pop(idx) + sel.sort() + + bls = [] + cand_b = top_b[:] + wb = [blue_comp.get(n, 0.5) for n in cand_b] + while len(bls) < bcnt and cand_b: + tw = sum(wb) + if tw <= 0: pk = cr.choice(cand_b) + else: + rv = cr.random() * tw + cum = 0 + for i, ww in enumerate(wb): + cum += ww + if rv <= cum: + pk = cand_b[i] + break + if pk not in bls: bls.append(pk) + idx = cand_b.index(pk); cand_b.pop(idx); wb.pop(idx) + bls.sort() + + score = round(sum(red_comp.get(n, 0) for n in sel) + sum(blue_comp.get(n, 0) for n in bls), 4) + mid = rmax / 2 + res.append({ + "reds": sel, + "blues": bls, + "score": score, + "odd_even": f"{sum(1 for n in sel if n % 2 == 1)}:{rcnt - sum(1 for n in sel if n % 2 == 1)}", + "big_small": f"{sum(1 for n in sel if n > mid)}:{rcnt - sum(1 for n in sel if n > mid)}", + "sum": sum(sel) + }) + + res.sort(key=lambda x: x["score"], reverse=True) + return res + + +# ==================== 主函数 ==================== + +def ensemble_recommendation(lt, data, rule, count=5): + """集成学习推荐主接口""" + learner = EnsembleLearner(lt, data, rule) + red_comp, blue_comp = learner.backtest_optimize(test_periods=30) + recommendations = learner.get_recommendations(count=count) + + return { + 'weights': learner.weights, + 'backtest': learner.backtest_results, + 'recommendations': recommendations, + 'combined_scores': { + 'red': red_comp, + 'blue': blue_comp + } + } + + +if __name__ == "__main__": + # 简单测试 + print("集成学习模块加载成功!") + print("可用模型:频率加权、贝叶斯平滑、马尔可夫链、K-Means聚类、残差回归") \ No newline at end of file diff --git a/skills/lottery_picker/evolution_tool.py b/skills/lottery_picker/evolution_tool.py new file mode 100644 index 0000000..dc4aaa8 --- /dev/null +++ b/skills/lottery_picker/evolution_tool.py @@ -0,0 +1,285 @@ +#!/usr/bin/env python3 +""" +回滚迭代跟踪工具 - 管理 AI 进化版本和回滚机制 +""" + +import json +import os +from datetime import datetime +from typing import Dict, List, Optional, Any +from pathlib import Path + + +class EvolutionTracker: + """回滚迭代跟踪器""" + + def __init__(self, data_dir: str = "./evolution_tracking"): + self.data_dir = Path(data_dir) + self.data_dir.mkdir(parents=True, exist_ok=True) + self.version_file = self.data_dir / "versions.json" + self.performance_log = self.data_dir / "performance.json" + self.rollback_log = self.data_dir / "rollbacks.json" + + def initialize(self): + """初始化跟踪器""" + # 初始化版本记录 + if not self.version_file.exists(): + initial_version = { + "version": 0, + "created_at": datetime.now().isoformat(), + "performance": { + "win_rate": 0.0, + "total_bets": 0, + "total_winnings": 0.0 + } + } + self.save_version(initial_version) + + # 初始化性能日志 + if not self.performance_log.exists(): + self.save_performance({ + "history": [], + "current": { + "total_bets": 0, + "win_rate": 0.0, + "earnings": 0.0, + "total_winnings": 0.0 + } + }) + + # 初始化回滚日志 + if not self.rollback_log.exists(): + self.save_rollbacks({ + "history": [], + "current_version": 0 + }) + + def save_version(self, version_data: Dict): + """保存版本信息""" + with open(self.version_file, 'w', encoding='utf-8') as f: + json.dump(version_data, f, ensure_ascii=False, indent=2) + + def load_version(self) -> Dict: + """加载当前版本""" + if self.version_file.exists(): + with open(self.version_file, 'r', encoding='utf-8') as f: + return json.load(f) + return {} + + def record_performance(self, version: int, win_rate: float, earnings: float): + """记录性能数据""" + performance = self.load_performance() + + entry = { + "version": version, + "recorded_at": datetime.now().isoformat(), + "win_rate": win_rate, + "earnings": earnings, + "total_bets": performance["current"].get("total_bets", 0), + "total_winnings": performance["current"].get("total_winnings", 0.0) + } + + performance["history"].append(entry) + + # 更新当前性能 + current = performance["current"] + current["total_bets"] += 1 + current["win_rate"] = win_rate + current["earnings"] = earnings + current["total_winnings"] = current.get("total_winnings", 0.0) + earnings + + self.save_performance(performance) + + def load_performance(self) -> Dict: + """加载性能日志""" + if self.performance_log.exists(): + with open(self.performance_log, 'r', encoding='utf-8') as f: + return json.load(f) + return {"history": [], "current": {}} + + def save_performance(self, performance: Dict): + """保存性能日志""" + with open(self.performance_log, 'w', encoding='utf-8') as f: + json.dump(performance, f, ensure_ascii=False, indent=2) + + def record_rollback(self, from_version: int, to_version: int, reason: str): + """记录回滚操作""" + rollbacks = self.load_rollbacks() + + entry = { + "from_version": from_version, + "to_version": to_version, + "reason": reason, + "timestamp": datetime.now().isoformat() + } + + rollbacks["history"].append(entry) + rollbacks["current_version"] = to_version + + self.save_rollbacks(rollbacks) + + def load_rollbacks(self) -> Dict: + """加载回滚日志""" + if self.rollback_log.exists(): + with open(self.rollback_log, 'r', encoding='utf-8') as f: + return json.load(f) + return {"history": [], "current_version": 0} + + def save_rollbacks(self, rollbacks: Dict): + """保存回滚日志""" + with open(self.rollback_log, 'w', encoding='utf-8') as f: + json.dump(rollbacks, f, ensure_ascii=False, indent=2) + + def get_version_history(self) -> List[Dict]: + """获取版本历史""" + performance = self.load_performance() + return performance.get("history", []) + + def get_best_version(self) -> Optional[Dict]: + """获取性能最好的版本""" + history = self.get_version_history() + if not history: + return None + + best = max(history, key=lambda x: x.get("win_rate", 0)) + return best + + def compare_versions(self, version1: int, version2: int) -> Dict: + """比较两个版本的性能""" + history = self.get_version_history() + + v1_data = next((h for h in history if h["version"] == version1), None) + v2_data = next((h for h in history if h["version"] == version2), None) + + if not v1_data or not v2_data: + return {"error": "版本不存在"} + + comparison = { + "version1": version1, + "version2": version2, + "v1_win_rate": v1_data.get("win_rate", 0), + "v2_win_rate": v2_data.get("win_rate", 0), + "v1_earnings": v1_data.get("earnings", 0.0), + "v2_earnings": v2_data.get("earnings", 0.0), + "better_version": version1 if v1_data.get("win_rate", 0) > v2_data.get("win_rate", 0) else version2 + } + + return comparison + + def generate_report(self) -> str: + """生成进化报告""" + performance = self.load_performance() + rollbacks = self.load_rollbacks() + + report = [ + "="*60, + "AI 进化跟踪报告", + "="*60, + f"当前版本:{rollbacks.get('current_version', 0)}", + f"回滚历史数:{len(rollbacks.get('history', []))}", + "\n当前性能数据:", + f" 胜率:{performance['current'].get('win_rate', 0):.2%}", + f" 累计投注:{performance['current'].get('total_bets', 0)}", + f" 累计收益:¥{performance['current'].get('total_winnings', 0.0):.2f}", + "\n版本历史:", + ] + + history = performance.get("history", []) + if history: + for i, entry in enumerate(history[-10:], 1): # 显示最后 10 个 + report.extend([ + f" [v{entry['version']}] 胜率:{entry['win_rate']:.2%}, " + f"收益:¥{entry['earnings']:.2f}, " + f"记录时间:{entry['recorded_at']}", + "" + ]) + else: + report.append(" 暂无历史数据") + + report.extend(["\n回滚历史:", ""]) + + if rollbacks.get("history"): + for i, entry in enumerate(rollbacks["history"]): + report.extend([ + f" 回滚 #{i+1}: v{entry['from_version']} → v{entry['to_version']}, " + f"原因:{entry['reason']}, " + f"时间:{entry['timestamp']}", + "" + ]) + else: + report.append(" 暂无回滚记录") + + report.extend(["="*60, ""]) + return "\n".join(report) + + def optimize_weights(self, learning_rate: float = 0.1, epochs: int = 100) -> Dict: + """优化模型权重 - 模拟进化过程""" + import random + + # 初始化权重 + weights = { + "red_hot": 0.3, + "red_cold": 0.2, + "consecutive": 0.15, + "sum_range": 0.15, + "parity": 0.1, + "entropy": 0.1 + } + + # 模拟进化过程 + for epoch in range(epochs): + # 模拟数据输入 + sample_data = [random.randint(1, 33) for _ in range(10)] + + # 计算损失 + loss = sum((w - random.random())**2 for w in weights.values()) + + # 梯度下降 + for key in weights: + gradient = sum(random.random() for _ in range(10)) / len(sample_data) + weights[key] -= learning_rate * gradient + + # 归一化权重 + total = sum(weights.values()) + weights = {k: round(v/total, 4) for k, v in weights.items()} + + return { + "optimized_weights": weights, + "epochs_completed": epochs, + "final_loss": round(loss, 4) + } + + +def main(): + """主函数 - 测试回滚跟踪工具""" + tracker = EvolutionTracker() + tracker.initialize() + + # 模拟一些版本记录 + versions = [ + (1, 0.05, 100.0), + (2, 0.12, 150.0), + (3, 0.08, 120.0), + (4, 0.15, 200.0), + (5, 0.18, 250.0) + ] + + for version, win_rate, earnings in versions: + tracker.record_performance(version, win_rate, earnings) + + # 模拟回滚 + tracker.record_rollback(5, 4, "性能下降") + + # 生成报告 + print(tracker.generate_report()) + + # 优化权重 + optimized = tracker.optimize_weights() + print("\n优化结果:") + print(f" 完成轮数:{optimized['epochs_completed']}") + print(f" 最终损失:{optimized['final_loss']}") + print(f" 优化权重:{optimized['optimized_weights']}") + + +if __name__ == "__main__": + main() diff --git a/skills/lottery_picker/lottery_tool.py b/skills/lottery_picker/lottery_tool.py new file mode 100644 index 0000000..7a16ba1 --- /dev/null +++ b/skills/lottery_picker/lottery_tool.py @@ -0,0 +1,1314 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +双色球 & 大乐透 号码分析与机选工具 +================================== +功能: 数据采集/分析/推荐/回滚/持久化 +数据源: 双色球(500.com) 大乐透(体彩API) +""" + +import json, re, os, hashlib, datetime, subprocess, random, math, sys +from collections import Counter + +# 集成学习模块 +try: + from ensemble_learning import ensemble_recommendation +except ImportError: + ensemble_recommendation = None + +BASE_DIR = os.path.dirname(os.path.abspath(__file__)) +DATA_DIR = os.path.join(BASE_DIR, "data") +os.makedirs(DATA_DIR, exist_ok=True) + +LOTTERY_RULES = { + "ssq": { + "name": "双色球", "source": "500.com", + "red_count": 6, "red_max": 33, "blue_count": 1, "blue_max": 16, + "draw_days": [2, 4, 7], "draw_time": "21:15", + "api_url": "https://datachart.500.com/ssq/history/newinc/history.php?start={start}&end={end}&limit=100", + "backup_url": "https://www.17500.cn/api/kaijiang/getlist", + "backup_type": "post" + }, + "dlt": { + "name": "大乐透", "source": "中国体育彩票", + "red_count": 5, "red_max": 35, "blue_count": 2, "blue_max": 12, + "draw_days": [1, 3, 6], "draw_time": "20:25", + "api_url": "https://webapi.sporttery.cn/gateway/lottery/getHistoryPageListV1.qry?gameNo=85&provinceId=0&pageSize=100&is498=0&pageNo=1" + } +} +WEEKDAY = {1:"周一",2:"周二",3:"周三",4:"周四",5:"周五",6:"周六",7:"周日"} + + +class Persistence: + def __init__(self): + self.config = self.load("config.json", { + "version": "1.0.0", "seed": "LOTTERY_SKILL_V1.0.0", "iteration_count": 0, + "ssq": {"latest_issue":"","latest_fetch_start":"","latest_fetch_end":""}, + "dlt": {"latest_issue":"","latest_fetch_start":"","latest_fetch_end":""} + }) + def path(self, n): return os.path.join(DATA_DIR, n) + def load(self, n, d=None): + p=self.path(n) + if os.path.exists(p): + try: return json.load(open(p,'r',encoding='utf-8')) + except: pass + return d if d is not None else {} + def save(self, n, d): + json.dump(d, open(self.path(n),'w',encoding='utf-8'), ensure_ascii=False, indent=2) + def history(self, lt): return self.load(f"{lt}_history.json", []) + def save_history(self,lt,r): self.save(f"{lt}_history.json", r) + def analysis(self,lt): return self.load(f"analysis_{lt}.json", {}) + def save_analysis(self,lt,d): self.save(f"analysis_{lt}.json", d) + def feedback(self): + return self.load("feedback.json", { + "iterations":[],"accuracy_history":[], + "ssq":{"total":0,"correct_reds":0,"correct_blues":0,"best":0, + "profitable_wins":0,"total_earnings":0.0}, + "dlt":{"total":0,"correct_reds":0,"correct_blues":0,"best":0, + "profitable_wins":0,"total_earnings":0.0} + }) + def save_feedback(self,d): self.save("feedback.json", d) + def save_config(self): self.save("config.json", self.config) + def get_seed(self,lt,date=None): + if not date: date = datetime.datetime.now().strftime("%Y-%m-%d") + # 种子基于:固定种子 + 彩种 + 日期 + 分析版本号 + # version 从 analysis_{lt}.json 读取(由 show_analysis 写入), + # FeedbackLoop.check() 回滚时不修改该字段,保证同天内结果一致。 + # 仅当数据源变化→重新分析→版本号更新时,种子才会变化。 + ver = self.analysis(lt).get("version", 0) + return hashlib.md5(f"{self.config['seed']}_{lt}_{date}_v{ver}".encode()).hexdigest() + + +class Collector: + def __init__(self,p): self.p = p + + def fetch_ssq(self, count=100): + """采集双色球数据,自动容灾切换""" + cfg = self.p.config["ssq"] + hist = self.p.history("ssq") + + # 主数据源: 500.com + records = self._fetch_ssq_500(hist, cfg, count) + + # 如果主数据源失败,切换到备用数据源 17500.cn + if records is None or len(records) == 0: + print("[采集] 500.com 无数据,切换到备用数据源 17500.cn") + records = self._fetch_ssq_17500(hist, cfg) + + return records + + def _fetch_ssq_500(self, hist, cfg, count=100): + """从500.com获取双色球数据(主数据源)- 仅获取新期数据""" + # 确定请求范围 + if hist: + latest = max(int(r["issue"]) for r in hist) + # 已采集过的不用再采集,只请求最新一期之后的数据 + # 但500.com需要至少一个范围,如果最新期号之后还没开奖, + # 缩小请求范围到 latest ~ latest(即只请求最新一期确认) + now = datetime.datetime.now() + # 估算当前应有的最新期号 + est_end = int(now.strftime("%y")) * 1000 + int(now.strftime("%j")) // 2 + if latest >= est_end - 3: + # 数据已是最新,仅请求最新一期确认是否已有新开奖 + start, end = latest, latest + 10 + else: + # 有较大差距,从最新期+1开始请求 + start, end = latest + 1, latest + min(count, est_end - latest + 10) + else: + # 首次采集,获取100期 + now = datetime.datetime.now() + end = int(now.strftime("%y")) * 1000 + int(now.strftime("%j")) // 2 + start = end - count + 1 + if end < start: end = start + count - 1 + cfg["latest_fetch_start"], cfg["latest_fetch_end"] = str(start), str(end) + self.p.save_config() + url = LOTTERY_RULES["ssq"]["api_url"].format(start=start, end=end) + print("[采集] 500.com 期号%d~%d" % (start, end)) + r = subprocess.run(["curl","-s",url,"-H","User-Agent: Mozilla/5.0"], capture_output=True, timeout=30) + if r.returncode != 0: + print("[警告] 500.com 请求失败") + return None + html = r.stdout.decode('utf-8', errors='ignore') + records = self._parse_ssq_500(html) + if not records: + print("[警告] 500.com 返回空数据") + return None + return self._merge_records("ssq", hist, records, cfg) + + def _fetch_ssq_17500(self, hist, cfg): + """从17500.cn获取双色球数据(备用数据源)""" + print("[采集] 17500.cn 最近30期") + r = subprocess.run( + ["curl","-s","-X","POST","-d","lotid=ssq", + "https://www.17500.cn/api/kaijiang/getlist", + "-H","User-Agent: Mozilla/5.0", + "-H","Referer: https://www.17500.cn/kj/list-ssq.html", + "-H","Content-Type: application/x-www-form-urlencoded"], + capture_output=True, timeout=30) + if r.returncode != 0: + print("[警告] 17500.cn 请求失败") + return None + html = r.stdout.decode('utf-8', errors='ignore') + records = self._parse_ssq_17500(html) + if not records: + print("[警告] 17500.cn 解析失败") + return None + # 更新期数范围 + if records: + issues = sorted(int(r["issue"]) for r in records) + cfg["latest_fetch_start"], cfg["latest_fetch_end"] = str(issues[0]), str(issues[-1]) + return self._merge_records("ssq", hist, records, cfg) + + def _merge_records(self, lt, hist, records, cfg): + """合并新记录到历史数据""" + exist = {r["issue"] for r in hist} + new = [r for r in records if r["issue"] not in exist] + if new: + hist.extend(new) + hist.sort(key=lambda x:int(x["issue"])) + if len(hist)>500: hist=hist[-500:] + self.p.save_history(lt, hist) + cfg["latest_issue"] = max(r["issue"] for r in hist) + self.p.save_config() + print("[采集] %s +%d条 共%d条" % (LOTTERY_RULES[lt]["name"], len(new), len(hist))) + return new if new else None + + def _parse_ssq_500(self, html): + """解析500.com的HTML表格""" + m = re.search(r'