algorithmic_trading / ui /dash_app.py
Edwin Salguero
feat(ui): add robust multi-interface UI system (Streamlit, Dash, Jupyter, WebSocket) with launcher, docs, and integration tests [skip ci]
9f44dc9
"""
Dash UI for Algorithmic Trading System
Enterprise-grade interactive dashboard with:
- Real-time market data visualization
- Advanced trading analytics
- Portfolio management
- Risk monitoring
- Strategy backtesting
"""
import dash
from dash import dcc, html, Input, Output, State, callback_context
import dash_bootstrap_components as dbc
from dash_bootstrap_components import themes
import plotly.graph_objects as go
import plotly.express as px
import pandas as pd
import numpy as np
import yaml
import os
import sys
from datetime import datetime, timedelta
import asyncio
import threading
import time
from typing import Dict, Any, Optional
# Add project root to path
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from agentic_ai_system.main import load_config
from agentic_ai_system.data_ingestion import load_data, validate_data, add_technical_indicators
from agentic_ai_system.finrl_agent import FinRLAgent, FinRLConfig
from agentic_ai_system.alpaca_broker import AlpacaBroker
from agentic_ai_system.orchestrator import run_backtest, run_live_trading
class TradingDashApp:
def __init__(self):
self.app = dash.Dash(
__name__,
external_stylesheets=[
themes.BOOTSTRAP,
"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css"
],
suppress_callback_exceptions=True
)
self.config = None
self.data = None
self.alpaca_broker = None
self.finrl_agent = None
self.setup_layout()
self.setup_callbacks()
def setup_layout(self):
"""Setup the main application layout"""
self.app.layout = dbc.Container([
# Header
dbc.Row([
dbc.Col([
html.H1([
html.I(className="fas fa-chart-line me-3"),
"Algorithmic Trading System"
], className="text-primary mb-4 text-center")
])
]),
# Navigation tabs
dbc.Tabs([
dbc.Tab(self.create_dashboard_tab(), label="Dashboard", tab_id="dashboard"),
dbc.Tab(self.create_data_tab(), label="Data", tab_id="data"),
dbc.Tab(self.create_trading_tab(), label="Trading", tab_id="trading"),
dbc.Tab(self.create_analytics_tab(), label="Analytics", tab_id="analytics"),
dbc.Tab(self.create_portfolio_tab(), label="Portfolio", tab_id="portfolio"),
dbc.Tab(self.create_settings_tab(), label="Settings", tab_id="settings")
], id="tabs", active_tab="dashboard"),
# Store components for data persistence
dcc.Store(id="config-store"),
dcc.Store(id="data-store"),
dcc.Store(id="alpaca-store"),
dcc.Store(id="finrl-store"),
dcc.Store(id="trading-status-store"),
# Interval for real-time updates
dcc.Interval(
id="interval-component",
interval=5*1000, # 5 seconds
n_intervals=0
)
], fluid=True)
def create_dashboard_tab(self):
"""Create the main dashboard tab"""
return dbc.Container([
# System status cards
dbc.Row([
dbc.Col(self.create_status_card("Trading Status", "Active", "success"), width=3),
dbc.Col(self.create_status_card("Portfolio Value", "$100,000", "info"), width=3),
dbc.Col(self.create_status_card("Daily P&L", "+$1,250", "success"), width=3),
dbc.Col(self.create_status_card("Risk Level", "Low", "warning"), width=3)
], className="mb-4"),
# Charts row
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("Price Chart"),
dbc.CardBody([
dcc.Graph(id="price-chart", style={"height": "400px"})
])
])
], width=8),
dbc.Col([
dbc.Card([
dbc.CardHeader("Portfolio Allocation"),
dbc.CardBody([
dcc.Graph(id="allocation-chart", style={"height": "400px"})
])
])
], width=4)
], className="mb-4"),
# Trading activity and alerts
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("Recent Trades"),
dbc.CardBody([
html.Div(id="trades-table")
])
])
], width=6),
dbc.Col([
dbc.Card([
dbc.CardHeader("System Alerts"),
dbc.CardBody([
html.Div(id="alerts-list")
])
])
], width=6)
])
])
def create_data_tab(self):
"""Create the data management tab"""
return dbc.Container([
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("Data Configuration"),
dbc.CardBody([
dbc.Row([
dbc.Col([
dbc.Label("Data Source"),
dbc.Select(
id="data-source-select",
options=[
{"label": "CSV File", "value": "csv"},
{"label": "Alpaca API", "value": "alpaca"},
{"label": "Synthetic Data", "value": "synthetic"}
],
value="csv"
)
], width=4),
dbc.Col([
dbc.Label("Symbol"),
dbc.Input(
id="symbol-input",
type="text",
value="AAPL",
placeholder="Enter symbol"
)
], width=4),
dbc.Col([
dbc.Label("Timeframe"),
dbc.Select(
id="timeframe-select",
options=[
{"label": "1 Minute", "value": "1m"},
{"label": "5 Minutes", "value": "5m"},
{"label": "15 Minutes", "value": "15m"},
{"label": "1 Hour", "value": "1h"},
{"label": "1 Day", "value": "1d"}
],
value="1m"
)
], width=4)
], className="mb-3"),
dbc.Row([
dbc.Col([
dbc.Button("Load Data", id="load-data-btn", color="primary", className="me-2"),
dbc.Button("Refresh Data", id="refresh-data-btn", color="secondary")
])
])
])
])
], width=6),
dbc.Col([
dbc.Card([
dbc.CardHeader("Data Statistics"),
dbc.CardBody([
html.Div(id="data-stats")
])
])
], width=6)
], className="mb-4"),
# Data visualization
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader([
html.Span("Market Data Visualization"),
dbc.ButtonGroup([
dbc.Button("Candlestick", id="candlestick-btn", size="sm"),
dbc.Button("Line", id="line-btn", size="sm"),
dbc.Button("Volume", id="volume-btn", size="sm")
], className="float-end")
]),
dbc.CardBody([
dcc.Graph(id="market-chart", style={"height": "500px"})
])
])
])
])
])
def create_trading_tab(self):
"""Create the trading controls tab"""
return dbc.Container([
# Trading configuration
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("Trading Configuration"),
dbc.CardBody([
dbc.Row([
dbc.Col([
dbc.Label("Capital"),
dbc.Input(
id="capital-input",
type="number",
value=100000,
step=1000
)
], width=3),
dbc.Col([
dbc.Label("Order Size"),
dbc.Input(
id="order-size-input",
type="number",
value=10,
step=1
)
], width=3),
dbc.Col([
dbc.Label("Max Position"),
dbc.Input(
id="max-position-input",
type="number",
value=100,
step=10
)
], width=3),
dbc.Col([
dbc.Label("Max Drawdown"),
dbc.Input(
id="max-drawdown-input",
type="number",
value=0.05,
step=0.01,
min=0,
max=1
)
], width=3)
], className="mb-3"),
dbc.Row([
dbc.Col([
dbc.Button("Start Trading", id="start-trading-btn", color="success", className="me-2"),
dbc.Button("Stop Trading", id="stop-trading-btn", color="danger", className="me-2"),
dbc.Button("Emergency Stop", id="emergency-stop-btn", color="warning")
])
])
])
])
], width=6),
dbc.Col([
dbc.Card([
dbc.CardHeader("Alpaca Connection"),
dbc.CardBody([
dbc.Row([
dbc.Col([
dbc.Label("API Key"),
dbc.Input(
id="alpaca-api-key",
type="password",
placeholder="Enter Alpaca API key"
)
], width=6),
dbc.Col([
dbc.Label("Secret Key"),
dbc.Input(
id="alpaca-secret-key",
type="password",
placeholder="Enter Alpaca secret key"
)
], width=6)
], className="mb-3"),
dbc.Row([
dbc.Col([
dbc.Button("Connect", id="connect-alpaca-btn", color="primary", className="me-2"),
dbc.Button("Disconnect", id="disconnect-alpaca-btn", color="secondary")
])
])
])
])
], width=6)
], className="mb-4"),
# Trading activity
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("Live Trading Activity"),
dbc.CardBody([
html.Div(id="trading-activity")
])
])
])
])
])
def create_analytics_tab(self):
"""Create the analytics tab"""
return dbc.Container([
# FinRL training
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("FinRL Model Training"),
dbc.CardBody([
dbc.Row([
dbc.Col([
dbc.Label("Algorithm"),
dbc.Select(
id="finrl-algorithm-select",
options=[
{"label": "PPO", "value": "PPO"},
{"label": "A2C", "value": "A2C"},
{"label": "DDPG", "value": "DDPG"},
{"label": "TD3", "value": "TD3"}
],
value="PPO"
)
], width=3),
dbc.Col([
dbc.Label("Learning Rate"),
dbc.Input(
id="learning-rate-input",
type="number",
value=0.0003,
step=0.0001,
min=0.0001,
max=0.01
)
], width=3),
dbc.Col([
dbc.Label("Training Steps"),
dbc.Input(
id="training-steps-input",
type="number",
value=100000,
step=1000
)
], width=3),
dbc.Col([
dbc.Label("Batch Size"),
dbc.Select(
id="batch-size-select",
options=[
{"label": "32", "value": 32},
{"label": "64", "value": 64},
{"label": "128", "value": 128},
{"label": "256", "value": 256}
],
value=64
)
], width=3)
], className="mb-3"),
dbc.Row([
dbc.Col([
dbc.Button("Start Training", id="start-training-btn", color="primary", className="me-2"),
dbc.Button("Stop Training", id="stop-training-btn", color="danger")
])
])
])
])
], width=6),
dbc.Col([
dbc.Card([
dbc.CardHeader("Training Progress"),
dbc.CardBody([
dbc.Progress(id="training-progress", value=0, className="mb-3"),
html.Div(id="training-metrics")
])
])
], width=6)
], className="mb-4"),
# Backtesting
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("Strategy Backtesting"),
dbc.CardBody([
dbc.Row([
dbc.Col([
dbc.Button("Run Backtest", id="run-backtest-btn", color="primary", className="me-2"),
dbc.Button("Export Results", id="export-backtest-btn", color="secondary")
])
]),
html.Div(id="backtest-results")
])
])
])
])
])
def create_portfolio_tab(self):
"""Create the portfolio management tab"""
return dbc.Container([
# Portfolio overview
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("Portfolio Overview"),
dbc.CardBody([
dbc.Row([
dbc.Col([
html.H4("Total Value", className="text-muted"),
html.H3(id="total-value", children="$100,000")
], width=3),
dbc.Col([
html.H4("Cash", className="text-muted"),
html.H3(id="cash-value", children="$25,000")
], width=3),
dbc.Col([
html.H4("Invested", className="text-muted"),
html.H3(id="invested-value", children="$75,000")
], width=3),
dbc.Col([
html.H4("P&L", className="text-muted"),
html.H3(id="pnl-value", children="+$1,250", className="text-success")
], width=3)
])
])
])
])
], className="mb-4"),
# Positions and allocation
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("Current Positions"),
dbc.CardBody([
html.Div(id="positions-table")
])
])
], width=8),
dbc.Col([
dbc.Card([
dbc.CardHeader("Allocation Chart"),
dbc.CardBody([
dcc.Graph(id="portfolio-allocation-chart", style={"height": "300px"})
])
])
], width=4)
])
])
def create_settings_tab(self):
"""Create the settings tab"""
return dbc.Container([
dbc.Row([
dbc.Col([
dbc.Card([
dbc.CardHeader("System Configuration"),
dbc.CardBody([
dbc.Row([
dbc.Col([
dbc.Label("Config File"),
dbc.Input(
id="config-file-input",
type="text",
value="config.yaml",
placeholder="Enter config file path"
)
], width=6),
dbc.Col([
dbc.Label("Log Level"),
dbc.Select(
id="log-level-select",
options=[
{"label": "DEBUG", "value": "DEBUG"},
{"label": "INFO", "value": "INFO"},
{"label": "WARNING", "value": "WARNING"},
{"label": "ERROR", "value": "ERROR"}
],
value="INFO"
)
], width=6)
], className="mb-3"),
dbc.Row([
dbc.Col([
dbc.Button("Load Config", id="load-config-btn", color="primary", className="me-2"),
dbc.Button("Save Config", id="save-config-btn", color="success")
])
])
])
])
], width=6),
dbc.Col([
dbc.Card([
dbc.CardHeader("System Status"),
dbc.CardBody([
html.Div(id="system-status")
])
])
], width=6)
])
])
def create_status_card(self, title, value, color):
"""Create a status card component"""
return dbc.Card([
dbc.CardBody([
html.H5(title, className="card-title text-muted"),
html.H3(value, className=f"text-{color}")
])
])
def setup_callbacks(self):
"""Setup all Dash callbacks"""
@self.app.callback(
Output("config-store", "data"),
Input("load-config-btn", "n_clicks"),
State("config-file-input", "value"),
prevent_initial_call=True
)
def load_configuration(n_clicks, config_file):
if n_clicks:
try:
config = load_config(config_file)
return config
except Exception as e:
return {"error": str(e)}
return dash.no_update
@self.app.callback(
Output("data-store", "data"),
Input("load-data-btn", "n_clicks"),
State("config-store", "data"),
prevent_initial_call=True
)
def load_market_data(n_clicks, config):
if n_clicks and config:
try:
data = load_data(config)
if data is not None:
return data.to_dict('records')
except Exception as e:
return {"error": str(e)}
return dash.no_update
@self.app.callback(
Output("price-chart", "figure"),
Input("data-store", "data"),
Input("interval-component", "n_intervals")
)
def update_price_chart(data, n_intervals):
if data and isinstance(data, list):
df = pd.DataFrame(data)
if not df.empty:
fig = go.Figure(data=[go.Candlestick(
x=df['timestamp'],
open=df['open'],
high=df['high'],
low=df['low'],
close=df['close']
)])
fig.update_layout(
title="Market Data",
xaxis_title="Date",
yaxis_title="Price ($)",
height=400
)
return fig
return go.Figure()
@self.app.callback(
Output("allocation-chart", "figure"),
Input("alpaca-store", "data"),
Input("interval-component", "n_intervals")
)
def update_allocation_chart(alpaca_data, n_intervals):
# Mock portfolio allocation data
labels = ['AAPL', 'GOOGL', 'MSFT', 'TSLA', 'Cash']
values = [30, 25, 20, 15, 10]
fig = go.Figure(data=[go.Pie(labels=labels, values=values)])
fig.update_layout(
title="Portfolio Allocation",
height=400
)
return fig
@self.app.callback(
Output("trading-activity", "children"),
Input("interval-component", "n_intervals")
)
def update_trading_activity(n_intervals):
# Mock trading activity
trades = [
{"time": "09:30:15", "symbol": "AAPL", "action": "BUY", "quantity": 10, "price": 150.25},
{"time": "09:35:22", "symbol": "GOOGL", "action": "SELL", "quantity": 5, "price": 2750.50},
{"time": "09:40:08", "symbol": "MSFT", "action": "BUY", "quantity": 15, "price": 320.75}
]
table_rows = []
for trade in trades:
color = "success" if trade["action"] == "BUY" else "danger"
table_rows.append(
dbc.Row([
dbc.Col(trade["time"], width=2),
dbc.Col(trade["symbol"], width=2),
dbc.Col(trade["action"], width=2, className=f"text-{color}"),
dbc.Col(str(trade["quantity"]), width=2),
dbc.Col(f"${trade['price']:.2f}", width=2),
dbc.Col(f"${trade['quantity'] * trade['price']:.2f}", width=2)
], className="mb-2")
)
return table_rows
def create_dash_app():
"""Create and return the Dash application"""
app = TradingDashApp()
return app.app
if __name__ == "__main__":
app = create_dash_app()
app.run_server(debug=True, host="0.0.0.0", port=8050)