feat: Trading Economics API for upcoming events with forecasts

- New services/te_calendar.py: fetch_upcoming(weeks_ahead) calls TE API
  for 9 countries (USD/EUR/GBP/JPY/AUD/CAD/NZD/CHF/CNY), converts to
  ff_calendar format, upserts with source='te_api'
- New endpoints: GET/POST /api/eco/te-key, POST /api/eco/te-sync,
  GET /api/eco/te-sync/status
- Daily scheduler in main.py: FF live sync + TE sync (if key configured)
  run 60s after startup then every 24h
- CalendarPage: TEPanel with key input (password field, Enter to save,
  "Get free key" link to tradingeconomics.com/api/login),
  "Sync upcoming (6 weeks)" button with polling

FF HTML scraper kept as fallback but TE API is the primary source
for upcoming forecasts (no Cloudflare blocking on server IPs).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
OpenSquared
2026-06-26 17:30:16 +02:00
parent d9762deca7
commit ec6fcc1e3d
4 changed files with 357 additions and 12 deletions

View File

@@ -95,21 +95,33 @@ def startup():
from services.institutional_scheduler import start_institutional_scheduler
start_institutional_scheduler()
# Daily FF scraper — runs once at startup, then every 24h
# Daily calendars sync — FF live (this week) + TE upcoming (if key configured)
import threading, time as _time
def _ff_scrape_loop():
_time.sleep(30) # let server fully boot first
def _daily_calendar_sync():
_time.sleep(60) # let server fully boot first
while True:
try:
print("[Startup] FF scrape upcoming weeks …", flush=True)
from services.ff_calendar import scrape_upcoming
result = scrape_upcoming(weeks_ahead=5)
print(f"[FF scrape] done: {result.get('total_upserted', 0)} upserted", flush=True)
# FF live JSON — this week actuals
from services.ff_calendar import sync_live
r = sync_live()
print(f"[Daily] FF live sync: {r}", flush=True)
except Exception as e:
print(f"[FF scrape] loop error: {e}", flush=True)
_time.sleep(86400) # 24h
print(f"[Daily] FF live sync error: {e}", flush=True)
threading.Thread(target=_ff_scrape_loop, daemon=True).start()
try:
# Trading Economics — upcoming forecasts (only if key is set)
from services.database import get_config
te_key = get_config("te_api_key") or ""
if te_key:
from services.te_calendar import fetch_upcoming
r = fetch_upcoming(weeks_ahead=6)
print(f"[Daily] TE sync: {r.get('total_upserted', 0)} upserted", flush=True)
except Exception as e:
print(f"[Daily] TE sync error: {e}", flush=True)
_time.sleep(86400) # repeat every 24h
threading.Thread(target=_daily_calendar_sync, daemon=True).start()
# Auto-import Forex Factory CSV if file is present (force, then delete CSV)
import threading