import os
import subprocess
import pandas as pd
import plotly.express as px
import gradio as gr
from dotenv import load_dotenv
from util import css, js, Color
from accounts import Account
from database import read_log
from typing import List
# Load environment variables
load_dotenv(override=True)
# Config
RUN_EVERY_N_MINUTES = int(os.getenv("RUN_EVERY_N_MINUTES", "60"))
RUN_EVEN_WHEN_MARKET_IS_CLOSED = os.getenv("RUN_EVEN_WHEN_MARKET_IS_CLOSED", "false").strip().lower() == "true"
USE_MANY_MODELS = os.getenv("USE_MANY_MODELS", "false").strip().lower() == "true"
names = ["Warren", "George", "Ray", "Cathie"]
lastnames = ["Patience", "Bold", "Systematic", "Crypto"]
if USE_MANY_MODELS:
model_names = [
"gpt-4.1-mini",
"deepseek-chat",
"gemini-2.5-flash-preview-04-17",
"grok-3-mini-beta",
]
short_model_names = ["GPT 4.1 Mini", "DeepSeek V3", "Gemini 2.5 Flash", "Grok 3 Mini"]
else:
model_names = ["gpt-4o-mini"] * 4
short_model_names = ["GPT 4o mini"] * 4
# UI Wrapper
mapper = {
"trace": Color.WHITE,
"agent": Color.CYAN,
"function": Color.GREEN,
"generation": Color.YELLOW,
"response": Color.MAGENTA,
"account": Color.RED,
}
class Trader:
def __init__(self, name: str, lastname: str, model_name: str):
self.name = name
self.lastname = lastname
self.model_name = model_name
self.account = Account.get(name)
def reload(self):
self.account = Account.get(self.name)
def get_title(self):
return f"
{self.name} ({self.model_name}) - {self.lastname}
"
def get_strategy(self):
return self.account.get_strategy()
def get_portfolio_value_df(self):
df = pd.DataFrame(self.account.portfolio_value_time_series, columns=["datetime", "value"])
df["datetime"] = pd.to_datetime(df["datetime"])
return df
def get_portfolio_value_chart(self):
df = self.get_portfolio_value_df()
fig = px.line(df, x="datetime", y="value")
margin = dict(l=40, r=20, t=20, b=40)
fig.update_layout(height=300, margin=margin, xaxis_title=None, yaxis_title=None,
paper_bgcolor="#bbb", plot_bgcolor="#dde")
fig.update_xaxes(tickformat="%m/%d", tickangle=45, tickfont=dict(size=8))
fig.update_yaxes(tickfont=dict(size=8), tickformat=",.0f")
return fig
def get_holdings_df(self):
holdings = self.account.get_holdings()
if not holdings:
return pd.DataFrame(columns=["Symbol", "Quantity"])
return pd.DataFrame([{"Symbol": s, "Quantity": q} for s, q in holdings.items()])
def get_transactions_df(self):
transactions = self.account.list_transactions()
if not transactions:
return pd.DataFrame(columns=["Timestamp", "Symbol", "Quantity", "Price", "Rationale"])
return pd.DataFrame(transactions)
def get_portfolio_value(self):
value = self.account.calculate_portfolio_value() or 0.0
pnl = self.account.calculate_profit_loss(value) or 0.0
color = "green" if pnl >= 0 else "red"
emoji = "⬆" if pnl >= 0 else "⬇"
return f"${value:,.0f} {emoji} ${pnl:,.0f}
"
def get_logs(self, previous=None):
logs = read_log(self.name, last_n=13)
response = "".join(
f"{ts} : [{t}] {msg}
"
for ts, t, msg in logs
)
response = f"{response}
"
return response if response != previous else gr.update()
class TraderView:
def __init__(self, trader: Trader):
self.trader = trader
def make_ui(self):
with gr.Column():
gr.HTML(self.trader.get_title())
self.portfolio_value = gr.HTML(self.trader.get_portfolio_value)
self.chart = gr.Plot(self.trader.get_portfolio_value_chart)
self.log = gr.HTML(self.trader.get_logs)
self.holdings_table = gr.Dataframe(
value=self.trader.get_holdings_df,
label="Holdings", headers=["Symbol", "Quantity"], max_height=300
)
self.transactions_table = gr.Dataframe(
value=self.trader.get_transactions_df,
label="Recent Transactions",
headers=["Timestamp", "Symbol", "Quantity", "Price", "Rationale"],
max_height=300
)
gr.Timer(value=120).tick(fn=self.refresh, inputs=[], outputs=[
self.portfolio_value, self.chart, self.holdings_table, self.transactions_table
], show_progress="hidden", queue=False)
gr.Timer(value=0.5).tick(fn=self.trader.get_logs, inputs=[self.log], outputs=[self.log],
show_progress="hidden", queue=False)
def refresh(self):
self.trader.reload()
return (
self.trader.get_portfolio_value(),
self.trader.get_portfolio_value_chart(),
self.trader.get_holdings_df(),
self.trader.get_transactions_df(),
)
def create_ui():
traders = [Trader(n, ln, sm) for n, ln, sm in zip(names, lastnames, short_model_names)]
trader_views = [TraderView(t) for t in traders]
with gr.Blocks(title="Traders", css=css, js=js) as ui:
with gr.Row():
for tv in trader_views:
tv.make_ui()
return ui
if __name__ == "__main__":
# Launch trading_floor.py as a separate subprocess
trading_floor_proc = subprocess.Popen(["python", "trading_floor.py"])
try:
ui = create_ui()
ui.launch(server_name="0.0.0.0", server_port=int(os.getenv("PORT", 7860)))
finally:
trading_floor_proc.terminate()
trading_floor_proc.wait()