diff options
Diffstat (limited to 'server/api/market.py')
-rw-r--r-- | server/api/market.py | 73 |
1 files changed, 46 insertions, 27 deletions
diff --git a/server/api/market.py b/server/api/market.py index 5102f03..a1f25ec 100644 --- a/server/api/market.py +++ b/server/api/market.py @@ -1,30 +1,45 @@ -from fastapi import APIRouter +from fastapi import APIRouter, Query, HTTPException from models.market import MarketData -from gridstatus import ISONE +from gridstatus import ISONE, CAISO, Ercot, MISO, NYISO, PJM from datetime import datetime, timedelta -from typing import List +from typing import List, Dict, Type router = APIRouter() -# Keeping the scope of this api to just one market right now -iso = ISONE() +MARKET_CLASSES: Dict[str, Type] = { + "ISONE": ISONE, + # "CAISO": CAISO, + # "ERCOT": Ercot, + "MISO": MISO, + "NYISO": NYISO, + # "PJM": PJM, +} # In-memory cache -_cached_day_ahead: List[MarketData] = [] -_cache_timestamp: datetime | None = None -_cached_real_time: List[MarketData] = [] -_cache_real_time_timestamp: datetime | None = None +_cached_day_ahead: Dict[str, List[MarketData]] = {} +_cached_day_ahead_timestamp: Dict[str, datetime] = {} +_cached_real_time: Dict[str, List[MarketData]] = {} +_cached_real_time_timestamp: Dict[str, datetime] = {} -# TODO: Error Handling -@router.get("/day-ahead", response_model=list[MarketData]) -def get_day_ahead_data(): - global _cached_day_ahead, _cache_timestamp +def get_iso_instance(market: str): + market = market.upper() + if market not in MARKET_CLASSES: + raise HTTPException(status_code=400, detail=f"Unsupported market '{market}'. Supported: {list(MARKET_CLASSES.keys())}") + return MARKET_CLASSES[market]() +@router.get("/day-ahead", response_model=List[MarketData]) +def get_day_ahead_data(market: str = Query("ISONE")): now = datetime.utcnow() - if _cache_timestamp is None or now - _cache_timestamp > timedelta(hours=1): + market = market.upper() + + if (market not in _cached_day_ahead_timestamp or + now - _cached_day_ahead_timestamp[market] > timedelta(hours=1)): + + iso = get_iso_instance(market) df = iso.get_lmp(date=datetime.now().date(), market="DAY_AHEAD_HOURLY", locations="ALL") - grouped = (df.groupby("Interval Start")[["LMP", "Energy", "Congestion", "Loss"]].mean().reset_index()) - _cached_day_ahead = [ + grouped = df.groupby("Interval Start")[["LMP", "Energy", "Congestion", "Loss"]].mean().reset_index() + + _cached_day_ahead[market] = [ MarketData( timestamp=row["Interval Start"], lmp=row["LMP"], @@ -34,19 +49,23 @@ def get_day_ahead_data(): ) for _, row in grouped.iterrows() ] - _cache_timestamp = now - - return _cached_day_ahead + _cached_day_ahead_timestamp[market] = now -@router.get("/real-time", response_model=list[MarketData]) -def get_real_time_data(): - global _cached_real_time, _cache_real_time_timestamp + return _cached_day_ahead[market] +@router.get("/real-time", response_model=List[MarketData]) +def get_real_time_data(market: str = Query("ISONE")): now = datetime.utcnow() - if _cache_real_time_timestamp is None or now - _cache_real_time_timestamp > timedelta(minutes=5): + market = market.upper() + + if (market not in _cached_real_time_timestamp or + now - _cached_real_time_timestamp[market] > timedelta(minutes=5)): + + iso = get_iso_instance(market) df = iso.get_lmp(date="today", market="REAL_TIME_5_MIN", locations="ALL") - grouped = (df.groupby("Interval Start")[["LMP", "Energy", "Congestion", "Loss"]].mean().reset_index()) - _cached_real_time = [ + grouped = df.groupby("Interval Start")[["LMP", "Energy", "Congestion", "Loss"]].mean().reset_index() + + _cached_real_time[market] = [ MarketData( timestamp=row["Interval Start"], lmp=row["LMP"], @@ -56,6 +75,6 @@ def get_real_time_data(): ) for _, row in grouped.iterrows() ] - _cache_real_time_timestamp = now + _cached_real_time_timestamp[market] = now - return _cached_real_time + return _cached_real_time[market] |