xufangzhi commited on
Commit
d77ebc2
ยท
verified ยท
1 Parent(s): 96dbe92

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +92 -270
app.py CHANGED
@@ -1,3 +1,4 @@
 
1
  import gradio as gr
2
  import numpy as np
3
  import json
@@ -6,6 +7,10 @@ import matplotlib.pyplot as plt
6
  import matplotlib
7
  matplotlib.use('Agg')
8
 
 
 
 
 
9
  class TradeArenaEnv_Deterministic:
10
  """
11
  Odyssey Arena - AI Trading Environment (Deterministic version)
@@ -96,7 +101,10 @@ class TradeArenaEnv_Deterministic:
96
  return round(float(total_value), 2)
97
 
98
 
99
- # Default configuration
 
 
 
100
  DEFAULT_CONFIG = {
101
  "num_days": 30,
102
  "stocks": ["TECH", "ENERGY", "FINANCE"],
@@ -111,33 +119,51 @@ DEFAULT_CONFIG = {
111
  "initial_cash": 10000,
112
  "price_noise_scale": 0,
113
  "timeline": {
114
- "day_1": {
115
- "variable_changes": [0.1, -0.2, 0.3],
116
- "news_text": "Federal Reserve hints at rate increase; Oil prices drop on oversupply concerns"
117
- },
118
- "day_2": {
119
- "variable_changes": [-0.1, 0.3, 0.2],
120
- "news_text": "Tech sector shows strong earnings; Energy stocks rally on production cuts"
121
- },
122
- "day_3": {
123
- "variable_changes": [0.2, 0.1, -0.1],
124
- "news_text": "Market sentiment cautious amid geopolitical tensions"
125
- },
126
- "day_4": {
127
- "variable_changes": [0.0, 0.2, 0.1],
128
- "news_text": "Stable interest rates; Energy sector momentum continues"
129
- },
130
- "day_5": {
131
- "variable_changes": [-0.2, -0.1, 0.0],
132
- "news_text": "Rate cut speculation; Market consolidation"
133
- }
134
  }
135
  }
136
 
137
- # Global state
 
 
 
138
  env = None
139
  history = []
140
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  def initialize_env(config_file=None):
142
  global env, history
143
 
@@ -145,14 +171,13 @@ def initialize_env(config_file=None):
145
  try:
146
  config = json.loads(config_file)
147
  except:
148
- return "โŒ Invalid JSON file", None, None, None, None
149
  else:
150
  config = DEFAULT_CONFIG
151
 
152
  env = TradeArenaEnv_Deterministic(config)
153
  obs = env.reset()
154
 
155
- # Initialize history
156
  history = [{
157
  'day': obs['day'],
158
  'total_value': obs['total_value'],
@@ -166,11 +191,11 @@ def initialize_env(config_file=None):
166
  create_portfolio_display(obs),
167
  create_news_display(obs),
168
  create_price_chart(),
169
- create_value_chart()
 
170
  )
171
 
172
  def create_portfolio_display(obs):
173
- """Create portfolio summary table"""
174
  data = []
175
  for stock in env.stocks:
176
  data.append({
@@ -179,12 +204,9 @@ def create_portfolio_display(obs):
179
  'Holdings': obs['positions'][stock],
180
  'Value': f"${obs['prices'][stock] * obs['positions'][stock]:.2f}"
181
  })
182
-
183
- df = pd.DataFrame(data)
184
- return df
185
 
186
  def create_news_display(obs):
187
- """Create news display"""
188
  if obs['news_next_day_text']:
189
  news_html = f"""
190
  <div style='background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
@@ -204,16 +226,13 @@ def create_news_display(obs):
204
  return "<div style='padding: 20px; background: #f0f0f0; border-radius: 10px; text-align: center;'>๐Ÿ“ญ No more news available</div>"
205
 
206
  def create_price_chart():
207
- """Create price history chart using matplotlib"""
208
  if len(history) <= 1:
209
  fig, ax = plt.subplots(figsize=(10, 6))
210
- ax.text(0.5, 0.5, 'Trade to see price history',
211
- ha='center', va='center', fontsize=14, color='gray')
212
  ax.axis('off')
213
  return fig
214
 
215
  df = pd.DataFrame(history)
216
-
217
  fig, ax = plt.subplots(figsize=(10, 6))
218
  colors = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6']
219
 
