Benjamin Consolvo commited on
Commit
9c621d2
·
1 Parent(s): 3e83ec5

background trading on sentiment updating in UI

Browse files
Files changed (1) hide show
  1. app.py +41 -65
app.py CHANGED
@@ -147,7 +147,7 @@ class NewsSentiment:
147
  sort_by='publishedAt', # <-- fixed argument name
148
  page=1)
149
  compound_score = 0
150
- for article in articles['articles'][:5]: # Check first 5 articles
151
  # print(f'article= {article}')
152
  score = self.sia.polarity_scores(article['title'])['compound']
153
  compound_score += score
@@ -182,6 +182,10 @@ class StockAnalyzer:
182
  bars_data = {}
183
  try:
184
  bars = alp_api.get_bars(list(symbols), timeframe).df
 
 
 
 
185
  for symbol in symbols:
186
  symbol_bars = bars[bars['symbol'] == symbol]
187
  if not symbol_bars.empty:
@@ -202,7 +206,6 @@ class StockAnalyzer:
202
  }
203
  }
204
  else:
205
- # Only log at debug level to avoid spamming warnings for missing bar data
206
  logger.debug(f"No bar data for symbol: {symbol}")
207
  bars_data[symbol] = {'bar_data': None}
208
  except Exception as e:
@@ -434,11 +437,19 @@ class TradingApp:
434
  st.button("Refresh Portfolio", on_click=refresh_portfolio)
435
 
436
  def auto_trade_based_on_sentiment(self, sentiment):
 
 
 
 
 
 
 
 
437
  actions = []
438
  symbol_to_name = self.analyzer.symbol_to_name
439
  for symbol, sentiment_value in sentiment.items():
440
  action = None
441
- is_market_open = self.alpaca.get_market_status() # Use self.alpaca instead of app.alpaca
442
  if sentiment_value == 'Positive':
443
  order = self.alpaca.buy(symbol, 1, reason="Sentiment: Positive")
444
  action = 'Buy'
@@ -464,43 +475,17 @@ class TradingApp:
464
  'sentiment': sentiment_value,
465
  'action': action
466
  })
467
- self.auto_trade_log = actions
468
  return actions
469
 
470
  def background_auto_trade(app):
471
- # This function runs in a background thread and updates session state
472
  while True:
 
 
473
  sentiment = app.sentiment.get_news_sentiment(app.analyzer.symbols)
474
- symbol_to_name = app.analyzer.symbol_to_name
475
- actions = []
476
- for symbol, sentiment_value in sentiment.items():
477
- action = None
478
- is_market_open = app.alpaca.get_market_status()
479
- if sentiment_value == 'Positive':
480
- order = app.alpaca.buy(symbol, 1, reason="Sentiment: Positive")
481
- action = 'Buy'
482
- elif sentiment_value == 'Negative':
483
- order = app.alpaca.sell(symbol, 1, reason="Sentiment: Negative")
484
- action = 'Sell'
485
- else:
486
- order = None
487
- action = 'Hold'
488
- logger.info(f"Held {symbol}")
489
-
490
- if order:
491
- if not is_market_open:
492
- _, _, next_open, _ = get_market_times(app.alpaca.alpaca)
493
- next_open_time = next_open.strftime('%Y-%m-%d %H:%M:%S') if next_open else "unknown"
494
- logger.warning(f"Market is currently closed. The {action.lower} order for 1 share of {symbol} has been submitted and will execute when the market opens at {next_open_time}.")
495
- else:
496
- logger.info(f"Order executed: {action} 1 share of {symbol}")
497
-
498
- actions.append({
499
- 'symbol': symbol,
500
- 'company_name': symbol_to_name.get(symbol, ''),
501
- 'sentiment': sentiment_value,
502
- 'action': action
503
- })
504
 
505
  # Create log entry
506
  log_entry = {
@@ -509,39 +494,30 @@ def background_auto_trade(app):
509
  "sentiment": sentiment
510
  }
511
 
512
- # Update session state - need to use a thread lock to safely update
513
- if hasattr(threading, "main_thread") and threading.current_thread() is threading.main_thread():
514
- # Direct update if in main thread
515
- update_auto_trade_log(log_entry)
516
- else:
517
- # Safer to just store the latest entry, which the main thread will pick up
518
- st.session_state["latest_auto_trade_entry"] = log_entry
519
-
520
- time.sleep(AUTO_TRADE_INTERVAL)
521
-
522
- def update_auto_trade_log(log_entry):
523
- """Update auto trade log in session state"""
524
- if AUTO_TRADE_LOG_KEY not in st.session_state:
525
- st.session_state[AUTO_TRADE_LOG_KEY] = []
526
-
527
- # Append the new entry
528
- st.session_state[AUTO_TRADE_LOG_KEY].append(log_entry)
529
-
530
- # Limit size to avoid memory issues (keep last 50 entries)
531
- if len(st.session_state[AUTO_TRADE_LOG_KEY]) > 50:
532
- st.session_state[AUTO_TRADE_LOG_KEY] = st.session_state[AUTO_TRADE_LOG_KEY][-50:]
533
 
534
  def get_auto_trade_log():
535
- """Get the auto trade log from session state"""
536
  if AUTO_TRADE_LOG_KEY not in st.session_state:
537
  st.session_state[AUTO_TRADE_LOG_KEY] = []
