Spaces:
Sleeping
Sleeping
Create core/stock_analysis.py
Browse files- core/stock_analysis.py +108 -0
core/stock_analysis.py
ADDED
@@ -0,0 +1,108 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# core/stock_analysis.py
|
2 |
+
|
3 |
+
import requests
|
4 |
+
import json
|
5 |
+
from datetime import datetime, timedelta
|
6 |
+
from tradingview_ta import TA_Handler, Interval
|
7 |
+
from langchain.document_loaders import WebBaseLoader
|
8 |
+
from langchain.docstore.document import Document
|
9 |
+
from bs4 import BeautifulSoup
|
10 |
+
from GoogleNews import GoogleNews
|
11 |
+
from langchain.prompts import PromptTemplate
|
12 |
+
from langchain.chains import StuffDocumentsChain, LLMChain
|
13 |
+
|
14 |
+
def clean_google_news_url(url: str):
|
15 |
+
for ext in [".html", ".cms"]:
|
16 |
+
if ext in url:
|
17 |
+
return url.split(ext)[0] + ext
|
18 |
+
return url.split("&")[0]
|
19 |
+
|
20 |
+
def get_google_news_documents(query: str, max_articles: int = 10, timeout: int = 10):
|
21 |
+
googlenews = GoogleNews(lang="en")
|
22 |
+
end_date = datetime.today()
|
23 |
+
start_date = end_date - timedelta(days=2)
|
24 |
+
googlenews.set_time_range(start_date.strftime("%m/%d/%Y"), end_date.strftime("%m/%d/%Y"))
|
25 |
+
googlenews.search(query)
|
26 |
+
articles = googlenews.result()
|
27 |
+
|
28 |
+
documents = []
|
29 |
+
for article in articles[:max_articles]:
|
30 |
+
url = clean_google_news_url(article.get("link"))
|
31 |
+
try:
|
32 |
+
response = requests.get(url, timeout=timeout, headers={"User-Agent": "Mozilla/5.0"})
|
33 |
+
response.raise_for_status()
|
34 |
+
soup = BeautifulSoup(response.text, "html.parser")
|
35 |
+
paragraphs = soup.find_all("p")
|
36 |
+
content = "\n".join([p.get_text(strip=True) for p in paragraphs if p.get_text(strip=True)])
|
37 |
+
if content and len(content) > 200:
|
38 |
+
doc = Document(
|
39 |
+
page_content=content,
|
40 |
+
metadata={
|
41 |
+
"source": "Google News",
|
42 |
+
"title": article.get("title", ""),
|
43 |
+
"published": article.get("date", ""),
|
44 |
+
"link": url,
|
45 |
+
}
|
46 |
+
)
|
47 |
+
documents.append(doc)
|
48 |
+
except Exception:
|
49 |
+
continue
|
50 |
+
return documents
|
51 |
+
|
52 |
+
def analyze_stock(ticker, llm):
|
53 |
+
try:
|
54 |
+
handler = TA_Handler(symbol=ticker, screener="india", exchange="NSE", interval=Interval.INTERVAL_1_DAY)
|
55 |
+
summary = handler.get_analysis().summary
|
56 |
+
except Exception:
|
57 |
+
return {"error": "Invalid ticker or failed to fetch trading data"}
|
58 |
+
|
59 |
+
urls = [
|
60 |
+
f"https://www.google.com/finance/quote/{ticker}:NSE?hl=en",
|
61 |
+
f"https://in.tradingview.com/symbols/NSE-{ticker}/",
|
62 |
+
f"https://in.tradingview.com/symbols/NSE-{ticker}/news/",
|
63 |
+
f"https://in.tradingview.com/symbols/NSE-{ticker}/minds/"
|
64 |
+
]
|
65 |
+
|
66 |
+
loader = WebBaseLoader(urls)
|
67 |
+
web_docs = loader.load()
|
68 |
+
|
69 |
+
news_docs = get_google_news_documents(f"Trending News for {ticker}", max_articles=10)
|
70 |
+
docs = web_docs + news_docs
|
71 |
+
|
72 |
+
prompt_template = """You are an expert Stock Market Trader...
|
73 |
+
|
74 |
+
Context:
|
75 |
+
{input_documents}
|
76 |
+
|
77 |
+
Task:
|
78 |
+
Summarize the stock...
|
79 |
+
RETURN JSON with this schema:
|
80 |
+
|
81 |
+
```json
|
82 |
+
{{
|
83 |
+
"stock_summary": {{
|
84 |
+
"company_name": "",
|
85 |
+
"ticker": "",
|
86 |
+
"exchange": "",
|
87 |
+
...
|
88 |
+
}},
|
89 |
+
...
|
90 |
+
}}
|
91 |
+
```
|
92 |
+
"""
|
93 |
+
|
94 |
+
prompt = PromptTemplate.from_template(prompt_template)
|
95 |
+
chain = StuffDocumentsChain(llm_chain=LLMChain(llm=llm, prompt=prompt), document_variable_name="input_documents")
|
96 |
+
response = chain.invoke({"input_documents": docs})
|
97 |
+
raw = response["output_text"].strip()
|
98 |
+
|
99 |
+
# Clean code block markdown if present
|
100 |
+
if raw.startswith("```json"):
|
101 |
+
raw = raw[len("```json"):]
|
102 |
+
if raw.endswith("```"):
|
103 |
+
raw = raw[:-3]
|
104 |
+
|
105 |
+
try:
|
106 |
+
return json.loads(raw.strip())
|
107 |
+
except json.JSONDecodeError:
|
108 |
+
return {"error": "Failed to parse model output", "raw": raw}
|