Add FastAPI backend for energy trading system

Implements FastAPI backend with ML model support for energy trading,
including price prediction models and RL-based battery trading policy.
Features dashboard, trading, backtest, and settings API routes with
WebSocket support for real-time updates.
This commit is contained in:
2026-02-12 00:59:26 +07:00
parent a22a13f6f4
commit fe76bc7629
72 changed files with 2931 additions and 0 deletions

View File

@@ -0,0 +1,53 @@
from app.ml.features.lag_features import add_lag_features
from app.ml.features.rolling_features import add_rolling_features
from app.ml.features.time_features import add_time_features
from app.ml.features.regional_features import add_regional_features
from app.ml.features.battery_features import add_battery_features
from typing import List, Optional
import pandas as pd
def build_price_features(
df: pd.DataFrame,
price_col: str = "real_time_price",
lags: Optional[List[int]] = None,
windows: Optional[List[int]] = None,
regions: Optional[List[str]] = None,
include_time: bool = True,
include_regional: bool = True,
) -> pd.DataFrame:
if lags is None:
lags = [1, 5, 10, 15, 30, 60]
if windows is None:
windows = [5, 10, 15, 30, 60]
result = df.copy()
if price_col in result.columns:
result = add_lag_features(result, price_col, lags)
result = add_rolling_features(result, price_col, windows)
if include_time and "timestamp" in result.columns:
result = add_time_features(result)
if include_regional and regions:
result = add_regional_features(result, regions)
return result
def build_battery_features(
df: pd.DataFrame,
price_df: pd.DataFrame,
battery_col: str = "charge_level_mwh",
capacity_col: str = "capacity_mwh",
timestamp_col: str = "timestamp",
battery_id_col: str = "battery_id",
) -> pd.DataFrame:
result = df.copy()
result = add_battery_features(result, price_df, battery_col, capacity_col, timestamp_col, battery_id_col)
return result
__all__ = ["build_price_features", "build_battery_features"]

View File

@@ -0,0 +1,35 @@
import pandas as pd
def add_battery_features(
df: pd.DataFrame,
price_df: pd.DataFrame,
battery_col: str = "charge_level_mwh",
capacity_col: str = "capacity_mwh",
timestamp_col: str = "timestamp",
battery_id_col: str = "battery_id",
) -> pd.DataFrame:
result = df.copy()
if battery_col in result.columns and capacity_col in result.columns:
result["charge_level_pct"] = result[battery_col] / result[capacity_col]
result["discharge_potential_mwh"] = result[battery_col] * result.get("efficiency", 0.9)
result["charge_capacity_mwh"] = result[capacity_col] - result[battery_col]
if price_df is not None and "real_time_price" in price_df.columns and timestamp_col in result.columns:
merged = result.merge(
price_df[[timestamp_col, "real_time_price"]],
on=timestamp_col,
how="left",
suffixes=("", "_market")
)
if "real_time_price_market" in merged.columns:
result["market_price"] = merged["real_time_price_market"]
result["charge_cost_potential"] = result["charge_capacity_mwh"] * result["market_price"]
result["discharge_revenue_potential"] = result["discharge_potential_mwh"] * result["market_price"]
return result
__all__ = ["add_battery_features"]

View File

@@ -0,0 +1,14 @@
from typing import List
import pandas as pd
def add_lag_features(df: pd.DataFrame, col: str, lags: List[int]) -> pd.DataFrame:
result = df.copy()
for lag in lags:
result[f"{col}_lag_{lag}"] = result[col].shift(lag)
return result
__all__ = ["add_lag_features"]

View File

@@ -0,0 +1,18 @@
from typing import List
import pandas as pd
def add_regional_features(df: pd.DataFrame, regions: List[str]) -> pd.DataFrame:
result = df.copy()
if "region" in result.columns and "real_time_price" in result.columns:
avg_price_by_region = result.groupby("region")["real_time_price"].mean()
for region in regions:
region_avg = avg_price_by_region.get(region, 0)
result[f"price_diff_{region}"] = result["real_time_price"] - region_avg
return result
__all__ = ["add_regional_features"]

View File

@@ -0,0 +1,17 @@
from typing import List
import pandas as pd
def add_rolling_features(df: pd.DataFrame, col: str, windows: List[int]) -> pd.DataFrame:
result = df.copy()
for window in windows:
result[f"{col}_rolling_mean_{window}"] = result[col].rolling(window=window).mean()
result[f"{col}_rolling_std_{window}"] = result[col].rolling(window=window).std()
result[f"{col}_rolling_min_{window}"] = result[col].rolling(window=window).min()
result[f"{col}_rolling_max_{window}"] = result[col].rolling(window=window).max()
return result
__all__ = ["add_rolling_features"]

View File

@@ -0,0 +1,35 @@
import pandas as pd
def add_time_features(df: pd.DataFrame, timestamp_col: str = "timestamp") -> pd.DataFrame:
result = df.copy()
if timestamp_col not in result.columns:
return result
result[timestamp_col] = pd.to_datetime(result[timestamp_col])
result["hour"] = result[timestamp_col].dt.hour
result["day_of_week"] = result[timestamp_col].dt.dayofweek
result["day_of_month"] = result[timestamp_col].dt.day
result["month"] = result[timestamp_col].dt.month
result["hour_sin"] = _sin_encode(result["hour"], 24)
result["hour_cos"] = _cos_encode(result["hour"], 24)
result["day_sin"] = _sin_encode(result["day_of_week"], 7)
result["day_cos"] = _cos_encode(result["day_of_week"], 7)
return result
def _sin_encode(x, period):
import numpy as np
return np.sin(2 * np.pi * x / period)
def _cos_encode(x, period):
import numpy as np
return np.cos(2 * np.pi * x / period)
__all__ = ["add_time_features"]