Fix create_plots() output.

This commit is contained in:
sam
2024-11-15 14:08:16 -08:00
parent b024a38040
commit 4975e1647d
+61 -124
View File
@@ -512,7 +512,7 @@ def analyze_bitcoin_prices(csv_path):
def create_plots(df, start=None, end=None, project_days=365): def create_plots(df, start=None, end=None, project_days=365):
""" """
Create plots including historical data and future projections. Create enhanced plots including market maturity visualization.
""" """
# Filter data based on date range # Filter data based on date range
mask = pd.Series(True, index=df.index) mask = pd.Series(True, index=df.index)
@@ -530,20 +530,20 @@ def create_plots(df, start=None, end=None, project_days=365):
maturity_score = calculate_market_maturity_score(plot_df) maturity_score = calculate_market_maturity_score(plot_df)
plot_df["Market_Maturity"] = maturity_score plot_df["Market_Maturity"] = maturity_score
# Generate projections with market maturity adjustments # Generate projections
projections = project_prices(plot_df, days_forward=project_days) 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 with additional subplot for maturity # Create figure with adjusted size for additional subplot
fig = plt.figure(figsize=(15, 18)) # Made taller to accommodate new subplot fig = plt.figure(figsize=(15, 20)) # Increased height 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(5, 1, 1) # Changed to 5,1 grid ax1 = plt.subplot(5, 1, 1)
# 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")
@@ -582,48 +582,54 @@ def create_plots(df, start=None, end=None, project_days=365):
# Customize y-axis # Customize y-axis
ax1.yaxis.set_major_formatter(plt.FuncFormatter(format_price)) ax1.yaxis.set_major_formatter(plt.FuncFormatter(format_price))
# Set custom y-axis ticks at meaningful price points
min_price = min(plot_df["Low"].min(), projections["Lower_95"].min()) min_price = min(plot_df["Low"].min(), projections["Lower_95"].min())
max_price = max(plot_df["High"].max(), projections["Upper_95"].max()) max_price = max(plot_df["High"].max(), projections["Upper_95"].max())
price_points = get_nice_price_points(min_price, max_price) price_points = get_nice_price_points(min_price, max_price)
ax1.set_yticks(price_points) ax1.set_yticks(price_points)
ax1.tick_params(axis="y", labelsize=8)
# Adjust y-axis label properties
ax1.tick_params(axis="y", labelsize=8) # Smaller font size
# Add some padding to prevent label cutoff
ax1.margins(y=0.02) ax1.margins(y=0.02)
# Adjust label padding to prevent overlap
ax1.yaxis.set_tick_params(pad=1)
# Add grid lines with adjusted opacity
ax1.grid(True, which="major", linestyle="-", alpha=0.5) ax1.grid(True, which="major", linestyle="-", alpha=0.5)
ax1.grid(True, which="minor", linestyle=":", alpha=0.2) ax1.grid(True, which="minor", linestyle=":", alpha=0.2)
ax1.set_title("Bitcoin Price History and Projections (Log Scale)" + hist_date_range) ax1.set_title("Bitcoin Price History and Projections (Log Scale)" + hist_date_range)
# Make legend font size smaller too for consistency
ax1.legend(fontsize=8) ax1.legend(fontsize=8)
# 2. Rolling volatility # 2. Market Maturity Score
ax2 = plt.subplot(4, 1, 2) ax2 = plt.subplot(5, 1, 2)
ax2.plot( 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_ylim(0, 1)
ax2.grid(True, alpha=0.3)
ax2.legend()
# Add futures launch annotation
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, 0.95, "Futures\nLaunch", rotation=90, va="top", ha="right"
)
# 3. Rolling volatility
ax3 = plt.subplot(5, 1, 3)
ax3.plot(
plot_df["Date"], plot_df["Date"],
plot_df["Rolling_Volatility_30d"], plot_df["Rolling_Volatility_30d"],
"r-", "r-",
label="30-Day Rolling Volatility", label="30-Day Rolling Volatility",
) )
ax2.set_title("30-Day Rolling Volatility (Annualized)" + hist_date_range) ax3.set_title("30-Day Rolling Volatility (Annualized)" + hist_date_range)
ax2.set_xlabel("Date") ax3.set_ylabel("Volatility")
ax2.set_ylabel("Volatility") ax3.grid(True)
ax2.grid(True) ax3.yaxis.set_major_formatter(plt.FuncFormatter(lambda y, _: "{:.0%}".format(y)))
ax2.yaxis.set_major_formatter(plt.FuncFormatter(lambda y, _: "{:.0%}".format(y))) ax3.legend()
ax2.legend()
# 3. Returns distribution # 4. Returns distribution
ax3 = plt.subplot(4, 1, 3) ax4 = plt.subplot(5, 1, 4)
returns_mean = plot_df["Daily_Return"].mean() returns_mean = plot_df["Daily_Return"].mean()
returns_std = plot_df["Daily_Return"].std() returns_std = plot_df["Daily_Return"].std()
filtered_returns = plot_df["Daily_Return"][ filtered_returns = plot_df["Daily_Return"][
@@ -631,30 +637,28 @@ def create_plots(df, start=None, end=None, project_days=365):
& (plot_df["Daily_Return"] < returns_mean + 5 * returns_std) & (plot_df["Daily_Return"] < returns_mean + 5 * returns_std)
] ]
sns.histplot(filtered_returns, bins=100, ax=ax3) sns.histplot(filtered_returns, bins=100, ax=ax4)
ax3.set_title( ax4.set_title(
"Distribution of Daily Returns (Excluding Extreme Outliers)" + hist_date_range "Distribution of Daily Returns (Excluding Extreme Outliers)" + hist_date_range
) )
ax3.set_xlabel("Daily Return") ax4.set_xlabel("Daily Return")
ax3.set_ylabel("Count") ax4.set_ylabel("Count")
ax3.xaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: "{:.0%}".format(x))) ax4.xaxis.set_major_formatter(plt.FuncFormatter(lambda x, _: "{:.0%}".format(x)))
# Add a vertical line for mean return # Add mean line
ax3.axvline(filtered_returns.mean(), color="r", linestyle="dashed", linewidth=1) ax4.axvline(filtered_returns.mean(), color="r", linestyle="dashed", linewidth=1)
ax3.text( ax4.text(
filtered_returns.mean(), filtered_returns.mean(),
ax3.get_ylim()[1], ax4.get_ylim()[1],
"Mean", "Mean",
rotation=90, rotation=90,
va="top", va="top",
ha="right", ha="right",
) )
# 4. Projection ranges # 5. Projection ranges
ax4 = plt.subplot(4, 1, 4) ax5 = plt.subplot(5, 1, 5)
timepoints = np.array(range(30, project_days, 30))
# Calculate and plot price ranges at different future points
timepoints = np.array(range(30, 365, 30))
timepoints = timepoints[timepoints <= project_days] timepoints = timepoints[timepoints <= project_days]
ranges = [] ranges = []
@@ -662,7 +666,7 @@ def create_plots(df, start=None, end=None, project_days=365):
positions = [] positions = []
for t in timepoints: for t in timepoints:
idx = t - 1 # Convert to 0-based index idx = t - 1
ranges.extend( ranges.extend(
[ [
projections["Lower_95"].iloc[idx], projections["Lower_95"].iloc[idx],
@@ -675,93 +679,26 @@ def create_plots(df, start=None, end=None, project_days=365):
labels.extend(["95% Lower", "68% Lower", "Median", "68% Upper", "95% Upper"]) labels.extend(["95% Lower", "68% Lower", "Median", "68% Upper", "95% Upper"])
positions.extend([t] * 5) positions.extend([t] * 5)
# Plot ranges (removed violin plot) ax5.scatter(positions, ranges, alpha=0.6)
ax4.scatter(positions, ranges, alpha=0.6)
# Add lines connecting the ranges
for t in timepoints: for t in timepoints:
idx = positions.index(t) idx = positions.index(t)
ax4.plot([t] * 5, ranges[idx : idx + 5], "k-", alpha=0.3) ax5.plot([t] * 5, ranges[idx : idx + 5], "k-", alpha=0.3)
# Set log scale first ax5.set_yscale("log")
ax4.set_yscale("log")
# Get the current order of magnitude for setting appropriate ticks
min_price = min(ranges) min_price = min(ranges)
max_price = max(ranges) max_price = max(ranges)
price_points = get_nice_price_points(min_price, max_price)
# Create price points at regular intervals on log scale ax5.set_yticks(price_points)
log_min = np.floor(np.log10(min_price)) ax5.yaxis.set_major_formatter(plt.FuncFormatter(format_price))
log_max = np.ceil(np.log10(max_price)) ax5.set_title("Projected Price Ranges at Future Timepoints")
price_points = [] ax5.set_xlabel("Days Forward")
for exp in range(int(log_min), int(log_max + 1)): ax5.set_ylabel("Price (USD)")
for mult in [1, 2, 5]: ax5.grid(True, alpha=0.3)
point = mult * 10**exp ax5.set_xticks(timepoints)
if min_price <= point <= max_price:
price_points.append(point)
ax4.set_yticks(price_points)
def price_formatter(x, p):
if x >= 1e6:
return f"${x/1e6:.1f}M"
if x >= 1e3:
return f"${x/1e3:.0f}K"
return f"${x:.0f}"
# Apply formatter to major ticks
ax4.yaxis.set_major_formatter(plt.FuncFormatter(price_formatter))
# Customize the plot
ax4.set_title("Projected Price Ranges at Future Timepoints")
ax4.set_xlabel("Days Forward")
ax4.set_ylabel("Price (USD)")
ax4.grid(True, alpha=0.3)
# 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 # Adjust layout
plt.tight_layout() plt.tight_layout(h_pad=1.0) # Increased spacing between subplots
# Save the plot # Save the plot
start_str = start if start else plot_df["Date"].min().strftime("%Y-%m-%d") start_str = start if start else plot_df["Date"].min().strftime("%Y-%m-%d")