Add market maturity projection adjustments.

This commit is contained in:
sam
2024-11-15 13:37:14 -08:00
parent 5b80eac207
commit b018296a51
+182 -21
View File
@@ -163,14 +163,111 @@ def calculate_volatility(df, short_window=30, medium_window=90, long_window=180)
return base_vol * volatility_scale return base_vol * volatility_scale
def calculate_market_maturity_score(df):
"""
Calculate a market maturity score (0-1) based on multiple indicators.
Higher scores indicate a more mature market.
"""
df = df.copy()
# 1. Volume-based metrics
df["log_volume"] = np.log(df["Volume"])
df["volume_ma"] = df["log_volume"].rolling(window=365).mean()
volume_growth = (df["volume_ma"] - df["volume_ma"].shift(365)) / df[
"volume_ma"
].shift(365)
# 2. Volatility maturity (lower volatility = more mature)
df["rolling_vol"] = df["Daily_Return"].rolling(window=365).std() * np.sqrt(365)
vol_maturity = 1 / (1 + df["rolling_vol"])
# 3. Market efficiency score
df["autocorr"] = (
df["Daily_Return"]
.rolling(window=30)
.apply(lambda x: abs(pd.Series(x).autocorr(1)))
)
efficiency = 1 - df["autocorr"] # Lower autocorrelation = more efficient
# 4. Futures market impact (post-2017)
futures_date = pd.Timestamp("2017-12-10")
futures_impact = (df["Date"] > futures_date).astype(float) * 0.2
# Combine scores with time-varying weights
weights = {"volume": 0.3, "volatility": 0.3, "efficiency": 0.2, "futures": 0.2}
maturity_score = (
weights["volume"] * volume_growth.clip(-1, 1).map(lambda x: (x + 1) / 2)
+ weights["volatility"] * vol_maturity
+ weights["efficiency"] * efficiency
+ weights["futures"] * futures_impact
)
# Normalize to 0-1 range and smooth
maturity_score = (maturity_score - maturity_score.min()) / (
maturity_score.max() - maturity_score.min()
)
maturity_score = maturity_score.rolling(window=30, min_periods=1).mean()
return maturity_score
def adjust_projections_for_maturity(df, projections, maturity_score):
"""
Adjust price projections based on market maturity score.
More mature markets should have tighter confidence intervals
and more conservative growth expectations.
"""
# Get final maturity score
final_maturity = maturity_score.iloc[-1]
# Adjust confidence intervals based on maturity
# More mature markets = tighter intervals
ci_adjustment = 1 - (final_maturity * 0.3) # Max 30% reduction in interval width
# Adjust expected returns based on maturity
# More mature markets = more conservative growth
returns_adjustment = 1 - (
final_maturity * 0.2
) # Max 20% reduction in expected returns
adjusted_projections = projections.copy()
# Adjust confidence intervals
for ci in [68, 95]:
upper_key = f"Upper_{ci}"
lower_key = f"Lower_{ci}"
median = adjusted_projections["Median"]
# Calculate distances from median
upper_distance = adjusted_projections[upper_key] - median
lower_distance = median - adjusted_projections[lower_key]
# Apply maturity-based adjustment
adjusted_projections[upper_key] = median + (upper_distance * ci_adjustment)
adjusted_projections[lower_key] = median - (lower_distance * ci_adjustment)
# Adjust expected trend
trend_distance = (
adjusted_projections["Expected_Trend"] - adjusted_projections["Median"]
)
adjusted_projections["Expected_Trend"] = (
adjusted_projections["Median"] + trend_distance * returns_adjustment
)
return adjusted_projections
def project_prices( def project_prices(
df, days_forward=365, simulations=1000, confidence_levels=[0.95, 0.68] df, days_forward=365, simulations=1000, confidence_levels=[0.95, 0.68]
): ):
""" """
Project future Bitcoin prices using Monte Carlo simulation. Project future Bitcoin prices using Monte Carlo simulation with market maturity adjustments.
Uses enhanced trend smoothing for better predictions.
""" """
# Calculate log returns for volatility estimation # Calculate market maturity score
maturity_score = calculate_market_maturity_score(df)
# Original calculations
df = df.copy() df = df.copy()
df["Log_Price"] = np.log(df["Close"]) df["Log_Price"] = np.log(df["Close"])
df["Log_Return"] = df["Log_Price"].diff() df["Log_Return"] = df["Log_Price"].diff()
@@ -193,7 +290,7 @@ def project_prices(
start=last_date + timedelta(days=1), periods=days_forward, freq="D" start=last_date + timedelta(days=1), periods=days_forward, freq="D"
) )
# Calculate expected returns with enhanced cycle boundary handling # Calculate expected returns with cycle boundary handling
future_cycle_days = [ future_cycle_days = [
(current_cycle_days + i) % (4 * 365) for i in range(days_forward) (current_cycle_days + i) % (4 * 365) for i in range(days_forward)
] ]
@@ -211,17 +308,30 @@ def project_prices(
expected_returns = np.array(expected_returns) expected_returns = np.array(expected_returns)
# Calculate volatility # Calculate volatility with maturity adjustment
volatility = calculate_volatility(df) base_volatility = calculate_volatility(df)
final_maturity = maturity_score.iloc[-1]
# Adjust volatility based on market maturity (more mature = lower volatility)
volatility_adjustment = 1 - (final_maturity * 0.3) # Max 30% reduction
volatility = base_volatility * volatility_adjustment
# Run Monte Carlo simulation # Run Monte Carlo simulation
np.random.seed(42) np.random.seed(42)
simulated_paths = np.zeros((days_forward, simulations)) simulated_paths = np.zeros((days_forward, simulations))
# Adjust trend expectations based on market maturity
trend_adjustment = 1 - (final_maturity * 0.2) # Max 20% reduction
adjusted_expected_returns = expected_returns * trend_adjustment
for sim in range(simulations): for sim in range(simulations):
skew = np.sign(expected_returns) * 0.087 # Small skew in direction of trend # Adjust skew based on market maturity (more mature = less skew)
base_skew = 0.087
skew_adjustment = 1 - (final_maturity * 0.4) # Max 40% reduction
skew = np.sign(adjusted_expected_returns) * base_skew * skew_adjustment
returns = np.random.normal( returns = np.random.normal(
loc=expected_returns + skew * volatility, loc=adjusted_expected_returns + skew * volatility,
scale=volatility, scale=volatility,
size=days_forward, size=days_forward,
) )
@@ -247,7 +357,12 @@ def project_prices(
) )
# Add expected trend line # Add expected trend line
results["Expected_Trend"] = last_price * np.exp(np.cumsum(expected_returns)) results["Expected_Trend"] = last_price * np.exp(
np.cumsum(adjusted_expected_returns)
)
# Add maturity score to results for analysis
results["Market_Maturity"] = final_maturity
return results return results
@@ -268,10 +383,17 @@ def analyze_bitcoin_prices(csv_path):
print(df.info()) print(df.info())
# Convert price columns to float and handle any potential formatting issues # Convert price columns to float and handle any potential formatting issues
price_columns = ["Price", "Open", "High", "Low"] numeric_columns = ["Price", "Open", "High", "Low", "Vol."] # Added Volume
for col in price_columns: for col in numeric_columns:
# Remove any commas in numbers # Remove any commas and 'K'/'M' suffixes
df[col] = df[col].astype(str).str.replace(",", "") df[col] = df[col].astype(str).str.replace(",", "")
# Convert K to thousands
df[col] = df[col].str.replace("K", "e3")
# Convert M to millions
df[col] = df[col].str.replace("M", "e6")
# Convert B to billions
df[col] = df[col].str.replace("B", "e9")
# Convert to numeric
df[col] = pd.to_numeric(df[col], errors="coerce") df[col] = pd.to_numeric(df[col], errors="coerce")
# Rename columns for clarity # Rename columns for clarity
@@ -282,7 +404,7 @@ def analyze_bitcoin_prices(csv_path):
# Print summary statistics after conversion # Print summary statistics after conversion
print("\nPrice Summary After Conversion:") print("\nPrice Summary After Conversion:")
print(df[["Close", "Open", "High", "Low"]].describe()) print(df[["Close", "Open", "High", "Low", "Volume"]].describe())
# Calculate daily returns # Calculate daily returns
df["Daily_Return"] = df["Close"].pct_change() df["Daily_Return"] = df["Close"].pct_change()
@@ -340,24 +462,24 @@ def create_plots(df, start=None, end=None, project_days=365):
if len(plot_df) == 0: if len(plot_df) == 0:
raise ValueError("No data found for the specified date range") raise ValueError("No data found for the specified date range")
# Generate projections # Calculate market maturity score
# cycle_returns, cycle_volatility = analyze_trends(plot_df) maturity_score = calculate_market_maturity_score(plot_df)
projections = project_prices(plot_df, days_forward=project_days) plot_df["Market_Maturity"] = maturity_score
# Create cycle visualization # Generate projections with market maturity adjustments
# visualize_cycle_patterns(plot_df, cycle_returns, cycle_volatility) projections = project_prices(plot_df, days_forward=project_days)
# Set up the style # Set up the style
plt.style.use("seaborn-v0_8") plt.style.use("seaborn-v0_8")
# Create figure # Create figure with additional subplot for maturity
fig = plt.figure(figsize=(15, 15)) fig = plt.figure(figsize=(15, 18)) # Made taller to accommodate new subplot
# Date range for titles # Date range for titles
hist_date_range = f" ({plot_df['Date'].min().strftime('%Y-%m-%d')} to {plot_df['Date'].max().strftime('%Y-%m-%d')})" hist_date_range = f" ({plot_df['Date'].min().strftime('%Y-%m-%d')} to {plot_df['Date'].max().strftime('%Y-%m-%d')})"
# 1. Price history and projections (log scale) # 1. Price history and projections (log scale)
ax1 = plt.subplot(4, 1, 1) ax1 = plt.subplot(5, 1, 1) # Changed to 5,1 grid
# Plot historical prices # Plot historical prices
ax1.semilogy(plot_df["Date"], plot_df["Close"], "b-", label="Historical Price") ax1.semilogy(plot_df["Date"], plot_df["Close"], "b-", label="Historical Price")
@@ -535,6 +657,45 @@ def create_plots(df, start=None, end=None, project_days=365):
# Set x-axis to show only our timepoints # Set x-axis to show only our timepoints
ax4.set_xticks(timepoints) ax4.set_xticks(timepoints)
# 2. Market Maturity Score (New)
ax2 = plt.subplot(5, 1, 2)
ax2.plot(
plot_df["Date"],
plot_df["Market_Maturity"],
color="purple",
label="Market Maturity Score",
)
ax2.set_title("Market Maturity Score" + hist_date_range)
ax2.set_xlabel("Date")
ax2.set_ylabel("Maturity Score (0-1)")
ax2.grid(True)
ax2.legend()
# Add annotations for key events
futures_date = pd.Timestamp("2017-12-10")
if futures_date >= plot_df["Date"].min() and futures_date <= plot_df["Date"].max():
ax2.axvline(futures_date, color="red", linestyle="--", alpha=0.5)
ax2.text(
futures_date,
ax2.get_ylim()[1],
"Futures\nLaunch",
rotation=90,
va="top",
ha="right",
)
# 3. Rolling volatility (now third subplot)
ax3 = plt.subplot(5, 1, 3)
# [Previous volatility plotting code...]
# 4. Returns distribution (now fourth subplot)
ax4 = plt.subplot(5, 1, 4)
# [Previous distribution plotting code...]
# 5. Projection ranges (now fifth subplot)
ax5 = plt.subplot(5, 1, 5)
# [Previous projection ranges plotting code...]
# Adjust layout # Adjust layout
plt.tight_layout() plt.tight_layout()