kosmoscpp commited on
Commit
b685ea7
Β·
verified Β·
1 Parent(s): 322cdc5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +83 -130
app.py CHANGED
@@ -1,155 +1,108 @@
1
  import gradio as gr
2
  import random
3
- import pandas as pd
4
  import plotly.graph_objects as go
5
 
6
- # --- SIMULATION STATE ---
7
- class MarketSimulation:
8
- def __init__(self):
9
- self.reset()
10
-
11
- def reset(self):
12
- self.balance = 10000.0
13
- self.shares = 0
14
- self.avg_buy_price = 0.0
15
- self.stock_price = 100.0
16
- self.price_history = [100.0]
17
- self.market_modes = ["Bull πŸ“ˆ", "Bear πŸ“‰", "Sideways ➑️"]
18
- self.current_mode = "Sideways ➑️"
19
- self.tick_counter = 0
20
 
21
- def tick(self):
22
- self.tick_counter += 1
23
-
24
- # V2: Shift market regimes every 15 seconds
25
- if self.tick_counter % 15 == 0:
26
- self.current_mode = random.choice(self.market_modes)
 
 
 
 
 
 
 
27
 
28
- # Base volatility (percentage-based)
29
- base_move = random.uniform(-0.03, 0.03) # Β±3%
30
 
31
- # Apply regime bias
32
- if self.current_mode == "Bull πŸ“ˆ":
33
- base_move += random.uniform(0.005, 0.02) # Upward bias
34
- elif self.current_mode == "Bear πŸ“‰":
35
- base_move -= random.uniform(0.005, 0.02) # Downward bias
36
-
37
- # Occasional "Black Swan" shock move (2% chance)
38
- if random.random() < 0.02:
39
- base_move += random.choice([-0.08, 0.08]) # Sudden Β±8% spike
40
-
41
- # Calculate new price
42
- self.stock_price = max(1.0, round(self.stock_price * (1 + base_move), 2))
43
- self.price_history.append(self.stock_price)
44
 