@@ -229,163 +248,36 @@ def create_price_chart():
229
  ax.set_facecolor('#f8f9fa')
230
  fig.patch.set_facecolor('white')
231
  plt.tight_layout()
232
-
233
  return fig
234
 
235
  def create_value_chart():
236
- """Create portfolio value chart using matplotlib"""
237
  if len(history) <= 1:
238
  fig, ax = plt.subplots(figsize=(10, 6))
239
- ax.text(0.5, 0.5, 'Trade to see portfolio value',
240
- ha='center', va='center', fontsize=14, color='gray')
241
  ax.axis('off')
242
  return fig
243
 
244
  df = pd.DataFrame(history)
245
-
246
  fig, ax = plt.subplots(figsize=(10, 6))
247
- ax.plot(df['day'], df['total_value'], marker='o', linewidth=3,
248
- color='#8b5cf6', label='Portfolio Value')
249
  ax.fill_between(df['day'], df['total_value'], alpha=0.2, color='#8b5cf6')
250
-
 
 
251
  ax.set_xlabel('Day', fontsize=12, fontweight='bold')
252
  ax.set_ylabel('Total Value ($)', fontsize=12, fontweight='bold')
253
  ax.set_title('Portfolio Value Over Time', fontsize=14, fontweight='bold', pad=20)
254
- ax.legend(loc='best', framealpha=0.9)
255
  ax.grid(True, alpha=0.3)
256
- ax.set_facecolor('#f8f9fa')
257
- fig.patch.set_facecolor('white')
258
-
259
- # Add initial value line
260
- initial_value = history[0]['total_value']
261
- ax.axhline(y=initial_value, color='red', linestyle='--', alpha=0.5, label=f'Initial: ${initial_value:.2f}')
262
- ax.legend(loc='best', framealpha=0.9)
263
-
264
  plt.tight_layout()
265
-
266
  return fig
267
 
268
- def execute_trade(stock, action, amount):
269
- """Execute a buy or sell trade"""
270
- global env, history
271
-
272
- if env is None:
273
- return "โŒ Please initialize the environment first", None, None, None, None
274
-
275
- try:
276
- amount = int(amount)
277
- if amount <= 0:
278
- return "โŒ Amount must be positive", None, None, None, None
279
-
280
- if action == "Buy":
281
- trade_action = {"buy": {stock: amount}, "sell": {}}
282
- else:
283
- trade_action = {"buy": {}, "sell": {stock: amount}}
284
-
285
- # Execute trade (modify positions without advancing day)
286
- if action == "Sell":
287
- idx = env.stocks.index(stock)
288
- qty = min(amount, env.positions[idx])
289
- if qty == 0:
290
- return f"โŒ No shares to sell", None, None, None, None
291
- revenue = env.prices[idx] * qty
292
- env.positions[idx] -= qty
293
- env.cash += revenue
294
- status = f"โœ… Sold {qty} shares of {stock} at ${env.prices[idx]:.2f}\n๐Ÿ’ฐ Revenue: ${revenue:.2f}"
295
- else: # Buy
296
- idx = env.stocks.index(stock)
297
- cost = env.prices[idx] * amount
298
- if cost > env.cash:
299
- return f"โŒ Insufficient cash!\n๐Ÿ’ต Need: ${cost:.2f}\n๐Ÿ’ฐ Have: ${env.cash:.2f}", None, None, None, None
300
- env.positions[idx] += amount
301
- env.cash -= cost
302
- status = f"โœ… Bought {amount} shares of {stock} at ${env.prices[idx]:.2f}\n๐Ÿ’ต Cost: ${cost:.2f}"
303
-
304
- obs = env._get_observation()
305
- status += f"\n\n๐Ÿ“Š Current Status:\n๐Ÿ’ฐ Cash: ${obs['cash']:.2f}\n๐Ÿ“ˆ Total Value: ${obs['total_value']:.2f}"
306
-
307
- return (
308
- status,
309
- create_portfolio_display(obs),
310
- create_news_display(obs),
311
- create_price_chart(),
312
- create_value_chart()
313
- )
314
-
315
- except Exception as e:
316
- return f"โŒ Error: {str(e)}", None, None, None, None
317
 
