Spaces:
Sleeping
Sleeping
Benjamin Consolvo
commited on
Commit
·
a55fced
1
Parent(s):
2a7dcf3
news sentiment combine
Browse files
app.py
CHANGED
|
@@ -303,6 +303,51 @@ class TradingApp:
|
|
| 303 |
self.data = self.analyzer.get_historical_data(self.analyzer.symbols)
|
| 304 |
self.auto_trade_log = [] # Store automatic trade actions
|
| 305 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 306 |
def display_charts(self):
|
| 307 |
# Dynamically adjust columns based on number of stocks and available width
|
| 308 |
symbols = list(self.data.keys())
|
|
@@ -357,32 +402,12 @@ class TradingApp:
|
|
| 357 |
st.header("Manual Trade")
|
| 358 |
symbol = st.text_input('Enter stock symbol')
|
| 359 |
|
| 360 |
-
# --- Sentiment Check Feature (
|
| 361 |
sentiment_result = None
|
| 362 |
article_headlines = []
|
| 363 |
if st.button("Check Sentiment"):
|
| 364 |
if symbol:
|
| 365 |
-
|
| 366 |
-
# Use NewsSentiment to get sentiment
|
| 367 |
-
sentiment_dict = self.sentiment.get_news_sentiment([symbol])
|
| 368 |
-
sentiment_result = sentiment_dict.get(symbol)
|
| 369 |
-
# Try NewsAPI headlines first, fallback to yfinance if fails
|
| 370 |
-
try:
|
| 371 |
-
articles = self.sentiment.newsapi.get_everything(q=symbol, language='en', sort_by='publishedAt', page=1)
|
| 372 |
-
article_headlines = [a['title'] for a in articles.get('articles', [])[:5]]
|
| 373 |
-
if not article_headlines:
|
| 374 |
-
raise Exception("No NewsAPI headlines")
|
| 375 |
-
except Exception:
|
| 376 |
-
# Fallback to yfinance headlines
|
| 377 |
-
try:
|
| 378 |
-
ticker = yf.Ticker(symbol)
|
| 379 |
-
news_items = ticker.news if hasattr(ticker, "news") else []
|
| 380 |
-
article_headlines = [item.get('title') for item in news_items[:5] if item.get('title')]
|
| 381 |
-
except Exception:
|
| 382 |
-
article_headlines = []
|
| 383 |
-
except Exception as e:
|
| 384 |
-
sentiment_result = None
|
| 385 |
-
article_headlines = []
|
| 386 |
else:
|
| 387 |
sentiment_result = None
|
| 388 |
article_headlines = []
|
|
@@ -458,6 +483,26 @@ class TradingApp:
|
|
| 458 |
else:
|
| 459 |
st.error("Please enter a valid stock symbol and trade details.")
|
| 460 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 461 |
def auto_trade_based_on_sentiment(self, sentiment):
|
| 462 |
"""Execute trades based on sentiment analysis and return actions taken."""
|
| 463 |
actions = self._execute_sentiment_trades(sentiment)
|
|
@@ -470,6 +515,9 @@ class TradingApp:
|
|
| 470 |
actions = []
|
| 471 |
symbol_to_name = self.analyzer.symbol_to_name
|
| 472 |
for symbol, sentiment_value in sentiment.items():
|
|
|
|
|
|
|
|
|
|
| 473 |
action = None
|
| 474 |
is_market_open = self.alpaca.get_market_status()
|
| 475 |
if sentiment_value == 'Positive':
|
|
@@ -556,7 +604,7 @@ def get_market_times(alpaca_api):
|
|
| 556 |
|
| 557 |
def main():
|
| 558 |
st.title("Ben's Stock Trading Application")
|
| 559 |
-
st.markdown("This is a fun stock trading application that uses Alpaca API
|
| 560 |
|
| 561 |
if not st.secrets['ALPACA_API_KEY'] or not st.secrets['NEWS_API_KEY']:
|
| 562 |
st.error("Please configure your ALPACA_API_KEY and NEWS_API_KEY")
|
|
|
|
| 303 |
self.data = self.analyzer.get_historical_data(self.analyzer.symbols)
|
| 304 |
self.auto_trade_log = [] # Store automatic trade actions
|
| 305 |
|
| 306 |
+
def get_newsapi_sentiment_and_headlines(self, symbol):
|
| 307 |
+
"""Get sentiment and headlines using NewsAPI for a symbol."""
|
| 308 |
+
sentiment_result = None
|
| 309 |
+
article_headlines = []
|
| 310 |
+
try:
|
| 311 |
+
sentiment_dict = self.sentiment.get_news_sentiment([symbol])
|
| 312 |
+
sentiment_result = sentiment_dict.get(symbol)
|
| 313 |
+
articles = self.sentiment.newsapi.get_everything(q=symbol, language='en', sort_by='publishedAt', page=1)
|
| 314 |
+
article_headlines = [a['title'] for a in articles.get('articles', [])[:5]]
|
| 315 |
+
except Exception as e:
|
| 316 |
+
logger.error(f"NewsAPI sentiment/headlines error for {symbol}: {e}")
|
| 317 |
+
return sentiment_result, article_headlines
|
| 318 |
+
|
| 319 |
+
def get_yfinance_sentiment_and_headlines(self, symbol):
|
| 320 |
+
"""Get sentiment and headlines using yfinance for a symbol."""
|
| 321 |
+
sentiment_result = None
|
| 322 |
+
article_headlines = []
|
| 323 |
+
try:
|
| 324 |
+
ticker = yf.Ticker(symbol)
|
| 325 |
+
news_items = ticker.news if hasattr(ticker, "news") else []
|
| 326 |
+
article_headlines = [item.get('title') for item in news_items[:5] if item.get('title')]
|
| 327 |
+
# Use VADER on yfinance headlines if available
|
| 328 |
+
if article_headlines:
|
| 329 |
+
compound_score = 0
|
| 330 |
+
for title in article_headlines:
|
| 331 |
+
score = self.sentiment.sia.polarity_scores(title)['compound']
|
| 332 |
+
compound_score += score
|
| 333 |
+
avg_score = compound_score / len(article_headlines)
|
| 334 |
+
if avg_score > 0.1:
|
| 335 |
+
sentiment_result = 'Positive'
|
| 336 |
+
elif avg_score < -0.1:
|
| 337 |
+
sentiment_result = 'Negative'
|
| 338 |
+
else:
|
| 339 |
+
sentiment_result = 'Neutral'
|
| 340 |
+
except Exception as e:
|
| 341 |
+
logger.error(f"yfinance sentiment/headlines error for {symbol}: {e}")
|
| 342 |
+
return sentiment_result, article_headlines
|
| 343 |
+
|
| 344 |
+
def get_combined_sentiment_and_headlines(self, symbol):
|
| 345 |
+
"""Try NewsAPI first, fallback to yfinance if needed."""
|
| 346 |
+
sentiment_result, article_headlines = self.get_newsapi_sentiment_and_headlines(symbol)
|
| 347 |
+
if not article_headlines:
|
| 348 |
+
sentiment_result, article_headlines = self.get_yfinance_sentiment_and_headlines(symbol)
|
| 349 |
+
return sentiment_result, article_headlines
|
| 350 |
+
|
| 351 |
def display_charts(self):
|
| 352 |
# Dynamically adjust columns based on number of stocks and available width
|
| 353 |
symbols = list(self.data.keys())
|
|
|
|
| 402 |
st.header("Manual Trade")
|
| 403 |
symbol = st.text_input('Enter stock symbol')
|
| 404 |
|
| 405 |
+
# --- Sentiment Check Feature (refactored) ---
|
| 406 |
sentiment_result = None
|
| 407 |
article_headlines = []
|
| 408 |
if st.button("Check Sentiment"):
|
| 409 |
if symbol:
|
| 410 |
+
sentiment_result, article_headlines = self.get_combined_sentiment_and_headlines(symbol)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 411 |
else:
|
| 412 |
sentiment_result = None
|
| 413 |
article_headlines = []
|
|
|
|
| 483 |
else:
|
| 484 |
st.error("Please enter a valid stock symbol and trade details.")
|
| 485 |
|
| 486 |
+
# Display portfolio information in the sidebar (restored)
|
| 487 |
+
st.header("Alpaca Cash Portfolio")
|
| 488 |
+
|
| 489 |
+
def refresh_portfolio():
|
| 490 |
+
account = self.alpaca.alpaca.get_account()
|
| 491 |
+
portfolio_data = {
|
| 492 |
+
"Metric": ["Cash Balance", "Buying Power", "Equity", "Portfolio Value"],
|
| 493 |
+
"Value": [
|
| 494 |
+
f"${int(float(account.cash)):,.0f}",
|
| 495 |
+
f"${int(float(account.buying_power)):,.0f}",
|
| 496 |
+
f"${int(float(account.equity)):,.0f}",
|
| 497 |
+
f"${int(float(account.portfolio_value)):,.0f}"
|
| 498 |
+
]
|
| 499 |
+
}
|
| 500 |
+
df = pd.DataFrame(portfolio_data)
|
| 501 |
+
st.table(df.to_dict(orient="records")) # Convert DataFrame to a list of dictionaries
|
| 502 |
+
|
| 503 |
+
refresh_portfolio()
|
| 504 |
+
st.button("Refresh Portfolio", on_click=refresh_portfolio)
|
| 505 |
+
|
| 506 |
def auto_trade_based_on_sentiment(self, sentiment):
|
| 507 |
"""Execute trades based on sentiment analysis and return actions taken."""
|
| 508 |
actions = self._execute_sentiment_trades(sentiment)
|
|
|
|
| 515 |
actions = []
|
| 516 |
symbol_to_name = self.analyzer.symbol_to_name
|
| 517 |
for symbol, sentiment_value in sentiment.items():
|
| 518 |
+
# Use refactored sentiment logic for each symbol
|
| 519 |
+
if sentiment_value is None or sentiment_value not in ['Positive', 'Negative', 'Neutral']:
|
| 520 |
+
sentiment_value, _ = self.get_combined_sentiment_and_headlines(symbol)
|
| 521 |
action = None
|
| 522 |
is_market_open = self.alpaca.get_market_status()
|
| 523 |
if sentiment_value == 'Positive':
|
|
|
|
| 604 |
|
| 605 |
def main():
|
| 606 |
st.title("Ben's Stock Trading Application")
|
| 607 |
+
st.markdown("This is a fun stock trading application that uses a combination of key frameworks like Alpaca API, yfinance, and News API for stock information and trading. Come and trade my money! Well, it's a paper account, so it's not real money. But still, have fun!")
|
| 608 |
|
| 609 |
if not st.secrets['ALPACA_API_KEY'] or not st.secrets['NEWS_API_KEY']:
|
| 610 |
st.error("Please configure your ALPACA_API_KEY and NEWS_API_KEY")
|