Edwin Salguero
feat(ui): add robust multi-interface UI system (Streamlit, Dash, Jupyter, WebSocket) with launcher, docs, and integration tests [skip ci]
9f44dc9
""" | |
Jupyter Widgets UI for Algorithmic Trading System | |
Interactive notebook interface for: | |
- Data exploration and visualization | |
- Strategy development and testing | |
- Model training and evaluation | |
- Real-time trading simulation | |
""" | |
import ipywidgets as widgets | |
from IPython.display import display, HTML, clear_output | |
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 | |
from typing import Dict, Any, Optional | |
import asyncio | |
import threading | |
import time | |
# 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 TradingJupyterUI: | |
def __init__(self): | |
self.config = None | |
self.data = None | |
self.alpaca_broker = None | |
self.finrl_agent = None | |
self.trading_active = False | |
self.setup_widgets() | |
def setup_widgets(self): | |
"""Setup all interactive widgets""" | |
# Configuration widgets | |
self.config_file = widgets.Text( | |
value='config.yaml', | |
description='Config File:', | |
style={'description_width': '120px'} | |
) | |
self.load_config_btn = widgets.Button( | |
description='Load Configuration', | |
button_style='primary', | |
icon='cog' | |
) | |
self.config_output = widgets.Output() | |
# Data widgets | |
self.data_source = widgets.Dropdown( | |
options=['csv', 'alpaca', 'synthetic'], | |
value='csv', | |
description='Data Source:', | |
style={'description_width': '120px'} | |
) | |
self.symbol_input = widgets.Text( | |
value='AAPL', | |
description='Symbol:', | |
style={'description_width': '120px'} | |
) | |
self.timeframe_input = widgets.Dropdown( | |
options=['1m', '5m', '15m', '1h', '1d'], | |
value='1m', | |
description='Timeframe:', | |
style={'description_width': '120px'} | |
) | |
self.load_data_btn = widgets.Button( | |
description='Load Data', | |
button_style='success', | |
icon='database' | |
) | |
self.data_output = widgets.Output() | |
# Alpaca widgets | |
self.alpaca_api_key = widgets.Password( | |
description='API Key:', | |
style={'description_width': '120px'} | |
) | |
self.alpaca_secret_key = widgets.Password( | |
description='Secret Key:', | |
style={'description_width': '120px'} | |
) | |
self.connect_alpaca_btn = widgets.Button( | |
description='Connect to Alpaca', | |
button_style='info', | |
icon='link' | |
) | |
self.alpaca_output = widgets.Output() | |
# FinRL widgets | |
self.finrl_algorithm = widgets.Dropdown( | |
options=['PPO', 'A2C', 'DDPG', 'TD3'], | |
value='PPO', | |
description='Algorithm:', | |
style={'description_width': '120px'} | |
) | |
self.learning_rate = widgets.FloatSlider( | |
value=0.0003, | |
min=0.0001, | |
max=0.01, | |
step=0.0001, | |
description='Learning Rate:', | |
style={'description_width': '120px'}, | |
readout_format='.4f' | |
) | |
self.training_steps = widgets.IntSlider( | |
value=100000, | |
min=1000, | |
max=1000000, | |
step=1000, | |
description='Training Steps:', | |
style={'description_width': '120px'} | |
) | |
self.batch_size = widgets.Dropdown( | |
options=[32, 64, 128, 256], | |
value=64, | |
description='Batch Size:', | |
style={'description_width': '120px'} | |
) | |
self.start_training_btn = widgets.Button( | |
description='Start Training', | |
button_style='warning', | |
icon='play' | |
) | |
self.finrl_output = widgets.Output() | |
# Trading widgets | |
self.capital_input = widgets.IntText( | |
value=100000, | |
description='Capital ($):', | |
style={'description_width': '120px'} | |
) | |
self.order_size_input = widgets.IntText( | |
value=10, | |
description='Order Size:', | |
style={'description_width': '120px'} | |
) | |
self.start_trading_btn = widgets.Button( | |
description='Start Trading', | |
button_style='danger', | |
icon='rocket' | |
) | |
self.stop_trading_btn = widgets.Button( | |
description='Stop Trading', | |
button_style='danger', | |
icon='stop' | |
) | |
self.trading_output = widgets.Output() | |
# Backtesting widgets | |
self.run_backtest_btn = widgets.Button( | |
description='Run Backtest', | |
button_style='primary', | |
icon='chart-line' | |
) | |
self.backtest_output = widgets.Output() | |
# Chart widgets | |
self.chart_type = widgets.Dropdown( | |
options=['Candlestick', 'Line', 'Volume', 'Technical Indicators'], | |
value='Candlestick', | |
description='Chart Type:', | |
style={'description_width': '120px'} | |
) | |
self.chart_output = widgets.Output() | |
# Setup callbacks | |
self.load_config_btn.on_click(self.on_load_config) | |
self.load_data_btn.on_click(self.on_load_data) | |
self.connect_alpaca_btn.on_click(self.on_connect_alpaca) | |
self.start_training_btn.on_click(self.on_start_training) | |
self.start_trading_btn.on_click(self.on_start_trading) | |
self.stop_trading_btn.on_click(self.on_stop_trading) | |
self.run_backtest_btn.on_click(self.on_run_backtest) | |
self.chart_type.observe(self.on_chart_type_change, names='value') | |
def on_load_config(self, b): | |
"""Handle configuration loading""" | |
with self.config_output: | |
clear_output() | |
try: | |
self.config = load_config(self.config_file.value) | |
print(f"β Configuration loaded from {self.config_file.value}") | |
print(f"Symbol: {self.config['trading']['symbol']}") | |
print(f"Capital: ${self.config['trading']['capital']:,}") | |
print(f"Timeframe: {self.config['trading']['timeframe']}") | |
print(f"Broker: {self.config['execution']['broker_api']}") | |
except Exception as e: | |
print(f"β Error loading configuration: {e}") | |
def on_load_data(self, b): | |
"""Handle data loading""" | |
with self.data_output: | |
clear_output() | |
try: | |
if self.config: | |
# Update config with widget values | |
self.config['data_source']['type'] = self.data_source.value | |
self.config['trading']['symbol'] = self.symbol_input.value | |
self.config['trading']['timeframe'] = self.timeframe_input.value | |
print(f"Loading data for {self.symbol_input.value}...") | |
self.data = load_data(self.config) | |
if self.data is not None and not self.data.empty: | |
print(f"β Loaded {len(self.data)} data points") | |
print(f"Date range: {self.data['timestamp'].min()} to {self.data['timestamp'].max()}") | |
print(f"Price range: ${self.data['close'].min():.2f} - ${self.data['close'].max():.2f}") | |
# Add technical indicators | |
self.data = add_technical_indicators(self.data) | |
print(f"β Added technical indicators") | |
# Update chart | |
self.update_chart() | |
else: | |
print("β Failed to load data") | |
else: | |
print("β οΈ Please load configuration first") | |
except Exception as e: | |
print(f"β Error loading data: {e}") | |
def on_connect_alpaca(self, b): | |
"""Handle Alpaca connection""" | |
with self.alpaca_output: | |
clear_output() | |
try: | |
if self.alpaca_api_key.value and self.alpaca_secret_key.value: | |
# Update config with API keys | |
if self.config: | |
self.config['alpaca']['api_key'] = self.alpaca_api_key.value | |
self.config['alpaca']['secret_key'] = self.alpaca_secret_key.value | |
self.config['execution']['broker_api'] = 'alpaca_paper' | |
print("Connecting to Alpaca...") | |
self.alpaca_broker = AlpacaBroker(self.config) | |
account_info = self.alpaca_broker.get_account_info() | |
if account_info: | |
print("β Connected to Alpaca") | |
print(f"Account ID: {account_info['account_id']}") | |
print(f"Status: {account_info['status']}") | |
print(f"Buying Power: ${account_info['buying_power']:,.2f}") | |
print(f"Portfolio Value: ${account_info['portfolio_value']:,.2f}") | |
else: | |
print("β Failed to connect to Alpaca") | |
else: | |
print("β οΈ Please load configuration first") | |
else: | |
print("β οΈ Please enter Alpaca API credentials") | |
except Exception as e: | |
print(f"β Error connecting to Alpaca: {e}") | |
def on_start_training(self, b): | |
"""Handle FinRL training""" | |
with self.finrl_output: | |
clear_output() | |
try: | |
if self.data is not None: | |
print("Starting FinRL training...") | |
# Create FinRL config | |
finrl_config = FinRLConfig( | |
algorithm=self.finrl_algorithm.value, | |
learning_rate=self.learning_rate.value, | |
batch_size=self.batch_size.value, | |
buffer_size=1000000, | |
learning_starts=100, | |
gamma=0.99, | |
tau=0.005, | |
train_freq=1, | |
gradient_steps=1, | |
verbose=1, | |
tensorboard_log='logs/finrl_tensorboard' | |
) | |
# Initialize agent | |
self.finrl_agent = FinRLAgent(finrl_config) | |
# Train the agent | |
result = self.finrl_agent.train( | |
data=self.data, | |
config=self.config, | |
total_timesteps=self.training_steps.value, | |
use_real_broker=False | |
) | |
if result['success']: | |
print("β Training completed successfully!") | |
print(f"Algorithm: {result['algorithm']}") | |
print(f"Timesteps: {result['total_timesteps']:,}") | |
print(f"Model saved: {result['model_path']}") | |
else: | |
print("β Training failed") | |
else: | |
print("β οΈ Please load data first") | |
except Exception as e: | |
print(f"β Error during training: {e}") | |
def on_start_trading(self, b): | |
"""Handle trading start""" | |
with self.trading_output: | |
clear_output() | |
try: | |
if self.config and self.alpaca_broker: | |
print("Starting live trading...") | |
self.trading_active = True | |
# Update config with widget values | |
self.config['trading']['capital'] = self.capital_input.value | |
self.config['execution']['order_size'] = self.order_size_input.value | |
# Start trading in background thread | |
def run_trading(): | |
try: | |
run_live_trading(self.config, self.data) | |
except Exception as e: | |
print(f"Trading error: {e}") | |
trading_thread = threading.Thread(target=run_trading) | |
trading_thread.daemon = True | |
trading_thread.start() | |
print("β Live trading started") | |
else: | |
print("β οΈ Please load configuration and connect to Alpaca first") | |
except Exception as e: | |
print(f"β Error starting trading: {e}") | |
def on_stop_trading(self, b): | |
"""Handle trading stop""" | |
with self.trading_output: | |
clear_output() | |
self.trading_active = False | |
print("β Trading stopped") | |
def on_run_backtest(self, b): | |
"""Handle backtesting""" | |
with self.backtest_output: | |
clear_output() | |
try: | |
if self.config and self.data is not None: | |
print("Running backtest...") | |
# Update config with widget values | |
self.config['trading']['capital'] = self.capital_input.value | |
result = run_backtest(self.config, self.data) | |
if result['success']: | |
print("β Backtest completed") | |
print(f"Total Return: {result['total_return']:.2%}") | |
print(f"Sharpe Ratio: {result['sharpe_ratio']:.2f}") | |
print(f"Max Drawdown: {result['max_drawdown']:.2%}") | |
print(f"Total Trades: {result['total_trades']}") | |
else: | |
print("β Backtest failed") | |
else: | |
print("β οΈ Please load configuration and data first") | |
except Exception as e: | |
print(f"β Error during backtest: {e}") | |
def on_chart_type_change(self, change): | |
"""Handle chart type change""" | |
if self.data is not None: | |
self.update_chart() | |
def update_chart(self): | |
"""Update the chart display""" | |
with self.chart_output: | |
clear_output() | |
if self.data is None: | |
return | |
if self.chart_type.value == "Candlestick": | |
fig = go.Figure(data=[go.Candlestick( | |
x=self.data['timestamp'], | |
open=self.data['open'], | |
high=self.data['high'], | |
low=self.data['low'], | |
close=self.data['close'] | |
)]) | |
fig.update_layout( | |
title=f"{self.config['trading']['symbol']} Candlestick Chart", | |
xaxis_title="Date", | |
yaxis_title="Price ($)", | |
height=500 | |
) | |
display(fig) | |
elif self.chart_type.value == "Line": | |
fig = px.line(self.data, x='timestamp', y='close', | |
title=f"{self.config['trading']['symbol']} Price Chart") | |
fig.update_layout(height=500) | |
display(fig) | |
elif self.chart_type.value == "Volume": | |
fig = go.Figure() | |
fig.add_trace(go.Bar( | |
x=self.data['timestamp'], | |
y=self.data['volume'], | |
name='Volume' | |
)) | |
fig.update_layout( | |
title=f"{self.config['trading']['symbol']} Volume Chart", | |
xaxis_title="Date", | |
yaxis_title="Volume", | |
height=500 | |
) | |
display(fig) | |
elif self.chart_type.value == "Technical Indicators": | |
fig = go.Figure() | |
# Add price | |
fig.add_trace(go.Scatter( | |
x=self.data['timestamp'], | |
y=self.data['close'], | |
name='Close Price', | |
line=dict(color='blue') | |
)) | |
# Add moving averages if available | |
if 'sma_20' in self.data.columns: | |
fig.add_trace(go.Scatter( | |
x=self.data['timestamp'], | |
y=self.data['sma_20'], | |
name='SMA 20', | |
line=dict(color='orange') | |
)) | |
if 'sma_50' in self.data.columns: | |
fig.add_trace(go.Scatter( | |
x=self.data['timestamp'], | |
y=self.data['sma_50'], | |
name='SMA 50', | |
line=dict(color='red') | |
)) | |
fig.update_layout( | |
title=f"{self.config['trading']['symbol']} Technical Indicators", | |
xaxis_title="Date", | |
yaxis_title="Price ($)", | |
height=500 | |
) | |
display(fig) | |
def display_interface(self): | |
"""Display the complete Jupyter interface""" | |
# Header | |
display(HTML(""" | |
<div style="text-align: center; margin-bottom: 20px;"> | |
<h1>π€ Algorithmic Trading System</h1> | |
<p>Interactive Jupyter Interface for Trading Analysis</p> | |
</div> | |
""")) | |
# Configuration section | |
display(HTML("<h2>βοΈ Configuration</h2>")) | |
config_widgets = widgets.VBox([ | |
widgets.HBox([self.config_file, self.load_config_btn]), | |
self.config_output | |
]) | |
display(config_widgets) | |
# Data section | |
display(HTML("<h2>π₯ Data Management</h2>")) | |
data_widgets = widgets.VBox([ | |
widgets.HBox([self.data_source, self.symbol_input, self.timeframe_input]), | |
widgets.HBox([self.load_data_btn]), | |
self.data_output | |
]) | |
display(data_widgets) | |
# Alpaca section | |
display(HTML("<h2>π¦ Alpaca Integration</h2>")) | |
alpaca_widgets = widgets.VBox([ | |
widgets.HBox([self.alpaca_api_key, self.alpaca_secret_key]), | |
widgets.HBox([self.connect_alpaca_btn]), | |
self.alpaca_output | |
]) | |
display(alpaca_widgets) | |
# FinRL section | |
display(HTML("<h2>π§ FinRL Training</h2>")) | |
finrl_widgets = widgets.VBox([ | |
widgets.HBox([self.finrl_algorithm, self.learning_rate]), | |
widgets.HBox([self.training_steps, self.batch_size]), | |
widgets.HBox([self.start_training_btn]), | |
self.finrl_output | |
]) | |
display(finrl_widgets) | |
# Trading section | |
display(HTML("<h2>π― Trading Controls</h2>")) | |
trading_widgets = widgets.VBox([ | |
widgets.HBox([self.capital_input, self.order_size_input]), | |
widgets.HBox([self.start_trading_btn, self.stop_trading_btn]), | |
self.trading_output | |
]) | |
display(trading_widgets) | |
# Backtesting section | |
display(HTML("<h2>π Backtesting</h2>")) | |
backtest_widgets = widgets.VBox([ | |
widgets.HBox([self.run_backtest_btn]), | |
self.backtest_output | |
]) | |
display(backtest_widgets) | |
# Chart section | |
display(HTML("<h2>π Data Visualization</h2>")) | |
chart_widgets = widgets.VBox([ | |
widgets.HBox([self.chart_type]), | |
self.chart_output | |
]) | |
display(chart_widgets) | |
def create_jupyter_interface(): | |
"""Create and return the Jupyter interface""" | |
ui = TradingJupyterUI() | |
return ui |