a bit of cleanup
This commit is contained in:
247
web/dashboard.py
247
web/dashboard.py
@ -308,6 +308,32 @@ class TradingDashboard:
|
||||
'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css'
|
||||
])
|
||||
|
||||
# # Add custom CSS for model data charts
|
||||
# self.app.index_string = '''
|
||||
# <!DOCTYPE html>
|
||||
# <html>
|
||||
# <head>
|
||||
# {%metas%}
|
||||
# <title>{%title%}</title>
|
||||
# {%favicon%}
|
||||
# {%css%}
|
||||
# <style>
|
||||
# .tiny { font-size: 10px !important; }
|
||||
# .model-data-chart .js-plotly-plot .plotly .modebar { display: none !important; }
|
||||
# .model-data-chart .js-plotly-plot .plotly .svg-container { border: 1px solid #444; border-radius: 4px; }
|
||||
# </style>
|
||||
# </head>
|
||||
# <body>
|
||||
# {%app_entry%}
|
||||
# <footer>
|
||||
# {%config%}
|
||||
# {%scripts%}
|
||||
# {%renderer%}
|
||||
# </footer>
|
||||
# </body>
|
||||
# </html>
|
||||
# '''
|
||||
|
||||
# Setup layout and callbacks
|
||||
self._setup_layout()
|
||||
self._setup_callbacks()
|
||||
@ -757,6 +783,46 @@ class TradingDashboard:
|
||||
], className="card", style={"width": "28%", "marginLeft": "2%"}),
|
||||
], className="row g-2 mb-3"),
|
||||
|
||||
# # Model Data Feed Charts - Small charts showing data fed to models
|
||||
# html.Div([
|
||||
# html.Div([
|
||||
# html.Div([
|
||||
# html.H6([
|
||||
# html.I(className="fas fa-database me-1"),
|
||||
# "Model Data Feeds"
|
||||
# ], className="card-title mb-2 small"),
|
||||
# html.Div([
|
||||
# # Row of 4 small charts
|
||||
# html.Div([
|
||||
# # 1m Chart
|
||||
# html.Div([
|
||||
# html.P("ETH 1m OHLCV", className="text-center mb-1 tiny text-muted"),
|
||||
# dcc.Graph(id="model-data-1m", style={"height": "120px"}, config={'displayModeBar': False}, className="model-data-chart")
|
||||
# ], style={"width": "24%"}),
|
||||
|
||||
# # 1h Chart
|
||||
# html.Div([
|
||||
# html.P("ETH 1h OHLCV", className="text-center mb-1 tiny text-muted"),
|
||||
# dcc.Graph(id="model-data-1h", style={"height": "120px"}, config={'displayModeBar': False}, className="model-data-chart")
|
||||
# ], style={"width": "24%", "marginLeft": "1%"}),
|
||||
|
||||
# # 1d Chart
|
||||
# html.Div([
|
||||
# html.P("ETH 1d OHLCV", className="text-center mb-1 tiny text-muted"),
|
||||
# dcc.Graph(id="model-data-1d", style={"height": "120px"}, config={'displayModeBar': False}, className="model-data-chart")
|
||||
# ], style={"width": "24%", "marginLeft": "1%"}),
|
||||
|
||||
# # BTC Reference Chart
|
||||
# html.Div([
|
||||
# html.P("BTC Reference", className="text-center mb-1 tiny text-muted"),
|
||||
# dcc.Graph(id="model-data-btc", style={"height": "120px"}, config={'displayModeBar': False}, className="model-data-chart")
|
||||
# ], style={"width": "24%", "marginLeft": "1%"})
|
||||
# ], className="d-flex")
|
||||
# ])
|
||||
# ], className="card-body p-2")
|
||||
# ], className="card")
|
||||
# ], className="mb-3"),
|
||||
|
||||
# Bottom row - Session performance and system status
|
||||
html.Div([
|
||||
|
||||
@ -836,7 +902,12 @@ class TradingDashboard:
|
||||
Output('closed-trades-table', 'children'),
|
||||
Output('system-status-icon', 'className'),
|
||||
Output('system-status-icon', 'title'),
|
||||
Output('system-status-details', 'children')
|
||||
Output('system-status-details', 'children'),
|
||||
# Model data feed charts
|
||||
# Output('model-data-1m', 'figure'),
|
||||
# Output('model-data-1h', 'figure'),
|
||||
# Output('model-data-1d', 'figure'),
|
||||
# Output('model-data-btc', 'figure')
|
||||
],
|
||||
[Input('interval-component', 'n_intervals')]
|
||||
)
|
||||
@ -1085,7 +1156,12 @@ class TradingDashboard:
|
||||
return (
|
||||
price_text, pnl_text, pnl_class, fees_text, position_text, position_class, trade_count_text, portfolio_text, mexc_status,
|
||||
price_chart, training_metrics, decisions_list, session_perf, closed_trades_table,
|
||||
system_status['icon_class'], system_status['title'], system_status['details']
|
||||
system_status['icon_class'], system_status['title'], system_status['details'],
|
||||
# # Model data feed charts
|
||||
# self._create_model_data_chart('ETH/USDT', '1m'),
|
||||
# self._create_model_data_chart('ETH/USDT', '1h'),
|
||||
# self._create_model_data_chart('ETH/USDT', '1d'),
|
||||
# self._create_model_data_chart('BTC/USDT', '1s')
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
@ -1102,7 +1178,12 @@ class TradingDashboard:
|
||||
[html.P("Error loading closed trades", className="text-danger")],
|
||||
"fas fa-circle text-danger fa-2x",
|
||||
"Error: Dashboard error - check logs",
|
||||
[html.P(f"Error: {str(e)}", className="text-danger")]
|
||||
[html.P(f"Error: {str(e)}", className="text-danger")],
|
||||
# Model data feed charts
|
||||
self._create_model_data_chart('ETH/USDT', '1m'),
|
||||
self._create_model_data_chart('ETH/USDT', '1h'),
|
||||
self._create_model_data_chart('ETH/USDT', '1d'),
|
||||
self._create_model_data_chart('BTC/USDT', '1s')
|
||||
)
|
||||
|
||||
# Clear history callback
|
||||
@ -1561,47 +1642,9 @@ class TradingDashboard:
|
||||
row=1, col=1
|
||||
)
|
||||
|
||||
# Add losing trade markers (border only triangles)
|
||||
if losing_entries_x:
|
||||
# Entry markers (triangle-up for LONG, triangle-down for SHORT - border only)
|
||||
fig.add_trace(
|
||||
go.Scatter(
|
||||
x=losing_entries_x,
|
||||
y=losing_entries_y,
|
||||
mode='markers',
|
||||
marker=dict(
|
||||
color='rgba(255, 107, 107, 0)', # Transparent fill
|
||||
size=12,
|
||||
symbol='triangle-up',
|
||||
line=dict(color='#ff6b6b', width=2) # Red border for losing
|
||||
),
|
||||
name="Losing Entry",
|
||||
showlegend=True,
|
||||
hovertemplate="<b>LOSING ENTRY</b><br>Price: $%{y:.2f}<br>Time: %{x}<extra></extra>"
|
||||
),
|
||||
row=1, col=1
|
||||
)
|
||||
# Add losing trade markers (border only triangles) - REMOVED for cleaner UI
|
||||
# Only dashed lines are sufficient for visualization
|
||||
|
||||
if losing_exits_x:
|
||||
# Exit markers (triangle-down for LONG, triangle-up for SHORT - border only)
|
||||
fig.add_trace(
|
||||
go.Scatter(
|
||||
x=losing_exits_x,
|
||||
y=losing_exits_y,
|
||||
mode='markers',
|
||||
marker=dict(
|
||||
color='rgba(255, 107, 107, 0)', # Transparent fill
|
||||
size=12,
|
||||
symbol='triangle-down',
|
||||
line=dict(color='#ff6b6b', width=2) # Red border for losing
|
||||
),
|
||||
name="Losing Exit",
|
||||
showlegend=True,
|
||||
hovertemplate="<b>LOSING EXIT</b><br>Price: $%{y:.2f}<br>Time: %{x}<extra></extra>"
|
||||
),
|
||||
row=1, col=1
|
||||
)
|
||||
|
||||
# Update layout with current timestamp and streaming status
|
||||
current_time = datetime.now().strftime("%H:%M:%S.%f")[:-3]
|
||||
latest_price = df['close'].iloc[-1] if not df.empty else 0
|
||||
@ -5291,6 +5334,124 @@ class TradingDashboard:
|
||||
except Exception as e:
|
||||
logger.warning(f"Error queuing signal for training: {e}")
|
||||
|
||||
def _create_model_data_chart(self, symbol, timeframe):
|
||||
"""Create a detailed model data chart for a specific symbol and timeframe"""
|
||||
try:
|
||||
# Determine the number of candles based on timeframe
|
||||
if timeframe == '1s':
|
||||
limit = 300 # Last 5 minutes of 1s data
|
||||
chart_title = f"{symbol} {timeframe} Ticks"
|
||||
elif timeframe == '1m':
|
||||
limit = 100 # Last 100 minutes
|
||||
chart_title = f"{symbol} {timeframe} OHLCV"
|
||||
elif timeframe == '1h':
|
||||
limit = 72 # Last 3 days
|
||||
chart_title = f"{symbol} {timeframe} OHLCV"
|
||||
elif timeframe == '1d':
|
||||
limit = 30 # Last 30 days
|
||||
chart_title = f"{symbol} {timeframe} OHLCV"
|
||||
else:
|
||||
limit = 50
|
||||
chart_title = f"{symbol} {timeframe}"
|
||||
|
||||
# Get historical data for the specified timeframe
|
||||
df = self.data_provider.get_historical_data(symbol, timeframe, limit=limit, refresh=True)
|
||||
|
||||
if df is not None and not df.empty:
|
||||
# Create candlestick chart with minimal styling for small charts
|
||||
fig = go.Figure()
|
||||
|
||||
# Add candlestick data
|
||||
fig.add_trace(go.Candlestick(
|
||||
x=df.index,
|
||||
open=df['open'],
|
||||
high=df['high'],
|
||||
low=df['low'],
|
||||
close=df['close'],
|
||||
name=f'{symbol}',
|
||||
showlegend=False,
|
||||
increasing_line_color='#00ff88',
|
||||
decreasing_line_color='#ff6b6b'
|
||||
))
|
||||
|
||||
# Minimal layout for small charts
|
||||
fig.update_layout(
|
||||
title=dict(
|
||||
text=f"{chart_title}<br><span style='font-size:8px'>({len(df)} bars)</span>",
|
||||
font=dict(size=10),
|
||||
x=0.5
|
||||
),
|
||||
template="plotly_dark",
|
||||
height=120,
|
||||
margin=dict(l=5, r=5, t=25, b=5),
|
||||
xaxis=dict(
|
||||
showgrid=False,
|
||||
showticklabels=False,
|
||||
fixedrange=True
|
||||
),
|
||||
yaxis=dict(
|
||||
showgrid=False,
|
||||
showticklabels=False,
|
||||
fixedrange=True
|
||||
),
|
||||
dragmode=False,
|
||||
font=dict(size=8)
|
||||
)
|
||||
|
||||
# Add annotation showing data freshness
|
||||
current_time = df.index[-1] if len(df) > 0 else datetime.now()
|
||||
data_age = (datetime.now() - current_time).total_seconds() if hasattr(current_time, 'timestamp') else 0
|
||||
|
||||
if data_age < 60:
|
||||
freshness_color = "#00ff88" # Green - fresh
|
||||
freshness_text = "LIVE"
|
||||
elif data_age < 300:
|
||||
freshness_color = "#ffaa00" # Orange - recent
|
||||
freshness_text = f"{int(data_age)}s"
|
||||
else:
|
||||
freshness_color = "#ff6b6b" # Red - stale
|
||||
freshness_text = f"{int(data_age/60)}m"
|
||||
|
||||
fig.add_annotation(
|
||||
x=0.95, y=0.95,
|
||||
xref="paper", yref="paper",
|
||||
text=freshness_text,
|
||||
showarrow=False,
|
||||
font=dict(color=freshness_color, size=8),
|
||||
bgcolor="rgba(0,0,0,0.3)",
|
||||
bordercolor=freshness_color,
|
||||
borderwidth=1
|
||||
)
|
||||
|
||||
return fig
|
||||
else:
|
||||
return self._create_empty_model_chart(chart_title, "No data")
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Error creating model data chart for {symbol} {timeframe}: {e}")
|
||||
return self._create_empty_model_chart(f"{symbol} {timeframe}", f"Error: {str(e)}")
|
||||
|
||||
def _create_empty_model_chart(self, title, message):
|
||||
"""Create an empty chart for model data feeds"""
|
||||
fig = go.Figure()
|
||||
fig.add_annotation(
|
||||
x=0.5, y=0.5,
|
||||
xref="paper", yref="paper",
|
||||
text=message,
|
||||
showarrow=False,
|
||||
font=dict(size=10, color="#888888")
|
||||
)
|
||||
fig.update_layout(
|
||||
title=dict(text=title, font=dict(size=10), x=0.5),
|
||||
template="plotly_dark",
|
||||
height=120,
|
||||
margin=dict(l=5, r=5, t=25, b=5),
|
||||
xaxis=dict(showgrid=False, showticklabels=False, fixedrange=True),
|
||||
yaxis=dict(showgrid=False, showticklabels=False, fixedrange=True),
|
||||
dragmode=False
|
||||
)
|
||||
return fig
|
||||
|
||||
def create_dashboard(data_provider: DataProvider = None, orchestrator: TradingOrchestrator = None, trading_executor: TradingExecutor = None) -> TradingDashboard:
|
||||
"""Factory function to create a trading dashboard"""
|
||||
return TradingDashboard(data_provider=data_provider, orchestrator=orchestrator, trading_executor=trading_executor)
|
Reference in New Issue
Block a user