diff --git a/model.py b/model.py index 0b6f338..1dd86a6 100644 --- a/model.py +++ b/model.py @@ -133,11 +133,37 @@ def analyze_trends(df): return position_returns +def calculate_volatility(df, short_window=30, medium_window=90, long_window=180): + """ + Calculate volatility using multiple timeframes and exponential weighting. + Returns a more nuanced estimate of current market volatility. + """ + df = df.copy() + + # Calculate log returns if not already present + if "Log_Return" not in df.columns: + df["Log_Price"] = np.log(df["Close"]) + df["Log_Return"] = df["Log_Price"].diff() + + # Calculate exponentially weighted volatilities for different timeframes + short_vol = df["Log_Return"].ewm(span=short_window).std().iloc[-1] + medium_vol = df["Log_Return"].ewm(span=medium_window).std().iloc[-1] + long_vol = df["Log_Return"].ewm(span=long_window).std().iloc[-1] + + # Blend the estimates with more weight on recent data + base_vol = 0.5 * short_vol + 0.3 * medium_vol + 0.2 * long_vol + + # Scale up volatility to target ~68% coverage + volatility_scale = 1.2 + return base_vol * volatility_scale + + def project_prices( df, days_forward=365, simulations=1000, confidence_levels=[0.95, 0.68] ): """ Project future Bitcoin prices using Monte Carlo simulation. + Now with enhanced volatility calculation. """ # Calculate log returns for volatility estimation df = df.copy() @@ -170,19 +196,23 @@ def project_prices( [cycle_trends.get(day, cycle_trends.mean()) for day in future_cycle_days] ) - # Calculate volatility using recent data - recent_volatility = df["Log_Return"].tail(90).std() + # Calculate enhanced volatility + volatility = calculate_volatility(df) - # Run Monte Carlo simulation - np.random.seed(42) # For reproducibility + # Run Monte Carlo simulation with enhanced volatility + np.random.seed(42) simulated_paths = np.zeros((days_forward, simulations)) for sim in range(simulations): - # Generate random returns using cycle-aware expected returns + # Generate random returns with slight skew based on expected returns + skew = np.sign(expected_returns) * 0.1 # Small skew in direction of trend returns = np.random.normal( - loc=expected_returns, scale=recent_volatility, size=days_forward + loc=expected_returns + skew * volatility, + scale=volatility, + size=days_forward, ) - # Since we're using log returns, we can simply sum them + + # Calculate price path cumulative_returns = np.cumsum(returns) price_path = last_price * np.exp(cumulative_returns) simulated_paths[:, sim] = price_path