feat: calendar

This commit is contained in:
OpenSquared
2026-07-02 11:27:45 +02:00
parent 8098104f5b
commit 5081514898
6 changed files with 286 additions and 118 deletions

View File

@@ -1,5 +1,7 @@
"""
Unified calendar sync — FXStreet (primary) with FF live fallback.
Unified calendar sync — ForexFactory only.
1. FF live JSON (nfs.faireconomy.media) → this week + next week (actual values, precise)
2. FF HTML scraper → weeks 3..N ahead (upcoming forecasts)
Called by the background loop in main.py and manually via POST /api/eco/calendar-sync.
"""
import logging
@@ -19,35 +21,38 @@ _sync_status: Dict[str, Any] = {
def calendar_sync(weeks_ahead: int = 8) -> Dict[str, Any]:
"""
Sync upcoming economic events:
1. Try FXStreet (no API key, ~300-400 events over weeks_ahead weeks)
2. Fall back to FF live JSON if FXStreet returns 403 or fails
Sync upcoming economic events from ForexFactory only:
- FF live JSON covers this week + next week (with actuals as they publish)
- FF HTML scraper covers weeks 3..weeks_ahead for forecasts
"""
global _sync_status
_sync_status["running"] = True
_sync_status["last_run"] = datetime.now(timezone.utc).isoformat()
result: Dict[str, Any] = {}
source = "unknown"
source = "ff"
try:
from services.fxstreet_calendar import fetch_upcoming as fxs_fetch
r = fxs_fetch(weeks_ahead=weeks_ahead)
if r.get("error"):
logger.warning(f"[calendar_sync] FXStreet error: {r['error']} — falling back to FF live")
from services.ff_calendar import sync_live
r_ff = sync_live()
result = {**r_ff, "_fallback": True, "_fxs_error": r["error"]}
source = "ff_fallback"
from services.ff_calendar import sync_live, scrape_upcoming
# Step 1 — live JSON (thisweek + nextweek) — picks up actuals in real time
r_live = sync_live()
result["live"] = r_live
# Step 2 — HTML scraper for weeks 3..N ahead (forecasts only, no actuals yet)
scrape_weeks = max(0, weeks_ahead - 2)
if scrape_weeks > 0:
r_scrape = scrape_upcoming(weeks_ahead=scrape_weeks)
result["scrape"] = r_scrape
else:
result = r
source = "fxstreet"
result["scrape"] = {"skipped": True}
except Exception as e:
logger.error(f"[calendar_sync] Exception: {e}")
logger.error(f"[calendar_sync] FF sync failed: {e}")
result = {"error": str(e)}
source = "error"
# After calendar upsert, log any forecast/actual changes to macro_series_log
# Log forecast/actual changes to macro_series_log
try:
from services.database import get_conn
from services.macro_series_log import scan_and_log_all