Spaces:
Sleeping
Sleeping
Create app.py
Browse files
app.py
ADDED
@@ -0,0 +1,114 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import gradio as gr
|
2 |
+
import yfinance as yf
|
3 |
+
import pandas as pd
|
4 |
+
import plotly.graph_objects as go
|
5 |
+
from transformers import pipeline
|
6 |
+
from datetime import datetime, timedelta
|
7 |
+
import requests
|
8 |
+
from bs4 import BeautifulSoup
|
9 |
+
import feedparser
|
10 |
+
|
11 |
+
# ------------------- Hugging Face Models -------------------
|
12 |
+
sentiment_analyzer = pipeline("text-classification", model="ProsusAI/finbert")
|
13 |
+
news_summarizer = pipeline("summarization", model="facebook/bart-large-cnn")
|
14 |
+
ner_model = pipeline("ner", model="dslim/bert-base-NER")
|
15 |
+
translator = pipeline("translation", model="Helsinki-NLP/opus-mt-ur-en")
|
16 |
+
|
17 |
+
# ------------------- Technical Analysis -------------------
|
18 |
+
def calculate_rsi(data, window=14):
|
19 |
+
delta = data['Close'].diff()
|
20 |
+
gain = (delta.where(delta > 0, 0)).rolling(window=window).mean()
|
21 |
+
loss = (-delta.where(delta < 0, 0)).rolling(window=window).mean()
|
22 |
+
rs = gain / loss
|
23 |
+
rsi = 100 - (100 / (1 + rs))
|
24 |
+
return rsi
|
25 |
+
|
26 |
+
# ------------------- Fetch PSX Data -------------------
|
27 |
+
def get_stock_data(ticker, period="1y"):
|
28 |
+
try:
|
29 |
+
stock = yf.Ticker(f"{ticker}.KAR")
|
30 |
+
data = stock.history(period=period)
|
31 |
+
if data.empty:
|
32 |
+
return None, "Error: Ticker not found or no data."
|
33 |
+
data['RSI'] = calculate_rsi(data)
|
34 |
+
return data, None
|
35 |
+
except Exception as e:
|
36 |
+
return None, f"Error: {str(e)}"
|
37 |
+
|
38 |
+
# ------------------- Sentiment & News Analysis -------------------
|
39 |
+
def analyze_sentiment(text):
|
40 |
+
return sentiment_analyzer(text)[0]['label']
|
41 |
+
|
42 |
+
def summarize_news(url):
|
43 |
+
response = requests.get(url)
|
44 |
+
soup = BeautifulSoup(response.text, 'html.parser')
|
45 |
+
paragraphs = soup.find_all('p')
|
46 |
+
article = ' '.join([p.get_text() for p in paragraphs[:5]])
|
47 |
+
summary = news_summarizer(article, max_length=100, min_length=30)[0]['summary_text']
|
48 |
+
return summary
|
49 |
+
|
50 |
+
def get_geo_news():
|
51 |
+
feed = feedparser.parse("https://www.dawn.com/feeds/pakistan")
|
52 |
+
news = []
|
53 |
+
for entry in feed.entries[:3]:
|
54 |
+
summary = summarize_news(entry.link)
|
55 |
+
sentiment = analyze_sentiment(entry.title)
|
56 |
+
news.append({"title": entry.title, "summary": summary, "sentiment": sentiment})
|
57 |
+
return news
|
58 |
+
|
59 |
+
# ------------------- Generate Signal -------------------
|
60 |
+
def generate_signal(rsi, sentiment_score, news_score):
|
61 |
+
if rsi < 30 and sentiment_score > 0.5 and news_score > 0.5:
|
62 |
+
return "STRONG BUY π’"
|
63 |
+
elif rsi > 70 or sentiment_score < -0.5:
|
64 |
+
return "STRONG SELL π΄"
|
65 |
+
else:
|
66 |
+
return "HOLD π‘"
|
67 |
+
|
68 |
+
# ------------------- Gradio Interface -------------------
|
69 |
+
def analyze_stock(ticker):
|
70 |
+
# Fetch Data
|
71 |
+
data, error = get_stock_data(ticker)
|
72 |
+
if error:
|
73 |
+
return error, None, None
|
74 |
+
|
75 |
+
# Technical Analysis
|
76 |
+
latest_rsi = data['RSI'].iloc[-1]
|
77 |
+
|
78 |
+
# Sentiment & News Analysis
|
79 |
+
news = get_geo_news()
|
80 |
+
sentiment_scores = [1 if n['sentiment'] == 'positive' else -1 for n in news]
|
81 |
+
sentiment_score = sum(sentiment_scores) / len(sentiment_scores) if sentiment_scores else 0
|
82 |
+
|
83 |
+
# Generate Signal
|
84 |
+
signal = generate_signal(latest_rsi, sentiment_score, 0.5)
|
85 |
+
|
86 |
+
# Plot
|
87 |
+
fig = go.Figure(data=[go.Candlestick(x=data.index,
|
88 |
+
open=data['Open'], high=data['High'],
|
89 |
+
low=data['Low'], close=data['Close'])])
|
90 |
+
fig.update_layout(title=f"{ticker} Price Chart")
|
91 |
+
|
92 |
+
# News Summary
|
93 |
+
news_html = "<h3>Latest Geopolitical News:</h3>"
|
94 |
+
for n in news:
|
95 |
+
news_html += f"<p><b>{n['title']}</b> ({n['sentiment']})<br>{n['summary']}</p>"
|
96 |
+
|
97 |
+
return signal, fig.to_html(), news_html
|
98 |
+
|
99 |
+
# ------------------- Launch Gradio App -------------------
|
100 |
+
iface = gr.Interface(
|
101 |
+
fn=analyze_stock,
|
102 |
+
inputs=gr.Textbox(label="Enter PSX Stock Ticker (e.g., HBL, LUCK)"),
|
103 |
+
outputs=[
|
104 |
+
gr.Textbox(label="Recommendation"),
|
105 |
+
gr.HTML(label="Price Chart"),
|
106 |
+
gr.HTML(label="News Analysis")
|
107 |
+
],
|
108 |
+
title="π’ PSX Stock Analysis Chatbot π΄",
|
109 |
+
description="Real-time Technical, Sentiment, and News Analysis for Pakistan Stock Exchange",
|
110 |
+
allow_flagging="never"
|
111 |
+
)
|
112 |
+
|
113 |
+
if __name__ == "__main__":
|
114 |
+
iface.launch(share=True) # Set share=True for public URL
|