Spaces:
Sleeping
Sleeping
| # src/forecast_utils.py | |
| import numpy as np | |
| import pandas as pd | |
| TRADING_DAYS = 252 | |
| def forecast_to_annual_return(forecast_series: pd.Series, | |
| last_price: float, | |
| method: str = "avg_daily", | |
| cap: float = 2.0) -> float: | |
| """ | |
| Convert a forecasted price series into an expected annual return. | |
| Args: | |
| forecast_series (pd.Series): Forecasted prices with DatetimeIndex. | |
| last_price (float): Last observed price before forecast starts. | |
| method (str): 'avg_daily' (default, safer) or 'horizon'. | |
| cap (float): Maximum allowed annualized return (as fraction, default 2.0 = 200%). | |
| Returns: | |
| float: Annualized expected return (capped). | |
| """ | |
| forecast_series = forecast_series.dropna() | |
| if forecast_series.empty: | |
| raise ValueError("Forecast series is empty") | |
| if method == "horizon": | |
| horizon_price = forecast_series.iloc[-1] | |
| horizon_days = len(forecast_series) | |
| horizon_return = horizon_price / last_price - 1 | |
| annual_return = (1 + horizon_return) ** (TRADING_DAYS / horizon_days) - 1 | |
| elif method == "avg_daily": | |
| daily_returns = forecast_series.pct_change().dropna() | |
| if daily_returns.empty: | |
| raise ValueError("Not enough forecast points to compute daily returns") | |
| avg_daily = daily_returns.mean() | |
| annual_return = (1 + avg_daily) ** TRADING_DAYS - 1 | |
| else: | |
| raise ValueError("method must be 'avg_daily' or 'horizon'") | |
| # Cap extreme values for stability | |
| annual_return = np.clip(annual_return, -cap, cap) | |
| return annual_return | |
| import matplotlib.pyplot as plt | |
| def plot_forecast_vs_actual(actual, forecast, title="TSLA Forecast vs Actual"): | |
| """ | |
| Plot actual vs forecasted stock prices. | |
| """ | |
| fig, ax = plt.subplots(figsize=(10, 5)) | |
| ax.plot(actual.index, actual.values, label="Actual", color="blue") | |
| ax.plot(forecast.index, forecast.values, label="Forecast", color="red") | |
| ax.set_title(title) | |
| ax.set_xlabel("Date") | |
| ax.set_ylabel("Price ($)") | |
| ax.legend() | |
| ax.grid(True) | |
| return fig | |