""" Phase 4: Economic Calendar API Integration Real-time economic events and market-moving indicators """ from fastapi import APIRouter, Query, HTTPException from datetime import datetime, timedelta from typing import List, Optional import httpx router = APIRouter(prefix="/api/economic-calendar", tags=["Economic Calendar"]) # Mock economic calendar data (in production, integrate with real APIs) # Popular APIs: Trading Economics, Forexfactory, Economic Calendar Pro, etc. SAMPLE_EVENTS = [ { "id": 1, "country": "US", "indicator": "Non-Farm Payroll", "event_date": (datetime.now() + timedelta(days=1)).isoformat(), "time": "08:30", "impact": "high", "forecast": "230000", "previous": "227000", "actual": None, "description": "Employment change in the non-agricultural sector", "importance": 3, }, { "id": 2, "country": "US", "indicator": "Unemployment Rate", "event_date": (datetime.now() + timedelta(days=1)).isoformat(), "time": "08:30", "impact": "high", "forecast": "3.8%", "previous": "3.8%", "actual": None, "description": "Percentage of the labor force that is jobless", "importance": 3, }, { "id": 3, "country": "US", "indicator": "Consumer Price Index", "event_date": (datetime.now() + timedelta(days=5)).isoformat(), "time": "12:30", "impact": "high", "forecast": "3.4%", "previous": "3.4%", "actual": None, "description": "Inflation rate measurement", "importance": 3, }, { "id": 4, "country": "US", "indicator": "Federal Funds Rate Decision", "event_date": (datetime.now() + timedelta(days=8)).isoformat(), "time": "18:00", "impact": "high", "forecast": "5.33%", "previous": "5.33%", "actual": None, "description": "Federal Reserve interest rate decision", "importance": 3, }, { "id": 5, "country": "EUR", "indicator": "ECB Interest Rate Decision", "event_date": (datetime.now() + timedelta(days=10)).isoformat(), "time": "12:45", "impact": "high", "forecast": "4.50%", "previous": "4.50%", "actual": None, "description": "European Central Bank rate decision", "importance": 3, }, { "id": 6, "country": "US", "indicator": "ISM Manufacturing PMI", "event_date": (datetime.now() + timedelta(days=2)).isoformat(), "time": "09:00", "impact": "medium", "forecast": "49.5", "previous": "49.0", "actual": None, "description": "Manufacturing sector activity indicator", "importance": 2, }, { "id": 7, "country": "US", "indicator": "Initial Jobless Claims", "event_date": (datetime.now() + timedelta(days=3)).isoformat(), "time": "08:30", "impact": "medium", "forecast": "215000", "previous": "216000", "actual": None, "description": "Weekly unemployment benefit applications", "importance": 2, }, { "id": 8, "country": "US", "indicator": "Retail Sales", "event_date": (datetime.now() + timedelta(days=7)).isoformat(), "time": "12:30", "impact": "medium", "forecast": "0.4%", "previous": "0.7%", "actual": None, "description": "Consumer spending and retail activity", "importance": 2, }, ] @router.get("/events") async def get_economic_events( days_ahead: int = Query(30, ge=1, le=180), countries: Optional[str] = Query(None), impact: Optional[str] = Query(None, regex="^(high|medium|low)$"), sort_by: str = Query("date", regex="^(date|importance|impact)$"), ): """ Get upcoming economic calendar events - **days_ahead**: Number of days to look ahead (1-180) - **countries**: Comma-separated country codes (US, EUR, GBP, JPY, etc.) - **impact**: Filter by impact level (high, medium, low) - **sort_by**: Sort results by date, importance, or impact """ events = SAMPLE_EVENTS.copy() # Filter by countries if countries: country_list = [c.strip() for c in countries.split(",")] events = [e for e in events if e["country"] in country_list] # Filter by impact if impact: impact_map = {"high": 3, "medium": 2, "low": 1} events = [e for e in events if e["importance"] == impact_map.get(impact, 2)] # Filter by days ahead cutoff_date = datetime.now() + timedelta(days=days_ahead) events = [ e for e in events if datetime.fromisoformat(e["event_date"]) <= cutoff_date ] # Sort if sort_by == "importance": events.sort(key=lambda x: x["importance"], reverse=True) elif sort_by == "impact": impact_order = {"high": 3, "medium": 2, "low": 1} events.sort(key=lambda x: impact_order.get(x["impact"], 1), reverse=True) else: # date events.sort(key=lambda x: x["event_date"]) return { "total": len(events), "events": events, "filter_applied": { "days_ahead": days_ahead, "countries": countries, "impact": impact, }, } @router.get("/today") async def get_today_events(): """Get economic events scheduled for today""" today = datetime.now().date() today_start = datetime.combine(today, datetime.min.time()).isoformat() today_end = datetime.combine(today, datetime.max.time()).isoformat() events = [ e for e in SAMPLE_EVENTS if today_start <= e["event_date"] <= today_end ] return { "date": today.isoformat(), "total": len(events), "events": events, } @router.get("/upcoming") async def get_upcoming_events(hours: int = Query(24, ge=1, le=168)): """ Get upcoming events within specified hours - **hours**: Number of hours ahead to check (1-168 hours = 1-7 days) """ now = datetime.now() cutoff = now + timedelta(hours=hours) events = [ e for e in SAMPLE_EVENTS if now <= datetime.fromisoformat(e["event_date"]) <= cutoff ] # Sort by time events.sort(key=lambda x: x["event_date"]) return { "now": now.isoformat(), "hours_ahead": hours, "total": len(events), "events": events, } @router.get("/high-impact") async def get_high_impact_events(): """Get only high-impact economic events for the next 30 days""" cutoff = datetime.now() + timedelta(days=30) events = [ e for e in SAMPLE_EVENTS if e["importance"] == 3 and datetime.fromisoformat(e["event_date"]) <= cutoff ] events.sort(key=lambda x: x["event_date"]) return { "total": len(events), "events": events, "note": "Only high-impact events that could significantly move gold prices", } @router.get("/by-country/{country}") async def get_country_events( country: str, days: int = Query(30, ge=1, le=180) ): """ Get economic events for a specific country - **country**: Country code (US, EUR, GBP, JPY, CHF, CAD, AUD, NZD, etc.) - **days**: Days to look ahead """ cutoff = datetime.now() + timedelta(days=days) events = [ e for e in SAMPLE_EVENTS if e["country"].upper() == country.upper() and datetime.fromisoformat(e["event_date"]) <= cutoff ] if not events: raise HTTPException( status_code=404, detail=f"No events found for country: {country}" ) events.sort(key=lambda x: x["event_date"]) return { "country": country.upper(), "days": days, "total": len(events), "events": events, } @router.get("/impact-analysis") async def get_impact_analysis(): """ Analyze economic impact on gold prices Returns analysis of how different economic indicators typically affect gold trading """ return { "gold_trading_impact": { "high_impact": { "indicators": [ "Interest Rate Decisions", "Inflation Data", "Employment Reports", "GDP Growth", ], "typical_response": "Gold typically moves 100-200 pips on high-impact events", "best_time": "Around event release time", }, "medium_impact": { "indicators": [ "PMI Indices", "Consumer Confidence", "Retail Sales", "Producer Prices", ], "typical_response": "Gold typically moves 50-100 pips", "best_time": "Watch 5-30 mins after release", }, "low_impact": { "indicators": [ "Housing Starts", "Factory Orders", "Building Permits", ], "typical_response": "Gold rarely moves significantly", "best_time": "Usually skipped by day traders", }, }, "inverse_correlation": { "US_Dollar_Strength": "Strong dollar typically weakens gold (inverse correlation)", "Interest_Rates": "Higher rates reduce gold appeal (inverse correlation)", "Risk_Appetite": "Risk-on environment weakens gold demand", "Inflation": "High inflation supports higher gold prices", }, "trading_tips": [ "Trade 30 mins after high-impact events when volatility settles", "Avoid trading during overlapping Fed/ECB announcements", "Watch preliminary indicators before main events", "Check gold correlation with USD index and bond yields", "Set wider stops during high-impact event windows", ], } @router.get("/calendar-view") async def get_calendar_view( month: Optional[int] = Query(None, ge=1, le=12), year: Optional[int] = Query(None), ): """ Get economic calendar in calendar view format - **month**: Specific month (1-12), defaults to current month - **year**: Specific year, defaults to current year """ now = datetime.now() view_month = month or now.month view_year = year or now.year calendar_events = {} for event in SAMPLE_EVENTS: event_date = datetime.fromisoformat(event["event_date"]) if ( event_date.month == view_month and event_date.year == view_year ): day = event_date.day if day not in calendar_events: calendar_events[day] = [] calendar_events[day].append( { "indicator": event["indicator"], "time": event["time"], "impact": event["impact"], "country": event["country"], } ) return { "month": view_month, "year": view_year, "calendar": calendar_events, "month_name": datetime(view_year, view_month, 1).strftime("%B"), } @router.post("/events/{event_id}/notify") async def set_event_notification(event_id: int, minutes_before: int = Query(30)): """ Set a notification reminder for an economic event - **event_id**: ID of the economic event - **minutes_before**: Notify X minutes before event (15-120) """ event = next((e for e in SAMPLE_EVENTS if e["id"] == event_id), None) if not event: raise HTTPException(status_code=404, detail="Event not found") return { "status": "notification_set", "event": event["indicator"], "notify_minutes_before": minutes_before, "event_time": event["event_date"], "notification_time": ( datetime.fromisoformat(event["event_date"]) - timedelta(minutes=minutes_before) ).isoformat(), } @router.get("/stats") async def get_economic_calendar_stats(): """Get statistics about upcoming economic events""" now = datetime.now() next_7_days = now + timedelta(days=7) next_30_days = now + timedelta(days=30) events_7 = [ e for e in SAMPLE_EVENTS if now <= datetime.fromisoformat(e["event_date"]) <= next_7_days ] events_30 = [ e for e in SAMPLE_EVENTS if now <= datetime.fromisoformat(e["event_date"]) <= next_30_days ] high_impact = [e for e in events_30 if e["importance"] == 3] return { "summary": { "total_events_30_days": len(events_30), "total_events_7_days": len(events_7), "high_impact_events": len(high_impact), "total_countries": len(set(e["country"] for e in events_30)), }, "by_impact": { "high": len([e for e in events_30 if e["importance"] == 3]), "medium": len([e for e in events_30 if e["importance"] == 2]), "low": len([e for e in events_30 if e["importance"] == 1]), }, "busiest_days": sorted( [ ( e["event_date"].split("T")[0], len( [ x for x in events_30 if x["event_date"].split("T")[0] == e["event_date"].split("T")[0] ] ), ) for e in events_30 ], key=lambda x: x[1], reverse=True, )[:5], }