| import gradio as gr |
| import random |
| import plotly.graph_objects as go |
|
|
| |
| init_state = { |
| "balance": 10000.0, |
| "shares": 0, |
| "avg_buy_price": 0.0, |
| "stock_price": 100.0, |
| "price_history": [100.0], |
| "current_mode": "Sideways β‘οΈ", |
| "tick_counter": 0 |
| } |
|
|
| def tick(state): |
| state["tick_counter"] += 1 |
| |
| |
| if state["tick_counter"] % 15 == 0: |
| state["current_mode"] = random.choice(["Bull π", "Bear π", "Sideways β‘οΈ"]) |
| |
| |
| base_move = random.uniform(-0.03, 0.03) |
| if state["current_mode"] == "Bull π": |
| base_move += random.uniform(0.005, 0.02) |
| elif state["current_mode"] == "Bear π": |
| base_move -= random.uniform(0.005, 0.02) |
| |
| if random.random() < 0.02: |
| base_move += random.choice([-0.08, 0.08]) |
| |
| state["stock_price"] = max(1.0, round(state["stock_price"] * (1 + base_move), 2)) |
| state["price_history"].append(state["stock_price"]) |
| if len(state["price_history"]) > 30: |
| state["price_history"].pop(0) |
| |
| return state, *get_ui_updates(state) |
|
|
| def buy_stock(state): |
| if state["balance"] >= state["stock_price"]: |
| shares_to_buy = min(10, int(state["balance"] // state["stock_price"])) |
| if shares_to_buy > 0: |
| total_cost = shares_to_buy * state["stock_price"] |
| total_shares = state["shares"] + shares_to_buy |
| state["avg_buy_price"] = ((state["shares"] * state["avg_buy_price"]) + total_cost) / total_shares |
| state["shares"] = total_shares |
| state["balance"] -= total_cost |
| return state, *get_ui_updates(state) |
|
|
| def sell_stock(state): |
| if state["shares"] > 0: |
| shares_to_sell = min(10, state["shares"]) |
| state["balance"] += shares_to_sell * state["stock_price"] |
| state["shares"] -= shares_to_sell |
| if state["shares"] == 0: |
| state["avg_buy_price"] = 0.0 |
| return state, *get_ui_updates(state) |
|
|
| def get_ui_updates(state): |
| pnl = (state["shares"] * state["stock_price"]) - (state["shares"] * state["avg_buy_price"]) |
| pnl_str = f"+${pnl:,.2f}" if pnl >= 0 else f"-${abs(pnl):,.2f}" |
| net_worth = state["balance"] + (state["shares"] * state["stock_price"]) |
| |
| fig = go.Figure() |
| fig.add_trace(go.Scatter(y=state["price_history"], mode='lines+markers', line=dict(color='#2196F3', width=3))) |
| fig.update_layout(template="plotly_dark", margin=dict(l=10, r=10, t=10, b=10), height=300) |
| |
| return ( |
| f"${state['balance']:,.2f}", |
| f"${state['stock_price']:,.2f}", |
| str(state["shares"]), |
| f"${state['avg_buy_price']:,.2f}", |
| pnl_str, |
| f"${net_worth:,.2f}", |
| f"Market: {state['current_mode']}", |
| fig |
| ) |
|
|
| with gr.Blocks(theme=gr.themes.Soft()) as demo: |
| gr.Markdown("# π StockSim") |
| state = gr.State(default=init_state.copy()) |
| |
| with gr.Row(): |
| with gr.Column(scale=2): |
| chart = gr.Plot() |
| regime_display = gr.Markdown("Market: Sideways β‘οΈ") |
| with gr.Column(scale=1): |
| net_worth_box = gr.Textbox(label="Net Worth") |
| balance_box = gr.Textbox(label="Cash") |
| shares_box = gr.Textbox(label="Shares") |
| avg_box = gr.Textbox(label="Avg Cost") |
| pnl_box = gr.Textbox(label="P&L") |
|
|
| with gr.Row(): |
| btn_buy = gr.Button("BUY 10", variant="primary") |
| btn_sell = gr.Button("SELL 10", variant="secondary") |
|
|
| outputs = [balance_box, gr.Textbox(visible=False), shares_box, avg_box, pnl_box, net_worth_box, regime_display, chart] |
| |
| btn_buy.click(buy_stock, inputs=[state], outputs=[state] + outputs) |
| btn_sell.click(sell_stock, inputs=[state], outputs=[state] + outputs) |
| |
| |
| timer = gr.Timer(1.0) |
| timer.tick(tick, inputs=[state], outputs=[state] + outputs) |
|
|
| demo.launch() |
|
|