318
- def advance_day():
319
- """Advance to next day"""
320
- global env, history
321
-
322
- if env is None:
323
- return "โŒ Please initialize the environment first", None, None, None, None
324
-
325
- try:
326
- obs, reward, done, info = env.step({"buy": {}, "sell": {}})
327
-
328
- # Add to history
329
- history.append({
330
- 'day': obs['day'],
331
- 'total_value': obs['total_value'],
332
- **obs['prices']
333
- })
334
-
335
- if done:
336
- initial_value = history[0]['total_value']
337
- profit = obs['total_value'] - initial_value
338
- profit_pct = (profit / initial_value) * 100
339
- status = f"๐Ÿ Simulation Complete!\n\n"
340
- status += f"๐Ÿ“… Final Day: {obs['day']}\n"
341
- status += f"๐Ÿ’ฐ Final Cash: ${obs['cash']:.2f}\n"
342
- status += f"๐Ÿ“Š Final Value: ${obs['total_value']:.2f}\n\n"
343
- status += f"{'๐Ÿ“ˆ' if profit >= 0 else '๐Ÿ“‰'} P&L: ${profit:+.2f} ({profit_pct:+.2f}%)"
344
- else:
345
- status = f"โœ… Advanced to Day {obs['day']}\n\n"
346
- status += f"๐Ÿ’ฐ Cash: ${obs['cash']:.2f}\n"
347
- status += f"๐Ÿ“Š Total Value: ${obs['total_value']:.2f}"
348
-
349
- return (
350
- status,
351
- create_portfolio_display(obs),
352
- create_news_display(obs),
353
- create_price_chart(),
354
- create_value_chart()
355
- )
356
-
357
- except Exception as e:
358
- return f"โŒ Error: {str(e)}", None, None, None, None
359
 
360
- def reset_env():
361
- """Reset the environment"""
362
- global env, history
363
-
364
- if env is None:
365
- return initialize_env()
366
-
367
- obs = env.reset()
368
- history = [{
369
- 'day': obs['day'],
370
- 'total_value': obs['total_value'],
371
- **obs['prices']
372
- }]
373
-
374
- status = f"๐Ÿ”„ Environment Reset!\n\n๐Ÿ“… Day: {obs['day']}\n๐Ÿ’ฐ Cash: ${obs['cash']:.2f}\n๐Ÿ“Š Total Value: ${obs['total_value']:.2f}"
375
-
376
- return (
377
- status,
378
- create_portfolio_display(obs),
379
- create_news_display(obs),
380
- None,
381
- None
382
- )
383
-
384
- # Custom CSS for better styling
385
  custom_css = """
386
- .gradio-container {
387
- font-family: 'Arial', sans-serif;
388
- }
389
  .gr-button-primary {
390
  background: linear-gradient(90deg, #667eea 0%, #764ba2 100%) !important;
391
  border: none !important;
@@ -396,122 +288,52 @@ custom_css = """
396
  }