45
- # Keep history manageable
46
- if len(self.price_history) > 40:
47
- self.price_history.pop(0)
48
-
49
- return self.get_status(), self.update_chart()
50
-
51
- def buy_stock(self):
52
- if self.balance >= self.stock_price:
53
- # Buy max possible shares for simplicity, or just 10 shares
54
- shares_to_buy = min(10, int(self.balance // self.stock_price))
55
- if shares_to_buy > 0:
56
- total_cost = shares_to_buy * self.stock_price
57
-
58
- # Recalculate average buy price
59
- total_shares = self.shares + shares_to_buy
60
- self.avg_buy_price = ((self.shares * self.avg_buy_price) + total_cost) / total_shares
61
-
62
- self.shares += shares_to_buy
63
- self.balance -= total_cost
64
- return self.get_status(), self.update_chart()
65
-
66
- def sell_stock(self):
67
- if self.shares > 0:
68
- shares_to_sell = min(10, self.shares)
69
- total_revenue = shares_to_sell * self.stock_price
70
- self.shares -= shares_to_sell
71
- self.balance += total_revenue
72
- if self.shares == 0:
73
- self.avg_buy_price = 0.0
74
- return self.get_status(), self.update_chart()
75
 
76
- def get_status(self):
77
- # Calculate Profit/Loss
78
- current_value = self.shares * self.stock_price
79
- cost_basis = self.shares * self.avg_buy_price
80
- pnl = current_value - cost_basis
81
- pnl_string = f"+${pnl:,.2f}" if pnl >= 0 else f"-${abs(pnl):,.2f}"
82
-
83
- portfolio_value = self.balance + current_value
84
-
85
- return (
86
- f"${self.balance:,.2f}",
87
- f"${self.stock_price:,.2f}",
88
- f"{self.shares}",
89
- f"${self.avg_buy_price:,.2f}",
90
- pnl_string,
91
- f"${portfolio_value:,.2f}",
92
- f"Current Regime: {self.current_mode}"
93
- )
94
 
95
- def update_chart(self):
96
- fig = go.Figure()
97
- fig.add_trace(go.Scatter(
98
- y=self.price_history,
99
- mode='lines+markers',
100
- line=dict(color='#2196F3', width=3),
101
- marker=dict(size=6),
102
- name='Price'
103
- ))
104
- fig.update_layout(
105
- title="Live Stock Price (Updates Every Second)",
106
- xaxis_title="Time (Ticks)",
107
- yaxis_title="Price ($)",
108
- template="plotly_dark",
109
- margin=dict(l=20, r=20, t=40, b=20),
110
- height=350
111
- )
112
- return fig
113
 
114
- # --- GRADIO INTERFACE ---
115
- sim = MarketSimulation()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
116
 
117
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
118
- gr.Markdown("# πŸ“ˆ PaperTrader Sim V2")
119
- gr.Markdown("A pure-Python live market sandbox. Watch out for regime shifts and black swan shocks!")
120
 
121
  with gr.Row():
122
  with gr.Column(scale=2):
123
- chart = gr.Plot(value=sim.update_chart())
124
- regime_display = gr.Markdown("Current Regime: Sideways ➑️")
125
-
126
  with gr.Column(scale=1):
127
- gr.Markdown("### πŸ’° Account & Portfolio")
128
- total_net_worth = gr.Textbox(label="Total Net Worth", value="$10,000.00", interactive=False)
129
- balance_box = gr.Textbox(label="Cash Balance", value="$10,000.00", interactive=False)
130
- shares_box = gr.Textbox(label="Shares Owned", value="0", interactive=False)
131
- avg_price_box = gr.Textbox(label="Avg Buy Price", value="$0.00", interactive=False)
132
- pnl_box = gr.Textbox(label="Profit / Loss", value="$0.00", interactive=False)
133
 
134
  with gr.Row():
135
- btn_buy = gr.Button("πŸ›’ BUY 10 SHARES", variant="primary")
136
- btn_sell = gr.Button("πŸ’΅ SELL 10 SHARES", variant="secondary")
137
- btn_reset = gr.Button("πŸ”„ Reset Simulation")
138
 
139
- # Connect UI Components
140
- status_outputs = [balance_box, gr.Textbox(visible=False), shares_box, avg_price_box, pnl_box, total_net_worth, regime_display]
141
 
142
- btn_buy.click(sim.buy_stock, outputs=[status_outputs[0], status_outputs[2], status_outputs[3], status_outputs[4], status_outputs[5], status_outputs[6], chart])
143
- btn_sell.click(sim.sell_stock, outputs=[status_outputs[0], status_outputs[2], status_outputs[3], status_outputs[4], status_outputs[5], status_outputs[6], chart])
144
 
145
- def reset_ui():
146
- sim.reset()
147
- return sim.get_status() + (sim.update_chart(),)
148
-
149
- btn_reset.click(reset_ui, outputs=[balance_box, gr.Textbox(visible=False), shares_box, avg_price_box, pnl_box, total_net_worth, regime_display, chart])
150
-
151
- # The magic engine: Trigger a tick every 1.0 second automatically
152
  timer = gr.Timer(1.0)
153
- timer.tick(sim.tick, outputs=[balance_box, gr.Textbox(visible=False), shares_box, avg_price_box, pnl_box, total_net_worth, regime_display, chart])
154
 
155
  demo.launch()
 
1
  import gradio as gr
2
  import random
 
3
  import plotly.graph_objects as go
4
 
5
+ # We use a simple dict for state to keep Hugging Face happy
6
+ init_state = {
7
+ "balance": 10000.0,
8
+ "shares": 0,
9
+ "avg_buy_price": 0.0,
10
+ "stock_price": 100.0,
11
+ "price_history": [100.0],
12
+ "current_mode": "Sideways ➑️",
13
+ "tick_counter": 0
14
+ }
 
 
 
 
15
 
16
+ def tick(state):
17
+ state["tick_counter"] += 1
18
+
19
+ # Change market modes
20
+ if state["tick_counter"] % 15 == 0:
21
+ state["current_mode"] = random.choice(["Bull πŸ“ˆ", "Bear πŸ“‰", "Sideways ➑️"])
22
+
23
+ # Percentage calculation
24
+ base_move = random.uniform(-0.03, 0.03)
25
+ if state["current_mode"] == "Bull πŸ“ˆ":
26
+ base_move += random.uniform(0.005, 0.02)
27
+ elif state["current_mode"] == "Bear πŸ“‰":
28
+ base_move -= random.uniform(0.005, 0.02)
29
 
30
+ if random.random() < 0.02: # Black swan
31
+ base_move += random.choice([-0.08, 0.08])
32
 
33
+ state["stock_price"] = max(1.0, round(state["stock_price"] * (1 + base_move), 2))
34
+ state["price_history"].append(state["stock_price"])
35
+ if len(state["price_history"]) > 30:
36
+ state["price_history"].pop(0)
 
 
 
 
 
 
 
 
 
37
 
38
+ return state, *get_ui_updates(state)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
39
 
40
+ def buy_stock(state):
41
+ if state["balance"] >= state["stock_price"]:
42
+ shares_to_buy = min(10, int(state["balance"] // state["stock_price"]))
43
+ if shares_to_buy > 0:
44
+ total_cost = shares_to_buy * state["stock_price"]
45
+ total_shares = state["shares"] + shares_to_buy
46
+ state["avg_buy_price"] = ((state["shares"] * state["avg_buy_price"]) + total_cost) / total_shares
47
+ state["shares"] = total_shares
48
+ state["balance"] -= total_cost
49
+ return state, *get_ui_updates(state)
 
 
 
 
 
 
 
 
50
 
51
+ def sell_stock(state):
52
+ if state["shares"] > 0:
53
+ shares_to_sell = min(10, state["shares"])
54
+ state["balance"] += shares_to_sell * state["stock_price"]
55
+ state["shares"] -= shares_to_sell
56
+ if state["shares"] == 0:
57
+ state["avg_buy_price"] = 0.0
58
+ return state, *get_ui_updates(state)
 
 
 
 
 
 
 
 
 
 
59
 
60
+ def get_ui_updates(state):
61
+ pnl = (state["shares"] * state["stock_price"]) - (state["shares"] * state["avg_buy_price"])
62
+ pnl_str = f"+${pnl:,.2f}" if pnl >= 0 else f"-${abs(pnl):,.2f}"
63
+ net_worth = state["balance"] + (state["shares"] * state["stock_price"])
64
+
65
+ fig = go.Figure()
66
+ fig.add_trace(go.Scatter(y=state["price_history"], mode='lines+markers', line=dict(color='#2196F3', width=3)))
67
+ fig.update_layout(template="plotly_dark", margin=dict(l=10, r=10, t=10, b=10), height=300)
68
+
69
+ return (
70
+ f"${state['balance']:,.2f}",
71
+ f"${state['stock_price']:,.2f}",
72
+ str(state["shares"]),
73
+ f"${state['avg_buy_price']:,.2f}",
74
+ pnl_str,
75
+ f"${net_worth:,.2f}",
76
+ f"Market: {state['current_mode']}",
77
+ fig
78
+ )
79
 
80
  with gr.Blocks(theme=gr.themes.Soft()) as demo:
81
+ gr.Markdown("# πŸ“ˆ StockSim")
82
+ state = gr.State(default=init_state.copy())
83
 
84
  with gr.Row():
85
  with gr.Column(scale=2):
86
+ chart = gr.Plot()
87
+ regime_display = gr.Markdown("Market: Sideways ➑️")
 
88
  with gr.Column(scale=1):
89
+ net_worth_box = gr.Textbox(label="Net Worth")
90
+ balance_box = gr.Textbox(label="Cash")
91
+ shares_box = gr.Textbox(label="Shares")
92
+ avg_box = gr.Textbox(label="Avg Cost")
93
+ pnl_box = gr.Textbox(label="P&L")
 
94
 
95
  with gr.Row():
96
+ btn_buy = gr.Button("BUY 10", variant="primary")
97
+ btn_sell = gr.Button("SELL 10", variant="secondary")
 
98
 
99
+ outputs = [balance_box, gr.Textbox(visible=False), shares_box, avg_box, pnl_box, net_worth_box, regime_display, chart]
 
100
 
101
+ btn_buy.click(buy_stock, inputs=[state], outputs=[state] + outputs)
102
+ btn_sell.click(sell_stock, inputs=[state], outputs=[state] + outputs)
103
 
104
+ # Timer updates state explicitly
 
 
 
 
 
 
105
  timer = gr.Timer(1.0)
106
+ timer.tick(tick, inputs=[state], outputs=[state] + outputs)
107
 
108
  demo.launch()