{"id":"7BMCECEe3L","url":"https://pastebin.ca/7BMCECEe3L","raw_url":"https://raw.anybin.ca/7BMCECEe3L","visibility":"public","access":"public","created_at":1789102294534,"expires_at":1789188694534,"fetch_limit":null,"fetches_used":0,"reads_remaining":null,"size_bytes":16850,"syntax_hint":"python","title":null,"filename":null,"change_note":null,"cipher":null,"cipher_meta":null,"parent_id":null,"root_id":"7BMCECEe3L","version":1,"owner_id":"06G565D7Z0FC42YKBY0XB93MTQ","recipient_id":null,"body":"        # ---- 数据完整性检查：如果排名缺失，尝试AI补全 ----\n        if home_rank is None or away_rank is None:\n            if AI_COMPLETE_MISSING_RANK:\n                print(f\"  🔄 {match.get('match_num', '')} {match.get('home_team', '')} vs {match.get('away_team', '')} 排名缺失，尝试AI补全...\")\n                \n                ai_data = ai_complete_missing_data(\n                    match,\n                    match.get('home_team', ''),\n                    match.get('away_team', ''),\n                    league\n                )\n                \n                if ai_data:\n                    home_rank = ai_data['home_rank']\n                    away_rank = ai_data['away_rank']\n                    home_strength = ai_data['home_strength']\n                    away_strength = ai_data['away_strength']\n                    ai_completed = True\n                    print(f\"  ✅ AI补全完成: 主{home_rank}位({home_strength:.1f})，客{away_rank}位({away_strength:.1f})，置信度:{ai_data.get('confidence', '未知')}\")\n                else:\n                    print(f\"  ⏭️ AI补全失败，跳过该场\")\n                    continue\n            else:\n                print(f\"  ⏭️ {match.get('match_num', '')} {match.get('home_team', '')} vs {match.get('away_team', '')} 排名缺失（AI补全已关闭），跳过\")\n                continue\n        else:\n            # 排名存在，正常计算\n            home_rank = int(home_rank)\n            away_rank = int(away_rank)\n            # 正常计算实力分\n            home_strength = calculate_strength(home_preview, True, league_params)\n            away_strength = calculate_strength(away_preview, False, league_params)\n            ai_completed = False\n\n        # 确保实力分有值\n        if home_strength is None or away_strength is None:\n            print(f\"  ⚠️ 实力分缺失，跳过\")\n            continue\n        # ---- 计算预期进球（新增这一行） ----\n        home_xg, away_xg = expected_goals(home_strength, away_strength, league_params)\n\n        result = {\n            'match_num': match['match_num'],\n            'ai_completed': ai_completed,\n            'home_team': match['home_team'],\n            'away_team': match['away_team'],\n            'league': league,\n            'strength': {'home': home_strength, 'away': away_strength},\n            'rank': {'home': home_rank, 'away': away_rank},\n            'expected_goals': {'home': round(home_xg, 2), 'away': round(away_xg, 2)},\n            'odds': {\n                'spf': match.get('spf', []),\n                'rqspf': match.get('rqspf', []),\n                'total_goals': match.get('total_goals', []),\n                'score': match.get('score', []),\n                'half_full': match.get('half_full', [])\n            },\n            'handicap': match.get('handicap', '0'),\n            'recommendations': [],\n            'trial_recommendations': [],\n            'ai_analysis': None,\n            'comparison': None\n        }\n        \n        # ---- 各玩法分析 ----\n        bias_signals = []\n        trial_signals = []\n        skip_ai = False\n        spf_result = analyze_spf(home_strength, away_strength, match.get('spf', []), league_params)\n        if spf_result:\n            if 'reverse' in spf_result:\n                rev = spf_result['reverse']\n                bias_signals.append({\n                    'play': 'SPF',\n                    'option': rev['recommend'],\n                    'bias': rev['recommend_bias'],\n                    'signal_type': '反向挖掘',\n                    'match_id': mid\n                })\n            else:\n                max_key = max(spf_result['bias'], key=lambda k: abs(spf_result['bias'][k]))\n                if abs(spf_result['bias'][max_key]) > 0.08 and spf_result['bias'][max_key] > 0:\n                    bias_signals.append({\n                        'play': 'SPF',\n                        'option': max_key,\n                        'bias': spf_result['bias'][max_key],\n                        'signal_type': '正向偏差',\n                        'match_id': mid\n                    })\n        \n        rqspf_result = analyze_rqspf(home_strength, away_strength, match.get('rqspf', []), match.get('handicap', '0'), league_params)\n        if rqspf_result:\n            if 'reverse' in rqspf_result:\n                rev = rqspf_result['reverse']\n                bias_signals.append({\n                    'play': 'RQSPF',\n                    'option': rev['recommend'],\n                    'bias': rev['recommend_bias'],\n                    'signal_type': '反向挖掘',\n                    'match_id': mid\n                })\n            else:\n                max_key = max(rqspf_result['bias'], key=lambda k: abs(rqspf_result['bias'][k]))\n                if abs(rqspf_result['bias'][max_key]) > 0.08 and rqspf_result['bias'][max_key] > 0:\n                    bias_signals.append({\n                        'play': 'RQSPF',\n                        'option': max_key,\n                        'bias': rqspf_result['bias'][max_key],\n                        'signal_type': '正向偏差',\n                        'match_id': mid\n                    })\n        \n        tg_result = analyze_total_goals(home_xg, away_xg, match.get('total_goals', []))\n        if tg_result and tg_result['max_bias'] > 0.15:\n            bias_signals.append({\n                'play': '总进球',\n                'option': TOTAL_GOALS_LABELS[tg_result['max_index']],\n                'bias': tg_result['max_bias'],\n                'signal_type': '正向偏差',\n                'match_id': mid\n            })\n\n                # ---- 平局推荐约束：只在实力接近时推荐 ----\n        strength_diff = abs(home_strength - away_strength)\n        before_count = len(bias_signals)\n        bias_signals = [\n            s for s in bias_signals \n            if not (s.get('option') in ['draw', '平局'] and strength_diff >= 20)\n        ]\n        if before_count > len(bias_signals):\n            print(f\"  ⚠️ 平局信号被过滤（实力分差{strength_diff:.1f} ≥ 20）\")\n        # ---- 试推演：半全场和比分（仅观察，不进入正式推荐） ----\n        trial_signals = []\n        \n        # 半全场试推演\n        try:\n            hf_result = analyze_half_full(home_strength, away_strength, match.get('half_full', []), league_params)\n            if hf_result and hf_result.get('max_bias') is not None:\n                if abs(hf_result['max_bias']) > 0.05:  # 试推演阈值放宽到5%\n                    trial_signals.append({\n                        'play': '半全场',\n                        'option': hf_result['recommend'],\n                        'bias': hf_result['max_bias'],\n                        'signal_type': '试推演',\n                        'match_id': mid\n                    })\n        except Exception as e:\n            print(f\"  ⚠️ 半全场试推演失败: {e}\")\n        \n        # 比分试推演\n        try:\n            score_result = analyze_score(home_xg, away_xg, match.get('score', []))\n            if score_result and score_result.get('max_bias') is not None:\n                if abs(score_result['max_bias']) > 0.03:  # 试推演阈值放宽到3%\n                    trial_signals.append({\n                        'play': '比分',\n                        'option': score_result['recommend'],\n                        'bias': score_result['max_bias'],\n                        'signal_type': '试推演',\n                        'match_id': mid\n                    })\n        except Exception as e:\n            print(f\"  ⚠️ 比分试推演失败: {e}\")\n        \n        # ---- 信心赋值（先赋值，再过滤） ----\n        for s in bias_signals:\n            s['confidence'] = '高' if abs(s['bias']) > 0.12 else '低'\n        \n        # ---- 只保留高信心信号 ----\n        bias_signals = [s for s in bias_signals if s['confidence'] == '高']\n\n        # ---- 🆕 加载历史记录并对比 ----\n        if mid:\n            history = load_history_for_match(mid)\n            if history and len(history) > 0:\n                prev_record = history[-1]\n                prev_signals = prev_record.get('recommendations', [])\n                \n                # ---- 检查当前信号与历史是否完全相同 ----\n                if bias_signals and prev_signals and signals_are_identical(bias_signals, prev_signals):\n                    # 完全一样：直接沿用历史，跳过AI分析\n                    bias_signals = prev_signals.copy()\n                    for s in bias_signals:\n                        s['match_id'] = mid\n                        if 'signal_type' not in s:\n                            s['signal_type'] = '历史沿用（无变化）'\n                    print(f\"  ⏭️ 偏差与历史完全相同，直接沿用 ({len(bias_signals)} 条)，跳过AI\")\n                    skip_ai = True\n                    result['comparison'] = {\n                        'previous_time': prev_record.get('generated_at', '未知'),\n                        'changes': None,\n                        'market_analysis': {'recommendation': '无变化，沿用历史'}\n                    }\n                else:\n                    # 有变化或新比赛：正常走对比\n                    changes = compare_odds_changes(result, prev_record)\n                    if changes:\n                        comparison_count += 1\n                        market_analysis = analyze_market_impact(changes, bias_signals)\n                        result['comparison'] = {\n                            'previous_time': prev_record.get('generated_at', '未知'),\n                            'changes': changes,\n                            'market_analysis': market_analysis\n                        }\n                    else:\n                        result['comparison'] = {\n                            'previous_time': prev_record.get('generated_at', '未知'),\n                            'changes': None,\n                            'market_analysis': {'recommendation': '无赔率变化'}\n                        }\n                \n                # ---- 当前无信号但有历史推荐，则沿用 ----\n                if not bias_signals and prev_signals:\n                    bias_signals = prev_signals.copy()\n                    for s in bias_signals:\n                        s['match_id'] = mid\n                        if 'signal_type' not in s:\n                            s['signal_type'] = '历史沿用'\n                    print(f\"  📋 沿用历史推荐 ({len(bias_signals)} 条)\")\n                    skip_ai = True\n            else:\n                # 没有历史记录\n                skip_ai = False\n        else:\n            skip_ai = False\n        \n        # ---- AI 分析（如果 skip_ai 为 True，则跳过） ----\n        if not skip_ai:\n            need_ai = any(abs(s['bias']) > 0.08 for s in bias_signals)\n            if need_ai:\n                ai_output = ai_analyze_match(match, home_strength, away_strength, home_rank, away_rank, bias_signals)\n                result['ai_analysis'] = ai_output\n                ai_usage_count += 1\n                if ai_output.get('修正建议') == '放弃':\n                    bias_signals = []\n                elif ai_output.get('修正建议') == '降低信心':\n                    for s in bias_signals:\n                        s['confidence'] = '低'\n        else:\n            # 直接沿用历史，无需AI分析\n            pass\n\n        result['recommendations'] = bias_signals\n        result['trial_recommendations'] = trial_signals\n        \n        # ---- 保存历史记录 ----\n        if mid:\n            history_record = {\n                'generated_at': datetime.now().isoformat(),\n                'match_num': match['match_num'],\n                'odds': result['odds'],\n                'handicap': result['handicap'],\n                'strength': result['strength'],\n                'recommendations': result['recommendations']\n            }\n            save_history_for_match(mid, history_record)\n        # ---- 生成详细推演分析 ----\n        detailed = generate_detailed_analysis(match, bias_signals, result.get('comparison'))\n        result['detailed_analysis'] = detailed\n        \n        match_results.append(result)\n        \n        # ---- 终端输出 ----\n        print(f\"【{result['match_num']}】 {result['home_team']} vs {result['away_team']} ({result['league']})\")\n        print(f\"  实力: {home_strength} - {away_strength} (排名:{home_rank} vs {away_rank})\")\n        print(f\"  预期进球: {home_xg:.2f} - {away_xg:.2f}\")\n        \n        if result.get('comparison'):\n            changes = result['comparison'].get('changes')\n            if changes and isinstance(changes, dict) and len(changes) > 0:\n                print(f\"  📊 赔率变化: 与 {result['comparison']['previous_time'][:16]} 对比\")\n                for key, change in changes.items():\n                    if isinstance(change, dict) and 'direction' in change:\n                        print(f\"    {key}: {change['from']} → {change['to']} ({change['direction']})\")\n            else:\n                print(f\"  📊 赔率变化: 无变化（与 {result['comparison']['previous_time'][:16]} 对比）\")\n            \n            if result['comparison'].get('market_analysis'):\n                ma = result['comparison']['market_analysis']\n                print(f\"  📈 市场分析: {ma.get('recommendation', '无变化')}\")\n                for s in ma.get('summary', [])[:3]:\n                    print(f\"    {s}\")\n\n        if bias_signals:\n            for s in bias_signals:\n                print(f\"  → {s['play']}: {s['option']} (偏差{s['bias']:+.0%}, 信心:{s.get('confidence', '中')})\")\n            print(\"  \" + \"-\"*40)\n            for line in detailed.split('\\n'):\n                if line.strip():\n                    print(f\"  {line}\")\n            print(\"  \" + \"-\"*40)\n\n        # ---- 试推演输出（仅观察） ----\n        if trial_signals:\n            print(f\"  📝 试推演（仅供观察，不投注）：\")\n            for t in trial_signals:\n                print(f\"    {t['play']}: {t['option']} (偏差{t['bias']:+.0%})\")    \n\n        if result.get('ai_analysis'):\n            print(f\"  💬 {result['ai_analysis'].get('说明', '')[:60]}\")\n        print()\n        time.sleep(0.1)\n    \n    if new_leagues:\n        print(f\"📌 发现 {len(new_leagues)} 个新联赛，已自动生成参数\")\n    \n    # ---- 保存报告（带时间戳，不覆盖） ----\n    timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')\n    \n    report = {\n        'generated_at': datetime.now().isoformat(),\n        'total_matches': len(matches),\n        'ai_analyzed': ai_usage_count,\n        'compared_matches': comparison_count,\n        'new_leagues': new_leagues,\n        'matches': match_results\n    }\n    \n    json_file = os.path.join(DAILY_DIR, f\"report_{timestamp}.json\")\n    with open(json_file, 'w', encoding='utf-8') as f:\n        json.dump(report, f, ensure_ascii=False, indent=2)\n    print(f\"✅ 完整报告已保存: {json_file}\")\n    \n    # ---- 文本摘要 ----\n    txt_file = os.path.join(SUMMARY_DIR, f\"summary_{timestamp}.txt\")\n    with open(txt_file, 'w', encoding='utf-8') as f:\n        f.write(f\"足球预测推演报告 (动态追踪版) - {datetime.now().strftime('%Y-%m-%d %H:%M')}\\n\")\n        f.write(\"=\" * 60 + \"\\n\\n\")\n        f.write(f\"总场次: {len(matches)}, AI分析: {ai_usage_count}, 赔率对比: {comparison_count}\\n\\n\")\n        for r in match_results:\n            f.write(f\"【{r['match_num']}】 {r['home_team']} vs {r['away_team']} ({r['league']})\\n\")\n            if r.get('comparison'):\n                ma = r['comparison'].get('market_analysis', {})\n                f.write(f\"  市场分析: {ma.get('recommendation', '无变化')}\\n\")\n            if r['recommendations']:\n                for s in r['recommendations']:\n                    f.write(f\"  → {s['play']}: {s['option']} (偏差{s['bias']:+.0%})\\n\")\n            if r.get('trial_recommendations'):\n                f.write(\"  📝 试推演（仅观察）：\\n\")\n                for t in r['trial_recommendations']:\n                    f.write(f\"    {t['play']}: {t['option']} (偏差{t['bias']:+.0%})\\n\")\n            else:\n                f.write(f\"  → 无推荐\\n\")\n            if r.get('ai_analysis'):\n                f.write(f\"  💬 {r['ai_analysis'].get('说明', '')}\\n\")\n            # ✅ 新增：写入详细推演分析\n            if r.get('detailed_analysis'):\n                f.write(r['detailed_analysis'] + \"\\n\")\n            f.write(\"\\n\")\n    print(f\"✅ 文本摘要已保存: {txt_file}\")\n    print(f\"📊 共 {len(matches)} 场比赛，AI分析 {ai_usage_count} 场，赔率对比 {comparison_count} 场\")\n\nif __name__ == '__main__':\n    main()"}