397
  """
398
 
399
- # Create Gradio Interface
400
  with gr.Blocks(theme=gr.themes.Soft(), css=custom_css, title="AI Trading Arena") as demo:
401
- gr.Markdown(
402
- """
403
- # ๐Ÿš€ AI Trading Arena
404
- ### Interactive Stock Trading Simulator
405
- Test your trading strategies in a deterministic market environment!
406
- """
407
- )
408
-
409
  with gr.Row():
410
  with gr.Column(scale=1):
411
  gr.Markdown("## ๐ŸŽฎ Control Panel")
412
-
413
  with gr.Accordion("๐Ÿ“ Configuration", open=False):
414
- gr.Markdown("Upload your custom JSON config or leave empty to use default")
 
 
 
 
 
415
  config_input = gr.Textbox(
416
  label="Custom Config JSON",
417
  placeholder='{"num_days": 30, "stocks": ["TECH", "ENERGY"], ...}',
418
- lines=3
419
  )
420
- init_btn = gr.Button("๐Ÿš€ Load Config", variant="primary", size="lg")
421
-
422
  with gr.Accordion("๐Ÿ’น Trading Operations", open=True):
423
- stock_dropdown = gr.Dropdown(
424
- choices=DEFAULT_CONFIG["stocks"],
425
- label="Select Stock",
426
- value=DEFAULT_CONFIG["stocks"][0]
427
- )
428
- action_radio = gr.Radio(
429
- choices=["Buy", "Sell"],
430
- label="Action",
431
- value="Buy"
432
- )
433
- amount_input = gr.Number(
434
- label="Amount (shares)",
435
- value=10,
436
- minimum=1,
437
- step=1
438
- )
439
  trade_btn = gr.Button("๐Ÿ“ˆ Execute Trade", variant="primary", size="lg")
440
-
441
- gr.Markdown("---")
442
-
443
  with gr.Row():
444
  advance_btn = gr.Button("โญ๏ธ Next Day", variant="primary", size="lg")
445
  reset_btn = gr.Button("๐Ÿ”„ Reset", variant="secondary", size="lg")
446
-
447
- status_output = gr.Textbox(
448
- label="๐Ÿ“Š Status & Messages",
449
- lines=8,
450
- interactive=False,
451
- show_copy_button=True
452
- )
453
-
454
  with gr.Column(scale=2):
455
  gr.Markdown("## ๐Ÿ“Š Market Dashboard")
456
-
457
- portfolio_table = gr.Dataframe(
458
- label="๐Ÿ’ผ Portfolio Holdings",
459
- interactive=False,
460
- wrap=True
461
- )
462
-
463
  news_display = gr.HTML(label="๐Ÿ“ฐ Market News")
464
-
465
- with gr.Tab("๐Ÿ“ˆ Price History"):
466
- price_chart = gr.Plot(label="Stock Prices Over Time")
467
-
468
- with gr.Tab("๐Ÿ’ฐ Portfolio Value"):
469
- value_chart = gr.Plot(label="Total Portfolio Value")
470
-
471
- gr.Markdown(
472
- """
473
- ---
474
- ### ๐Ÿ“– How to Use
475
- 1. **Initialize**: Click "Load Config" or start with default settings
476
- 2. **Trade**: Select stock, choose Buy/Sell, enter amount, and execute
477
- 3. **Advance**: Click "Next Day" to see how news affects prices
478
- 4. **Monitor**: Watch your portfolio value change over time
479
-
480
- ๐Ÿ’ก **Tip**: Check the news preview to make informed trading decisions!
481
- """
482
- )
483
-
484
- # Event handlers
485
- init_btn.click(
486
- fn=initialize_env,
487
- inputs=[config_input],
488
- outputs=[status_output, portfolio_table, news_display, price_chart, value_chart]
489
- )
490
-
491
- reset_btn.click(
492
- fn=reset_env,
493
- inputs=[],
494
- outputs=[status_output, portfolio_table, news_display, price_chart, value_chart]
495
- )
496
-
497
- trade_btn.click(
498
- fn=execute_trade,
499
- inputs=[stock_dropdown, action_radio, amount_input],
500
- outputs=[status_output, portfolio_table, news_display, price_chart, value_chart]
501
- )
502
-
503
- advance_btn.click(
504
- fn=advance_day,
505
- inputs=[],
506
- outputs=[status_output, portfolio_table, news_display, price_chart, value_chart]
507
- )
508
-
509
- # Initialize on load
510
- demo.load(
511
- fn=initialize_env,
512
- inputs=[],
513
- outputs=[status_output, portfolio_table, news_display, price_chart, value_chart]
514
- )
515
 
516
  if __name__ == "__main__":
517
- demo.launch()
 
1
+ import os
2
  import gradio as gr
3
  import numpy as np
4
  import json
 
7
  import matplotlib
8
  matplotlib.use('Agg')
9
 
10
+ # =========================================
11
+ # ======= Environment Core Definition ======
12
+ # =========================================
13
+
14
  class TradeArenaEnv_Deterministic:
15
  """
16
  Odyssey Arena - AI Trading Environment (Deterministic version)
 
101
  return round(float(total_value), 2)
102
 
103
 
104
+ # =========================================
105
+ # =========== Default Config ==============
106
+ # =========================================
107
+
108
  DEFAULT_CONFIG = {
109
  "num_days": 30,
110
  "stocks": ["TECH", "ENERGY", "FINANCE"],
 
119
  "initial_cash": 10000,
120
  "price_noise_scale": 0,
121
  "timeline": {
122
+ "day_1": {"variable_changes": [0.1, -0.2, 0.3],
123
+ "news_text": "Federal Reserve hints at rate increase; Oil prices drop on oversupply concerns"},
124
+ "day_2": {"variable_changes": [-0.1, 0.3, 0.2],
125
+ "news_text": "Tech sector shows strong earnings; Energy stocks rally on production cuts"},
126
+ "day_3": {"variable_changes": [0.2, 0.1, -0.1],
127
+ "news_text": "Market sentiment cautious amid geopolitical tensions"},
128
+ "day_4": {"variable_changes": [0.0, 0.2, 0.1],
129
+ "news_text": "Stable interest rates; Energy sector momentum continues"},
130
+ "day_5": {"variable_changes": [-0.2, -0.1, 0.0],
131
+ "news_text": "Rate cut speculation; Market consolidation"}
 
 
 
 
 
 
 
 
 
 
132
  }
133
  }
