Ich frage mich, warum wir eine Temperaturzeitreihe als Ornstein-Uhlenbeck-Prozess modellieren möchten und ob dies praktische Auswirkungen hat. Nun, es tut es. Während der OU -Prozess in der Physik verwendet wird, um die Geschwindigkeit von Partikeln in Reibung zu modellieren, wird es auch in der Finanzierung zur Modellierung von Zinsen, Volatilität oder der mittleren Umkehrung verwendet. Diese Temperaturanwendung stammt aus einem Bereich, in dem ich persönliche Erfahrung habe. Meine These, „Neuronale Netzwerkansätze zur Preisgestaltung Wetterderivats “, konzentriert sich auf die Entwicklung Neue Methoden zur Preisgestaltung dieser Derivate.

Der OU -Prozess wird üblicherweise bei Preis -Wetter -Derivaten verwendet. Ein finanzielles Derivat, der eine Auszahlung auf der Grundlage eines zugrunde liegenden Klimaindex (Temperatur, Niederschlag, Windgeschwindigkeit, Schneefall) liefern soll. Diese Derivate ermöglichen jedem, der Klimaisiken ausgesetzt ist, um ihre Risiko -Exposition zu verwalten. Das könnten Landwirte besorgt sein, die sich über Dürre besorgt sind, Energieunternehmen besorgt über steigende Heizkosten oder saisonale Einzelhändler, die über eine schlechte Saison besorgt sind. Sie können viel mehr darüber lesen WetterderivateAntonis Alexandridis Okay. (mit dem ich derzeit arbeite) und Achilleas D. Zapranis.

SDEs tauchen überall dort auf, wo die Unsicherheit liegt. Der Schwarze ScholesAnwesend Schrödinger GleichungenUnd Geometrische Brownsche Bewegung sind alle stochastische Differentialgleichungen. Diese Gleichungen beschreiben, wie sich Systeme entwickeln, wenn eine zufällige Komponente beteiligt ist. SDEs sind besonders nützlich für die Prognose unter Unsicherheit, sei es BevölkerungswachstumAnwesend epidemische Ausbreitungoder Aktienkurse. Heute werden wir uns ansehen, wie wir die Temperatur mithilfe der modellieren können Ornstein -Uhlenbeck Prozess, der sich ziemlich wie das verhält Wärmegleichung, Ein PDE, der Temperatur FL beschreibtow.

Grafik des Autors

In meinen letzten beiden Geschichten:

Die Klimadaten sind auch in meinem Github verfügbar, sodass wir diesen Schritt überspringen können.

Heute werden wir:

  • Erklären Sie, wie der OU-Prozess eine Temperaturzeitreihen modellieren kann.
  • Sprechen Sie über Mittelwertprozesse, stochastische Differentialgleichungen und darüber, wie sich der OU-Prozess auf die Wärmegleichung bezieht.
  • Verwenden Sie Python, um einen Ornstein-Uhlenbeck (OU) -Prozess, eine stochastische Differentialgleichung (SDE), in die NASA-Klimadaten zu passen.
Grafik des Autors

In der obigen Abbildung können wir sehen, wie sich die Volatilität mit den Jahreszeiten ändert. Dies betont einen wichtigen Grund, warum der OU-Prozess für die Modellierung dieser Zeitreihe intestine geeignet ist, da er nicht die konstante Volatilität annimmt.

Der OU-Prozess, die Mittelwertversion und die Wärmegleichung

Wir werden den OU -Prozess verwenden, um zu modellieren, wie sich die Temperatur im Laufe der Zeit zu einem festen Punkt ändert. In unserer Gleichung repräsentiert T

