Spaces:
Build error
Build error
| """ | |
| live_data.py โ ๅ จ็นๅพ API ๅฎๆถๆดๆฐ๏ผไธไพ่ตๆฌๅฐ CSV๏ผ | |
| ===================================================== | |
| ๆฐๆฎๆบ: | |
| 1. FRED API โ ็พๅฝๅฎ่งใไปทๆ ผใๅฉ็ใISM ๅญๆๆฐ (~90) | |
| 2. Yahoo Finance โ ๅๅไปทๆ ผใๆฑ็ใๆณขๅจ็ๆๆฐ (~25) | |
| 3. World Bank API โ ๅ จ็ๅคงๅฎๅๅไปทๆ ผ (~40) | |
| 4. akshare โ ไธญๅฝๅฎ่ง M1/M2/PMI (~9) | |
| 5. CFTC โ ๆไปๆฅๅ๏ผ่ชๅจไธ่ฝฝ CSV๏ผ(~30) | |
| 6. EIA API โ ็พๅฝ่ฝๆบๅบๅญ/ไบง้ (~8) | |
| 7. GPR Index โ ๅฐ็ผๆฟๆฒป้ฃ้ฉๆๆฐ (Caldara & Iacoviello) | |
| 8. ๆดพ็็นๅพ โ ไปๅๅง็นๅพ่ชๅจ่ฎก็ฎ (~55) | |
| ็จๆณ: | |
| python live_data.py # ๅ จ้ๆดๆฐ | |
| python live_data.py --test # ๆต่ฏๅ API ่ฟ้ๆง | |
| python live_data.py --source fred # ๅชๆดๆฐๆไธชๆฐๆฎๆบ | |
| """ | |
| import pandas as pd | |
| import numpy as np | |
| import os, json, warnings, argparse, time, requests | |
| from datetime import datetime, timedelta | |
| warnings.filterwarnings('ignore') | |
| os.chdir(r'e:\ๅคงไธไธ\ๆฏ่ต\่ฑๆๆฏ\ๆจกๅ') | |
| FRED_KEY = 'fc02a6e6a359a4cc16f0f1752d258011' | |
| EIA_KEY = '9Nv5PhLREMmmKeo0zJ2U3Zu21Bntf8DfhEKBpi55' | |
| OUTPUT_DIR = 'output' | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # 1. FRED API โ ็พๅฝๅฎ่ง + ไปทๆ ผ + ๅฉ็ + ISM | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| FRED_MAP = { | |
| # โโ ไปทๆ ผ โโ | |
| 'WTI_spot': 'DCOILWTICO', | |
| 'Brent_spot': 'DCOILBRENTEU', | |
| 'natgas_spot_henry': 'DHHNGSP', | |
| 'gold_spot': 'GOLDAMGBD228NLBM', | |
| # โโ ้ๆฑ/ๅฎ่ง โโ | |
| 'pmi_us_mfg': 'MANEMP', | |
| 'ipi_us': 'INDPRO', | |
| 'nonfarm_us': 'PAYEMS', | |
| 'usd_index': 'DTWEXBGS', | |
| 'cpi_us': 'CPIAUCSL', | |
| 'fed_funds_rate': 'FEDFUNDS', | |
| 'yield_spread_10y2y': 'T10Y2Y', | |
| 'us_10y_yield': 'DGS10', | |
| # โโ ้ฃ้ฉ โโ | |
| 'vix': 'VIXCLS', | |
| # โโ ไพ็ป โโ | |
| 'us_oil_inventory_total': 'WCESTUS1', | |
| 'us_crude_production': 'MCRFPUS2', | |
| # โโ ๅฉ็/ๆถ็็ โโ | |
| 'libor_usd_3m': 'USD3MTD156N', | |
| '็พๅฝ:ๅฝๅบๆถ็็:3ๅนด': 'DGS3', | |
| '็พๅฝ:ๅฝๅบๆถ็็:7ๅนด': 'DGS7', | |
| '็พๅฝ:ๅฝๅบๆถ็็:10ๅนด': 'DGS10', | |
| '็พๅฝ:ๅฝๅบๆถ็็:30ๅนด': 'DGS30', | |
| '็พๅฝ:ๅฝๅบๅฐๆๆถ็็(ไปฅ้่ไธบๆ ็):7ๅนด': 'DFII7', | |
| '็พๅฝ:ๅฝๅบๆถ็็ๅฉๅทฎ:10ๅนด-2ๅนด': 'T10Y2Y', | |
| # โโ ่ดงๅธ โโ | |
| '็พๅฝ:M1:้ๅญฃ่ฐ': 'M1NS', | |
| 'us_m2_yoy': 'WM2NS', | |
| '็พๅฝ:่ดงๅธไนๆฐ:M1:้ๅญฃ่ฐ': 'MULT', | |
| '็พๅฝ:่ดงๅธไนๆฐ:M2:้ๅญฃ่ฐ': 'M2REAL', | |
| # โโ ISM ๅถ้ ไธ PMI ๅญๆๆฐ๏ผ40ไธช๏ผโโ | |
| '็พๅฝ:ISM:ๅถ้ ไธPMI': 'NAPM', | |
| '็พๅฝ:ISM:ๅถ้ ไธPMI:ๆฐ่ฎขๅ': 'NAPMNOI', | |
| '็พๅฝ:ISM:ๅถ้ ไธPMI:่ชๆๅบๅญ': 'NAPMII', | |
| '็พๅฝ:ISM:ๅถ้ ไธPMI:ๅฎขๆทๅบๅญ': 'NAPMCI', | |
| '็พๅฝ:ISM:ๅถ้ ไธPMI:ๅฐฑไธ': 'NAPMEI', | |
| '็พๅฝ:ISM:ๅถ้ ไธPMI:็ฉไปท': 'NAPMPRI', | |
| '็พๅฝ:ISM:ๅถ้ ไธPMI:ไบงๅบ': 'NAPMPI', | |
| '็พๅฝ:ISM:ๅถ้ ไธPMI:ไพๅบๅไบคไป': 'NAPMSDI', | |
| '็พๅฝ:ISM:ๅถ้ ไธPMI:ๆฐๅบๅฃ่ฎขๅ': 'NAPMEX', | |
| '็พๅฝ:ISM:ๅถ้ ไธPMI:่ฟๅฃ': 'NAPMIMP', | |
| '็พๅฝ:ISM:ๆๅกไธPMI': 'NMFBAI', | |
| '็พๅฝ:ISM:ๆๅกไธPMI:ๅฐฑไธ': 'NMFEI', | |
| '็พๅฝ:ISM:ๆๅกไธPMI:ๆฐ่ฎขๅ': 'NMFNOI', | |
| '็พๅฝ:ISM:ๆๅกไธPMI:็ฉไปท': 'NMFPI', | |
| '็พๅฝ:ISM:ๆๅกไธPMI:ๅไธๆดปๅจ': 'NMFBAI', | |
| '็พๅฝ:ISM:ๆๅกไธPMI:ไพๅบๅไบคไป': 'NMFSDI', | |
| '็พๅฝ:ISM:ๆๅกไธPMI:ๅบๅญ': 'NMFII', | |
| '็พๅฝ:ISM:ๆๅกไธPMI:่ฎขๅๅบๅญ': 'NMFOBI', | |
| '็พๅฝ:ISM:ๆๅกไธPMI:ๆฐๅบๅฃ่ฎขๅ': 'NMFEXI', | |
| '็พๅฝ:ISM:ๆๅกไธPMI:่ฟๅฃ': 'NMFIMI', | |
| 'pmi_us_svc': 'NMFBAI', | |
| 'employment_us_ๅญฃ่ฐ': 'PAYEMS', | |
| # โโ GDP โโ | |
| 'gdp_us_yoy': 'A191RL1Q225SBEA', | |
| 'gdp_japan_yoy': 'JPNRGDPEXP', | |
| 'gdp_germany_yoy': 'CLVMNACSCAB1GQDE', | |
| 'gdp_uk_yoy': 'CLVMNACSCAB1GQUK', | |
| 'gdp_australia_yoy': 'NAEXKP01AUQ189S', | |
| # โโ PPI/CPI โโ | |
| 'ppi_us_yoy': 'PPIACO', | |
| 'ppi_us_ๅๆฏ': 'PPIACO', | |
| 'cpi_japan': 'JPNCPIALLMINMEI', | |
| 'ppi_japan': 'JPNPROINDMISMEI', | |
| 'ppi_eurozone': 'EA19PRMNIG01GYSAM', | |
| 'ipi_europe': 'EA19PRINTO01GYSAM', | |
| 'ppi_russia': 'RUSCPIALLMINMEI', | |
| # โโ ๆฌงๆดฒ/ๆฅๆฌๅฎ่ง โโ | |
| 'pmi_eu_mfg': 'BSCICP03EZM460S', | |
| 'ez_econ_sentiment': 'CSCICP03EZM460S', | |
| 'ez_consumer_conf': 'CSCICP03EZM460S', | |
| 'pmi_japan': 'BSCICP03JPM460S', | |
| 'pmi_canada': 'BSCICP03CAM460S', | |
| # โโ OFR โโ | |
| '็พๅฝ:OFR้่ๅๅๆๆฐ': 'STLFSI2', | |
| # โโ ็พ่ๅจ่ตไบง่ดๅบ่กจ (FREDๆๆฑๆป) โโ | |
| 'fed_balance_sheet_total': 'WALCL', | |
| # โโ ๅฝ้ ๅฉ็ (LIBORโSOFRๆฟไปฃ) โโ | |
| 'libor_3m_่ฑ้LIBOR3M': 'GBP3MTD156N', | |
| 'libor_3m_ๆฅๅ LIBOR3M': 'JPY3MTD156N', | |
| 'eurlibor_3m': 'EUR3MTD156N', | |
| # โโ ็พๅ ๆๆฐ(ๆฐๅ ดๅธๅบ) โโ | |
| 'usd_index_em': 'DTWEXEMEGS', | |
| # โโ ็พๅฝ็ณๆฒนๆถ่ดน/้ๆฑ (FRED้ๅEIA) โโ | |
| 'us_oil_consumption': 'MTTIMUS2', | |
| 'demand_us': 'MTTIMUS2', | |
| # โโ ็พๅฝ่ดธๆ โโ | |
| 'us_crude_export_yoy': 'MCREXUS2', | |
| 'us_crude_import_yoy': 'MCRIMUS2', | |
| # โโ ็พ่ๅจ่ตไบง่ดๅบ่กจๆ็ป (ๅทฒ้ช่ฏๅฏ็จ) โโ | |
| 'fed_balance_sheet_ๅญๆฌพๆบๆๅจๅค(ไธๅ ๆฌFHLBๅญๆฌพ)': 'WRESBAL', | |
| 'fed_balance_sheet_่ฏๅธๅ่ดญๅ่ฎฎ(ๅ ๆฌๅฎๆนๅจๅคไธญ็ๅ ถไปๅบๆ)': 'WLRRAL', | |
| 'fed_balance_sheet_ๅฏน่้ฆๆฟๅบ็ๅฏๆฏ็ฅจๅญๆฌพ': 'WDFOL', | |
| 'fed_balance_sheet_่้ฆๅจๅค้ถ่ก่ก็ฅจ': 'TREAST', | |
| 'fed_balance_sheet_่้ฆไฝๆฟ่ดทๆฌพ้ถ่ก(FHLB)ๅๆฌพ': 'WSHOMCB', | |
| # โโ ๅจๅค/้ๆฑ โโ | |
| 'reserve_us': 'TOTRESNS', | |
| # โโ HIBOR โโ | |
| 'hibor_3m': 'HKONTD156N', | |
| # โโ ไธญๅฝPMI (FRED OECD proxy) โโ | |
| 'pmi_china': 'CHNPMINDMISMEI', | |
| } | |
| def fetch_fred_bulk(start='2000-01-01'): | |
| """ๆน้ไป FRED ๆๅๆๆๆ ๅฐ็ๆๆ ใ""" | |
| from fredapi import Fred | |
| fred = Fred(api_key=FRED_KEY) | |
| end = datetime.now().strftime('%Y-%m-%d') | |
| results = {} | |
| total = len(FRED_MAP) | |
| print(f"\n[FRED] ๆๅ {total} ไธชๆๆ ...") | |
| for i, (name, sid) in enumerate(FRED_MAP.items()): | |
| try: | |
| data = fred.get_series(sid, observation_start=start, observation_end=end) | |
| if data is not None and len(data) > 0: | |
| monthly = data.resample('ME').last().dropna() | |
| results[name] = monthly | |
| print(f" [{i+1}/{total}] โ {name:<35} {len(monthly)} ๆ") | |
| else: | |
| print(f" [{i+1}/{total}] โ {name:<35} ๆ ๆฐๆฎ") | |
| except Exception as e: | |
| print(f" [{i+1}/{total}] โ {name:<35} {str(e)[:40]}") | |
| df = pd.DataFrame(results) if results else pd.DataFrame() | |
| print(f" FRED ๅฎๆ: {len(results)}/{total}") | |
| return df | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # 2. YAHOO FINANCE โ ๅๅ/ๆฑ็/ๆณขๅจ็ | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| YFINANCE_MAP = { | |
| # === ๆฑ็ === | |
| 'usd_cny': 'CNY=X', | |
| 'usd_jpy': 'JPY=X', | |
| # === ๆณขๅจ็ === | |
| 'vix_nasdaq': '^VXN', | |
| 'ovx_crude_vol': '^OVX', | |
| '็บณๆฏ่พพๅ 100ๆณขๅจ็ๆๆฐ': '^VXN', | |
| # === ่ดต้ๅฑ === | |
| 'iron_ore_spot': 'TIOC.SI', | |
| 'nickel_price': '^SPGSNKP', | |
| '็ฐ่ดงไปท(ไผฆๆฆๅธๅบ):้ป้:็พๅ ': 'GC=F', | |
| '็ฐ่ดงไปท(ไผฆๆฆๅธๅบ):็ฝ้ถ:็พๅ ': 'SI=F', | |
| 'COMEXๅพฎๅ้ป้': 'MGC=F', | |
| # === ๅคงๅฎๅๅ๏ผๆฟไปฃ World Bank๏ผ=== | |
| 'ๅ จ็:ๅฎ้ ๅธๅบไปทๆ ผ:้': 'HG=F', | |
| 'ๅ จ็:ๅฎ้ ๅธๅบไปทๆ ผ:้': 'ALI=F', | |
| 'ๅ จ็:ๅฎ้ ๅธๅบไปทๆ ผ:้': 'ZINC.L', | |
| 'ๅ จ็:ๅฎ้ ๅธๅบไปทๆ ผ:็็ฑณ': 'ZC=F', | |
| 'ๅ จ็:ๅฎ้ ๅธๅบไปทๆ ผ:ๅคง็ฑณ': 'ZR=F', | |
| 'ๅ จ็:ๅฎ้ ๅธๅบไปทๆ ผ:ๆฃ่ฑ': 'CT=F', | |
| 'ๅ จ็:ๅฎ้ ๅธๅบไปทๆ ผ:่ฑ็ฒ': 'ZM=F', | |
| 'ๅ จ็:ๅฎ้ ๅธๅบไปทๆ ผ:่ฑๆฒน': 'ZL=F', | |
| 'ๅ จ็:ๅฎ้ ๅธๅบไปทๆ ผ:็ณ(CESE็ฌฌ11ๅทๅๅๆ่ฟ็ๆชๆฅๅคดๅฏธ)': 'SB=F', | |
| 'ๅ จ็:ๅฎ้ ๅธๅบไปทๆ ผ:ๅคฉ็ถๆฐ(ๅฐๅฐผๅบๅฃๆฅๆฌ)': 'TTF=F', | |
| 'ๅ จ็:ๅๅไปทๆ ผ:ๅๆฒน:ไธๅคงๅธๅบๅนณๅๅผ': 'CL=F', | |
| # === ๅไบงๅ === | |
| 'agri_prices_ๅคง่ฑ': 'ZS=F', | |
| 'agri_prices_ๅฐ้บฆ': 'ZW=F', | |
| 'agri_prices_็้บฆ': 'ZO=F', | |
| # === ๆดๅคๅๅ๏ผๆฟไปฃ World Bank ๅฉไฝ๏ผ=== | |
| 'natgas_index': 'NG=F', # ๅคฉ็ถๆฐๆ่ดง | |
| 'ๅ จ็:ๅฎ้ ๅธๅบไปทๆ ผ:็่': 'LE=F', # ๆดป็ๆ่ดง | |
| 'ๅ จ็:ๅฎ้ ๅธๅบไปทๆ ผ:ๆฉๆฆๆฒน': 'ZL=F', # ่ฑๆฒน่ฟไผผ | |
| 'ๅ จ็:ๅฎ้ ๅธๅบไปทๆ ผ:้ฑผ็ฒ': 'ZM=F', # ่ฑ็ฒ่ฟไผผ | |
| # === ๆฑ็proxy๏ผ็จไบไพ็ป/่ฝๆบ่ก็๏ผ=== | |
| 'micex_brent': 'USDRUB=X', # USD/RUBไฝไธบMICEX-Brent proxy | |
| } | |
| def fetch_yfinance(start='2000-01-01'): | |
| """ไป Yahoo Finance ๆๅไปทๆ ผ/ๆฑ็/ๅๅๆฐๆฎ๏ผๆฟไปฃ World Bank๏ผใ""" | |
| import yfinance as yf | |
| tickers = {k: v for k, v in YFINANCE_MAP.items() if v is not None} | |
| total = len(tickers) | |
| print(f"\n[YFinance] ๆๅ {total} ไธชๆๆ ๏ผๅซๅๅๆฟไปฃ World Bank๏ผ...") | |
| results = {} | |
| for i, (name, ticker) in enumerate(tickers.items()): | |
| try: | |
| data = yf.download(ticker, start=start, progress=False, auto_adjust=True) | |
| if data is not None and len(data) > 0: | |
| close = data['Close'] | |
| if hasattr(close, 'columns'): | |
| close = close.iloc[:, 0] | |
| monthly = close.resample('ME').last().dropna() | |
| results[name] = monthly | |
| print(f" [{i+1}/{total}] โ {name:<35} {len(monthly)} ๆ") | |
| else: | |
| print(f" [{i+1}/{total}] โ {name:<35} ๆ ๆฐๆฎ") | |
| except Exception as e: | |
| print(f" [{i+1}/{total}] โ {name:<35} {str(e)[:40]}") | |
| df = pd.DataFrame(results) if results else pd.DataFrame() | |
| print(f" YFinance ๅฎๆ: {len(results)}/{total}") | |
| return df | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # 3. (ๅทฒๅๅนถๅฐ YFinance) World Bank ๅๅ โ yfinance ๆ่ดงๆฟไปฃ | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def fetch_worldbank(): | |
| """World Bank ๅทฒ่ขซ yfinance ๅๅๆ่ดงๆฟไปฃ๏ผ่ฟๅ็ฉบ DataFrameใ""" | |
| print(f"\n[World Bank] ๅทฒๅๅนถๅฐ YFinance ๅๅๆ่ดง๏ผ่ทณ่ฟ") | |
| return pd.DataFrame() | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # 4. AKSHARE โ ไธญๅฝๅฎ่ง | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def fetch_akshare(): | |
| """ไป akshare ๆๅไธญๅฝๅฎ่งๆฐๆฎ๏ผM1/M2/PMI/GDP/CPI/PPI๏ผใ""" | |
| print(f"\n[akshare] ๆๅไธญๅฝๅฎ่งๆฐๆฎ...") | |
| results = {} | |
| try: | |
| import akshare as ak | |
| # ไธญๅฝ M1/M2 (่ดงๅธไพๅบ้) | |
| try: | |
| ms = ak.macro_china_money_supply() | |
| if ms is not None and len(ms) > 0: | |
| ms['date'] = pd.to_datetime(ms.iloc[:, 0], errors='coerce') | |
| ms = ms.dropna(subset=['date']).set_index('date').sort_index() | |
| for col in ms.columns: | |
| ccl = col.lower() | |
| if 'M2' in col and 'ๅๆฏ' in col: | |
| results['ไธญๅฝ:M2:ๅๆฏ'] = pd.to_numeric(ms[col], errors='coerce') | |
| elif 'M1' in col and 'ๅๆฏ' in col: | |
| results['ไธญๅฝ:M1:ๅๆฏ'] = pd.to_numeric(ms[col], errors='coerce') | |
| elif 'M2' in col and 'ๆฐ้' in col: | |
| results['ไธญๅฝ:M2'] = pd.to_numeric(ms[col], errors='coerce') | |
| elif 'M1' in col and 'ๆฐ้' in col: | |
| results['ไธญๅฝ:M1'] = pd.to_numeric(ms[col], errors='coerce') | |
| for k in ['ไธญๅฝ:M1', 'ไธญๅฝ:M2', 'ไธญๅฝ:M1:ๅๆฏ', 'ไธญๅฝ:M2:ๅๆฏ']: | |
| if k in results: | |
| print(f" โ {k:<25} {results[k].notna().sum()} ๆ") | |
| except Exception as e: | |
| print(f" โ money_supply: {str(e)[:40]}") | |
| # ไธญๅฝ PMI | |
| try: | |
| pmi = ak.macro_china_pmi() | |
| if pmi is not None and len(pmi) > 0: | |
| pmi['date'] = pd.to_datetime(pmi.iloc[:, 0], errors='coerce') | |
| pmi = pmi.dropna(subset=['date']).set_index('date').sort_index() | |
| for col in pmi.columns: | |
| if 'ๅถ้ ไธ' in col and 'ๆๆฐ' in col: | |
| results['pmi_china_mfg'] = pd.to_numeric(pmi[col], errors='coerce') | |
| print(f" โ pmi_china_mfg {results['pmi_china_mfg'].notna().sum()} ๆ") | |
| elif '้ๅถ้ ไธ' in col and 'ๆๆฐ' in col: | |
| results['pmi_china_nonsvc'] = pd.to_numeric(pmi[col], errors='coerce') | |
| print(f" โ pmi_china_nonsvc {results['pmi_china_nonsvc'].notna().sum()} ๆ") | |
| except Exception as e: | |
| print(f" โ pmi_china: {str(e)[:40]}") | |
| # ไธญๅฝ CPI (ๆๅบฆ) | |
| try: | |
| cpi = ak.macro_china_cpi_monthly() | |
| if cpi is not None and len(cpi) > 0: | |
| cpi['date'] = pd.to_datetime(cpi.iloc[:, 0], errors='coerce') | |
| cpi = cpi.dropna(subset=['date']).set_index('date').sort_index() | |
| if len(cpi.columns) > 1: | |
| results['cpi_china'] = pd.to_numeric(cpi.iloc[:, 0], errors='coerce') | |
| print(f" โ cpi_china {results['cpi_china'].notna().sum()} ๆ") | |
| except Exception as e: | |
| print(f" โ cpi_china: {str(e)[:40]}") | |
| # ไธญๅฝ PPI | |
| try: | |
| ppi = ak.macro_china_ppi() | |
| if ppi is not None and len(ppi) > 0: | |
| ppi['date'] = pd.to_datetime(ppi.iloc[:, 0], errors='coerce') | |
| ppi = ppi.dropna(subset=['date']).set_index('date').sort_index() | |
| if len(ppi.columns) > 1: | |
| results['ppi_china_yoy'] = pd.to_numeric(ppi.iloc[:, 1], errors='coerce') | |
| print(f" โ ppi_china_yoy {results['ppi_china_yoy'].notna().sum()} ๆ") | |
| except Exception as e: | |
| print(f" โ ppi_china: {str(e)[:40]}") | |
| # ไธญๅฝ GDP | |
| try: | |
| gdp = ak.macro_china_gdp() | |
| if gdp is not None and len(gdp) > 0: | |
| gdp['date'] = pd.to_datetime(gdp.iloc[:, 0], errors='coerce') | |
| gdp = gdp.dropna(subset=['date']).set_index('date').sort_index() | |
| for col in gdp.columns: | |
| if 'ๅๆฏๅข้ฟ' in col: | |
| results['gdp_china_yoy'] = pd.to_numeric(gdp[col], errors='coerce') | |
| results['gdp_china_growth_yoy'] = results['gdp_china_yoy'] | |
| print(f" โ gdp_china_yoy {results['gdp_china_yoy'].notna().sum()} ๅญฃ") | |
| break | |
| except Exception as e: | |
| print(f" โ gdp_china: {str(e)[:40]}") | |
| # SHIBOR 3M | |
| try: | |
| shibor = ak.rate_interbank(market="ไธๆตท้ถ่กๅไธๆๅๅธๅบ", | |
| symbol="Shiborไบบๆฐๅธ", indicator="3ๆ") | |
| if shibor is not None and len(shibor) > 0: | |
| shibor['date'] = pd.to_datetime(shibor.iloc[:, 0], errors='coerce') | |
| shibor = shibor.set_index('date').sort_index() | |
| series = pd.to_numeric(shibor.iloc[:, 0], errors='coerce') | |
| monthly = series.resample('ME').last().dropna() | |
| results['shibor_3m'] = monthly | |
| print(f" โ shibor_3m {len(monthly)} ๆ") | |
| except Exception as e: | |
| print(f" โ shibor_3m: {str(e)[:40]}") | |
| except ImportError: | |
| print(" โ akshare ๆชๅฎ่ฃ (pip install akshare)") | |
| except Exception as e: | |
| print(f" โ akshare ้่ฏฏ: {e}") | |
| df = pd.DataFrame(results) if results else pd.DataFrame() | |
| print(f" akshare ๅฎๆ: {len(results)} ๆๆ ") | |
| return df | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # 5. CFTC ๆไปๆฅๅ๏ผ่ชๅจไธ่ฝฝ๏ผ | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def fetch_cftc(): | |
| """ไป CFTC ๅฎ็ฝ่ชๅจไธ่ฝฝๆไปๆฅๅใ""" | |
| print(f"\n[CFTC] ไธ่ฝฝๆไปๆฅๅ...") | |
| results = {} | |
| try: | |
| # CFTC Disaggregated Futures-Only reports | |
| year = datetime.now().year | |
| url = f"https://www.cftc.gov/dea/newcot/{year}fut.zip" | |
| import zipfile, io | |
| resp = requests.get(url, timeout=30) | |
| if resp.status_code == 200: | |
| z = zipfile.ZipFile(io.BytesIO(resp.content)) | |
| fname = z.namelist()[0] | |
| df_cftc = pd.read_csv(z.open(fname)) | |
| # Filter for crude oil | |
| oil = df_cftc[df_cftc['Market_and_Exchange_Names'].str.contains( | |
| 'CRUDE OIL', case=False, na=False)] | |
| if len(oil) > 0: | |
| oil['date'] = pd.to_datetime(oil['As_of_Date_In_Form_YYMMDD'], | |
| format='%y%m%d', errors='coerce') | |
| oil = oil.set_index('date').sort_index() | |
| # Extract key positioning columns | |
| col_map = { | |
| 'ice_wti_mm_long': 'M_Money_Positions_Long_All', | |
| 'ice_wti_mm_short': 'M_Money_Positions_Short_All', | |
| } | |
| for name, col in col_map.items(): | |
| if col in oil.columns: | |
| series = pd.to_numeric(oil[col], errors='coerce') | |
| monthly = series.resample('ME').last().dropna() | |
| results[name] = monthly | |
| print(f" โ {name:<30} {len(monthly)} ๆ") | |
| print(f" CFTC ๅฎๆ: {len(results)} ๆๆ ") | |
| else: | |
| print(f" โ CFTC ไธ่ฝฝๅคฑ่ดฅ: HTTP {resp.status_code}") | |
| except Exception as e: | |
| print(f" โ CFTC ้่ฏฏ: {str(e)[:50]}") | |
| return pd.DataFrame(results) if results else pd.DataFrame() | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # 6. EIA API | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| EIA_SERIES = { | |
| 'us_oil_inventory_total': 'PET.WCESTUS1.W', | |
| 'us_crude_production': 'PET.WCRFPUS2.W', | |
| } | |
| def fetch_eia(): | |
| """ไป EIA API ๆๅ่ฝๆบๆฐๆฎใ""" | |
| print(f"\n[EIA] ๆๅ {len(EIA_SERIES)} ไธชๆๆ ...") | |
| results = {} | |
| for name, sid in EIA_SERIES.items(): | |
| try: | |
| url = f"https://api.eia.gov/v2/seriesid/{sid}" | |
| resp = requests.get(url, params={'api_key': EIA_KEY}, timeout=30) | |
| if resp.status_code == 200: | |
| data = resp.json() | |
| if 'response' in data and 'data' in data['response']: | |
| records = data['response']['data'] | |
| df_tmp = pd.DataFrame(records) | |
| df_tmp['date'] = pd.to_datetime(df_tmp['period']) | |
| df_tmp['value'] = pd.to_numeric(df_tmp['value'], errors='coerce') | |
| series = df_tmp.set_index('date')['value'].sort_index() | |
| monthly = series.resample('ME').last().dropna() | |
| results[name] = monthly | |
| print(f" โ {name:<30} {len(monthly)} ๆ") | |
| else: | |
| print(f" โ {name:<30} ๆ ๆฐๆฎ") | |
| else: | |
| print(f" โ {name:<30} HTTP {resp.status_code}") | |
| except Exception as e: | |
| print(f" โ {name:<30} {str(e)[:40]}") | |
| return pd.DataFrame(results) if results else pd.DataFrame() | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # 7. GPR ๅฐ็ผๆฟๆฒป้ฃ้ฉๆๆฐ (Caldara & Iacoviello) | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def fetch_gpr(): | |
| """ไปๅญฆๆฏ็ฝ็ซ่ชๅจไธ่ฝฝ GPR ๅฐ็ผๆฟๆฒป้ฃ้ฉๆๆฐใ | |
| ๆฟไปฃ30ไธชไบๅผๅฐ็ผไบไปถ่ๆๅ้ใ | |
| ๆฅๆบ: https://www.matteoiacoviello.com/gpr.htm | |
| """ | |
| print(f"\n[GPR] ไธ่ฝฝๅฐ็ผๆฟๆฒป้ฃ้ฉๆๆฐ (Caldara & Iacoviello)...") | |
| results = {} | |
| urls = [ | |
| "https://www.matteoiacoviello.com/gpr_files/data_gpr_monthly.xls", | |
| "https://www.matteoiacoviello.com/gpr_files/data_gpr_daily_recent.xls", | |
| ] | |
| try: | |
| df_gpr = None | |
| for url in urls: | |
| try: | |
| df_gpr = pd.read_excel(url) | |
| print(f" ไธ่ฝฝๆๅ: {url.split('/')[-1]}") | |
| break | |
| except Exception: | |
| continue | |
| if df_gpr is not None and len(df_gpr) > 0: | |
| # ๆพๅฐๆฅๆๅๅGPRๅ | |
| date_col = [c for c in df_gpr.columns if 'date' in c.lower() or 'month' in c.lower()] | |
| gpr_cols = [c for c in df_gpr.columns if 'gpr' in c.lower()] | |
| if date_col: | |
| df_gpr['date'] = pd.to_datetime(df_gpr[date_col[0]], errors='coerce') | |
| else: | |
| df_gpr['date'] = pd.to_datetime(df_gpr.iloc[:, 0], errors='coerce') | |
| df_gpr = df_gpr.dropna(subset=['date']).set_index('date').sort_index() | |
| for col in gpr_cols: | |
| series = pd.to_numeric(df_gpr[col], errors='coerce') | |
| monthly = series.resample('ME').last().dropna() | |
| if len(monthly) > 0: | |
| clean_name = col.strip().lower().replace(' ', '_') | |
| if clean_name in ('gpr', 'gprd'): | |
| clean_name = 'gpr_index' | |
| results[clean_name] = monthly | |
| print(f" โ {clean_name:<30} {len(monthly)} ๆ") | |
| if 'gpr_index' not in results and results: | |
| first_key = list(results.keys())[0] | |
| results['gpr_index'] = results[first_key] | |
| print(f" โ gpr_index (alias of {first_key})") | |
| else: | |
| print(f" โ GPR ๆฐๆฎไธบ็ฉบ") | |
| except Exception as e: | |
| print(f" โ GPR ไธ่ฝฝๅคฑ่ดฅ: {str(e)[:50]}") | |
| df = pd.DataFrame(results) if results else pd.DataFrame() | |
| print(f" GPR ๅฎๆ: {len(results)} ๆๆ ") | |
| return df | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # MERGE + FEATURE ENGINEERING | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def merge_all_sources(fred_df, yf_df, wb_df, ak_df, cftc_df, eia_df, gpr_df=None, | |
| existing_path='output/panel_monthly.csv'): | |
| """ๅๅนถๆๆๆฐๆฎๆบ๏ผ็ๆๆดๆฐๅ็้ขๆฟใ""" | |
| print(f"\n[MERGE] ๅๅนถๆๆๆฐๆฎๆบ...") | |
| existing = pd.read_csv(existing_path, index_col=0, parse_dates=True) | |
| print(f" ็ฐๆ้ขๆฟ: {len(existing)} ่ก ร {len(existing.columns)} ๅ") | |
| print(f" ๆชๆญข: {existing.index[-1].strftime('%Y-%m')}") | |
| sources = [ | |
| ('FRED', fred_df), ('YFinance', yf_df), ('WorldBank', wb_df), | |
| ('akshare', ak_df), ('CFTC', cftc_df), ('EIA', eia_df), | |
| ('GPR', gpr_df), | |
| ] | |
| updated_cols = 0 | |
| new_rows = 0 | |
| # ไปทๆ ผๅ้่ฆ็จ FRED ๆฐๆฎ่ฆ็ๅทฒๆๅผ๏ผ็กฎไฟๆฐๆฎๆบไธ่ดๆง | |
| PRICE_OVERRIDE_COLS = {'WTI_spot', 'Brent_spot'} | |
| for src_name, src_df in sources: | |
| if src_df is None or len(src_df) == 0: | |
| continue | |
| for col in src_df.columns: | |
| if col in existing.columns: | |
| is_price_override = (col in PRICE_OVERRIDE_COLS and src_name == 'FRED') | |
| # ่กฅๅ จ็ผบๅคฑๅผ + ่ฟฝๅ ๆฐๆฅๆ | |
| for d in src_df.index: | |
| if d not in existing.index: | |
| existing.loc[d] = np.nan | |
| new_rows += 1 | |
| if pd.notna(src_df.at[d, col]): | |
| if pd.isna(existing.at[d, col]) or is_price_override: | |
| existing.at[d, col] = src_df.at[d, col] | |
| updated_cols += 1 | |
| else: | |
| # ๆฐๅ | |
| existing = existing.join(src_df[[col]], how='outer') | |
| existing = existing.sort_index() | |
| # ้ๆฐ่ฎก็ฎๆดพ็็นๅพ | |
| print(f" ้ๆฐ่ฎก็ฎๆดพ็็นๅพ...") | |
| try: | |
| from data_pipeline import engineer_features | |
| existing = engineer_features(existing) | |
| except Exception as e: | |
| print(f" โ engineer_features ่ทณ่ฟ: {e}") | |
| # ISM ็ฏๆฏ๏ผ22ไธช๏ผ๏ผไป ISM ๅญๆๆฐ่ชๅจ่ฎก็ฎ MoM | |
| ism_mom_pairs = [ | |
| ('็พๅฝ:ISM:ๅถ้ ไธPMI:็ฏๆฏ', '็พๅฝ:ISM:ๅถ้ ไธPMI'), | |
| ('็พๅฝ:ISM:ๅถ้ ไธPMI:ๆฐ่ฎขๅ:็ฏๆฏ', '็พๅฝ:ISM:ๅถ้ ไธPMI:ๆฐ่ฎขๅ'), | |
| ('็พๅฝ:ISM:ๅถ้ ไธPMI:่ฎขๅๅบๅญ:็ฏๆฏ', '็พๅฝ:ISM:ๅถ้ ไธPMI:่ชๆๅบๅญ'), | |
| ('็พๅฝ:ISM:ๅถ้ ไธPMI:่ชๆๅบๅญ:็ฏๆฏ', '็พๅฝ:ISM:ๅถ้ ไธPMI:่ชๆๅบๅญ'), | |
| ('็พๅฝ:ISM:ๅถ้ ไธPMI:ๅฎขๆทๅบๅญ:็ฏๆฏ', '็พๅฝ:ISM:ๅถ้ ไธPMI:ๅฎขๆทๅบๅญ'), | |
| ('็พๅฝ:ISM:ๅถ้ ไธPMI:ๆฐๅบๅฃ่ฎขๅ:็ฏๆฏ', '็พๅฝ:ISM:ๅถ้ ไธPMI:ๆฐๅบๅฃ่ฎขๅ'), | |
| ('็พๅฝ:ISM:ๅถ้ ไธPMI:่ฟๅฃ:็ฏๆฏ', '็พๅฝ:ISM:ๅถ้ ไธPMI:่ฟๅฃ'), | |
| ('็พๅฝ:ISM:ๅถ้ ไธPMI:ไบงๅบ:็ฏๆฏ', '็พๅฝ:ISM:ๅถ้ ไธPMI:ไบงๅบ'), | |
| ('็พๅฝ:ISM:ๅถ้ ไธPMI:ไพๅบๅไบคไป:็ฏๆฏ', '็พๅฝ:ISM:ๅถ้ ไธPMI:ไพๅบๅไบคไป'), | |
| ('็พๅฝ:ISM:ๅถ้ ไธPMI:็ฉไปท:็ฏๆฏ', '็พๅฝ:ISM:ๅถ้ ไธPMI:็ฉไปท'), | |
| ('็พๅฝ:ISM:ๅถ้ ไธPMI:ๅฐฑไธ:็ฏๆฏ', '็พๅฝ:ISM:ๅถ้ ไธPMI:ๅฐฑไธ'), | |
| ('็พๅฝ:ISM:ๆๅกไธPMI:็ฏๆฏ', '็พๅฝ:ISM:ๆๅกไธPMI'), | |
| ('็พๅฝ:ISM:ๆๅกไธPMI:ๅไธๆดปๅจ:็ฏๆฏ', '็พๅฝ:ISM:ๆๅกไธPMI:ๅไธๆดปๅจ'), | |
| ('็พๅฝ:ISM:ๆๅกไธPMI:ๆฐ่ฎขๅ:็ฏๆฏ', '็พๅฝ:ISM:ๆๅกไธPMI:ๆฐ่ฎขๅ'), | |
| ('็พๅฝ:ISM:ๆๅกไธPMI:ๅฐฑไธ:็ฏๆฏ', '็พๅฝ:ISM:ๆๅกไธPMI:ๅฐฑไธ'), | |
| ('็พๅฝ:ISM:ๆๅกไธPMI:ไพๅบๅไบคไป:็ฏๆฏ', '็พๅฝ:ISM:ๆๅกไธPMI:ไพๅบๅไบคไป'), | |
| ('็พๅฝ:ISM:ๆๅกไธPMI:ๅบๅญ:็ฏๆฏ', '็พๅฝ:ISM:ๆๅกไธPMI:ๅบๅญ'), | |
| ('็พๅฝ:ISM:ๆๅกไธPMI:็ฉไปท:็ฏๆฏ', '็พๅฝ:ISM:ๆๅกไธPMI:็ฉไปท'), | |
| ('็พๅฝ:ISM:ๆๅกไธPMI:่ฎขๅๅบๅญ:็ฏๆฏ', '็พๅฝ:ISM:ๆๅกไธPMI:่ฎขๅๅบๅญ'), | |
| ('็พๅฝ:ISM:ๆๅกไธPMI:ๆฐๅบๅฃ่ฎขๅ:็ฏๆฏ', '็พๅฝ:ISM:ๆๅกไธPMI:ๆฐๅบๅฃ่ฎขๅ'), | |
| ('็พๅฝ:ISM:ๆๅกไธPMI:่ฟๅฃ:็ฏๆฏ', '็พๅฝ:ISM:ๆๅกไธPMI:่ฟๅฃ'), | |
| ('็พๅฝ:ISM:ๆๅกไธPMI:ๅบๅญๆฏๆฐ:็ฏๆฏ', '็พๅฝ:ISM:ๆๅกไธPMI:ๅบๅญ'), | |
| ] | |
| ism_count = 0 | |
| for mom_col, base_col in ism_mom_pairs: | |
| if base_col in existing.columns: | |
| existing[mom_col] = existing[base_col].diff() | |
| ism_count += 1 | |
| if ism_count: | |
| print(f" โ ISM ็ฏๆฏ: {ism_count} ไธชๆดพ็็นๅพ") | |
| out_path = os.path.join(OUTPUT_DIR, 'panel_monthly_live.csv') | |
| existing.to_csv(out_path) | |
| print(f" โ ๆดๆฐๅ้ขๆฟ: {len(existing)} ่ก ร {len(existing.columns)} ๅ") | |
| print(f" โ ๆชๆญข: {existing.index[-1].strftime('%Y-%m')}") | |
| print(f" โ ๅทฒไฟๅญ: {out_path}") | |
| return existing | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # TEST MODE | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def test_apis(): | |
| """ๆต่ฏๅ API ่ฟ้ๆงใ""" | |
| print("=" * 65) | |
| print("API ่ฟ้ๆงๆต่ฏ") | |
| print("=" * 65) | |
| results = {} | |
| # FRED | |
| try: | |
| from fredapi import Fred | |
| fred = Fred(api_key=FRED_KEY) | |
| d = fred.get_series('DCOILWTICO', observation_start='2026-01-01') | |
| results['FRED'] = f"โ ่ฟ้ ({len(d)} ๆก)" | |
| except Exception as e: | |
| results['FRED'] = f"โ {str(e)[:40]}" | |
| # YFinance | |
| try: | |
| import yfinance as yf | |
| d = yf.download('CL=F', period='5d', progress=False) | |
| results['YFinance'] = f"โ ่ฟ้ ({len(d)} ๆก)" | |
| except Exception as e: | |
| results['YFinance'] = f"โ {str(e)[:40]}" | |
| # World Bank | |
| try: | |
| resp = requests.head("https://thedocs.worldbank.org/en/doc/5d903e848db1d1b83e0ec8f744e55570-0350012021/related/CMO-Historical-Data-Monthly.xlsx", | |
| timeout=10) | |
| results['WorldBank'] = f"โ ๅฏ่ฎฟ้ฎ (HTTP {resp.status_code})" | |
| except Exception as e: | |
| results['WorldBank'] = f"โ {str(e)[:40]}" | |
| # akshare | |
| try: | |
| import akshare as ak | |
| results['akshare'] = f"โ ๅทฒๅฎ่ฃ (v{ak.__version__})" | |
| except Exception as e: | |
| results['akshare'] = f"โ {str(e)[:40]}" | |
| # CFTC | |
| try: | |
| year = datetime.now().year | |
| resp = requests.head(f"https://www.cftc.gov/dea/newcot/{year}fut.zip", timeout=10) | |
| results['CFTC'] = f"โ ๅฏ่ฎฟ้ฎ (HTTP {resp.status_code})" | |
| except Exception as e: | |
| results['CFTC'] = f"โ {str(e)[:40]}" | |
| # EIA | |
| try: | |
| resp = requests.get(f"https://api.eia.gov/v2/seriesid/PET.WCESTUS1.W", | |
| params={'api_key': EIA_KEY}, timeout=10) | |
| results['EIA'] = f"โ ่ฟ้ (HTTP {resp.status_code})" | |
| except Exception as e: | |
| results['EIA'] = f"โ {str(e)[:40]}" | |
| # GPR | |
| try: | |
| resp = requests.head("https://www.matteoiacoviello.com/gpr_files/data_gpr_monthly.xls", | |
| timeout=10) | |
| results['GPR'] = f"โ ๅฏ่ฎฟ้ฎ (HTTP {resp.status_code})" | |
| except Exception as e: | |
| results['GPR'] = f"โ {str(e)[:40]}" | |
| print() | |
| for src, status in results.items(): | |
| print(f" {src:15s} {status}") | |
| ok = sum(1 for v in results.values() if 'โ' in v) | |
| print(f"\n {ok}/{len(results)} ไธชๆฐๆฎๆบๅฏ็จ") | |
| return results | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| # MAIN | |
| # โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ | |
| def main(test=False, source=None): | |
| print("=" * 65) | |
| print("ๅ จ็นๅพ API ๆฐๆฎๆดๆฐ") | |
| print("=" * 65) | |
| if test: | |
| test_apis() | |
| return | |
| start = '2000-01-01' | |
| # ๆๆฐๆฎๆบๆๅ | |
| fred_df = yf_df = wb_df = ak_df = cftc_df = eia_df = gpr_df = None | |
| if source is None or source == 'fred': | |
| fred_df = fetch_fred_bulk(start) | |
| if source is None or source == 'yfinance': | |
| yf_df = fetch_yfinance(start) | |
| if source is None or source == 'worldbank': | |
| wb_df = fetch_worldbank() | |
| if source is None or source == 'akshare': | |
| ak_df = fetch_akshare() | |
| if source is None or source == 'cftc': | |
| cftc_df = fetch_cftc() | |
| if source is None or source == 'eia': | |
| eia_df = fetch_eia() | |
| if source is None or source == 'gpr': | |
| gpr_df = fetch_gpr() | |
| # ๅๅนถ | |
| panel = merge_all_sources(fred_df, yf_df, wb_df, ak_df, cftc_df, eia_df, gpr_df) | |
| print(f"\n{'='*65}") | |
| print("โ ๆฐๆฎๆดๆฐๅฎๆ") | |
| print(f"{'='*65}") | |
| if __name__ == '__main__': | |
| parser = argparse.ArgumentParser(description='ๅ จ็นๅพ API ๆฐๆฎๆดๆฐ') | |
| parser.add_argument('--test', action='store_true', help='ๆต่ฏ API ่ฟ้ๆง') | |
| parser.add_argument('--source', choices=['fred', 'yfinance', 'worldbank', | |
| 'akshare', 'cftc', 'eia', 'gpr'], | |
| help='ๅชๆดๆฐๆไธชๆฐๆฎๆบ') | |
| args = parser.parse_args() | |
| main(test=args.test, source=args.source) | |