134
 
135
+ # =========================================
136
+ # =========== Global State ================
137
+ # =========================================
138
+
139
  env = None
140
  history = []
141
 
142
+
143
+ # =========================================
144
+ # =========== Utility Functions ===========
145
+ # =========================================
146
+
147
+ def list_config_files():
148
+ config_dir = "config"
149
+ if not os.path.exists(config_dir):
150
+ return []
151
+ return [f for f in os.listdir(config_dir) if f.endswith(".json")]
152
+
153
+ def load_config_from_file(filename):
154
+ try:
155
+ path = os.path.join("config", filename)
156
+ with open(path, "r") as f:
157
+ config = json.load(f)
158
+ return json.dumps(config, indent=2)
159
+ except Exception as e:
160
+ return f"โŒ Error reading {filename}: {str(e)}"
161
+
162
+
163
+ # =========================================
164
+ # ============ Core Logic =================
165
+ # =========================================
166
+
167
  def initialize_env(config_file=None):
168
  global env, history
169
 
 
171
  try:
172
  config = json.loads(config_file)
173
  except:
174
+ return "โŒ Invalid JSON file", None, None, None, None, gr.update()
175
  else:
176
  config = DEFAULT_CONFIG
177
 
178
  env = TradeArenaEnv_Deterministic(config)
179
  obs = env.reset()
