oilverse-api / run.py
ๅญ™ๅฎถๆ˜Ž
deploy: OilVerse for HuggingFace (Node.js 18 fix)
fab9847
Raw
History Blame Contribute Delete
9.8 kB
"""
run.py โ€” ไธปๅ…ฅๅฃ๏ผšๆ•ฐๆฎๆ›ดๆ–ฐ โ†’ ๅผ•ๆ“Ž โ†’ ๅˆ†ๆž โ†’ ๅฏนๅ†ฒ โ†’ ๅ› ๆžœ โ†’ Dashboard
===================================================================
็”จๆณ•๏ผš
python run.py # ๅฎŒๆ•ดๆต็จ‹๏ผˆๅซๆ•ฐๆฎๆ›ดๆ–ฐ+ๆ–ฐ้—ป๏ผ‰
python run.py --skip-update # ่ทณ่ฟ‡ๆ•ฐๆฎๆ›ดๆ–ฐ๏ผŒ็›ดๆŽฅ็”จ็Žฐๆœ‰ๆ•ฐๆฎ
"""
import os, json, webbrowser, argparse
import pandas as pd
from config import BASE_DIR, OUTPUT_DIR, OUTPUT_FILES, INDUSTRIES, PRICE_COLS
from core.engine import load_panel, run_walk_forward
from core.analysis import apply_industry_rules, generate_all_reports, evaluate_results, run_ablation
from core.hedging import compute_all_industry_hedges, backtest_hedging
from core.feature_selection import run_feature_funnel
os.chdir(BASE_DIR)
os.makedirs(OUTPUT_DIR, exist_ok=True)
def run_benchmark(panel_path, benchmark, price_col, features_override=None):
"""ๅฏนๅ•ไธชๆฒนไปทๅŸบๅ‡†่ฟ่กŒๅฎŒๆ•ด walk-forward ๆต็จ‹ใ€‚"""
print(f"\n{'โ”€'*65}")
print(f" Benchmark: {benchmark} ({price_col})")
print(f"{'โ”€'*65}")
# Load panel
panel, features = load_panel(panel_path, price_col=price_col)
# Walk-forward
results, shap_data = run_walk_forward(panel, features)
print(f" ๆต‹่ฏ•ๆœˆๆ•ฐ: {len(results)}")
# Industry rules
for i, row in results.iterrows():
rules = apply_industry_rules(row)
for k, v in rules.items():
results.at[i, k] = v
# NLG reports
reports = generate_all_reports(results)
# Tag benchmark
results['benchmark'] = benchmark
return results, shap_data, reports, panel, features
def main(skip_update=False):
# โ•โ•โ•โ• Step 0: Data Update โ•โ•โ•โ•
if not skip_update:
print("โ•" * 65)
print("Step 0: ๅ…จ็‰นๅพ API ๆ•ฐๆฎๆ›ดๆ–ฐ")
print("โ•" * 65)
try:
from pipeline.live_data import main as live_main
live_main()
panel_path = 'output/panel_monthly_live.csv'
if os.path.exists(panel_path):
print(f"โœ“ ไฝฟ็”จๆ›ดๆ–ฐๅŽ็š„้ขๆฟ: {panel_path}")
else:
panel_path = 'output/panel_monthly.csv'
except Exception as e:
print(f"โš  ๆ•ฐๆฎๆ›ดๆ–ฐ่ทณ่ฟ‡: {e}")
panel_path = 'output/panel_monthly.csv'
else:
print("่ทณ่ฟ‡ๆ•ฐๆฎๆ›ดๆ–ฐ")
if os.path.exists('output/panel_monthly_live.csv'):
panel_path = 'output/panel_monthly_live.csv'
else:
panel_path = 'output/panel_monthly.csv'
# โ•โ•โ•โ• Step 1: Feature Selection Funnel โ•โ•โ•โ•
print("\n" + "โ•" * 65)
print("Step 1: ็‰นๅพ็ญ›้€‰ๆผๆ–— (329โ†’17)")
print("โ•" * 65)
funnel = run_feature_funnel('output/panel_monthly.csv')
with open(OUTPUT_FILES['feat_sel'], 'w', encoding='utf-8') as f:
json.dump(funnel, f, ensure_ascii=False, indent=2)
print(f"โœ“ ็‰นๅพ็ญ›้€‰: {OUTPUT_FILES['feat_sel']}")
# โ•โ•โ•โ• Step 2: Walk-Forward for EACH benchmark โ•โ•โ•โ•
print("\n" + "โ•" * 65)
print("Step 2: Walk-Forward ้ข„ๆต‹ (WTI + Brent)")
print("โ•" * 65)
all_results = {}
all_shap = {}
all_reports = {}
all_panels = {}
all_features = {}
for benchmark, price_col in PRICE_COLS.items():
results, shap_data, reports, panel, features = run_benchmark(
panel_path, benchmark, price_col)
all_results[benchmark] = results
all_shap[benchmark] = shap_data
all_reports[benchmark] = reports
all_panels[benchmark] = panel
all_features[benchmark] = features
# Use WTI as primary for hedging/evaluation (backward compat)
primary = 'WTI'
results = all_results[primary]
# โ•โ•โ•โ• Step 3: Hedging (based on WTI) โ•โ•โ•โ•
print("\n" + "โ•" * 65)
print("Step 3: ๅฏนๅ†ฒๅ†ณ็ญ–่ฎก็ฎ—")
print("โ•" * 65)
latest = results.iloc[-1]
hedging_data = compute_all_industry_hedges(latest)
hedging_json = {}
for ind, hd in hedging_data.items():
hedging_json[ind] = {
'industry_zh': hd['industry_zh'],
'exposure': hd['exposure'],
'elasticity': hd['elasticity'],
'recommended_ratio': hd['recommended_ratio'],
'recommended_ratio_pct': hd['recommended_ratio_pct'],
'recommended_tool': hd['recommended_tool'],
'rationale': hd['rationale'],
'matrix': hd['matrix'],
'tool_comparison': hd['tool_comparison'],
}
print(f" {hd['industry_zh']}: ๆŽจ่ๅฏนๅ†ฒ {hd['recommended_ratio_pct']}, "
f"ๅทฅๅ…ท={hd['recommended_tool']}")
with open(OUTPUT_FILES['hedging'], 'w', encoding='utf-8') as f:
json.dump(hedging_json, f, ensure_ascii=False, indent=2)
print(f"โœ“ Hedging: {OUTPUT_FILES['hedging']}")
# Hedge Backtest
print(" [ๅ›žๆต‹ๅฏนๅ†ฒ็ญ–็•ฅ โ€” ่ฟ‡ๅŽป60ๆœˆ]")
backtest_data = backtest_hedging(results)
backtest_json = {}
for ind, bt in backtest_data.items():
backtest_json[ind] = bt
print(f" {bt['industry_zh']}: ็ดฏ่ฎก่Š‚็œ${bt['total_saving']:.1f}M, "
f"ๆณขๅŠจ็އ้™ไฝŽ{bt['vol_reduction']:.0f}%, "
f"ๆœ€ๅคงๅ›žๆ’คๆ”นๅ–„${bt['dd_improvement']:.1f}M")
with open(OUTPUT_FILES['backtest'], 'w', encoding='utf-8') as f:
json.dump(backtest_json, f, ensure_ascii=False, indent=2)
print(f"โœ“ Backtest: {OUTPUT_FILES['backtest']}")
# โ•โ•โ•โ• Step 4: NLG Reports โ•โ•โ•โ•
print("\n" + "โ•" * 65)
print("Step 4: NLG ๆŠฅๅ‘Š็”Ÿๆˆ")
print("โ•" * 65)
for bm, reports in all_reports.items():
print(f" {bm}: {len(reports)} ไปฝๆŠฅๅ‘Š")
# โ•โ•โ•โ• Step 5: Evaluation โ•โ•โ•โ•
for bm, res in all_results.items():
print(f"\n--- Evaluation: {bm} ---")
evaluate_results(res)
# โ•โ•โ•โ• Step 6: Save โ•โ•โ•โ•
print("\n" + "โ•" * 65)
print("Step 6: ไฟๅญ˜็ป“ๆžœ")
print("โ•" * 65)
# Save per-benchmark results
for bm, res in all_results.items():
out_path = os.path.join(OUTPUT_DIR, f'v2_results_{bm}.csv')
res.to_csv(out_path, index=False)
print(f"โœ“ ็ป“ๆžœ [{bm}]: {out_path}")
# Also save primary as the main results (backward compat)
results.to_csv(OUTPUT_FILES['results'], index=False)
print(f"โœ“ ็ป“ๆžœ [primary]: {OUTPUT_FILES['results']}")
# SHAP (primary)
with open(OUTPUT_FILES['shap'], 'w', encoding='utf-8') as f:
json.dump(all_shap[primary][-12:], f, ensure_ascii=False, indent=2)
print(f"โœ“ SHAP: {OUTPUT_FILES['shap']}")
# NLG (merge all benchmarks)
merged_reports = {}
for bm, reps in all_reports.items():
for dt, report in reps.items():
key = f"{dt}_{bm}" if bm != primary else dt
merged_reports[key] = report
# Also save per-benchmark
with open(os.path.join(OUTPUT_DIR, f'v2_nlg_{bm}.json'), 'w', encoding='utf-8') as f:
json.dump(reps, f, ensure_ascii=False, indent=2)
with open(OUTPUT_FILES['nlg'], 'w', encoding='utf-8') as f:
json.dump(merged_reports, f, ensure_ascii=False, indent=2)
print(f"โœ“ NLG: {OUTPUT_FILES['nlg']}")
# Scenarios (primary)
scenario_data = {}
for _, row in results.tail(12).iterrows():
dt = pd.Timestamp(row['test_date']).strftime('%Y-%m')
scenario_data[dt] = {
'base': round(row['scenario_base'] * 100, 2),
'vix_shock': round(row['scenario_vix_shock'] * 100, 2),
'supply_cut': round(row['scenario_supply_cut'] * 100, 2),
'demand_crash': round(row['scenario_demand_crash'] * 100, 2),
}
with open(OUTPUT_FILES['scenarios'], 'w', encoding='utf-8') as f:
json.dump(scenario_data, f, indent=2)
print(f"โœ“ Scenarios: {OUTPUT_FILES['scenarios']}")
# Regime (primary)
regime_data = {}
for _, row in results.iterrows():
dt = pd.Timestamp(row['test_date']).strftime('%Y-%m')
regime_data[dt] = {
'match': row.get('regime_match', 'Unknown'),
'similarity': row.get('regime_similarity', 0),
'type': row.get('regime_type', 'normal'),
}
with open(OUTPUT_FILES['regime'], 'w', encoding='utf-8') as f:
json.dump(regime_data, f, ensure_ascii=False, indent=2)
print(f"โœ“ Regime: {OUTPUT_FILES['regime']}")
# โ•โ•โ•โ• Step 7: Ablation (primary only) โ•โ•โ•โ•
print("\n" + "โ•" * 65)
print("Step 7: ๆถˆ่žๅฎž้ชŒ")
print("โ•" * 65)
ablation_results = run_ablation(all_panels[primary], all_features[primary])
with open(OUTPUT_FILES['ablation'], 'w') as f:
json.dump(ablation_results, f, indent=2)
print(f"โœ“ Ablation: {OUTPUT_FILES['ablation']}")
# โ•โ•โ•โ• Step 7b: Causal Analysis โ•โ•โ•โ•
print("\n" + "โ•" * 65)
print("Step 7b: ๅ› ๆžœๅ› ๅญ็ฝ‘็ปœๅˆ†ๆž")
print("โ•" * 65)
try:
from pipeline.causal_analysis import run_full_causal_analysis
causal_result = run_full_causal_analysis(panel_path)
print(f"โœ“ ๅ› ๆžœๅˆ†ๆž: {OUTPUT_FILES.get('causal', 'output/causal_analysis.json')}")
except Exception as e:
print(f"โš  ๅ› ๆžœๅˆ†ๆž่ทณ่ฟ‡: {e}")
# โ•โ•โ•โ• Step 8: Done โ•โ•โ•โ•
print("\n" + "โ•" * 65)
print("โœ… ๅ…จ้ƒจๅฎŒๆˆ!")
print("โ•" * 65)
print(" ๅฏๅŠจๅ‰็ซฏ: cd frontend && npm run dev")
print(" ๅฏๅŠจAPI: python api_server.py")
if __name__ == '__main__':
parser = argparse.ArgumentParser(description='ๆฒนไปท้ฃŽ้™ฉๅˆ†ๆžๅนณๅฐ โ€” ไธ€้”ฎๅฏๅŠจ')
parser.add_argument('--skip-update', action='store_true',
help='่ทณ่ฟ‡ FRED/EIA ๆ•ฐๆฎๆ›ดๆ–ฐ๏ผŒ็›ดๆŽฅไฝฟ็”จ็Žฐๆœ‰ๆ•ฐๆฎ')
args = parser.parse_args()
main(skip_update=args.skip_update)