Spaces:
Running
Running
Update app.py
Browse files
app.py
CHANGED
|
@@ -344,7 +344,7 @@ def predict_price(symbol):
|
|
| 344 |
}
|
| 345 |
tech_summary = "\n".join([f"{k}: {v}" for k, v in indicators.items()])
|
| 346 |
|
| 347 |
-
# Prepare LSTM
|
| 348 |
features = np.column_stack([
|
| 349 |
df['Open'], df['High'], df['Low'], df['Close'],
|
| 350 |
df['RSI'], df['RSI'], df['RSI']
|
|
@@ -366,7 +366,7 @@ def predict_price(symbol):
|
|
| 366 |
last_close = float(df['Close'].iloc[-1])
|
| 367 |
confidence = round(abs(predicted_price - last_close) * 0.05, 2)
|
| 368 |
|
| 369 |
-
#
|
| 370 |
yt_sent = get_youtube_sentiment(symbol)
|
| 371 |
yt_adj = predicted_price * (0.02 * yt_sent)
|
| 372 |
adjusted_price = round(predicted_price + yt_adj, 2)
|
|
@@ -377,7 +377,7 @@ def predict_price(symbol):
|
|
| 377 |
elif predicted_price < last_close * 0.98:
|
| 378 |
reco = "Sell"
|
| 379 |
|
| 380 |
-
# Sentiment &
|
| 381 |
industry = get_industry(symbol)
|
| 382 |
stock_sent, stock_headlines = get_headlines_sentiment(symbol)
|
| 383 |
industry_sent, industry_headlines = get_headlines_sentiment(industry)
|
|
@@ -391,7 +391,7 @@ def predict_price(symbol):
|
|
| 391 |
π£ Sentiment score: {round(stock_sent, 2)} | Industry: {round(industry_sent, 2)}
|
| 392 |
π Financials: {', '.join([f"{k}: {v}" for k, v in financials.items()])}"""
|
| 393 |
|
| 394 |
-
# Chart 1 β
|
| 395 |
plt.figure(figsize=(8, 4))
|
| 396 |
plt.plot(df.index, df['Close'], label='Actual', marker='o')
|
| 397 |
plt.plot(df.index, predicted_prices, label='Predicted', marker='x')
|
|
@@ -403,7 +403,7 @@ def predict_price(symbol):
|
|
| 403 |
buf1.seek(0)
|
| 404 |
chart1 = PIL.Image.open(buf1)
|
| 405 |
|
| 406 |
-
# Chart 2 β
|
| 407 |
intraday = yf.download(symbol, period='1d', interval='1m')
|
| 408 |
plt.figure(figsize=(8, 4))
|
| 409 |
plt.plot(intraday.index, intraday['Close'], color='orange')
|
|
@@ -434,7 +434,7 @@ def predict_price(symbol):
|
|
| 434 |
buf_sent.seek(0)
|
| 435 |
sentiment_chart = PIL.Image.open(buf_sent)
|
| 436 |
|
| 437 |
-
#
|
| 438 |
sample_input = scaled.reshape(1, 60, 7)
|
| 439 |
base_pred = model.predict(sample_input)[0][0]
|
| 440 |
impacts = []
|
|
@@ -444,9 +444,17 @@ def predict_price(symbol):
|
|
| 444 |
new_pred = model.predict(perturbed)[0][0]
|
| 445 |
impacts.append(new_pred - base_pred)
|
| 446 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 447 |
plt.figure(figsize=(8, 4))
|
| 448 |
-
plt.barh(
|
| 449 |
-
plt.title("π§ Feature
|
|
|
|
|
|
|
|
|
|
| 450 |
plt.tight_layout()
|
| 451 |
buf_shap = io.BytesIO()
|
| 452 |
plt.savefig(buf_shap, format="png")
|
|
@@ -467,13 +475,14 @@ def predict_price(symbol):
|
|
| 467 |
tech_summary,
|
| 468 |
corp_info,
|
| 469 |
sector_chart,
|
| 470 |
-
shap_chart
|
| 471 |
)
|
| 472 |
|
| 473 |
except Exception as e:
|
| 474 |
blank = PIL.Image.new("RGB", (400, 200))
|
| 475 |
return ("Error", "-", str(e), blank, blank, blank, blank, "No news", "No news", {}, "", "", blank, blank)
|
| 476 |
|
|
|
|
| 477 |
def simulate_portfolio(portfolio_str):
|
| 478 |
try:
|
| 479 |
entries = [e.strip() for e in portfolio_str.split(',')]
|
|
|
|
| 344 |
}
|
| 345 |
tech_summary = "\n".join([f"{k}: {v}" for k, v in indicators.items()])
|
| 346 |
|
| 347 |
+
# Prepare features for LSTM
|
| 348 |
features = np.column_stack([
|
| 349 |
df['Open'], df['High'], df['Low'], df['Close'],
|
| 350 |
df['RSI'], df['RSI'], df['RSI']
|
|
|
|
| 366 |
last_close = float(df['Close'].iloc[-1])
|
| 367 |
confidence = round(abs(predicted_price - last_close) * 0.05, 2)
|
| 368 |
|
| 369 |
+
# YouTube sentiment adjustment
|
| 370 |
yt_sent = get_youtube_sentiment(symbol)
|
| 371 |
yt_adj = predicted_price * (0.02 * yt_sent)
|
| 372 |
adjusted_price = round(predicted_price + yt_adj, 2)
|
|
|
|
| 377 |
elif predicted_price < last_close * 0.98:
|
| 378 |
reco = "Sell"
|
| 379 |
|
| 380 |
+
# Sentiment & Fundamentals
|
| 381 |
industry = get_industry(symbol)
|
| 382 |
stock_sent, stock_headlines = get_headlines_sentiment(symbol)
|
| 383 |
industry_sent, industry_headlines = get_headlines_sentiment(industry)
|
|
|
|
| 391 |
π£ Sentiment score: {round(stock_sent, 2)} | Industry: {round(industry_sent, 2)}
|
| 392 |
π Financials: {', '.join([f"{k}: {v}" for k, v in financials.items()])}"""
|
| 393 |
|
| 394 |
+
# Chart 1 β Actual vs Predicted
|
| 395 |
plt.figure(figsize=(8, 4))
|
| 396 |
plt.plot(df.index, df['Close'], label='Actual', marker='o')
|
| 397 |
plt.plot(df.index, predicted_prices, label='Predicted', marker='x')
|
|
|
|
| 403 |
buf1.seek(0)
|
| 404 |
chart1 = PIL.Image.open(buf1)
|
| 405 |
|
| 406 |
+
# Chart 2 β Intraday
|
| 407 |
intraday = yf.download(symbol, period='1d', interval='1m')
|
| 408 |
plt.figure(figsize=(8, 4))
|
| 409 |
plt.plot(intraday.index, intraday['Close'], color='orange')
|
|
|
|
| 434 |
buf_sent.seek(0)
|
| 435 |
sentiment_chart = PIL.Image.open(buf_sent)
|
| 436 |
|
| 437 |
+
# π Feature Influence Chart
|
| 438 |
sample_input = scaled.reshape(1, 60, 7)
|
| 439 |
base_pred = model.predict(sample_input)[0][0]
|
| 440 |
impacts = []
|
|
|
|
| 444 |
new_pred = model.predict(perturbed)[0][0]
|
| 445 |
impacts.append(new_pred - base_pred)
|
| 446 |
|
| 447 |
+
# Simplify RSI to 1 feature
|
| 448 |
+
feature_names = ['Open', 'High', 'Low', 'Close', 'RSI']
|
| 449 |
+
impact_values = [impacts[0], impacts[1], impacts[2], impacts[3], np.mean(impacts[4:])]
|
| 450 |
+
colors = ['green' if v >= 0 else 'red' for v in impact_values]
|
| 451 |
+
|
| 452 |
plt.figure(figsize=(8, 4))
|
| 453 |
+
bars = plt.barh(feature_names, impact_values, color=colors)
|
| 454 |
+
plt.title("π§ Feature Influence on LSTM Prediction")
|
| 455 |
+
plt.xlabel("Impact on Price")
|
| 456 |
+
for bar, value in zip(bars, impact_values):
|
| 457 |
+
plt.text(bar.get_width(), bar.get_y() + bar.get_height()/2, f"{value:.4f}", va='center', ha='left', fontsize=9)
|
| 458 |
plt.tight_layout()
|
| 459 |
buf_shap = io.BytesIO()
|
| 460 |
plt.savefig(buf_shap, format="png")
|
|
|
|
| 475 |
tech_summary,
|
| 476 |
corp_info,
|
| 477 |
sector_chart,
|
| 478 |
+
shap_chart
|
| 479 |
)
|
| 480 |
|
| 481 |
except Exception as e:
|
| 482 |
blank = PIL.Image.new("RGB", (400, 200))
|
| 483 |
return ("Error", "-", str(e), blank, blank, blank, blank, "No news", "No news", {}, "", "", blank, blank)
|
| 484 |
|
| 485 |
+
|
| 486 |
def simulate_portfolio(portfolio_str):
|
| 487 |
try:
|
| 488 |
entries = [e.strip() for e in portfolio_str.split(',')]
|