180
 
 
181
  history = [{
182
  'day': obs['day'],
183
  'total_value': obs['total_value'],
 
191
  create_portfolio_display(obs),
192
  create_news_display(obs),
193
  create_price_chart(),
194
+ create_value_chart(),
195
+ gr.update(choices=env.stocks, value=env.stocks[0])
196
  )
197
 
198
  def create_portfolio_display(obs):
 
199
  data = []
200
  for stock in env.stocks:
201
  data.append({
 
204
  'Holdings': obs['positions'][stock],
205
  'Value': f"${obs['prices'][stock] * obs['positions'][stock]:.2f}"
206
  })
207
+ return pd.DataFrame(data)
 
 
208
 
209
  def create_news_display(obs):
 
210
  if obs['news_next_day_text']:
211
  news_html = f"""
212
  <div style='background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 
226
  return "<div style='padding: 20px; background: #f0f0f0; border-radius: 10px; text-align: center;'>๐Ÿ“ญ No more news available</div>"
227
 
228
  def create_price_chart():
 
229
  if len(history) <= 1:
230
  fig, ax = plt.subplots(figsize=(10, 6))
231
+ ax.text(0.5, 0.5, 'Trade to see price history', ha='center', va='center', fontsize=14, color='gray')
 
232
  ax.axis('off')
233
  return fig
234
 
235
  df = pd.DataFrame(history)
 
236
  fig, ax = plt.subplots(figsize=(10, 6))
237
  colors = ['#3b82f6', '#10b981', '#f59e0b', '#ef4444', '#8b5cf6']
238
 
 
248
  ax.set_facecolor('#f8f9fa')
249
  fig.patch.set_facecolor('white')
250
  plt.tight_layout()
 
251
  return fig
252
 
253
  def create_value_chart():
 
254
  if len(history) <= 1:
255
  fig, ax = plt.subplots(figsize=(10, 6))
256
+ ax.text(0.5, 0.5, 'Trade to see portfolio value', ha='center', va='center', fontsize=14, color='gray')
 
257
  ax.axis('off')
258
  return fig
259
 
260
  df = pd.DataFrame(history)
 
261
  fig, ax = plt.subplots(figsize=(10, 6))
262
+ ax.plot(df['day'], df['total_value'], marker='o', linewidth=3, color='#8b5cf6', label='Portfolio Value')
 
263
  ax.fill_between(df['day'], df['total_value'], alpha=0.2, color='#8b5cf6')
264
+ initial_value = history[0]['total_value']
265
+ ax.axhline(y=initial_value, color='red', linestyle='--', alpha=0.5, label=f'Initial: ${initial_value:.2f}')
266
+ ax.legend(loc='best', framealpha=0.9)
267
  ax.set_xlabel('Day', fontsize=12, fontweight='bold')
268
  ax.set_ylabel('Total Value ($)', fontsize=12, fontweight='bold')
269
  ax.set_title('Portfolio Value Over Time', fontsize=14, fontweight='bold', pad=20)
 
270
  ax.grid(True, alpha=0.3)
 
 
 
 
 
 
 
 
271
  plt.tight_layout()
 
272
  return fig
273
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
274
 
275
+ # =========================================
276
+ # ============ UI Definition ==============
277
+ # =========================================
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
279
  custom_css = """
280
+ .gradio-container { font-family: 'Arial', sans-serif; }
 
 
281
  .gr-button-primary {
282
  background: linear-gradient(90deg, #667eea 0%, #764ba2 100%) !important;
283
  border: none !important;
 
288
  }
289
  """
290
 
 
291
  with gr.Blocks(theme=gr.themes.Soft(), css=custom_css, title="AI Trading Arena") as demo:
292
+ gr.Markdown("# ๐Ÿš€ AI Trading Arena\n### Interactive Stock Trading Simulator")
293
+
 
 
 
 
 
 
294
  with gr.Row():
295
  with gr.Column(scale=1):
296
  gr.Markdown("## ๐ŸŽฎ Control Panel")
297
+
298
  with gr.Accordion("๐Ÿ“ Configuration", open=False):
299
+ config_file_dropdown = gr.Dropdown(
300
+ choices=list_config_files(),
301
+ label="Choose Config File from /config",
302
+ value=list_config_files()[0] if list_config_files() else None
303
+ )
304
+ load_file_btn = gr.Button("๐Ÿ“‚ Load from File", variant="secondary")
305
  config_input = gr.Textbox(
306
  label="Custom Config JSON",
307
  placeholder='{"num_days": 30, "stocks": ["TECH", "ENERGY"], ...}',
308
+ lines=4
309
  )
310
+ init_btn = gr.Button("๐Ÿš€ Initialize Environment", variant="primary", size="lg")
311
+
312
  with gr.Accordion("๐Ÿ’น Trading Operations", open=True):
313
+ stock_dropdown = gr.Dropdown(choices=DEFAULT_CONFIG["stocks"], label="Select Stock", value=DEFAULT_CONFIG["stocks"][0])
314
+ action_radio = gr.Radio(choices=["Buy", "Sell"], label="Action", value="Buy")
315
+ amount_input = gr.Number(label="Amount (shares)", value=10, minimum=1, step=1)
 
 
 
 
 
 
 
 
 
 
 
 
 
316
  trade_btn = gr.Button("๐Ÿ“ˆ Execute Trade", variant="primary", size="lg")
317
+
 
 
318
  with gr.Row():
319
  advance_btn = gr.Button("โญ๏ธ Next Day", variant="primary", size="lg")
320
  reset_btn = gr.Button("๐Ÿ”„ Reset", variant="secondary", size="lg")
321
+
322
+ status_output = gr.Textbox(label="๐Ÿ“Š Status & Messages", lines=8, interactive=False)
323
+
 
 
 
 
 
324
  with gr.Column(scale=2):
325
  gr.Markdown("## ๐Ÿ“Š Market Dashboard")
326
+ portfolio_table = gr.Dataframe(label="๐Ÿ’ผ Portfolio Holdings", interactive=False)
 
 
 
 
 
 
327
  news_display = gr.HTML(label="๐Ÿ“ฐ Market News")
328
+ with gr.Tab("๐Ÿ“ˆ Price History"): price_chart = gr.Plot(label="Stock Prices Over Time")
329
+ with gr.Tab("๐Ÿ’ฐ Portfolio Value"): value_chart = gr.Plot(label="Total Portfolio Value")
330
+
331
+ # === Button Bindings ===
332
+ load_file_btn.click(fn=load_config_from_file, inputs=[config_file_dropdown], outputs=[config_input])
333
+ init_btn.click(fn=initialize_env, inputs=[config_input],
334
+ outputs=[status_output, portfolio_table, news_display, price_chart, value_chart, stock_dropdown])
335
+
336
+ demo.load(fn=initialize_env, inputs=[], outputs=[status_output, portfolio_table, news_display, price_chart, value_chart, stock_dropdown])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
337
 
338
  if __name__ == "__main__":
339
+ demo.launch()