Spaces:
Sleeping
Sleeping
Manus AI
Fix: Plotting error in core/plot.py by correcting MACD Histogram column name from 'macdh_12_26_9' to 'macdhist_12_26_9'.
0c2f6bc | import plotly.graph_objects as go | |
| import plotly.express as px | |
| import pandas as pd | |
| import logging | |
| import matplotlib.pyplot as plt | |
| from plotly.subplots import make_subplots | |
| import numpy as np | |
| logging.basicConfig(level=logging.INFO) | |
| def plot_indicators(df, ticker): | |
| try: | |
| fig = make_subplots( | |
| rows=7, cols=1, shared_xaxes=True, vertical_spacing=0.03, | |
| subplot_titles=( | |
| 'Price & Moving Averages', 'Volume', 'MACD & RSI', | |
| 'Stochastic & Williams %R', 'ADX & DI', 'ATR & CCI', 'Signal Strength' | |
| ), | |
| row_heights=[0.4, 0.1, 0.15, 0.15, 0.15, 0.15, 0.15] | |
| ) | |
| # Price and Moving Averages | |
| fig.add_trace( | |
| go.Candlestick( | |
| x=df.index, open=df['Open'], high=df['High'], low=df['Low'], close=df['value'], | |
| name='Price', increasing_line_color='#00CC96', decreasing_line_color='#EF553B' | |
| ), row=1, col=1 | |
| ) | |
| for ma in ['sma_10', 'sma_20', 'sma_50', 'ema_12', 'ema_26', 'ema_50']: | |
| if ma in df: | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df[ma], name=ma.upper(), line=dict(width=1.5)), | |
| row=1, col=1 | |
| ) | |
| if 'bbu_20_2' in df: | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df['bbu_20_2'], name='BB Upper', line=dict(color='gray', dash='dot')), | |
| row=1, col=1 | |
| ) | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df['bbm_20_2'], name='BB Middle', line=dict(color='gray')), | |
| row=1, col=1 | |
| ) | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df["bbl_20_2"], name="BB Lower", line=dict(color="gray", dash="dot")), | |
| row=1, col=1 | |
| ) | |
| # Position Size and Risk Annotation | |
| if 'atr_14' in df: | |
| atr = df['atr_14'].iloc[-1] | |
| stop_distance = atr * 2 | |
| position_size = (10000 * 0.01) / stop_distance | |
| fig.add_annotation( | |
| text=f"Position Size: {position_size:.0f} shares (1% risk, ATR {atr:.2f})", | |
| xref="paper", yref="paper", x=0.05, y=0.95, showarrow=False, | |
| font=dict(color="black", size=12) | |
| ) | |
| # Volume | |
| fig.add_trace( | |
| go.Bar(x=df.index, y=df["Volume"], name="Volume", marker_color="blue", opacity=0.5), | |
| row=2, col=1 | |
| ) | |
| # MACD & RSI | |
| if 'macd_12_26_9' in df: | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df['macd_12_26_9'], name='MACD', line=dict(color='blue')), | |
| row=3, col=1 | |
| ) | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df["macds_12_26_9"], name="MACD Signal", line=dict(color="orange")), | |
| row=3, col=1 | |
| ) | |
| fig.add_trace( | |
| go.Bar(x=df.index, y=df["macdhist_12_26_9"], name="MACD Hist", marker_color="gray"), | |
| row=3, col=1 | |
| ) | |
| if 'rsi_14' in df: | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df["rsi_14"], name="RSI 14", line=dict(color="purple")), | |
| row=3, col=1 | |
| ) | |
| fig.add_hline(y=70, line_dash="dash", line_color="red", row=3, col=1) | |
| fig.add_hline(y=30, line_dash="dash", line_color="green", row=3, col=1) | |
| if 'rsi_21' in df: | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df["rsi_21"], name="RSI 21", line=dict(color="magenta", dash="dash")), | |
| row=3, col=1 | |
| ) | |
| if 'rsi_50' in df: | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df["rsi_50"], name="RSI 50", line=dict(color="cyan", dash="dot")), | |
| row=3, col=1 | |
| ) | |
| # Stochastic & Williams %R | |
| if 'stochk_14_3_3' in df: | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df["stochk_14_3_3"], name="Stoch %K", line=dict(color="blue")), | |
| row=4, col=1 | |
| ) | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df["stochd_14_3_3"], name="Stoch %D", line=dict(color="orange")), | |
| row=4, col=1 | |
| ) | |
| fig.add_hline(y=80, line_dash="dash", line_color="red", row=4, col=1) | |
| fig.add_hline(y=20, line_dash="dash", line_color="green", row=4, col=1) | |
| if 'willr_14' in df: | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df["willr_14"], name="Williams %R", line=dict(color="green")), | |
| row=4, col=1 | |
| ) | |
| fig.add_hline(y=-20, line_dash="dash", line_color="red", row=4, col=1) | |
| fig.add_hline(y=-80, line_dash="dash", line_color="green", row=4, col=1) | |
| # ADX & DI | |
| if 'adx_14' in df: | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df['adx_14'], name='ADX', line=dict(color='blue')), | |
| row=5, col=1 | |
| ) | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df.get('pdi_14'), name='+DI', line=dict(color='green')), | |
| row=5, col=1 | |
| ) | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df.get('mdi_14'), name='-DI', line=dict(color='red')), | |
| row=5, col=1 | |
| ) | |
| fig.add_hline(y=25, line_dash="dash", line_color="black", row=5, col=1) | |
| # ATR & CCI | |
| if 'atr_14' in df: | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df["atr_14"], name="ATR", line=dict(color="orange")), | |
| row=6, col=1 | |
| ) | |
| if 'cci_20' in df: | |
| fig.add_trace( | |
| go.Scatter(x=df.index, y=df["cci_20"], name="CCI", line=dict(color="purple")), | |
| row=6, col=1 | |
| ) | |
| fig.add_hline(y=100, line_dash="dash", line_color="red", row=6, col=1) | |
| fig.add_hline(y=-100, line_dash="dash", line_color="green", row=6, col=1) | |
| # Signal Strength Plot | |
| if all(col in df for col in ['RSI_Signal', 'MACD_Signal', 'ADX_Signal', 'Sentiment_Signal', 'Model_Signal']): | |
| signal_strength = ( | |
| df['RSI_Signal'].abs() + | |
| df['MACD_Signal'].abs() + | |
| df['ADX_Signal'].abs() + | |
| df['Sentiment_Signal'].abs() + | |
| df['Model_Signal'].abs() | |
| ) | |
| fig.add_trace( | |
| go.Scatter( | |
| x=df.index, y=signal_strength, name='Signal Strength', | |
| line=dict(color='teal'), fill='tozeroy' | |
| ), row=7, col=1 | |
| ) | |
| fig.add_hline(y=3, line_dash="dash", line_color="orange", row=7, col=1, annotation_text="Strong Signal Threshold") | |
| fig.update_layout( | |
| title=f"{ticker} Price and Technical Indicators", | |
| template="plotly_white", | |
| height=2400, | |
| width=1400, | |
| showlegend=True, | |
| xaxis_rangeslider_visible=False, | |
| margin=dict(l=50, r=50, t=100, b=50), | |
| xaxis=dict(tickformat="%Y-%m-%d", minor=dict(ticks="inside", showgrid=True), gridcolor="lightgrey"), | |
| plot_bgcolor="white", | |
| paper_bgcolor="white", | |
| hovermode="x unified" | |
| ) | |
| return fig | |
| except Exception as e: | |
| logging.error(f"Plot indicators error: {e}") | |
| return None | |
| def plot_future_forecast(df, result, indicators): | |
| fig, ax = plt.subplots(figsize=(10, 6)) | |
| ax.plot(df.index, df["value"], label="Historical Value", color="blue", linewidth=2) | |
| for ind in indicators: | |
| if ind in df.columns: | |
| ax.plot(df.index, df[ind], label=ind, linestyle='--') | |
| if "latest_prediction" in result: | |
| last_date = df.index[-1] | |
| horizon = len(result["latest_prediction"]) | |
| future_dates = pd.date_range(start=last_date + pd.Timedelta(days=1), periods=horizon, freq='B') | |
| ax.plot(future_dates, result["latest_prediction"], label="Forecast Value", color="orange", linestyle="--", linewidth=2) | |
| for i, val in enumerate(result["latest_prediction"]): | |
| ax.text(future_dates[i], val, f"{val:.2f}", color="orange") | |
| ax.legend() | |
| ax.set_title("Historical Data, Indicators, and Future Forecast") | |
| ax.set_xlabel("Date") | |
| ax.set_ylabel("Value") | |
| ax.grid(True) | |
| plt.tight_layout() | |
| return fig | |
| # Other plotting functions remain unchanged | |
| def plot_forecast(result, df): | |
| try: | |
| actual = result.get("actual", []) | |
| forecast = result.get("forecast", []) | |
| if not actual or not forecast: | |
| return None | |
| dates = df.index[-len(actual):] | |
| fig = go.Figure() | |
| fig.add_trace(go.Scatter(x=dates, y=actual, name='Actual', line=dict(color='blue'))) | |
| fig.add_trace(go.Scatter(x=dates, y=forecast, name='Forecast', line=dict(color='orange'))) | |
| fig.update_layout( | |
| title="Backtest: Actual vs Forecast", | |
| template="plotly_white", | |
| height=600, | |
| xaxis_title="Date", | |
| yaxis_title="Price", | |
| xaxis=dict(tickformat="%Y-%m-%d", minor=dict(ticks="inside", showgrid=True), gridcolor="lightgrey"), | |
| yaxis=dict(gridcolor="lightgrey"), | |
| plot_bgcolor="white", | |
| paper_bgcolor="white" | |
| ) | |
| return fig | |
| except Exception as e: | |
| logging.error(f"Plot forecast error: {e}") | |
| return None | |
| # ... (other plotting functions like plot_future_forecast, plot_metrics_r2, etc., remain as provided) | |
| def plot_metrics_r2(result): | |
| try: | |
| metrics = result.get("metrics", {}) | |
| if not metrics: | |
| return None | |
| fig = go.Figure() | |
| fig.add_trace(go.Bar( | |
| x=['R²', 'MAPE'], | |
| y=[metrics.get('R2', 0), metrics.get('MAPE', 0)], | |
| marker_color=['#1E90FF', '#FF6347'] | |
| )) | |
| fig.update_layout( | |
| title="R² and MAPE Metrics", | |
| template="plotly_white", | |
| height=600, | |
| xaxis_title="Metric", | |
| yaxis_title="Value", | |
| xaxis=dict(minor=dict(ticks="inside", showgrid=True), gridcolor="lightgrey"), | |
| yaxis=dict(gridcolor="lightgrey"), | |
| plot_bgcolor="white", | |
| paper_bgcolor="white" | |
| ) | |
| return fig | |
| except Exception as e: | |
| logging.error(f"Plot R2 error: {e}") | |
| return None | |
| def plot_metrics_errors(result): | |
| try: | |
| metrics = result.get("metrics", {}) | |
| if not metrics: | |
| return None | |
| fig = go.Figure() | |
| fig.add_trace(go.Bar( | |
| x=['RMSE', 'MAE'], | |
| y=[metrics.get('RMSE', 0), metrics.get('MAE', 0)], | |
| marker_color=['#32CD32', '#9370DB'] | |
| )) | |
| fig.update_layout( | |
| title="Error Metrics", | |
| template="plotly_white", | |
| height=600, | |
| xaxis_title="Metric", | |
| yaxis_title="Value", | |
| xaxis=dict(minor=dict(ticks="inside", showgrid=True), gridcolor="lightgrey"), | |
| yaxis=dict(gridcolor="lightgrey"), | |
| plot_bgcolor="white", | |
| paper_bgcolor="white" | |
| ) | |
| return fig | |
| except Exception as e: | |
| logging.error(f"Plot metrics errors: {e}") | |
| return None | |
| def plot_metrics_precision_recall(result): | |
| try: | |
| metrics = result.get("metrics", {}) | |
| if not metrics: | |
| return None | |
| fig = go.Figure() | |
| fig.add_trace(go.Bar( | |
| x=['Precision', 'Recall'], | |
| y=[metrics.get('Precision', 0), metrics.get('Recall', 0)], | |
| marker_color=['#1E90FF', '#FF6347'] | |
| )) | |
| fig.update_layout( | |
| title="Precision and Recall Metrics", | |
| template="plotly_white", | |
| height=600, | |
| xaxis_title="Metric", | |
| yaxis_title="Value", | |
| xaxis=dict(minor=dict(ticks="inside", showgrid=True), gridcolor="lightgrey"), | |
| yaxis=dict(gridcolor="lightgrey"), | |
| plot_bgcolor="white", | |
| paper_bgcolor="white" | |
| ) | |
| return fig | |
| except Exception as e: | |
| logging.error(f"Plot precision recall error: {e}") | |
| return None | |
| def plot_metrics_risk(result): | |
| try: | |
| metrics = result.get("metrics", {}) | |
| if not metrics: | |
| return None | |
| fig = go.Figure() | |
| fig.add_trace(go.Bar( | |
| x=['MASE', 'Sharpe', 'Volatility'], | |
| y=[metrics.get('MASE', 0), metrics.get('Sharpe', 0), metrics.get('Volatility', 0)], | |
| marker_color=['#32CD32', '#9370DB', '#FFD700'] | |
| )) | |
| fig.update_layout( | |
| title="Risk Metrics", | |
| template="plotly_white", | |
| height=600, | |
| xaxis_title="Metric", | |
| yaxis_title="Value", | |
| xaxis=dict(minor=dict(ticks="inside", showgrid=True), gridcolor="lightgrey"), | |
| yaxis=dict(gridcolor="lightgrey"), | |
| plot_bgcolor="white", | |
| paper_bgcolor="white" | |
| ) | |
| return fig | |
| except Exception as e: | |
| logging.error(f"Plot risk metrics error: {e}") | |
| return None | |
| def plot_loss_curve(result): | |
| try: | |
| train_loss = result.get("train_loss", []) | |
| val_loss = result.get("val_loss", []) | |
| if not train_loss: | |
| return None | |
| epochs = list(range(1, len(train_loss) + 1)) | |
| fig = go.Figure() | |
| fig.add_trace(go.Scatter(x=epochs, y=train_loss, mode='lines', name='Train Loss', line=dict(color='#00CC96'))) | |
| fig.add_trace(go.Scatter(x=epochs, y=val_loss, mode='lines', name='Validation Loss', line=dict(color='#EF553B'))) | |
| fig.update_layout( | |
| title="Training and Validation Loss", | |
| template="plotly_white", | |
| height=600, | |
| xaxis_title="Epoch", | |
| yaxis_title="Loss", | |
| xaxis=dict(minor=dict(ticks="inside", showgrid=True), gridcolor="lightgrey"), | |
| yaxis=dict(gridcolor="lightgrey"), | |
| plot_bgcolor="white", | |
| paper_bgcolor="white" | |
| ) | |
| return fig | |
| except Exception as e: | |
| logging.error(f"Plot loss curve error: {e}") | |
| return None | |
| def plot_model_architecture(result): | |
| try: | |
| summary_text = result.get("model_summary", "No model summary available.") | |
| if not summary_text or summary_text == "No model summary available.": | |
| summary_text = "Model architecture summary could not be generated." | |
| fig = go.Figure() | |
| fig.add_annotation( | |
| text=summary_text.replace('\n', '<br>'), | |
| xref="paper", | |
| yref="paper", | |
| x=0, | |
| y=1, | |
| showarrow=False, | |
| font=dict(size=14, family="Courier New", color="#1E90FF"), | |
| align="left", | |
| bgcolor="white", | |
| bordercolor="black", | |
| borderwidth=1, | |
| width=600, | |
| height=400 | |
| ) | |
| fig.update_layout( | |
| title="Model Architecture", | |
| template="plotly_white", | |
| height=600, | |
| showlegend=False, | |
| xaxis=dict(visible=False), | |
| yaxis=dict(visible=False), | |
| plot_bgcolor="white", | |
| paper_bgcolor="white" | |
| ) | |
| return fig | |
| except Exception as e: | |
| logging.error(f"Plot model architecture error: {e}") | |
| return None | |
| def plot_signals(signals_df, ticker): | |
| try: | |
| fig = go.Figure() | |
| fig.add_trace(go.Scatter( | |
| x=signals_df.index, | |
| y=signals_df['Price'], | |
| mode='lines', | |
| name='Price' | |
| )) | |
| buy_signals = signals_df[signals_df['Signal'] == 'Buy'] | |
| sell_signals = signals_df[signals_df['Signal'] == 'Sell'] | |
| fig.add_trace(go.Scatter( | |
| x=buy_signals.index, | |
| y=buy_signals['Price'], | |
| mode='markers', | |
| marker=dict(symbol='triangle-up', size=10, color='green'), | |
| name='Buy Signal' | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=sell_signals.index, | |
| y=sell_signals['Price'], | |
| mode='markers', | |
| marker=dict(symbol='triangle-down', size=10, color='red'), | |
| name='Sell Signal' | |
| )) | |
| fig.update_layout( | |
| title=f'{ticker} Trading Signals', | |
| xaxis_title='Date', | |
| yaxis_title='Price', | |
| template="plotly_white", | |
| xaxis_rangeslider_visible=False | |
| ) | |
| return fig | |
| except Exception as e: | |
| logging.error(f"Plot signals error: {e}") | |
| return None | |
| def plot_backtest(equity_df, trades_df, ticker): | |
| try: | |
| fig = go.Figure() | |
| fig.add_trace(go.Scatter( | |
| x=equity_df.index, | |
| y=equity_df['Equity'], | |
| mode='lines', | |
| name='Equity Curve' | |
| )) | |
| buy_trades = trades_df[trades_df['Type'] == 'Buy'] | |
| sell_trades = trades_df[trades_df['Type'] == 'Sell'] | |
| exit_trades = trades_df[trades_df['Type'] == 'Exit'] | |
| fig.add_trace(go.Scatter( | |
| x=buy_trades['Date'], | |
| y=buy_trades['Price'], | |
| mode='markers', | |
| marker=dict(symbol='triangle-up', size=10, color='green'), | |
| name='Buy Trades' | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=sell_trades['Date'], | |
| y=sell_trades['Price'], | |
| mode='markers', | |
| marker=dict(symbol='triangle-down', size=10, color='red'), | |
| name='Sell Trades' | |
| )) | |
| fig.add_trace(go.Scatter( | |
| x=exit_trades['Date'], | |
| y=exit_trades['Price'], | |
| mode='markers', | |
| marker=dict(symbol='circle', size=8, color='blue'), | |
| name='Exit Trades' | |
| )) | |
| fig.update_layout( | |
| title=f'{ticker} Backtest Equity Curve and Trades', | |
| xaxis_title='Date', | |
| yaxis_title='Equity / Price', | |
| template="plotly_white", | |
| xaxis_rangeslider_visible=False | |
| ) | |
| return fig | |
| except Exception as e: | |
| logging.error(f"Plot backtest error: {e}") | |
| return None | |