Add market maturity projection adjustments.
This commit is contained in:
@@ -163,14 +163,111 @@ def calculate_volatility(df, short_window=30, medium_window=90, long_window=180)
|
||||
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(
|
||||
df, days_forward=365, simulations=1000, confidence_levels=[0.95, 0.68]
|
||||
):
|
||||
"""
|
||||
Project future Bitcoin prices using Monte Carlo simulation.
|
||||
Uses enhanced trend smoothing for better predictions.
|
||||
Project future Bitcoin prices using Monte Carlo simulation with market maturity adjustments.
|
||||
"""
|
||||
# Calculate log returns for volatility estimation
|
||||
# Calculate market maturity score
|
||||
maturity_score = calculate_market_maturity_score(df)
|
||||
|
||||
# Original calculations
|
||||
df = df.copy()
|
||||
df["Log_Price"] = np.log(df["Close"])
|
||||
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"
|
||||
)
|
||||
|
||||
# Calculate expected returns with enhanced cycle boundary handling
|
||||
# Calculate expected returns with cycle boundary handling
|
||||
future_cycle_days = [
|
||||
(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)
|
||||
|
||||
# Calculate volatility
|
||||
volatility = calculate_volatility(df)
|
||||
# Calculate volatility with maturity adjustment
|
||||
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
|
||||
np.random.seed(42)
|
||||
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):
|
||||
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(
|
||||
loc=expected_returns + skew * volatility,
|
||||
loc=adjusted_expected_returns + skew * volatility,
|
||||
scale=volatility,
|
||||
size=days_forward,
|
||||
)
|
||||
@@ -247,7 +357,12 @@ def project_prices(
|
||||
)
|
||||
|
||||
# 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
|
||||
|
||||
@@ -268,10 +383,17 @@ def analyze_bitcoin_prices(csv_path):
|
||||
print(df.info())
|
||||
|
||||
# Convert price columns to float and handle any potential formatting issues
|
||||
price_columns = ["Price", "Open", "High", "Low"]
|
||||
for col in price_columns:
|
||||
# Remove any commas in numbers
|
||||
numeric_columns = ["Price", "Open", "High", "Low", "Vol."] # Added Volume
|
||||
for col in numeric_columns:
|
||||
# Remove any commas and 'K'/'M' suffixes
|
||||
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")
|
||||
|
||||
# Rename columns for clarity
|
||||
@@ -282,7 +404,7 @@ def analyze_bitcoin_prices(csv_path):
|
||||
|
||||
# Print summary statistics 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
|
||||
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:
|
||||
raise ValueError("No data found for the specified date range")
|
||||
|
||||
# Generate projections
|
||||
# cycle_returns, cycle_volatility = analyze_trends(plot_df)
|
||||
projections = project_prices(plot_df, days_forward=project_days)
|
||||
# Calculate market maturity score
|
||||
maturity_score = calculate_market_maturity_score(plot_df)
|
||||
plot_df["Market_Maturity"] = maturity_score
|
||||
|
||||
# Create cycle visualization
|
||||
# visualize_cycle_patterns(plot_df, cycle_returns, cycle_volatility)
|
||||
# Generate projections with market maturity adjustments
|
||||
projections = project_prices(plot_df, days_forward=project_days)
|
||||
|
||||
# Set up the style
|
||||
plt.style.use("seaborn-v0_8")
|
||||
|
||||
# Create figure
|
||||
fig = plt.figure(figsize=(15, 15))
|
||||
# Create figure with additional subplot for maturity
|
||||
fig = plt.figure(figsize=(15, 18)) # Made taller to accommodate new subplot
|
||||
|
||||
# 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')})"
|
||||
|
||||
# 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
|
||||
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
|
||||
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
|
||||
plt.tight_layout()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user