(dt

Grafik des Autors
Grafik des Autors

Kappa (κ) ist die Geschwindigkeit der mittleren Umkehrung. Ähnlich wie sein Titel implizieren, steuert diese ständige, wie schnell unsere Temperatur auf ihre saisonalen Mittel zurückkehrt. Durch die durchschnittliche Umkehrung in diesem Kontext bedeutet dies, dass wir erwarten würden, dass die Temperatur von morgen oder in der nächsten Woche ein eisiger Tag ist, der kälter als der Durchschnitt ist, da es um diesen saisonalen Mittelwert geht.

(
dt

Diese Konstante spielt eine ähnliche Rolle in der Wärmegleichung, wo sie beschreibt, wie schnell ein Stab der Temperatur zunimmt, wenn sie erhitzt wird. In der Wärmegleichung ist κ die thermische Diffusivität, die beschreibt, wie schnell die Wärme durch ein Materials fließt.

(
frac { partial u ( mathbf {x}, t)} { partial t} = { shade {crimson} { kappa}} , nabla^2 u ( mathbf {x}, t) + q ( mathbf {x}, T)
)

  • κ = 0: Keine gemeine Umkehrung. Der Prozess reduziert sich mit Drift auf die Brownsche Bewegung.
  • 0 <κ <1: Schwache mittlere Umkehrung. Die Schocks bestehen bestehen, der langsame Verfall der Autokorrelation.
  • κ> 1: Starke mittlere Umkehrung. Abweichungen werden schnell korrigiert, und der Prozess wird eng um μ um μ geklustert.

Fourier -Serie

Schätzung der Mittelwert und Volatilität

(s
(sigma^2

Anpassung des Ornstein -Uhlenbeck -Prozesses an Mumbai -Daten

Wir passen den Mittelwert unseres Temperaturprozesses an

  • Passen Sie den Mittelwert mit der Fourier -Serie an
  • Modell kurzfristige Abhängigkeit-und berechnen Sie den Mittelwertversionsparameter κ unter Verwendung des AR (1) -Prozesses
  • Volatilität mit Fourier -Serie passen

Den Mittelwert anpassen

Durch die Anpassung unserer Daten an 80% der Daten können wir unsere SDE später verwenden, um die Temperaturen zu prognostizieren. Unser Mittelwert ist eine OLS -Regression, die S (T) zu unseren Daten anpasst.

Grafik des Autors
# --------------------
# Config (parameters and paths for evaluation)
# --------------------

CITY        = "Mumbai, India"     # Title of town being analyzed (utilized in labels/plots)
SEED        = 42                  # Random seed for reproducibility of outcomes
SPLIT_FRAC  = 0.80                # Fraction of information to make use of for coaching (relaxation for testing)
MEAN_HARM   = 2                   # Variety of harmonic phrases to make use of for modeling the imply (seasonality)
VOL_HARM    = 3                   # Variety of harmonic phrases to make use of for modeling volatility (seasonality)
LJUNG_LAGS  = 10                  # Variety of lags for Ljung-Field check (verify autocorrelation in residuals)
EPS         = 1e-12               # Small worth to keep away from division by zero or log(0) points
MIN_TEST_N  = 8                   # Minimal variety of check factors required to maintain a legitimate check set

# --------------------
# Paths (the place enter/output information are saved)
# --------------------

# Base listing in Google Drive the place local weather information and outcomes are saved
BASE_DIR    = Path("/content material/drive/MyDrive/TDS/Local weather")

# Subdirectory particularly for outputs of the Mumbai evaluation
OUT_BASE    = BASE_DIR / "Benth_Mumbai"

# Subfolder for saving plots generated throughout evaluation
PLOTS_DIR   = OUT_BASE / "plots"

# Make sure the output directories exist (create them in the event that they don’t)
OUT_BASE.mkdir(dad and mom=True, exist_ok=True)
PLOTS_DIR.mkdir(dad and mom=True, exist_ok=True)

# Path to the local weather information CSV file (enter dataset)
CSV_PATH    = BASE_DIR / "climate_data.csv"

Es ist viel schwieriger, das Sign im Rauschen zu sehen, wenn wir uns die Residuen unserer Regression betrachten. Wir könnten versucht sein anzunehmen, dass dies weiße Noise ist, aber wir würden uns irren. Diese Daten sind immer noch abhängig, eine serielle Abhängigkeit. Wenn wir die Volatilität später mit einer Fourier -Serie modellieren möchten, müssen wir diese Abhängigkeit in die Daten berücksichtigen.

Grafik des Autors
# =================================================================
# 1) MEAN MODEL: residuals after becoming seasonal imply + diagnostics
# =================================================================

# Residuals after subtracting fitted seasonal imply (earlier than AR step)
mu_train = mean_fit.predict(prepare)
x_train = prepare("DAT") - mu_train

# Save imply mannequin regression abstract to textual content file
save_model_summary_txt(mean_fit, DIAG_DIR / f"{m_slug}_mean_OLS_summary.txt")

# --- Plot: noticed DAT vs fitted seasonal imply (prepare + check) ---
fig = plt.determine(figsize=(12,5))
plt.plot(prepare("Date"), prepare("DAT"), lw=1, alpha=0.8, label="DAT (prepare)")
plt.plot(prepare("Date"), mu_train, lw=2, label="μ̂

# Predict and plot fitted imply for check set
mu_test = mean_fit.predict(check(("pattern") + (c for c in prepare.columns if c.startswith(("cos","sin")))(:2*MEAN_HARM)))
plt.plot(check("Date"), check("DAT"), lw=1, alpha=0.8, label="DAT (check)")
plt.plot(check("Date"), mu_test, lw=2, label="μ̂

plt.title("Mumbai — DAT and seasonal imply match")
plt.xlabel("Date"); plt.ylabel("Temperature (DAT)")
plt.legend()
fig.tight_layout(); fig.savefig(DIAG_DIR / f"{m_slug}_mean_fit_timeseries.png", dpi=160); plt.shut(fig)

# --- Plot: imply residuals (time sequence) ---
fig = plt.determine(figsize=(12,4))
plt.plot(prepare("Date"), x_train, lw=1)
plt.axhline(0, shade="ok", lw=1)
plt.title("Mumbai — Residuals after imply match (x_t = DAT - μ̂)")
plt.xlabel("Date"); plt.ylabel("x_t")
fig.tight_layout(); fig.savefig(DIAG_DIR / f"{m_slug}_mean_residuals_timeseries.png", dpi=160); plt.shut(fig)

κ, ϕ und der autoregressive Prozess

Passen Sie einen AR (1) -Prozess an. Dies sagt Ihnen die Geschwindigkeit der mittleren Umkehrung.

Ein AR (1) -Prozess kann die Abhängigkeit zwischen unserem Relaxation zum Zeitschritt T auf dem Wert bei T-1 erfassen. Der genaue Wert für κ hängt vom Ort ab. Für Mumbai κ = 0,861. Dies bedeutet, dass die Temperaturen ziemlich schnell zum Mittelwert zurückkehren.

.

# =======================================================
# 2) AR(1) MODEL: match and residual diagnostics
# =======================================================

# Extract AR(1) parameter φ and save abstract
phi = float(ar_fit.params.iloc(0)) if len(ar_fit.params) else np.nan
save_model_summary_txt(ar_fit, DIAG_DIR / f"{m_slug}_ar1_summary.txt")

# --- Scatterplot: x_t vs x_{t-1} with fitted line φ x_{t-1} ---
x_t = x_train.iloc(1:).values
x_tm1 = x_train.iloc(:-1).values
x_pred = phi * x_tm1
fig = plt.determine(figsize=(5.8,5.2))
plt.scatter(x_tm1, x_t, s=10, alpha=0.6, label="Noticed")
xline = np.linspace(np.min(x_tm1), np.max(x_tm1), 2)
plt.plot(xline, phi*xline, lw=2, label=f"Fitted: x_t = {phi:.3f} x_(t-1)")
plt.title("Mumbai — AR(1) scatter: x_t vs x_{t-1}")
plt.xlabel("x_{t-1}"); plt.ylabel("x_t"); plt.legend()
fig.tight_layout(); fig.savefig(DIAG_DIR / f"{m_slug}_ar1_scatter.png", dpi=160); plt.shut(fig)

# --- Time sequence: precise vs fitted AR(1) values ---
fig = plt.determine(figsize=(12,4))
plt.plot(prepare("Date").iloc(1:), x_t, lw=1, label="x_t")
plt.plot(prepare("Date").iloc(1:), x_pred, lw=2, label=r"$hat{x}_t = phi x_{t-1}$")
plt.axhline(0, shade="ok", lw=1)
plt.title("Mumbai — AR(1) fitted vs noticed deviations")
plt.xlabel("Date"); plt.ylabel("x_t")
plt.legend()
fig.tight_layout(); fig.savefig(DIAG_DIR / f"{m_slug}_ar1_timeseries_fit.png", dpi=160); plt.shut(fig)

# --- Save AR diagnostics (φ + Ljung-Field check p-value) ---
from statsmodels.stats.diagnostic import acorr_ljungbox
attempt:
    lb = acorr_ljungbox(e_train, lags=(10), return_df=True)
    lb_p = float(lb("lb_pvalue").iloc(0))   # check for autocorrelation
    lb_stat = float(lb("lb_stat").iloc(0))
besides Exception:
    lb_p = np.nan; lb_stat = np.nan

pd.DataFrame(({"phi": phi, "ljungbox_stat_lag10": lb_stat, "ljungbox_p_lag10": lb_p}))
    .to_csv(DIAG_DIR / f"{m_slug}_ar1_diagnostics.csv", index=False)
Grafik des Autors

In der obigen Grafik können wir sehen, wie unser AR (1) -Prozess in Orange die Temperatur des nächsten Tages von 1981 bis 2016 schätzt. Wir können die Verwendung des AR (1) -Prozesses weiter rechtfertigen und weitere Instinct in sie einbringen, indem wir Xₜ und Xₜ₋₁ aufnehmen. Hier können wir sehen, wie diese Werte eindeutig korreliert sind. Indem wir es so umrahmen, können wir auch sehen, dass der AR (1) -Prozess einfach eine lineare Regression ist. Wir müssen nicht einmal einen Drift-/Abfangbegriff hinzufügen, da wir den Mittelwert entfernt haben. Daher ist unser Abfang immer Null.

Grafik des Autors

Wenn wir uns jetzt unsere AR (1) -Reen ansehen, können wir darüber nachdenken, wie wir die Volatilität unserer Temperatur über die Zeit modellieren können. Aus der folgenden Grafik können wir sehen, wie unsere Residuen scheinbar regelmäßig über die Zeit pulsieren. Wir können postulieren, dass die Punkte im Jahr mit höheren Residuen Zeiträumen mit höherer Volatilität entsprechen.

# --- Residuals from AR(1) mannequin ---
e_train = ar_fit.resid.astype(float).values
fig = plt.determine(figsize=(12,4))
plt.plot(prepare("Date").iloc(1:), e_train, lw=1)
plt.axhline(0, shade="ok", lw=1)
plt.title("Mumbai — AR(1) residuals ε_t")
plt.xlabel("Date"); plt.ylabel("ε_t")
fig.tight_layout(); fig.savefig(DIAG_DIR / f"{m_slug}_ar1_residuals_timeseries.png", dpi=160); plt.shut(fig)
Grafik des Autors

Volatility Fourier -Serie

Unsere Lösung besteht darin, die Volatilität mit einer Fourier -Serie zu modellieren, ähnlich wie wir es für unser Mittel getan haben. Dazu müssen wir einige Transformationen in unserem Relaxation εₜ vornehmen. Wir betrachten das quadratische εₜ², weil die Volatilität per Definition nicht negativ sein kann.

( varepsilon_t = sigma_t eta_t qquad varepsilon_t^{2} = sigma_t^{2} eta_t^{2} )

Wir können uns diese Residuen als aus teilweise standardisierten Schocks ηₜ mit einem Mittelwert von 0 und einer Varianz von 1 (darstellen Zufälligkeit) und Volatilität σₜ vorstellen. Wir wollen für σₜ isolieren. Dies kann leichter erreicht werden, indem Sie das Protokoll nehmen. Noch wichtiger ist jedoch, dass unsere Fehlerbegriffe additiv additiv machen.

( displayStyle y_t: = log ( varepsilon_t^2) = unterbrace { log sigma_t^2} _ { textual content {deterministisch, saisonal}}} + unterbrace { log eta_t^2}} _ {{{{ textual content textual content textual content ua {{ textual content { textual content {{ textual content teur

Zusammenfassend lässt sich sagen, dass das Modellierungsprotokoll (εₜ²) hilft:

  • Halten Sie σ^t₂> 0
  • Reduzieren Sie den Effekt, den Ausreißer auf die Passform haben, was bedeutet, dass sie weniger signifikante Auswirkungen auf die Passform haben werden.

Nachdem wir das Protokoll (εₜ²) angemessen haben, exponentieren wir es später, wenn wir uns jemals wiederherstellen möchten.

( displayStyle WideHat { sigma} _t^{, 2} ;

# =======================================================
# 3) VOLATILITY REGRESSION: match log(ε_t^2) on seasonal harmonics
# =======================================================
if vol_fit shouldn't be None:
    # Compute log-squared residuals (proxy for variance)
    log_eps2 = np.log(e_train**2 + 1e-12)

    # Use cosine/sine harmonics as regressors for volatility
    feats = prepare.iloc(1:)((c for c in prepare.columns if c.startswith(("cos","sin")))(:2*VOL_HARM))
    vol_terms = (f"{b}{ok}" for ok in vary(1, VOL_HARM + 1) for b in ("cos","sin"))
    Xbeta = np.asarray(vol_fit.predict(prepare.iloc(1:)(vol_terms)), dtype=float)

    # --- Time plot: noticed vs fitted log-variance ---
    fig = plt.determine(figsize=(12,4))
    plt.plot(prepare("Date").iloc(1:), log_eps2, lw=1, label="log(ε_t^2)")
    plt.plot(prepare("Date").iloc(1:), Xbeta, lw=2, label="Fitted log-variance")
    plt.title("Mumbai — Volatility regression (log ε_t^2)")
    plt.xlabel("Date"); plt.ylabel("log variance")
    plt.legend()
    fig.tight_layout(); fig.savefig(DIAG_DIR / f"{m_slug}_volatility_logvar_timeseries.png", dpi=160); plt.shut(fig)

    # --- Plot: estimated volatility σ̂
    fig = plt.determine(figsize=(12,4))
    plt.plot(prepare("Date").iloc(1:), sigma_tr, lw=1.5, label="σ̂ (prepare)")
    if 'sigma_te' in globals():
        plt.plot(check("Date"), sigma_te, lw=1.5, label="σ̂ (check)")
    plt.title("Mumbai — Conditional volatility σ̂
    plt.xlabel("Date"); plt.ylabel("σ̂")
    plt.legend()
    fig.tight_layout(); fig.savefig(DIAG_DIR / f"{m_slug}_volatility_sigma_timeseries.png", dpi=160); plt.shut(fig)

    # --- Coefficients of volatility regression (with CI) ---
    vol_coef = coef_ci_frame(vol_fit).sort_values("estimate")
    fig = plt.determine(figsize=(8, max(4, 0.4*len(vol_coef))))
    y = np.arange(len(vol_coef))
    plt.errorbar(vol_coef("estimate"), y, xerr=1.96*vol_coef("stderr"), fmt="o", capsize=3)
    plt.yticks(y, vol_coef("time period"))
    plt.axvline(0, shade="ok", lw=1)
    plt.title("Mumbai — Volatility mannequin coefficients (95% CI)")
    plt.xlabel("Estimate")
    fig.tight_layout(); fig.savefig(DIAG_DIR / f"{m_slug}_volatility_coefficients.png", dpi=160); plt.shut(fig)

    # Save volatility regression abstract
    save_model_summary_txt(vol_fit, DIAG_DIR / f"{m_slug}_volatility_summary.txt")
else:
    print("Volatility mannequin not accessible (too few factors or regression failed). Skipping vol plots.")

print("Diagnostics saved to:", DIAG_DIR)
Grafik des Autors

(dt

Demo der logarithmischen Stabilisierung

Die folgenden Diagramme veranschaulichen, wie der Logarithmus bei der Anpassung der Volatilität hilft. Das Relaxation εₜ² enthält signifikante Ausreißer, teilweise aufgrund des quadratischen Betriebs, um eine constructive Sicherheitsfähigkeit auf der stabilisierten Skala zu gewährleisten. Ohne die logarithmische Stabilisierung dominieren die großen Schocks die Anpassung und das Endergebnis ist verzerrt.

Grafik des Autors
Grafik des Autors
Grafik des Autors
Grafik des Autors
# Repair the seasonal plot from the earlier cell (indexing bug) and
# add a *second situation* with uncommon massive outliers as an instance instability
# when regressing on skewed eps^2 straight.

# ------------------------------
# 1) Seasonal view (fastened indexing)
# ------------------------------
# Recreate the arrays from the prior simulation cell by re-executing the identical seed & setup
np.random.seed(7)                 # deterministic reproducibility
n_days = 730                      # two artificial years (365 * 2)
t = np.arange(n_days)             # day index 0..729
omega = 2 * np.pi / 365.0         # seasonal frequency (one-year interval)

# True (data-generating) log-variance is seasonal through sin/cos
a_true, b_true, c_true = 0.2, 0.9, -0.35
log_var_true = a_true + b_true * np.sin(omega * t) + c_true * np.cos(omega * t)

# Convert log-variance to straightforward deviation: sigma = exp(0.5 * log var)
sigma_true = np.exp(0.5 * log_var_true)

# White noise improvements; variance scaled by sigma_true
z = np.random.regular(measurement=n_days)
eps = sigma_true * z              # heteroskedastic residuals
eps2 = eps**2                     # "variance-like" goal if regressing uncooked eps^2

# Design matrix with intercept + annual sin/cos harmonics
X = np.column_stack((np.ones(n_days), np.sin(omega * t), np.cos(omega * t)))

# --- Match A: OLS on uncooked eps^2 (delicate to skew/outliers) ---
beta_A, *_ = np.linalg.lstsq(X, eps2, rcond=None)
var_hat_A = X @ beta_A            # fitted variance (might be adverse from OLS)
var_hat_A = np.clip(var_hat_A, 1e-8, None)  # clip to keep away from negatives
sigma_hat_A = np.sqrt(var_hat_A)  # convert to sigma

# --- Match B: OLS on log(eps^2) (stabilizes scale & reduces skew) ---
eps_safe = 1e-12                  # small epsilon to keep away from log(0)
y_log = np.log(eps2 + eps_safe)   # stabilized goal
beta_B, *_ = np.linalg.lstsq(X, y_log, rcond=None)
log_var_hat_B = X @ beta_B
sigma_hat_B = np.exp(0.5 * log_var_hat_B)

# Day-of-year index for seasonal averaging throughout years
doy = t % 365

# Right ordering for the primary 12 months's DOY values
order365 = np.argsort(doy(:365))

def seasonal_mean(x):
    """
    Common the 2 years day-by-day to get a single seasonal curve.
    Assumes x has size 730 (two years); returns length-365 array.
    """
    return 0.5 * (x(:365) + x(365:730))

# Plot one "artificial 12 months" view of the seasonal sigma sample
plt.determine(figsize=(12, 5))
plt.plot(np.type(doy(:365)), seasonal_mean(sigma_true)(order365), label="True sigma seasonality")
plt.plot(np.type(doy(:365)), seasonal_mean(sigma_hat_A)(order365), label="Fitted from eps^2 regression", alpha=0.9)
plt.plot(np.type(doy(:365)), seasonal_mean(sigma_hat_B)(order365), label="Fitted from log(eps^2) regression", alpha=0.9)
plt.title("Seasonal Volatility Sample: True vs Fitted (one-year view) – Fastened")
plt.xlabel("Day of 12 months")
plt.ylabel("Sigma")
plt.legend()
plt.tight_layout()
plt.present()

# ------------------------------
# 2) OUTLIER SCENARIO as an instance instability
# ------------------------------
np.random.seed(21)                # separate seed for the outlier experiment

def run_scenario(n_days=730, outlier_rate=0.05, outlier_scale=8.0):
    """
    Generate two-year heteroskedastic residuals with occasional big shocks
    to imitate heavy tails. Evaluate:
      - Match A: regress uncooked eps^2 on sin/cos (might be unstable, adverse matches)
      - Match B: regress log(eps^2) on sin/cos (extra secure underneath heavy tails)
    Return fitted sigmas and error metrics (MAE/MAPE), plus diagnostics.
    """
    t = np.arange(n_days)
    omega = 2 * np.pi / 365.0

    # Identical true seasonal log-variance as above
    a_true, b_true, c_true = 0.2, 0.9, -0.35
    log_var_true = a_true + b_true * np.sin(omega * t) + c_true * np.cos(omega * t)
    sigma_true = np.exp(0.5 * log_var_true)

    # Base regular improvements
    z = np.random.regular(measurement=n_days)

    # Inject uncommon, big shocks to create heavy tails in eps^2
    masks = np.random.rand(n_days) < outlier_rate
    z(masks) *= outlier_scale

    # Heteroskedastic residuals and their squares
    eps = sigma_true * z
    eps2 = eps**2

    # Identical sin/cos design
    X = np.column_stack((np.ones(n_days), np.sin(omega * t), np.cos(omega * t)))

    # --- Match A: uncooked eps^2 on X (OLS) ---
    beta_A, *_ = np.linalg.lstsq(X, eps2, rcond=None)
    var_hat_A_raw = X @ beta_A
    neg_frac = np.imply(var_hat_A_raw < 0.0)        # fraction of adverse variance predictions
    var_hat_A = np.clip(var_hat_A_raw, 1e-8, None) # clip to make sure non-negative variance
    sigma_hat_A = np.sqrt(var_hat_A)

    # --- Match B: log(eps^2) on X (OLS on log-scale) ---
    y_log = np.log(eps2 + 1e-12)
    beta_B, *_ = np.linalg.lstsq(X, y_log, rcond=None)
    log_var_hat_B = X @ beta_B
    sigma_hat_B = np.exp(0.5 * log_var_hat_B)

    # Error metrics evaluating fitted sigmas to the true sigma path
    mae = lambda a, b: np.imply(np.abs(a - b))
    mape = lambda a, b: np.imply(np.abs((a - b) / (a + 1e-12))) * 100

    mae_A = mae(sigma_true, sigma_hat_A)
    mae_B = mae(sigma_true, sigma_hat_B)
    mape_A = mape(sigma_true, sigma_hat_A)
    mape_B = mape(sigma_true, sigma_hat_B)

    return {
        "t": t,
        "sigma_true": sigma_true,
        "sigma_hat_A": sigma_hat_A,
        "sigma_hat_B": sigma_hat_B,
        "eps2": eps2,
        "y_log": y_log,
        "neg_frac": neg_frac,
        "mae_A": mae_A, "mae_B": mae_B,
        "mape_A": mape_A, "mape_B": mape_B
    }

# Run with 5% outliers scaled 10x to make the purpose apparent
res = run_scenario(outlier_rate=0.05, outlier_scale=10.0)

print("nOUTLIER SCENARIO (5% of days have 10x shocks) — illustrating instability when utilizing eps^2 straight")
print(f"  MAE  (sigma):  uncooked eps^2 regression = {res('mae_A'):.4f}   |   log(eps^2) regression = {res('mae_B'):.4f}")
print(f"  MAPE (sigma):  uncooked eps^2 regression = {res('mape_A'):.2f}% |   log(eps^2) regression = {res('mape_B'):.2f}%")
print(f"  Unfavorable variance predictions earlier than clipping (uncooked match): {res('neg_frac'):.2%}")

# Visible comparability: true sigma vs two fitted approaches underneath outliers
plt.determine(figsize=(12, 5))
plt.plot(res("t"), res("sigma_true"), label="True sigma
plt.plot(res("t"), res("sigma_hat_A"), label="Fitted sigma from eps^2 regression", alpha=0.9)
plt.plot(res("t"), res("sigma_hat_B"), label="Fitted sigma from log(eps^2) regression", alpha=0.9)
plt.title("True vs Fitted Volatility with Uncommon Giant Shocks")
plt.xlabel("Day")
plt.ylabel("Sigma")
plt.legend()
plt.tight_layout()
plt.present()

# Present how the targets behave when outliers are current
plt.determine(figsize=(12, 5))
plt.plot(res("t"), res("eps2"), label="eps^2 (now extraordinarily heavy-tailed as a consequence of outliers)")
plt.title("eps^2 underneath outliers: unstable goal for regression")
plt.xlabel("Day")
plt.ylabel("eps^2")
plt.legend()
plt.tight_layout()
plt.present()

plt.determine(figsize=(12, 5))
plt.plot(res("t"), res("y_log"), label="log(eps^2): compressed & stabilized")
plt.title("log(eps^2) underneath outliers: stabilized scale")
plt.xlabel("Day")
plt.ylabel("log(eps^2)")
plt.legend()
plt.tight_layout()
plt.present()

Abschluss

Nachdem wir all diese Parameter in diese SDE eingerichtet haben, können wir in Betracht ziehen, sie zur Vorhersage zu verwenden. Aber hierher zu kommen conflict nicht einfach. Unsere Lösung stützte sich stark auf zwei Schlüsselkonzepte.

  • Funktionen können durch eine Fourier -Serie angenähert werden.
  • Der mittlere Umkehrparameter κ entspricht ϕ aus unserem AR (1) -Prozess.

Wann immer einfügen eine stochastische Differentialgleichung, wann immer stochastischIch finde es lustig, darüber nachzudenken, wie das Wort abgeleitet ist Stokhos, Bedeutung Ziel. Noch lustiger ist, wie sich dieses Wort zu verwandelte Stokhazästhai, Bedeutung Ziel/Vermutung. Dieser Prozess muss einen Fehler und ein schreckliches Ziel mit sich gebracht haben.

Referenzen

Alexandridis, AK & Zapranis, AD (2013). Wetterderivate: Modellierung und Preisgestaltung wetterbezogenes Risiko. Springer. https://doi.org/10.1007/978-1-4614-6071-8

Benth, Fe & Šaltytė Benth, J. (2007). Die Volatilität der Temperatur und die Preisgestaltung von Wetterderivaten. Quantitative Finanzen, 7 (5), 553–561. https://doi.org/10.1080/14697680601155334


Github

Webseite

Marco Hening Tallarico
Autor

Von admin

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert