aromidvar commited on
Commit
b1ccdc7
·
verified ·
1 Parent(s): ad0545f

Update core/plot.py

Browse files
Files changed (1) hide show
  1. core/plot.py +173 -68
core/plot.py CHANGED
@@ -1,22 +1,22 @@
1
  import plotly.graph_objects as go
2
  import plotly.express as px
3
  import pandas as pd
4
- import logging
5
-
6
- logging.basicConfig(level=logging.DEBUG, filename="/tmp/debug.log", filemode="a")
7
-
8
- import plotly.graph_objects as go
9
- from plotly.subplots import make_subplots
10
- import pandas as pd
11
  import numpy as np
12
  import logging
13
  import matplotlib.pyplot as plt
 
 
14
  from torchviz import make_dot
 
15
 
16
- logging.basicConfig(level=logging.INFO)
17
 
18
  def plot_indicators(df, ticker):
19
  try:
 
 
 
 
20
  fig = make_subplots(
21
  rows=7, cols=1, shared_xaxes=True, vertical_spacing=0.03,
22
  subplot_titles=(
@@ -27,23 +27,33 @@ def plot_indicators(df, ticker):
27
  )
28
 
29
  # Price and Moving Averages
30
- fig.add_trace(
31
- go.Candlestick(
32
- x=df['Date'], open=df['Open'], high=df['High'], low=df['Low'], close=df['value'],
33
- name='Price', increasing_line_color='#00CC96', decreasing_line_color='#EF553B'
34
- ), row=1, col=1
35
- )
 
 
 
 
 
36
  for ma in ['sma_10', 'sma_20', 'sma_50', 'ema_12', 'ema_26', 'ema_50']:
37
  if ma in df:
 
38
  fig.add_trace(
39
  go.Scatter(x=df['Date'], y=df[ma], name=ma.upper(), line=dict(width=1.5)),
40
  row=1, col=1
41
  )
42
- # Handle both key variants: generated with '2.0' vs '2'
 
 
 
43
  bb_u = 'bbu_20_2.0' if 'bbu_20_2.0' in df else ('bbu_20_2' if 'bbu_20_2' in df else None)
44
  bb_m = 'bbm_20_2.0' if 'bbm_20_2.0' in df else ('bbm_20_2' if 'bbm_20_2' in df else None)
45
  bb_l = 'bbl_20_2.0' if 'bbl_20_2.0' in df else ('bbl_20_2' if 'bbl_20_2' in df else None)
46
  if bb_u and bb_m and bb_l:
 
47
  fig.add_trace(
48
  go.Scatter(x=df['Date'], y=df[bb_u], name='BB Upper', line=dict(color='gray', dash='dot')),
49
  row=1, col=1
@@ -56,9 +66,12 @@ def plot_indicators(df, ticker):
56
  go.Scatter(x=df['Date'], y=df[bb_l], name='BB Lower', line=dict(color='gray', dash='dot')),
57
  row=1, col=1
58
  )
 
 
59
 
60
- # Enhanced Signal Plotting
61
- if 'Signal' in df.columns:
 
62
  buy_signals = df[df['Signal'] == 'Buy']
63
  sell_signals = df[df['Signal'] == 'Sell']
64
  hold_signals = df[df['Signal'] == 'Hold']
@@ -83,26 +96,36 @@ def plot_indicators(df, ticker):
83
  opacity=0.5
84
  ), row=1, col=1
85
  )
 
 
86
 
87
  # Position Size and Risk Annotation
88
  if 'atr_14' in df:
89
  atr = df['atr_14'].iloc[-1]
90
  stop_distance = atr * 2
91
- position_size = (10000 * 0.01) / stop_distance
 
92
  fig.add_annotation(
93
  text=f"Position Size: {position_size:.0f} shares (1% risk, ATR {atr:.2f})",
94
  xref="paper", yref="paper", x=0.05, y=0.95, showarrow=False,
95
  font=dict(color="black", size=12)
96
  )
 
 
97
 
98
  # Volume
99
- fig.add_trace(
100
- go.Bar(x=df['Date'], y=df['Volume'], name='Volume', marker_color='blue', opacity=0.5),
101
- row=2, col=1
102
- )
 
 
 
 
103
 
104
  # MACD & RSI
105
- if 'macd_12_26_9' in df:
 
106
  fig.add_trace(
107
  go.Scatter(x=df['Date'], y=df['macd_12_26_9'], name='MACD', line=dict(color='blue')),
108
  row=3, col=1
@@ -115,28 +138,35 @@ def plot_indicators(df, ticker):
115
  go.Bar(x=df['Date'], y=df['macdh_12_26_9'], name='MACD Hist', marker_color='gray'),
116
  row=3, col=1
117
  )
 
 
 
118
  if 'rsi_14' in df:
 
119
  fig.add_trace(
120
  go.Scatter(x=df['Date'], y=df['rsi_14'], name='RSI 14', line=dict(color='purple')),
121
  row=3, col=1
122
  )
123
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[70, 70],
124
- showlegend=False, line=dict(color='red', dash='dash', width=1)), row=3, col=1)
125
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[30, 30],
126
- showlegend=False, line=dict(color='green', dash='dash', width=1)), row=3, col=1)
127
- if 'rsi_21' in df:
128
- fig.add_trace(
129
- go.Scatter(x=df['Date'], y=df['rsi_21'], name='RSI 21', line=dict(color='magenta', dash='dash')),
130
- row=3, col=1
131
- )
132
- if 'rsi_50' in df:
133
- fig.add_trace(
134
- go.Scatter(x=df['Date'], y=df['rsi_50'], name='RSI 50', line=dict(color='cyan', dash='dot')),
135
- row=3, col=1
136
- )
 
 
137
 
138
  # Stochastic & Williams %R
139
- if 'stochk_14_3_3' in df:
 
140
  fig.add_trace(
141
  go.Scatter(x=df['Date'], y=df['stochk_14_3_3'], name='Stoch %K', line=dict(color='blue')),
142
  row=4, col=1
@@ -146,54 +176,71 @@ def plot_indicators(df, ticker):
146
  row=4, col=1
147
  )
148
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[80, 80],
149
- showlegend=False, line=dict(color='red', dash='dash', width=1)), row=4, col=1)
150
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[20, 20],
151
- showlegend=False, line=dict(color='green', dash='dash', width=1)), row=4, col=1)
 
 
 
152
  if 'willr_14' in df:
 
153
  fig.add_trace(
154
  go.Scatter(x=df['Date'], y=df['willr_14'], name='Williams %R', line=dict(color='green')),
155
  row=4, col=1
156
  )
157
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[-20, -20],
158
- showlegend=False, line=dict(color='red', dash='dash', width=1)), row=4, col=1)
159
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[-80, -80],
160
- showlegend=False, line=dict(color='green', dash='dash', width=1)), row=4, col=1)
 
 
161
 
162
  # ADX & DI
163
- if 'adx_14' in df:
 
164
  fig.add_trace(
165
  go.Scatter(x=df['Date'], y=df['adx_14'], name='ADX', line=dict(color='blue')),
166
  row=5, col=1
167
  )
168
  fig.add_trace(
169
- go.Scatter(x=df['Date'], y=df.get('pdi_14'), name='+DI', line=dict(color='green')),
170
  row=5, col=1
171
  )
172
  fig.add_trace(
173
- go.Scatter(x=df['Date'], y=df.get('mdi_14'), name='-DI', line=dict(color='red')),
174
  row=5, col=1
175
  )
176
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[25, 25],
177
- showlegend=False, line=dict(color='black', dash='dash', width=1)), row=5, col=1)
 
 
178
 
179
  # ATR & CCI
180
  if 'atr_14' in df:
 
181
  fig.add_trace(
182
  go.Scatter(x=df['Date'], y=df['atr_14'], name='ATR', line=dict(color='orange')),
183
  row=6, col=1
184
  )
 
 
 
185
  if 'cci_20' in df:
 
186
  fig.add_trace(
187
  go.Scatter(x=df['Date'], y=df['cci_20'], name='CCI', line=dict(color='purple')),
188
  row=6, col=1
189
  )
190
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[100, 100],
191
- showlegend=False, line=dict(color='red', dash='dash', width=1)), row=6, col=1)
192
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[-100, -100],
193
- showlegend=False, line=dict(color='green', dash='dash', width=1)), row=6, col=1)
 
 
194
 
195
- # Signal Strength Plot
196
  if all(col in df for col in ['RSI_Signal', 'MACD_Signal', 'ADX_Signal', 'Sentiment_Signal', 'Model_Signal']):
 
197
  signal_strength = (
198
  df['RSI_Signal'].abs() +
199
  df['MACD_Signal'].abs() +
@@ -208,7 +255,9 @@ def plot_indicators(df, ticker):
208
  ), row=7, col=1
209
  )
210
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[3, 3],
211
- showlegend=False, line=dict(color='orange', dash='dash', width=1)), row=7, col=1)
 
 
212
 
213
  fig.update_layout(
214
  title=f"{ticker} Price and Technical Indicators",
@@ -223,21 +272,33 @@ def plot_indicators(df, ticker):
223
  paper_bgcolor="white",
224
  hovermode="x unified"
225
  )
 
226
  return fig
227
  except Exception as e:
228
- logging.error(f"Plot indicators error: {e}")
229
  return None
 
230
  def plot_future_forecast(df, result, timeframe):
231
  try:
 
232
  fig = go.Figure()
233
- fig.add_trace(go.Scatter(x=df['Date'], y=df['value'], name='Historical Close', line=dict(color='blue')))
 
 
 
 
 
234
  if "latest_prediction" in result:
235
  last_date = df['Date'].iloc[-1]
236
  horizon = len(result["latest_prediction"]) if hasattr(result["latest_prediction"], '__len__') else 1
237
  freq_map = {'1m': 'T', '2m': '2T', '5m': '5T', '15m': '15T', '30m': '30T', '60m': 'H', '90m': '90T', '1h': 'H', '1d': 'D', '5d': '5D', '1wk': 'W', '1mo': 'M', '3mo': '3M'}
238
  future_dates = pd.date_range(start=last_date + pd.Timedelta(days=1), periods=horizon, freq=freq_map.get(timeframe, 'D'))
239
  pred_values = result["latest_prediction"].tolist() if hasattr(result["latest_prediction"], 'tolist') else list(result["latest_prediction"]) if hasattr(result["latest_prediction"], '__iter__') else [result["latest_prediction"]]
 
240
  fig.add_trace(go.Scatter(x=future_dates, y=pred_values, name='Forecast Close', line=dict(color='orange', dash='dash')))
 
 
 
241
  fig.update_layout(
242
  title="Historical Data and Future Forecast",
243
  template="plotly_white",
@@ -245,19 +306,22 @@ def plot_future_forecast(df, result, timeframe):
245
  xaxis_title="Date",
246
  yaxis_title="Value"
247
  )
 
248
  return fig
249
  except Exception as e:
250
- logging.error(f"Plot future forecast error: {e}")
251
  return None
252
-
253
- # Other plotting functions remain unchanged
254
  def plot_forecast(result, df):
255
  try:
 
256
  actual = result.get("actual", [])
257
  forecast = result.get("forecast", [])
258
  if not actual or not forecast:
 
259
  return None
260
  dates = df['Date'].iloc[-len(actual):]
 
261
  fig = go.Figure()
262
  fig.add_trace(go.Scatter(x=dates, y=actual, name='Actual', line=dict(color='blue')))
263
  fig.add_trace(go.Scatter(x=dates, y=forecast, name='Forecast', line=dict(color='orange')))
@@ -272,18 +336,20 @@ def plot_forecast(result, df):
272
  plot_bgcolor="white",
273
  paper_bgcolor="white"
274
  )
 
275
  return fig
276
  except Exception as e:
277
- logging.error(f"Plot forecast error: {e}")
278
  return None
279
 
280
- # ... (other plotting functions like plot_future_forecast, plot_metrics_r2, etc., remain as provided)
281
-
282
  def plot_metrics_r2(result):
283
  try:
 
284
  metrics = result.get("metrics", {})
285
  if not metrics:
 
286
  return None
 
287
  fig = go.Figure()
288
  fig.add_trace(go.Bar(
289
  x=['R²', 'MAPE'],
@@ -301,16 +367,20 @@ def plot_metrics_r2(result):
301
  plot_bgcolor="white",
302
  paper_bgcolor="white"
303
  )
 
304
  return fig
305
  except Exception as e:
306
- logging.error(f"Plot R2 error: {e}")
307
  return None
308
 
309
  def plot_metrics_errors(result):
310
  try:
 
311
  metrics = result.get("metrics", {})
312
  if not metrics:
 
313
  return None
 
314
  fig = go.Figure()
315
  fig.add_trace(go.Bar(
316
  x=['RMSE', 'MAE'],
@@ -328,16 +398,20 @@ def plot_metrics_errors(result):
328
  plot_bgcolor="white",
329
  paper_bgcolor="white"
330
  )
 
331
  return fig
332
  except Exception as e:
333
- logging.error(f"Plot metrics errors: {e}")
334
  return None
335
 
336
  def plot_metrics_precision_recall(result):
337
  try:
 
338
  metrics = result.get("metrics", {})
339
  if not metrics:
 
340
  return None
 
341
  fig = go.Figure()
342
  fig.add_trace(go.Bar(
343
  x=['Precision', 'Recall'],
@@ -355,16 +429,20 @@ def plot_metrics_precision_recall(result):
355
  plot_bgcolor="white",
356
  paper_bgcolor="white"
357
  )
 
358
  return fig
359
  except Exception as e:
360
- logging.error(f"Plot precision recall error: {e}")
361
  return None
362
 
363
  def plot_metrics_risk(result):
364
  try:
 
365
  metrics = result.get("metrics", {})
366
  if not metrics:
 
367
  return None
 
368
  fig = go.Figure()
369
  fig.add_trace(go.Bar(
370
  x=['MASE', 'Sharpe', 'Volatility'],
@@ -382,17 +460,21 @@ def plot_metrics_risk(result):
382
  plot_bgcolor="white",
383
  paper_bgcolor="white"
384
  )
 
385
  return fig
386
  except Exception as e:
387
- logging.error(f"Plot risk metrics error: {e}")
388
  return None
389
 
390
  def plot_loss_curve(result):
391
  try:
 
392
  train_loss = result.get("train_loss", [])
393
  val_loss = result.get("val_loss", [])
394
  if not train_loss:
 
395
  return None
 
396
  epochs = list(range(1, len(train_loss) + 1))
397
  fig = go.Figure()
398
  fig.add_trace(go.Scatter(x=epochs, y=train_loss, mode='lines', name='Train Loss', line=dict(color='#00CC96')))
@@ -408,21 +490,25 @@ def plot_loss_curve(result):
408
  plot_bgcolor="white",
409
  paper_bgcolor="white"
410
  )
 
411
  return fig
412
  except Exception as e:
413
- logging.error(f"Plot loss curve error: {e}")
414
  return None
415
 
416
-
417
  def plot_model_architecture(result):
418
  try:
 
419
  model = result.get('model')
420
  if not model:
 
421
  return None
 
422
  dummy_input = torch.randn(1, result['arch']['window'], result['arch']['input_dim'])
423
  graph = make_dot(model(dummy_input), params=dict(model.named_parameters()))
424
  graph.format = 'png'
425
  graph.render("model_arch", cleanup=True)
 
426
  fig = go.Figure()
427
  fig.add_layout_image(
428
  dict(
@@ -438,21 +524,35 @@ def plot_model_architecture(result):
438
  template="plotly_dark",
439
  showlegend=False
440
  )
 
441
  return fig
442
  except Exception as e:
443
- logging.error(f"Plot model architecture error: {e}")
444
  return None
445
 
446
  def plot_signals(signals_df, ticker):
447
  try:
448
- logging.debug(f"Plotting signals for {ticker}")
 
449
  fig = go.Figure()
450
  x_col = 'Date' if 'Date' in signals_df.columns else signals_df.index
451
- fig.add_trace(go.Scatter(x=signals_df[x_col], y=signals_df['Price'], mode='lines', name='Price', line=dict(color='blue')))
 
 
 
 
452
  buy_signals = signals_df[signals_df['Signal'] == 'Buy']
453
  sell_signals = signals_df[signals_df['Signal'] == 'Sell']
454
- fig.add_trace(go.Scatter(x=buy_signals[x_col], y=buy_signals['Price'], mode='markers', name='Buy', marker=dict(symbol='triangle-up', size=10, color='green')))
455
- fig.add_trace(go.Scatter(x=sell_signals[x_col], y=sell_signals['Price'], mode='markers', name='Sell', marker=dict(symbol='triangle-down', size=10, color='red')))
 
 
 
 
 
 
 
 
456
  fig.update_layout(
457
  title=f"{ticker} Trading Signals",
458
  xaxis_title="Date",
@@ -468,14 +568,18 @@ def plot_signals(signals_df, ticker):
468
 
469
  def plot_backtest(result, df, ticker):
470
  try:
 
471
  actual = result.get('actual', [])
472
  forecast = result.get('forecast', [])
473
  if len(actual) == 0 or len(forecast) == 0:
 
474
  return None
 
475
  last_historical_date = df['Date'].iloc[-len(actual) - 1]
476
  historical_dates = df['Date'].iloc[-len(actual) - 1: -len(actual) + len(actual)]
477
  forecast_dates = pd.date_range(start=last_historical_date + timedelta(days=1), periods=len(forecast))
478
  historical_values = df['value'].iloc[-len(actual) - 1: -len(actual) + len(actual)]
 
479
  fig = go.Figure()
480
  fig.add_trace(go.Scatter(x=historical_dates, y=historical_values, mode='lines', name='Historical', line=dict(color='blue')))
481
  fig.add_trace(go.Scatter(x=forecast_dates, y=forecast, mode='lines', name='Forecast', line=dict(color='orange', dash='dash')))
@@ -486,7 +590,8 @@ def plot_backtest(result, df, ticker):
486
  template="plotly_dark",
487
  showlegend=True
488
  )
 
489
  return fig
490
  except Exception as e:
491
- logging.error(f"Plot backtest error: {e}")
492
  return None
 
1
  import plotly.graph_objects as go
2
  import plotly.express as px
3
  import pandas as pd
 
 
 
 
 
 
 
4
  import numpy as np
5
  import logging
6
  import matplotlib.pyplot as plt
7
+ import base64
8
+ from datetime import timedelta
9
  from torchviz import make_dot
10
+ import torch
11
 
12
+ logging.basicConfig(level=logging.DEBUG, filename="/tmp/debug.log", filemode="a", format='%(asctime)s - %(levelname)s - %(message)s')
13
 
14
  def plot_indicators(df, ticker):
15
  try:
16
+ logging.info(f"Starting plot_indicators for {ticker}")
17
+ logging.debug(f"DataFrame columns: {df.columns.tolist()}")
18
+ logging.debug(f"Sample data: {df.head().to_dict()}")
19
+
20
  fig = make_subplots(
21
  rows=7, cols=1, shared_xaxes=True, vertical_spacing=0.03,
22
  subplot_titles=(
 
27
  )
28
 
29
  # Price and Moving Averages
30
+ if all(col in df for col in ['Date', 'Open', 'High', 'Low', 'value']):
31
+ logging.debug("Adding candlestick trace")
32
+ fig.add_trace(
33
+ go.Candlestick(
34
+ x=df['Date'], open=df['Open'], high=df['High'], low=df['Low'], close=df['value'],
35
+ name='Price', increasing_line_color='#00CC96', decreasing_line_color='#EF553B'
36
+ ), row=1, col=1
37
+ )
38
+ else:
39
+ logging.warning("Missing columns for candlestick: 'Date', 'Open', 'High', 'Low', 'value'")
40
+
41
  for ma in ['sma_10', 'sma_20', 'sma_50', 'ema_12', 'ema_26', 'ema_50']:
42
  if ma in df:
43
+ logging.debug(f"Adding {ma} trace")
44
  fig.add_trace(
45
  go.Scatter(x=df['Date'], y=df[ma], name=ma.upper(), line=dict(width=1.5)),
46
  row=1, col=1
47
  )
48
+ else:
49
+ logging.warning(f"{ma} not found in DataFrame")
50
+
51
+ # Bollinger Bands
52
  bb_u = 'bbu_20_2.0' if 'bbu_20_2.0' in df else ('bbu_20_2' if 'bbu_20_2' in df else None)
53
  bb_m = 'bbm_20_2.0' if 'bbm_20_2.0' in df else ('bbm_20_2' if 'bbm_20_2' in df else None)
54
  bb_l = 'bbl_20_2.0' if 'bbl_20_2.0' in df else ('bbl_20_2' if 'bbl_20_2' in df else None)
55
  if bb_u and bb_m and bb_l:
56
+ logging.debug("Adding Bollinger Bands traces")
57
  fig.add_trace(
58
  go.Scatter(x=df['Date'], y=df[bb_u], name='BB Upper', line=dict(color='gray', dash='dot')),
59
  row=1, col=1
 
66
  go.Scatter(x=df['Date'], y=df[bb_l], name='BB Lower', line=dict(color='gray', dash='dot')),
67
  row=1, col=1
68
  )
69
+ else:
70
+ logging.warning(f"Bollinger Bands columns missing: {bb_u}, {bb_m}, {bb_l}")
71
 
72
+ # Signals
73
+ if 'Signal' in df:
74
+ logging.debug("Adding signal traces")
75
  buy_signals = df[df['Signal'] == 'Buy']
76
  sell_signals = df[df['Signal'] == 'Sell']
77
  hold_signals = df[df['Signal'] == 'Hold']
 
96
  opacity=0.5
97
  ), row=1, col=1
98
  )
99
+ else:
100
+ logging.warning("Signal column not found in DataFrame")
101
 
102
  # Position Size and Risk Annotation
103
  if 'atr_14' in df:
104
  atr = df['atr_14'].iloc[-1]
105
  stop_distance = atr * 2
106
+ position_size = (10000 * 0.01) / stop_distance if stop_distance != 0 else 0
107
+ logging.debug(f"ATR: {atr}, Position Size: {position_size}")
108
  fig.add_annotation(
109
  text=f"Position Size: {position_size:.0f} shares (1% risk, ATR {atr:.2f})",
110
  xref="paper", yref="paper", x=0.05, y=0.95, showarrow=False,
111
  font=dict(color="black", size=12)
112
  )
113
+ else:
114
+ logging.warning("atr_14 not found for position size annotation")
115
 
116
  # Volume
117
+ if 'Volume' in df:
118
+ logging.debug("Adding volume trace")
119
+ fig.add_trace(
120
+ go.Bar(x=df['Date'], y=df['Volume'], name='Volume', marker_color='blue', opacity=0.5),
121
+ row=2, col=1
122
+ )
123
+ else:
124
+ logging.warning("Volume column not found in DataFrame")
125
 
126
  # MACD & RSI
127
+ if 'macd_12_26_9' in df and 'macds_12_26_9' in df and 'macdh_12_26_9' in df:
128
+ logging.debug("Adding MACD traces")
129
  fig.add_trace(
130
  go.Scatter(x=df['Date'], y=df['macd_12_26_9'], name='MACD', line=dict(color='blue')),
131
  row=3, col=1
 
138
  go.Bar(x=df['Date'], y=df['macdh_12_26_9'], name='MACD Hist', marker_color='gray'),
139
  row=3, col=1
140
  )
141
+ else:
142
+ logging.warning("MACD columns (macd_12_26_9, macds_12_26_9, macdh_12_26_9) not found")
143
+
144
  if 'rsi_14' in df:
145
+ logging.debug("Adding RSI 14 trace")
146
  fig.add_trace(
147
  go.Scatter(x=df['Date'], y=df['rsi_14'], name='RSI 14', line=dict(color='purple')),
148
  row=3, col=1
149
  )
150
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[70, 70],
151
+ showlegend=False, line=dict(color='red', dash='dash', width=1)), row=3, col=1)
152
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[30, 30],
153
+ showlegend=False, line=dict(color='green', dash='dash', width=1)), row=3, col=1)
154
+ else:
155
+ logging.warning("rsi_14 not found in DataFrame")
156
+
157
+ for rsi in ['rsi_21', 'rsi_50']:
158
+ if rsi in df:
159
+ logging.debug(f"Adding {rsi} trace")
160
+ fig.add_trace(
161
+ go.Scatter(x=df['Date'], y=df[rsi], name=rsi.upper(), line=dict(color='magenta' if rsi == 'rsi_21' else 'cyan', dash='dash' if rsi == 'rsi_21' else 'dot')),
162
+ row=3, col=1
163
+ )
164
+ else:
165
+ logging.warning(f"{rsi} not found in DataFrame")
166
 
167
  # Stochastic & Williams %R
168
+ if 'stochk_14_3_3' in df and 'stochd_14_3_3' in df:
169
+ logging.debug("Adding Stochastic traces")
170
  fig.add_trace(
171
  go.Scatter(x=df['Date'], y=df['stochk_14_3_3'], name='Stoch %K', line=dict(color='blue')),
172
  row=4, col=1
 
176
  row=4, col=1
177
  )
178
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[80, 80],
179
+ showlegend=False, line=dict(color='red', dash='dash', width=1)), row=4, col=1)
180
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[20, 20],
181
+ showlegend=False, line=dict(color='green', dash='dash', width=1)), row=4, col=1)
182
+ else:
183
+ logging.warning("Stochastic columns (stochk_14_3_3, stochd_14_3_3) not found")
184
+
185
  if 'willr_14' in df:
186
+ logging.debug("Adding Williams %R trace")
187
  fig.add_trace(
188
  go.Scatter(x=df['Date'], y=df['willr_14'], name='Williams %R', line=dict(color='green')),
189
  row=4, col=1
190
  )
191
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[-20, -20],
192
+ showlegend=False, line=dict(color='red', dash='dash', width=1)), row=4, col=1)
193
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[-80, -80],
194
+ showlegend=False, line=dict(color='green', dash='dash', width=1)), row=4, col=1)
195
+ else:
196
+ logging.warning("willr_14 not found in DataFrame")
197
 
198
  # ADX & DI
199
+ if all(col in df for col in ['adx_14', 'pdi_14', 'mdi_14']):
200
+ logging.debug("Adding ADX and DI traces")
201
  fig.add_trace(
202
  go.Scatter(x=df['Date'], y=df['adx_14'], name='ADX', line=dict(color='blue')),
203
  row=5, col=1
204
  )
205
  fig.add_trace(
206
+ go.Scatter(x=df['Date'], y=df['pdi_14'], name='+DI', line=dict(color='green')),
207
  row=5, col=1
208
  )
209
  fig.add_trace(
210
+ go.Scatter(x=df['Date'], y=df['mdi_14'], name='-DI', line=dict(color='red')),
211
  row=5, col=1
212
  )
213
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[25, 25],
214
+ showlegend=False, line=dict(color='black', dash='dash', width=1)), row=5, col=1)
215
+ else:
216
+ logging.warning("ADX/DI columns (adx_14, pdi_14, mdi_14) not found")
217
 
218
  # ATR & CCI
219
  if 'atr_14' in df:
220
+ logging.debug("Adding ATR trace")
221
  fig.add_trace(
222
  go.Scatter(x=df['Date'], y=df['atr_14'], name='ATR', line=dict(color='orange')),
223
  row=6, col=1
224
  )
225
+ else:
226
+ logging.warning("atr_14 not found in DataFrame")
227
+
228
  if 'cci_20' in df:
229
+ logging.debug("Adding CCI trace")
230
  fig.add_trace(
231
  go.Scatter(x=df['Date'], y=df['cci_20'], name='CCI', line=dict(color='purple')),
232
  row=6, col=1
233
  )
234
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[100, 100],
235
+ showlegend=False, line=dict(color='red', dash='dash', width=1)), row=6, col=1)
236
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[-100, -100],
237
+ showlegend=False, line=dict(color='green', dash='dash', width=1)), row=6, col=1)
238
+ else:
239
+ logging.warning("cci_20 not found in DataFrame")
240
 
241
+ # Signal Strength
242
  if all(col in df for col in ['RSI_Signal', 'MACD_Signal', 'ADX_Signal', 'Sentiment_Signal', 'Model_Signal']):
243
+ logging.debug("Adding signal strength trace")
244
  signal_strength = (
245
  df['RSI_Signal'].abs() +
246
  df['MACD_Signal'].abs() +
 
255
  ), row=7, col=1
256
  )
257
  fig.add_trace(go.Scatter(x=[df['Date'].min(), df['Date'].max()], y=[3, 3],
258
+ showlegend=False, line=dict(color='orange', dash='dash', width=1)), row=7, col=1)
259
+ else:
260
+ logging.warning("Signal strength columns (RSI_Signal, MACD_Signal, ADX_Signal, Sentiment_Signal, Model_Signal) not found")
261
 
262
  fig.update_layout(
263
  title=f"{ticker} Price and Technical Indicators",
 
272
  paper_bgcolor="white",
273
  hovermode="x unified"
274
  )
275
+ logging.info(f"Indicators plot generated for {ticker}")
276
  return fig
277
  except Exception as e:
278
+ logging.error(f"Plot indicators error: {str(e)}")
279
  return None
280
+
281
  def plot_future_forecast(df, result, timeframe):
282
  try:
283
+ logging.debug(f"Starting plot_future_forecast for timeframe: {timeframe}")
284
  fig = go.Figure()
285
+ if 'Date' in df and 'value' in df:
286
+ logging.debug("Adding historical close trace")
287
+ fig.add_trace(go.Scatter(x=df['Date'], y=df['value'], name='Historical Close', line=dict(color='blue')))
288
+ else:
289
+ logging.warning("Missing 'Date' or 'value' columns for historical close")
290
+
291
  if "latest_prediction" in result:
292
  last_date = df['Date'].iloc[-1]
293
  horizon = len(result["latest_prediction"]) if hasattr(result["latest_prediction"], '__len__') else 1
294
  freq_map = {'1m': 'T', '2m': '2T', '5m': '5T', '15m': '15T', '30m': '30T', '60m': 'H', '90m': '90T', '1h': 'H', '1d': 'D', '5d': '5D', '1wk': 'W', '1mo': 'M', '3mo': '3M'}
295
  future_dates = pd.date_range(start=last_date + pd.Timedelta(days=1), periods=horizon, freq=freq_map.get(timeframe, 'D'))
296
  pred_values = result["latest_prediction"].tolist() if hasattr(result["latest_prediction"], 'tolist') else list(result["latest_prediction"]) if hasattr(result["latest_prediction"], '__iter__') else [result["latest_prediction"]]
297
+ logging.debug(f"Adding forecast trace with {horizon} points")
298
  fig.add_trace(go.Scatter(x=future_dates, y=pred_values, name='Forecast Close', line=dict(color='orange', dash='dash')))
299
+ else:
300
+ logging.warning("latest_prediction not found in result")
301
+
302
  fig.update_layout(
303
  title="Historical Data and Future Forecast",
304
  template="plotly_white",
 
306
  xaxis_title="Date",
307
  yaxis_title="Value"
308
  )
309
+ logging.info("Future forecast plot generated")
310
  return fig
311
  except Exception as e:
312
+ logging.error(f"Plot future forecast error: {str(e)}")
313
  return None
314
+
 
315
  def plot_forecast(result, df):
316
  try:
317
+ logging.debug("Starting plot_forecast")
318
  actual = result.get("actual", [])
319
  forecast = result.get("forecast", [])
320
  if not actual or not forecast:
321
+ logging.warning("Actual or forecast data missing")
322
  return None
323
  dates = df['Date'].iloc[-len(actual):]
324
+ logging.debug(f"Adding actual and forecast traces with {len(actual)} points")
325
  fig = go.Figure()
326
  fig.add_trace(go.Scatter(x=dates, y=actual, name='Actual', line=dict(color='blue')))
327
  fig.add_trace(go.Scatter(x=dates, y=forecast, name='Forecast', line=dict(color='orange')))
 
336
  plot_bgcolor="white",
337
  paper_bgcolor="white"
338
  )
339
+ logging.info("Forecast plot generated")
340
  return fig
341
  except Exception as e:
342
+ logging.error(f"Plot forecast error: {str(e)}")
343
  return None
344
 
 
 
345
  def plot_metrics_r2(result):
346
  try:
347
+ logging.debug("Starting plot_metrics_r2")
348
  metrics = result.get("metrics", {})
349
  if not metrics:
350
+ logging.warning("Metrics data missing")
351
  return None
352
+ logging.debug(f"Metrics available: {metrics.keys()}")
353
  fig = go.Figure()
354
  fig.add_trace(go.Bar(
355
  x=['R²', 'MAPE'],
 
367
  plot_bgcolor="white",
368
  paper_bgcolor="white"
369
  )
370
+ logging.info("R² and MAPE metrics plot generated")
371
  return fig
372
  except Exception as e:
373
+ logging.error(f"Plot R2 error: {str(e)}")
374
  return None
375
 
376
  def plot_metrics_errors(result):
377
  try:
378
+ logging.debug("Starting plot_metrics_errors")
379
  metrics = result.get("metrics", {})
380
  if not metrics:
381
+ logging.warning("Metrics data missing")
382
  return None
383
+ logging.debug(f"Metrics available: {metrics.keys()}")
384
  fig = go.Figure()
385
  fig.add_trace(go.Bar(
386
  x=['RMSE', 'MAE'],
 
398
  plot_bgcolor="white",
399
  paper_bgcolor="white"
400
  )
401
+ logging.info("Error metrics plot generated")
402
  return fig
403
  except Exception as e:
404
+ logging.error(f"Plot metrics errors: {str(e)}")
405
  return None
406
 
407
  def plot_metrics_precision_recall(result):
408
  try:
409
+ logging.debug("Starting plot_metrics_precision_recall")
410
  metrics = result.get("metrics", {})
411
  if not metrics:
412
+ logging.warning("Metrics data missing")
413
  return None
414
+ logging.debug(f"Metrics available: {metrics.keys()}")
415
  fig = go.Figure()
416
  fig.add_trace(go.Bar(
417
  x=['Precision', 'Recall'],
 
429
  plot_bgcolor="white",
430
  paper_bgcolor="white"
431
  )
432
+ logging.info("Precision and recall metrics plot generated")
433
  return fig
434
  except Exception as e:
435
+ logging.error(f"Plot precision recall error: {str(e)}")
436
  return None
437
 
438
  def plot_metrics_risk(result):
439
  try:
440
+ logging.debug("Starting plot_metrics_risk")
441
  metrics = result.get("metrics", {})
442
  if not metrics:
443
+ logging.warning("Metrics data missing")
444
  return None
445
+ logging.debug(f"Metrics available: {metrics.keys()}")
446
  fig = go.Figure()
447
  fig.add_trace(go.Bar(
448
  x=['MASE', 'Sharpe', 'Volatility'],
 
460
  plot_bgcolor="white",
461
  paper_bgcolor="white"
462
  )
463
+ logging.info("Risk metrics plot generated")
464
  return fig
465
  except Exception as e:
466
+ logging.error(f"Plot risk metrics error: {str(e)}")
467
  return None
468
 
469
  def plot_loss_curve(result):
470
  try:
471
+ logging.debug("Starting plot_loss_curve")
472
  train_loss = result.get("train_loss", [])
473
  val_loss = result.get("val_loss", [])
474
  if not train_loss:
475
+ logging.warning("Train loss data missing")
476
  return None
477
+ logging.debug(f"Train loss length: {len(train_loss)}, Val loss length: {len(val_loss)}")
478
  epochs = list(range(1, len(train_loss) + 1))
479
  fig = go.Figure()
480
  fig.add_trace(go.Scatter(x=epochs, y=train_loss, mode='lines', name='Train Loss', line=dict(color='#00CC96')))
 
490
  plot_bgcolor="white",
491
  paper_bgcolor="white"
492
  )
493
+ logging.info("Loss curve plot generated")
494
  return fig
495
  except Exception as e:
496
+ logging.error(f"Plot loss curve error: {str(e)}")
497
  return None
498
 
 
499
  def plot_model_architecture(result):
500
  try:
501
+ logging.debug("Starting plot_model_architecture")
502
  model = result.get('model')
503
  if not model:
504
+ logging.warning("Model not found in result")
505
  return None
506
+ logging.debug(f"Model architecture: {result.get('arch', {})}")
507
  dummy_input = torch.randn(1, result['arch']['window'], result['arch']['input_dim'])
508
  graph = make_dot(model(dummy_input), params=dict(model.named_parameters()))
509
  graph.format = 'png'
510
  graph.render("model_arch", cleanup=True)
511
+ logging.debug("Model architecture graph rendered")
512
  fig = go.Figure()
513
  fig.add_layout_image(
514
  dict(
 
524
  template="plotly_dark",
525
  showlegend=False
526
  )
527
+ logging.info("Model architecture plot generated")
528
  return fig
529
  except Exception as e:
530
+ logging.error(f"Plot model architecture error: {str(e)}")
531
  return None
532
 
533
  def plot_signals(signals_df, ticker):
534
  try:
535
+ logging.debug(f"Starting plot_signals for {ticker}")
536
+ logging.debug(f"Signals DataFrame columns: {signals_df.columns.tolist()}")
537
  fig = go.Figure()
538
  x_col = 'Date' if 'Date' in signals_df.columns else signals_df.index
539
+ if 'Price' in signals_df:
540
+ logging.debug("Adding price trace")
541
+ fig.add_trace(go.Scatter(x=signals_df[x_col], y=signals_df['Price'], mode='lines', name='Price', line=dict(color='blue')))
542
+ else:
543
+ logging.warning("Price column not found in signals_df")
544
  buy_signals = signals_df[signals_df['Signal'] == 'Buy']
545
  sell_signals = signals_df[signals_df['Signal'] == 'Sell']
546
+ if not buy_signals.empty:
547
+ logging.debug(f"Adding {len(buy_signals)} buy signal markers")
548
+ fig.add_trace(go.Scatter(x=buy_signals[x_col], y=buy_signals['Price'], mode='markers', name='Buy', marker=dict(symbol='triangle-up', size=10, color='green')))
549
+ else:
550
+ logging.warning("No buy signals found")
551
+ if not sell_signals.empty:
552
+ logging.debug(f"Adding {len(sell_signals)} sell signal markers")
553
+ fig.add_trace(go.Scatter(x=sell_signals[x_col], y=sell_signals['Price'], mode='markers', name='Sell', marker=dict(symbol='triangle-down', size=10, color='red')))
554
+ else:
555
+ logging.warning("No sell signals found")
556
  fig.update_layout(
557
  title=f"{ticker} Trading Signals",
558
  xaxis_title="Date",
 
568
 
569
  def plot_backtest(result, df, ticker):
570
  try:
571
+ logging.debug(f"Starting plot_backtest for {ticker}")
572
  actual = result.get('actual', [])
573
  forecast = result.get('forecast', [])
574
  if len(actual) == 0 or len(forecast) == 0:
575
+ logging.warning("Actual or forecast data missing")
576
  return None
577
+ logging.debug(f"Actual length: {len(actual)}, Forecast length: {len(forecast)}")
578
  last_historical_date = df['Date'].iloc[-len(actual) - 1]
579
  historical_dates = df['Date'].iloc[-len(actual) - 1: -len(actual) + len(actual)]
580
  forecast_dates = pd.date_range(start=last_historical_date + timedelta(days=1), periods=len(forecast))
581
  historical_values = df['value'].iloc[-len(actual) - 1: -len(actual) + len(actual)]
582
+ logging.debug("Adding historical and forecast traces")
583
  fig = go.Figure()
584
  fig.add_trace(go.Scatter(x=historical_dates, y=historical_values, mode='lines', name='Historical', line=dict(color='blue')))
585
  fig.add_trace(go.Scatter(x=forecast_dates, y=forecast, mode='lines', name='Forecast', line=dict(color='orange', dash='dash')))
 
590
  template="plotly_dark",
591
  showlegend=True
592
  )
593
+ logging.info(f"Backtest plot generated for {ticker}")
594
  return fig
595
  except Exception as e:
596
+ logging.error(f"Plot backtest error: {str(e)}")
597
  return None