aboutsummaryrefslogtreecommitdiff
path: root/server/api/market.py
blob: a1f25ec179bbaf36b3aad07e5058a456160f0efe (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
from fastapi import APIRouter, Query, HTTPException
from models.market import MarketData
from gridstatus import ISONE, CAISO, Ercot, MISO, NYISO, PJM
from datetime import datetime, timedelta
from typing import List, Dict, Type

router = APIRouter()

MARKET_CLASSES: Dict[str, Type] = {
    "ISONE": ISONE,
    # "CAISO": CAISO,
    # "ERCOT": Ercot,
    "MISO": MISO,
    "NYISO": NYISO,
    # "PJM": PJM,
}

# In-memory cache
_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] = {}

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()
    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[market] = [
            MarketData(
                timestamp=row["Interval Start"],
                lmp=row["LMP"],
                energy=row["Energy"],
                congestion=row["Congestion"],
                loss=row["Loss"],
            )
            for _, row in grouped.iterrows()
        ]
        _cached_day_ahead_timestamp[market] = now

    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()
    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[market] = [
            MarketData(
                timestamp=row["Interval Start"],
                lmp=row["LMP"],
                energy=row["Energy"],
                congestion=row["Congestion"],
                loss=row["Loss"],
            )
            for _, row in grouped.iterrows()
        ]
        _cached_real_time_timestamp[market] = now

    return _cached_real_time[market]