| """ |
| MarketMind Streamlit Dashboard. |
| |
| Live playback of the market simulation. |
| Lets the user dynamically change agent composition and watch the emergent behavior. |
| """ |
|
|
| import sys |
| import os |
| import time |
| import pandas as pd |
| import streamlit as st |
|
|
| |
| sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) |
|
|
| from engine.simulation import SimulationEngine, SimulationConfig |
| from agents.momentum_agent import MomentumAgent |
| from agents.mean_reversion_agent import MeanReversionAgent |
| from agents.fundamental_agent import FundamentalAgent |
| from agents.market_maker_agent import MarketMakerAgent |
| from agents.noise_trader import NoiseTrader |
| from dashboard.plots import plot_price_chart, plot_agent_pnl, plot_spread |
|
|
| st.set_page_config(page_title="MarketMind Simulation", layout="wide", page_icon="📈") |
|
|
|
|
| def build_agents(n_mom: int, n_mr: int, n_fund: int, n_noise: int, n_mm: int) -> list: |
| """Build the agent pool based on slider inputs.""" |
| agents = [] |
| for i in range(n_mom): |
| agents.append(MomentumAgent(f"momentum_{i+1}")) |
| for i in range(n_mr): |
| agents.append(MeanReversionAgent(f"meanrev_{i+1}")) |
| for i in range(n_fund): |
| agents.append(FundamentalAgent(f"fundamental_{i+1}", fair_value=100.0)) |
| for i in range(n_noise): |
| agents.append(NoiseTrader(f"noise_{i+1}")) |
| for i in range(n_mm): |
| agents.append(MarketMakerAgent(f"marketmaker_{i+1}")) |
| return agents |
|
|
|
|
| def main(): |
| st.title("📈 MarketMind: Agent-Based Market Simulation") |
| st.markdown("Observe emergent market behavior based on LLM agent composition.") |
|
|
| |
| st.sidebar.header("⚙️ Agent Composition") |
| st.sidebar.markdown("Change the mix of traders to test market stability.") |
|
|
| n_mom = st.sidebar.slider("Momentum Traders", 0, 10, 2) |
| n_mr = st.sidebar.slider("Mean Reversion Traders", 0, 10, 1) |
| n_fund = st.sidebar.slider("Fundamental Traders (Anchor)", 0, 10, 1) |
| n_noise = st.sidebar.slider("Noise Traders", 0, 10, 1) |
| n_mm = st.sidebar.slider("Market Makers", 0, 5, 1) |
|
|
| st.sidebar.markdown("---") |
| num_ticks = st.sidebar.slider("Simulation Ticks", 50, 500, 150, step=50) |
| playback_speed = st.sidebar.slider("Playback Speed (ms)", 0, 200, 50, step=10) |
|
|
| if st.sidebar.button("🚀 Run Simulation", type="primary"): |
| run_simulation_and_play(n_mom, n_mr, n_fund, n_noise, n_mm, num_ticks, playback_speed) |
| else: |
| st.info("Configure your agents in the sidebar and click **Run Simulation**.") |
|
|
|
|
| def run_simulation_and_play(n_mom, n_mr, n_fund, n_noise, n_mm, num_ticks, playback_speed): |
| |
| agents = build_agents(n_mom, n_mr, n_fund, n_noise, n_mm) |
| if not agents: |
| st.error("You need at least one agent to run a simulation!") |
| return |
|
|
| config = SimulationConfig( |
| num_ticks=num_ticks, |
| initial_price=100.0, |
| use_llm=False, |
| log_to_csv=False, |
| ) |
|
|
| engine = SimulationEngine(agents, config) |
| |
| with st.spinner(f"Running simulation offline ({num_ticks} ticks)..."): |
| engine.run() |
|
|
| |
| ticks_data = engine.csv_rows |
| pnl_data = engine.agent_pnl_rows |
|
|
| st.success(f"Simulation generated! Playing back...") |
|
|
| |
| col1, col2 = st.columns([3, 1]) |
| |
| with col1: |
| price_placeholder = st.empty() |
| spread_placeholder = st.empty() |
| |
| with col2: |
| regime_placeholder = st.empty() |
| st.markdown("### Agent Leaderboard") |
| leaderboard_placeholder = st.empty() |
|
|
| |
| curr_ticks = [] |
| curr_prices = [] |
| curr_spreads = [] |
| curr_pnls = {agent.agent_id: [] for agent in agents} |
|
|
| |
| for tick_idx in range(len(ticks_data)): |
| tick_info = ticks_data[tick_idx] |
| t = tick_info["tick"] |
| |
| curr_ticks.append(t) |
| curr_prices.append(tick_info["mid_price"] if tick_info["mid_price"] is not None else 100.0) |
| curr_spreads.append(tick_info["spread"] if tick_info["spread"] is not None else 0.0) |
|
|
| |
| tick_pnl_rows = [row for row in pnl_data if row["tick"] == t] |
| for row in tick_pnl_rows: |
| curr_pnls[row["agent_id"]].append(row["pnl"]) |
|
|
| |
| |
| price_fig = plot_price_chart(curr_ticks, curr_prices, true_fair_values=[100.0] * len(curr_ticks)) |
| price_placeholder.plotly_chart(price_fig, use_container_width=True, key=f"p_{t}") |
|
|
| spread_fig = plot_spread(curr_ticks, curr_spreads) |
| spread_placeholder.plotly_chart(spread_fig, use_container_width=True, key=f"s_{t}") |
|
|
| |
| regime = tick_info["regime"] |
| color = "green" if regime == "Efficient" else "orange" if regime == "Trending" else "red" |
| regime_placeholder.markdown(f"### Market Regime: <span style='color:{color}'>{regime}</span>", unsafe_allow_html=True) |
|
|
| |
| |
| current_leaderboard = sorted( |
| [{"Agent": row["agent_id"], "Type": row["agent_type"], "PnL": f"${row['pnl']:.2f}", "Pos": row["position"]} for row in tick_pnl_rows], |
| key=lambda x: float(x["PnL"].replace('$', '')), |
| reverse=True |
| ) |
| df_leaderboard = pd.DataFrame(current_leaderboard) |
| leaderboard_placeholder.dataframe(df_leaderboard, use_container_width=True, hide_index=True) |
|
|
| |
| if playback_speed > 0: |
| time.sleep(playback_speed / 1000.0) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|