538
-
539
- # Check if we have a new entry from background thread
540
- if "latest_auto_trade_entry" in st.session_state:
541
- update_auto_trade_log(st.session_state["latest_auto_trade_entry"])
542
- # Clear the latest entry after adding it
543
- del st.session_state["latest_auto_trade_entry"]
544
-
545
  return st.session_state[AUTO_TRADE_LOG_KEY]
546
 
547
  def get_market_times(alpaca_api):
@@ -561,7 +537,7 @@ def main():
561
  st.markdown("This is a fun stock trading application that uses Alpaca API for trading and News API for sentiment analysis. Come and trade my money! Well, it's a paper account, so it's not real money. But still, have fun!")
562
 
563
  if not st.secrets['ALPACA_API_KEY'] or not st.secrets['NEWS_API_KEY']:
564
- st.error("Please configure your API keys in secrets.toml")
565
  return
566
 
567
  # Prevent Streamlit from rerunning the script on every widget interaction
 
147
  sort_by='publishedAt', # <-- fixed argument name
148
  page=1)
149
  compound_score = 0
150
+ for article in articles['articles'][:5] # Check first 5 articles
151
  # print(f'article= {article}')
152
  score = self.sia.polarity_scores(article['title'])['compound']
153
  compound_score += score
 
182
  bars_data = {}
183
  try:
184
  bars = alp_api.get_bars(list(symbols), timeframe).df
185
+ if 'symbol' not in bars.columns:
186
+ logger.warning("The 'symbol' column is missing in the bars DataFrame.")
187
+ return {symbol: {'bar_data': None} for symbol in symbols}
188
+
189
  for symbol in symbols:
190
  symbol_bars = bars[bars['symbol'] == symbol]
191
  if not symbol_bars.empty:
 
206
  }
207
  }
208
  else:
 
209
  logger.debug(f"No bar data for symbol: {symbol}")
210
  bars_data[symbol] = {'bar_data': None}
211
  except Exception as e:
 
437
  st.button("Refresh Portfolio", on_click=refresh_portfolio)
438
 
439
  def auto_trade_based_on_sentiment(self, sentiment):
440
+ """Execute trades based on sentiment analysis and return actions taken."""
441
+ actions = self._execute_sentiment_trades(sentiment)
442
+ self.auto_trade_log = actions
443
+ return actions
444
+
445
+ def _execute_sentiment_trades(self, sentiment):
446
+ """Helper method to execute trades based on sentiment.
447
+ Used by both auto_trade_based_on_sentiment and background_auto_trade."""
448
  actions = []
449
  symbol_to_name = self.analyzer.symbol_to_name
450
  for symbol, sentiment_value in sentiment.items():
451
  action = None
452
+ is_market_open = self.alpaca.get_market_status()
453
  if sentiment_value == 'Positive':
454
  order = self.alpaca.buy(symbol, 1, reason="Sentiment: Positive")
455
  action = 'Buy'
 
475
  'sentiment': sentiment_value,
476
  'action': action
477
  })
 
478
  return actions
479
 
480
  def background_auto_trade(app):
481
+ """This function runs in a background thread and updates session state with automatic trades."""
482
  while True:
483
+ start_time = time.time() # Record the start time of the iteration
484
+
485
  sentiment = app.sentiment.get_news_sentiment(app.analyzer.symbols)
486
+
487
+ # Use the shared method to execute trades
488
+ actions = app._execute_sentiment_trades(sentiment)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
489
 
490
  # Create log entry
491
  log_entry = {
 
494
  "sentiment": sentiment
495
  }
496
 
497
+ # Update session state - ensure the UI reflects the latest data
498
+ if AUTO_TRADE_LOG_KEY not in st.session_state:
499
+ st.session_state[AUTO_TRADE_LOG_KEY] = []
500
+
501
+ st.session_state[AUTO_TRADE_LOG_KEY].append(log_entry)
502
+
503
+ # Limit size to avoid memory issues (keep last 50 entries)
504
+ if len(st.session_state[AUTO_TRADE_LOG_KEY]) > 50:
505
+ st.session_state[AUTO_TRADE_LOG_KEY] = st.session_state[AUTO_TRADE_LOG_KEY][-50:]
506
+
507
+ # Log the update
508
+ logger.info(f"Auto-trade completed. Actions: {actions}")
509
+
510
+ # Calculate the time taken for this iteration
511
+ elapsed_time = time.time() - start_time
512
+ sleep_time = max(0, AUTO_TRADE_INTERVAL - elapsed_time) # Ensure non-negative sleep time
513
+
514
+ logger.info(f"Sleeping for {sleep_time:.2f} seconds before the next auto-trade.")
515
+ time.sleep(sleep_time)
 
 
516
 
517
  def get_auto_trade_log():
518
+ """Get the auto trade log from session state."""
519
  if AUTO_TRADE_LOG_KEY not in st.session_state:
520
  st.session_state[AUTO_TRADE_LOG_KEY] = []
 
 
 
 
 
 
 
521
  return st.session_state[AUTO_TRADE_LOG_KEY]
522
 
523
  def get_market_times(alpaca_api):
 
537
  st.markdown("This is a fun stock trading application that uses Alpaca API for trading and News API for sentiment analysis. Come and trade my money! Well, it's a paper account, so it's not real money. But still, have fun!")
538
 
539
  if not st.secrets['ALPACA_API_KEY'] or not st.secrets['NEWS_API_KEY']:
540
+ st.error("Please configure your ALPACA_API_KEY and NEWS_API_KEY")
541
  return
542
 
543
  # Prevent Streamlit from rerunning the script on every widget interaction