Spaces:
Sleeping
Sleeping
File size: 2,750 Bytes
5841846 94fb523 5841846 94fb523 5841846 94fb523 5841846 94fb523 5841846 | 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 | import numpy as np
import pandas as pd
from scipy.stats import t
def calculate_garch_volatility(residuals: pd.Series, omega: float, alpha: float, beta: float, initial_vol: float) -> pd.Series:
"""
Recursively calculates GARCH conditional volatility on the full series of residuals.
Handles leading NaNs by starting the recursion at the first valid index.
Formula:
sigma^2_t = omega + alpha * e^2_{t-1} + beta * sigma^2_{t-1}
"""
first_valid = residuals.first_valid_index()
if first_valid is None:
return pd.Series(np.nan, index=residuals.index)
valid_res = residuals.loc[first_valid:]
cond_var = np.zeros(len(valid_res))
cond_var[0] = initial_vol ** 2
for t_idx in range(1, len(valid_res)):
prev_res = valid_res.iloc[t_idx - 1]
if np.isnan(prev_res):
cond_var[t_idx] = cond_var[t_idx - 1]
else:
cond_var[t_idx] = omega + alpha * (prev_res ** 2) + beta * cond_var[t_idx - 1]
vol_series = pd.Series(np.sqrt(cond_var), index=valid_res.index)
return vol_series.reindex(residuals.index)
def simulate_pvar(day_mean_prices: pd.Series, day_vols: pd.Series, nu: float,
n_simulations: int = 10000, confidence_level: float = 0.95):
"""
Runs a Monte Carlo path simulation using the Student-t distribution
parameterized by GARCH volatility and SARIMA expected price mean.
Returns:
simulated_paths: (24, N_SIMULATIONS) array
var_vals: (24,) array containing Value-at-Risk limits for each hour
"""
n_hours = len(day_mean_prices)
simulated_paths = np.zeros((n_hours, n_simulations))
var_vals = np.zeros(n_hours)
# Calculate the target percentile (e.g. 95% confidence -> 5th percentile)
percentile = 100 * (1.0 - confidence_level)
np.random.seed(42)
for i in range(n_hours):
mu_t = day_mean_prices.iloc[i]
sigma_t = day_vols.iloc[i]
# Draw from Student-t
draws = t.rvs(df=nu, loc=mu_t, scale=sigma_t, size=n_simulations)
simulated_paths[i, :] = draws
var_vals[i] = np.percentile(draws, percentile)
return simulated_paths, var_vals
def get_prosumer_action(var_value: float, mean_value: float) -> int:
"""
Prosumer Traffic Light control logic:
2 (Green): Inject (Value-at-Risk is positive, risk of loss is 0)
1 (Yellow): Buffer (Expected price positive, but Value-at-Risk is negative; buffer energy)
0 (Red): Curtail (Both expected price and Value-at-Risk are negative; halt injects)
"""
if var_value > 0:
return 2
elif mean_value > 0 and var_value <= 0:
return 1
else:
return 0
|