Files
Jyotisha/scripts/event_prediction_model.py
732642856 81bf996164 fix: 8个严重bug修复 - 预测引擎从空壳恢复为全功能
修复清单:
1. jyotish_engine.py: 缺少 List 类型导入 (NameError崩溃)
2. cmd_predict: 传给EventPredictionModel的ascendant是str不是dict
3. cmd_predict: 未传入dasha/congregation/vivah_saham/chara_dasha数据
4. Dasha timeline: MD的is_current字段未设置
5. event_prediction_model: status检查不兼容中英混合格式
6. event_prediction_model: Dasha数据格式不匹配(current_dasha vs current_mahadasha)
7. event_prediction_model: Chara Dasha key名不匹配(dasha_sequence vs dasha_list)
8. cmd_predict: 序列化缺少confidence/dasha_signals/transit_signals字段

修复前: predict命令直接fallback到空壳,所有事件概率30%,0信号
修复后: marriage=43%(1静态+1Dasha), career=54%(3Dasha)
2026-05-03 20:50:05 +08:00

660 lines
28 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
事件预测模型 v5.0 — 三层验证法(全功能版)
第一层:静态星盘分析(Yoga、宫位、行星聚集、Vivah Saham、Ashtakavarga
第二层:Dasha激活(Vimshottari + Chara Dasha with Antardasha
第三层:过境触发(Double Transit PAC+D9、Transit LL/7L、行星聚集、Vivah Saham激活)
v5.0 重大升级:所有分析函数从空壳升级为真实逻辑,接入 jyotish_engine 的全部计算能力
"""
from typing import Dict, List, Optional, Tuple
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
# ============================================================================
# 数据结构
# ============================================================================
class EventType(Enum):
"""事件类型"""
MARRIAGE = "marriage"
CAREER = "career"
WEALTH = "wealth"
HEALTH = "health"
EDUCATION = "education"
CHILDREN = "children"
TRAVEL = "travel"
SPIRITUAL = "spiritual"
class RiskLevel(Enum):
"""风险等级"""
LOW = ""
MEDIUM = ""
HIGH = ""
CRITICAL = "极高"
class Confidence(Enum):
"""置信度(三级)"""
HIGH = "" # 多层验证确认
MEDIUM = "" # 两层验证
LOW = "" # 单层信号或未经验证
@dataclass
class Prediction:
"""预测结果"""
event_type: EventType
description: str
probability: float # 0-100
risk_level: RiskLevel
confidence: Confidence = Confidence.LOW
timing: Optional[str] = None
timing_windows: List[Dict] = field(default_factory=list)
key_factors: List[str] = field(default_factory=list)
recommendations: List[str] = field(default_factory=list)
dasha_signals: List[str] = field(default_factory=list)
transit_signals: List[str] = field(default_factory=list)
method: str = "三层验证法 v5.0"
# ============================================================================
# 常量
# ============================================================================
SIGNS = ['Aries', 'Taurus', 'Gemini', 'Cancer', 'Leo', 'Virgo',
'Libra', 'Scorpio', 'Sagittarius', 'Capricorn', 'Aquarius', 'Pisces']
SIGN_LORDS = {
'Aries': 'Mars', 'Taurus': 'Venus', 'Gemini': 'Mercury', 'Cancer': 'Moon',
'Leo': 'Sun', 'Virgo': 'Mercury', 'Libra': 'Venus', 'Scorpio': 'Mars',
'Sagittarius': 'Jupiter', 'Capricorn': 'Saturn', 'Aquarius': 'Saturn', 'Pisces': 'Jupiter',
}
# 事件→宫位映射
EVENT_HOUSES = {
EventType.MARRIAGE: [7, 2, 11, 5],
EventType.CAREER: [10, 6, 9, 11],
EventType.WEALTH: [2, 11, 5, 9],
EventType.HEALTH: [6, 8, 12, 1],
EventType.EDUCATION: [4, 5, 9, 2],
EventType.CHILDREN: [5, 9, 2, 11],
EventType.TRAVEL: [3, 9, 12, 7],
EventType.SPIRITUAL: [9, 12, 8, 4],
}
# 事件→ Karaka 映射
EVENT_KARAKAS = {
EventType.MARRIAGE: ['Venus', 'Jupiter', '7L'],
EventType.CAREER: ['Sun', 'Mercury', 'Saturn', '10L'],
EventType.WEALTH: ['Jupiter', 'Venus', 'Mercury', '2L'],
EventType.HEALTH: ['Saturn', 'Moon', '6L'],
EventType.EDUCATION: ['Mercury', 'Jupiter', '4L'],
EventType.CHILDREN: ['Jupiter', '5L'],
EventType.TRAVEL: ['Mercury', 'Rahu', '9L'],
EventType.SPIRITUAL: ['Jupiter', 'Saturn', 'Ketu', '9L'],
}
# Vimshottari Dasha 行星
DASHA_PLANETS = ['Ketu', 'Venus', 'Sun', 'Moon', 'Mars', 'Rahu', 'Jupiter', 'Saturn', 'Mercury']
DASHA_YEARS = {
'Ketu': 7, 'Venus': 20, 'Sun': 6, 'Moon': 10, 'Mars': 7,
'Rahu': 18, 'Jupiter': 16, 'Saturn': 19, 'Mercury': 17,
}
# Graha Drishti 相位
PLANET_ASPECTS = {
'Sun': [7], 'Moon': [7], 'Mars': [4, 7, 8], 'Mercury': [7],
'Jupiter': [5, 7, 9], 'Venus': [7], 'Saturn': [3, 7, 10],
'Rahu': [5, 7, 9], 'Ketu': [5, 7, 9],
}
# ============================================================================
# 预测引擎
# ============================================================================
class EventPredictionModel:
"""事件预测模型 v5.0 — 全功能三层验证法"""
def __init__(self, chart_data: Dict, dasha_data: Optional[Dict] = None,
transit_data: Optional[Dict] = None,
congregation_data: Optional[Dict] = None,
vivah_saham_data: Optional[Dict] = None,
chara_dasha_data: Optional[Dict] = None,
double_transit_data: Optional[Dict] = None,
ll7l_data: Optional[Dict] = None):
self.chart = chart_data
self.dasha = dasha_data or {}
self.transit = transit_data or {}
self.congregation = congregation_data or {}
self.vivah_saham = vivah_saham_data or {}
self.chara_dasha = chara_dasha_data or {}
self.double_transit = double_transit_data or {}
self.ll7l = ll7l_data or {}
self.predictions = []
# 预计算 ascendant 索引
asc_sign = self.chart.get('ascendant', {}).get('sign', 'Aries')
self.asc_idx = SIGNS.index(asc_sign) if asc_sign in SIGNS else 0
# 预计算宫主星
self.house_lords = {}
for i in range(12):
h_sign = SIGNS[(self.asc_idx + i) % 12]
self.house_lords[i + 1] = SIGN_LORDS.get(h_sign, '')
# 预计算行星宫位
self.planet_houses = {}
for pn, pd in self.chart.get('planets', {}).items():
if isinstance(pd, dict) and 'house' in pd:
self.planet_houses[pn] = pd['house']
# 预计算行星经度(度数精确)
self.planet_lons = {}
for pn, pd in self.chart.get('planets', {}).items():
if isinstance(pd, dict) and 'degree' in pd:
self.planet_lons[pn] = pd['degree']
def predict_all_events(self) -> List[Prediction]:
"""预测所有类型的事件"""
predictions = []
for evt_type in EventType:
pred = self.predict_event(evt_type)
if pred:
predictions.append(pred)
self.predictions = predictions
return predictions
def predict_event(self, evt_type: EventType) -> Optional[Prediction]:
"""预测单个事件类型(核心方法)"""
# ── 第一层:静态星盘分析 ──
static = self._layer1_static(evt_type)
# ── 第二层:Dasha激活 ──
dasha_signals = self._layer2_dasha(evt_type)
# ── 第三层:过境触发 ──
transit_signals = self._layer3_transit(evt_type)
# ── 综合概率 ──
probability = self._calc_probability(static, dasha_signals, transit_signals)
risk = self._assess_risk(static, evt_type)
# ── 置信度判定 ──
layer_count = sum([
1 if static['total_signals'] > 0 else 0,
1 if len(dasha_signals) > 0 else 0,
1 if len(transit_signals) > 0 else 0,
])
if layer_count >= 3:
confidence = Confidence.HIGH
elif layer_count >= 2:
confidence = Confidence.MEDIUM
else:
confidence = Confidence.LOW
# ── 时间窗预测 ──
timing_windows = self._calc_timing_windows(evt_type, dasha_signals, transit_signals)
# ── 关键因素汇总 ──
key_factors = []
for s in static['signals']:
key_factors.append(f"[静态] {s}")
for s in dasha_signals:
key_factors.append(f"[Dasha] {s}")
for s in transit_signals:
key_factors.append(f"[Transit] {s}")
# ── 建议 ──
recommendations = self._gen_recommendations(evt_type, static, dasha_signals, transit_signals)
# ── 时间描述 ──
timing = self._describe_timing(timing_windows, dasha_signals, transit_signals)
return Prediction(
event_type=evt_type,
description=evt_type.value,
probability=probability,
risk_level=risk,
confidence=confidence,
timing=timing,
timing_windows=timing_windows,
key_factors=key_factors,
recommendations=recommendations,
dasha_signals=dasha_signals,
transit_signals=transit_signals,
)
# ========================================================================
# 第一层:静态星盘分析
# ========================================================================
def _layer1_static(self, evt_type: EventType) -> Dict:
"""第一层:全面静态分析"""
result = {'signals': [], 'total_signals': 0, 'details': {}}
target_houses = EVENT_HOUSES.get(evt_type, [])
target_karakas = EVENT_KARAKAS.get(evt_type, [])
# 1. 宫位强旺检查(主宫+辅助宫)
for h in target_houses:
h_lord = self.house_lords.get(h, '')
if h_lord and self.planet_houses.get(h_lord) in target_houses:
result['signals'].append(f'{h}宫主{h_lord}落入相关宫位({self.planet_houses[h_lord]}宫)')
# 检查主宫有无吉星落陷/受克
for pn, ph in self.planet_houses.items():
if ph == h and pn in target_karakas:
pd = self.chart.get('planets', {}).get(pn, {})
status = pd.get('status', '')
# 支持"擢升(Exalted)"、"落陷(Debilitated)"、"入庙(Own Sign)"等中英混合格式
status_lower = status.lower() if status else ''
if 'exalted' in status_lower or '擢升' in status:
result['signals'].append(f'Karaka {pn}{h}宫擢升')
elif 'own' in status_lower or '入庙' in status:
result['signals'].append(f'Karaka {pn}{h}宫入庙')
elif 'debilitated' in status_lower or '落陷' in status:
result['signals'].append(f'Karaka {pn}{h}宫落陷(负面)')
# 2. 行星聚集分析(使用 congregation_data
if self.congregation.get('congregations'):
for c in self.congregation['congregations']:
ch = c.get('house', 0)
if ch in target_houses:
strength_label = {'strong': '吉星主导', 'mixed': '吉凶混合', 'malefic_heavy': '凶星主导'}
s = strength_label.get(c['strength'], c['strength'])
result['signals'].append(
f"行星聚集: {c['planets']}聚于{ch}宫({s})"
f"影响{','.join(c.get('impact', []))}")
# 3. Vivah Saham(婚姻事件特有)
if evt_type == EventType.MARRIAGE and self.vivah_saham:
vs = self.vivah_saham
if vs.get('saham_lon'):
sahams_house = vs.get('saham_house', 0)
if sahams_house in [7, 1, 5, 9]:
result['signals'].append(
f"Vivah Saham在{sahams_house}宫({vs['saham_sign']}) "
f"{vs['saham_deg_in_sign']:.1f}°,婚姻敏感点高度相关")
# 本命行星与 Saham 的合相
for cj in vs.get('natal_conjuncts', []):
result['signals'].append(
f"本命{cj['planet']}与Vivah Saham合相({cj['diff_deg']}°)")
# 4. Ashtakavarga 检查(如果数据可用)
av_data = self.transit.get('ashtakavarga') or self.dasha.get('ashtakavarga')
if av_data and isinstance(av_data, dict):
for h in target_houses[:2]: # 只检查前两个主宫
sav = av_data.get('bhinnashtakavarga', {}).get(str(h), {}).get('total', 0)
if sav and sav >= 28:
result['signals'].append(f'Ashtakavarga: {h}宫SAV={sav}(≥28,强)')
elif sav and sav <= 18:
result['signals'].append(f'Ashtakavarga: {h}宫SAV={sav}(≤18,弱)')
result['total_signals'] = len(result['signals'])
return result
# ========================================================================
# 第二层:Dasha 激活
# ========================================================================
def _layer2_dasha(self, evt_type: EventType) -> List[str]:
"""第二层:Dasha 激活分析(Vimshottari + Chara"""
signals = []
target_houses = EVENT_HOUSES.get(evt_type, [])
target_karakas = EVENT_KARAKAS.get(evt_type, [])
# ── 2a. Vimshottari Dasha ──
if self.dasha:
# 适配 full-reading 输出格式:current_dasha.lord + current_dasha.antardasha[]
md_lord = ''
ad_lord = ''
# 方式1current_dasha 格式(full-reading输出)
current_md = self.dasha.get('current_dasha')
if current_md and isinstance(current_md, dict):
md_lord = current_md.get('lord', '')
# 从 antardasha 列表中找 is_current=True 的
for ad in current_md.get('antardasha', []):
if ad.get('is_current'):
ad_lord = ad.get('lord', '')
break
else:
# 方式2current_mahadasha / current_antardasha 格式
md_lord = self.dasha.get('current_mahadasha', {}).get('lord', '')
ad_lord = self.dasha.get('current_antardasha', {}).get('lord', '')
if md_lord:
md_house = self.planet_houses.get(md_lord, 0)
# MD 主星是否关联目标宫位
if md_house in target_houses:
signals.append(f'当前Vimshottari MD {md_lord}{md_house}宫(目标宫位)')
# MD 主星是否就是目标 Karaka
if md_lord in [k for k in target_karakas if not k.endswith('L')]:
signals.append(f'当前MD {md_lord}{evt_type.value}的Karaka')
# MD 主星是否是目标宫的宫主星
for h in target_houses:
if self.house_lords.get(h) == md_lord:
signals.append(f'当前MD {md_lord}{h}宫主(目标宫)')
if ad_lord:
ad_house = self.planet_houses.get(ad_lord, 0)
if ad_house in target_houses:
signals.append(f'当前AD {ad_lord}{ad_house}宫(目标宫位)')
for h in target_houses:
if self.house_lords.get(h) == ad_lord:
signals.append(f'当前AD {ad_lord}{h}宫主(目标宫)')
# MD+AD 组合信号(高权重)
if md_lord and ad_lord:
md_house2 = self.planet_houses.get(md_lord, 0)
if ad_house in target_houses and md_house2 in target_houses:
signals.append(f'★ MD+AD双激活目标宫位({md_lord}+{ad_lord})')
# ── 2b. Chara Dasha (Jaimini) ──
if self.chara_dasha:
# 适配实际格式:dasha_sequence[] 或 dasha_list[]
cd_list = self.chara_dasha.get('dasha_sequence') or self.chara_dasha.get('dasha_list', [])
if cd_list:
# 当前 Chara Mahadasha(第一个条目)
current_cd = cd_list[0] if cd_list else {}
cd_sign = current_cd.get('sign', '')
cd_lord = current_cd.get('lord', '') or SIGN_LORDS.get(cd_sign, '')
cd_house = self.planet_houses.get(cd_lord, 0)
if cd_house in target_houses:
signals.append(f'当前Chara Dasha {cd_sign}({cd_lord})在{cd_house}宫(目标宫)')
# Chara Antardasha
antardashas = current_cd.get('antardashas') or current_cd.get('antardasha', [])
if antardashas:
current_ad = antardashas[0] if antardashas else {}
ad_sign = current_ad.get('sign', '')
ad_lord_name = current_ad.get('lord', '') or SIGN_LORDS.get(ad_sign, '')
ad_h = self.planet_houses.get(ad_lord_name, 0)
if ad_h in target_houses:
signals.append(f'Chara AD {ad_sign}({ad_lord_name})在{ad_h}宫(目标宫)')
return signals
# ========================================================================
# 第三层:过境触发
# ========================================================================
def _layer3_transit(self, evt_type: EventType) -> List[str]:
"""第三层:过境触发分析"""
signals = []
target_houses = EVENT_HOUSES.get(evt_type, [])
event_house = target_houses[0] if target_houses else 7 # 主宫
# ── 3a. Double Transit PAC + D9 (KN Rao) ──
if self.double_transit:
dt = self.double_transit
dt_list = dt.get('double_transit', [])
for item in dt_list:
layer = item.get('layer', '')
strength = item.get('strength', '')
# 检查是否命中目标宫
target = item.get('target', '')
# 解析 target 中的宫号
import re
nums = re.findall(r'(\d+)宫', target)
for n in nums:
if int(n) == event_house:
label = '' if strength == 'strong' else ''
signals.append(
f'{label} Double Transit PAC [{layer}层]: '
f'Jupiter+Saturn同时PAC到{event_house}宫({strength})')
# 汇总
d1_j = dt.get('d1', {}).get('jupiter', {})
d1_s = dt.get('d1', {}).get('saturn', {})
if d1_j or d1_s:
j_targets = list(d1_j.keys()) if isinstance(d1_j, dict) else []
s_targets = list(d1_s.keys()) if isinstance(d1_s, dict) else []
for t in j_targets:
nums = re.findall(r'(\d+)宫', t)
for n in nums:
if int(n) == event_house:
signals.append(f'Transit Jupiter PAC到{event_house}宫D1层')
for t in s_targets:
nums = re.findall(r'(\d+)宫', t)
for n in nums:
if int(n) == event_house:
signals.append(f'Transit Saturn PAC到{event_house}宫D1层')
# ── 3b. Transit LL/7L (婚姻特有) ──
if evt_type == EventType.MARRIAGE and self.ll7l:
ll7l = self.ll7l
# P5: Transit LL PAC natal 7L / Transit 7L PAC natal LL
p5_signals = ll7l.get('p5', [])
for s in p5_signals:
signals.append(f'Transit LL/7L P5: {s}')
# P8: Transit LL in 7H / Transit 7L in Lagna
p8_signals = ll7l.get('p8', [])
for s in p8_signals:
signals.append(f'Transit LL/7L P8: {s}')
# Parivartana
pariv = ll7l.get('parivartana', [])
if pariv:
for p in pariv:
signals.append(f'Transit LL/7L Parivartana: {p}')
# ── 3c. Vivah Saham 过境激活(婚姻特有)──
if evt_type == EventType.MARRIAGE and self.vivah_saham:
vs_activations = self.vivah_saham.get('transit_activations', [])
for act in vs_activations:
signals.append(f'Vivah Saham过境激活: {act}')
# ── 3d. 基础过境分析(从 transit_data 中提取)──
if self.transit:
# Jupiter 过境目标宫
jup_house = self.transit.get('jupiter', {}).get('house', 0)
if jup_house in target_houses:
signals.append(f'Jupiter过境{jup_house}宫(目标宫)')
# Saturn 过境目标宫
sat_house = self.transit.get('saturn', {}).get('house', 0)
if sat_house in target_houses:
signals.append(f'Saturn过境{sat_house}宫(目标宫)')
# Rahu/Ketu 过境
rahu_house = self.transit.get('rahu', {}).get('house', 0)
ketu_house = self.transit.get('ketu', {}).get('house', 0)
for rn, rh in [('Rahu', rahu_house), ('Ketu', ketu_house)]:
if rh in target_houses:
signals.append(f'{rn}过境{rh}宫(目标宫)')
# Sade SatiMoon 过境相关)
sade_sati = self.transit.get('sade_sati', {})
if sade_sati and evt_type in [EventType.CAREER, EventType.HEALTH, EventType.MARRIAGE]:
phase = sade_sati.get('phase', '')
if phase:
signals.append(f'Sade Sati {phase}期(影响情绪和决策)')
return signals
# ========================================================================
# 概率、风险、时间窗计算
# ========================================================================
def _calc_probability(self, static: Dict, dasha: List, transit: List) -> float:
"""综合概率计算"""
base = 30.0 # 基线概率
# 静态信号
static_score = min(static['total_signals'] * 5, 25)
base += static_score
# Dasha 信号
dasha_score = min(len(dasha) * 8, 30)
base += dasha_score
# MD+AD 双激活加分
if any('' in s for s in dasha):
base += 10
# Transit 信号
transit_score = min(len(transit) * 6, 25)
base += transit_score
# Double Transit strong 加分
if any('' in s for s in transit):
base += 15
return min(100, max(0, round(base, 1)))
def _assess_risk(self, static: Dict, evt_type: EventType) -> RiskLevel:
"""风险评估"""
if evt_type == EventType.HEALTH:
# 健康事件特殊处理(高信号 = 高风险)
if static['total_signals'] >= 3:
return RiskLevel.CRITICAL
elif static['total_signals'] >= 2:
return RiskLevel.HIGH
return RiskLevel.LOW
else:
if static['total_signals'] >= 4:
return RiskLevel.HIGH
elif static['total_signals'] >= 2:
return RiskLevel.MEDIUM
return RiskLevel.LOW
def _calc_timing_windows(self, evt_type: EventType, dasha: List, transit: List) -> List[Dict]:
"""计算时间窗"""
windows = []
# 从 Dasha 提取时间信息
if self.dasha:
md_info = self.dasha.get('current_mahadasha', {})
if md_info.get('start') and md_info.get('end'):
windows.append({
'type': 'Vimshottari MD',
'period': f"{md_info.get('start', '?')} ~ {md_info.get('end', '?')}",
'source': md_info.get('lord', '?'),
'weight': 0.6,
})
# 从 Chara Dasha 提取
if self.chara_dasha:
cd_list = self.chara_dasha.get('dasha_list', [])
if cd_list:
cd = cd_list[0]
start = cd.get('start_year', 0)
end = cd.get('end_year', 0)
if start:
windows.append({
'type': 'Chara Dasha',
'period': f"{start} ~ {end}" if end else f"{start}",
'source': cd.get('sign', '?'),
'weight': 0.4,
})
return windows
def _describe_timing(self, windows, dasha, transit) -> str:
"""生成时间描述"""
parts = []
if windows:
for w in windows:
parts.append(f"{w['type']}({w['period']})")
if dasha:
parts.append(f"{len(dasha)}个Dasha信号")
if transit:
parts.append(f"{len(transit)}个Transit信号")
if not parts:
return "无明确时间窗"
return " | ".join(parts)
def _gen_recommendations(self, evt_type, static, dasha, transit) -> List[str]:
"""生成建议"""
recs = []
if evt_type == EventType.MARRIAGE:
if len(dasha) >= 2 and len(transit) >= 1:
recs.append("Dasha+Transit双重激活,婚姻事件窗已开,积极把握")
elif len(dasha) >= 1:
recs.append("Dasha激活中,等待Transit触发确认")
if any('落陷' in s for s in static['signals']):
recs.append("相关Karaka落陷,建议补救措施")
elif evt_type == EventType.CAREER:
if len(dasha) >= 2 and len(transit) >= 1:
recs.append("事业突破事件窗已开,抓住机遇")
recs.append("持续关注10宫主Dasha和Jupiter/Saturn过境10宫")
elif evt_type == EventType.HEALTH:
if any('受克' in s or '落陷' in s for s in static['signals']):
recs.append("有健康警示信号,建议定期体检")
recs.append("注意Saturn过境6/8/12宫期间的身体健康")
elif evt_type == EventType.WEALTH:
if any('' in s for s in transit):
recs.append("Double Transit激活财富宫,投资/商业机会窗口")
if not recs:
recs.append("保持观察,等待多层信号确认")
return recs
# ============================================================================
# 报告生成
# ============================================================================
def generate_prediction_report(model: EventPredictionModel) -> str:
"""生成预测报告"""
if not model.predictions:
model.predict_all_events()
lines = []
lines.append("=" * 60)
lines.append("事件预测报告 — 三层验证法 v5.0")
lines.append("=" * 60)
lines.append("")
# 置信度分布
high_count = sum(1 for p in model.predictions if p.confidence == Confidence.HIGH)
med_count = sum(1 for p in model.predictions if p.confidence == Confidence.MEDIUM)
low_count = sum(1 for p in model.predictions if p.confidence == Confidence.LOW)
lines.append(f"分析范围: {len(model.predictions)} 类事件")
lines.append(f"置信度: 高={high_count}, 中={med_count}, 低={low_count}")
lines.append("")
for pred in model.predictions:
conf_icon = {'': '', '': '', '': ''}.get(pred.confidence.value, '')
lines.append(f"{conf_icon}{pred.event_type.value}】置信度:{pred.confidence.value} 概率:{pred.probability:.0f}% 风险:{pred.risk_level.value}")
if pred.timing:
lines.append(f" 时间窗: {pred.timing}")
for f in pred.key_factors[:5]: # 最多显示5个
lines.append(f" - {f}")
for r in pred.recommendations[:3]:
lines.append(f"{r}")
lines.append("")
return "\n".join(lines)
if __name__ == "__main__":
# 测试示例
test_chart = {
'ascendant': {'sign': 'Leo', 'degree': 130.5},
'planets': {
'Sun': {'house': 1, 'sign': 'Leo', 'degree': 132.0, 'status': 'own_sign'},
'Moon': {'house': 7, 'sign': 'Aquarius', 'degree': 312.0, 'status': ''},
'Mars': {'house': 4, 'sign': 'Scorpio', 'degree': 222.0, 'status': 'own_sign'},
'Mercury': {'house': 1, 'sign': 'Leo', 'degree': 145.0, 'status': ''},
'Jupiter': {'house': 3, 'sign': 'Libra', 'degree': 192.0, 'status': 'debilitated'},
'Venus': {'house': 12, 'sign': 'Cancer', 'degree': 97.0, 'status': ''},
'Saturn': {'house': 7, 'sign': 'Aquarius', 'degree': 325.0, 'status': 'own_sign'},
'Rahu': {'house': 10, 'sign': 'Taurus', 'degree': 60.0, 'status': ''},
'Ketu': {'house': 4, 'sign': 'Scorpio', 'degree': 240.0, 'status': ''},
}
}
model = EventPredictionModel(test_chart)
print(generate_prediction_report(model))