Implement trend smoothing.
This commit is contained in:
@@ -105,7 +105,7 @@ def get_nice_price_points(min_price, max_price):
|
||||
|
||||
def analyze_trends(df):
|
||||
"""
|
||||
Analyze Bitcoin price trends using log returns.
|
||||
Analyze Bitcoin price trends using log returns with simple moving average smoothing.
|
||||
"""
|
||||
df = df.copy()
|
||||
|
||||
@@ -123,14 +123,18 @@ def analyze_trends(df):
|
||||
# Group by position in cycle
|
||||
position_returns = df.groupby("Cycle_Days")["Log_Return"].mean()
|
||||
|
||||
# Smooth the returns using Savitzky-Golay filter
|
||||
window = 91 # About 3 months
|
||||
if len(position_returns) > window:
|
||||
position_returns = pd.Series(
|
||||
savgol_filter(position_returns, window, 3), index=position_returns.index
|
||||
)
|
||||
# Simple moving average smoothing
|
||||
window = 60
|
||||
smoothed_returns = position_returns.rolling(
|
||||
window=window,
|
||||
center=True, # Center the window for better trend capture
|
||||
min_periods=int(window / 2), # Allow partial windows to reduce edge effects
|
||||
).mean()
|
||||
|
||||
return position_returns
|
||||
# Fill any NaN values at the edges
|
||||
smoothed_returns = smoothed_returns.fillna(method="bfill").fillna(method="ffill")
|
||||
|
||||
return smoothed_returns
|
||||
|
||||
|
||||
def calculate_volatility(df, short_window=30, medium_window=90, long_window=180):
|
||||
@@ -163,14 +167,14 @@ def project_prices(
|
||||
):
|
||||
"""
|
||||
Project future Bitcoin prices using Monte Carlo simulation.
|
||||
Now with enhanced volatility calculation.
|
||||
Uses enhanced trend smoothing for better predictions.
|
||||
"""
|
||||
# Calculate log returns for volatility estimation
|
||||
df = df.copy()
|
||||
df["Log_Price"] = np.log(df["Close"])
|
||||
df["Log_Return"] = df["Log_Price"].diff()
|
||||
|
||||
# Get cycle-based trends
|
||||
# Get smoothed trends
|
||||
cycle_trends = analyze_trends(df)
|
||||
|
||||
# Get current position in halving cycle
|
||||
@@ -188,24 +192,33 @@ def project_prices(
|
||||
start=last_date + timedelta(days=1), periods=days_forward, freq="D"
|
||||
)
|
||||
|
||||
# Calculate expected returns for future dates based on cycle position
|
||||
# Calculate expected returns with enhanced cycle boundary handling
|
||||
future_cycle_days = [
|
||||
(current_cycle_days + i) % (4 * 365) for i in range(days_forward)
|
||||
]
|
||||
expected_returns = np.array(
|
||||
[cycle_trends.get(day, cycle_trends.mean()) for day in future_cycle_days]
|
||||
)
|
||||
expected_returns = []
|
||||
|
||||
# Calculate enhanced volatility
|
||||
for day in future_cycle_days:
|
||||
# Get base trend value
|
||||
base_trend = cycle_trends.get(day, cycle_trends.mean())
|
||||
|
||||
# Add slight mean reversion for extreme values
|
||||
if abs(base_trend) > 2 * cycle_trends.std():
|
||||
base_trend *= 0.8 # Dampen extreme trends
|
||||
|
||||
expected_returns.append(base_trend)
|
||||
|
||||
expected_returns = np.array(expected_returns)
|
||||
|
||||
# Calculate volatility
|
||||
volatility = calculate_volatility(df)
|
||||
|
||||
# Run Monte Carlo simulation with enhanced volatility
|
||||
# Run Monte Carlo simulation
|
||||
np.random.seed(42)
|
||||
simulated_paths = np.zeros((days_forward, simulations))
|
||||
|
||||
for sim in range(simulations):
|
||||
# Generate random returns with slight skew based on expected returns
|
||||
skew = np.sign(expected_returns) * 0.1 # Small skew in direction of trend
|
||||
skew = np.sign(expected_returns) * 0.087 # Small skew in direction of trend
|
||||
returns = np.random.normal(
|
||||
loc=expected_returns + skew * volatility,
|
||||
scale=volatility,
|
||||
|
||||
Reference in New Issue
Block a user