File size: 6,572 Bytes
c3bf538 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 |
import yfinance as yf
import pandas as pd
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
from typing import Dict, Any
import time
def get_llm_analysis(ticker: str, company_name: str, intelligence_briefing: Dict[str, Any]) -> Dict[str, Any]:
"""
Uses Gemini 1.5 Flash to analyze historical data and news to generate
a forecast and a complete investment thesis.
"""
print(f"Starting LLM-powered analysis for {ticker} with Gemini 1.5 Flash...")
# clean and format ticker properly
original_ticker = ticker
if not ticker.endswith(('.NS', '.BO', '.L', '.TO')):
# For Indian stocks, try NSE first
ticker = f"{ticker}.NS"
print(f"Formatted ticker from '{original_ticker}' to '{ticker}' for Yahoo Finance")
historical_data_text = "Could not fetch historical price data due to repeated errors."
for attempt in range(3):
try:
print(f"Attempt {attempt + 1}/3 to download historical data for {ticker}...")
stock_data = yf.download(ticker, period="100d", interval="1d", progress=False)
if not stock_data.empty:
# Convert to a more readable format for the LLM
stock_data = stock_data.round(2) # Round to 2 decimal places
# Include only the last 20 days for LLM context efficiency
recent_data = stock_data.tail(20)
historical_data_text = f"Recent 20 days of data for {ticker}:\n{recent_data.to_string()}"
print("-> Successfully downloaded historical data.")
break
else:
raise ValueError("Downloaded data is empty.")
except Exception as e:
print(f"-> Attempt {attempt + 1} failed: {e}")
# If NSE fails, try BSE
if attempt == 0 and ticker.endswith('.NS'):
ticker = f"{original_ticker}.BO"
print(f"Retrying with BSE ticker: {ticker}")
elif attempt < 2:
print(" Waiting 2 seconds before retrying...")
time.sleep(2)
# If all attempts failed, check if we can get basic info
if "Could not fetch historical price data" in historical_data_text:
try:
print("Attempting to get basic stock info as fallback...")
stock = yf.Ticker(ticker)
info = stock.info
if info and info.get('regularMarketPrice'):
current_price = info.get('regularMarketPrice')
previous_close = info.get('previousClose', current_price)
historical_data_text = f"Limited data available for {ticker}:\nCurrent Price: ₹{current_price}\nPrevious Close: ₹{previous_close}"
print("-> Got basic stock info as fallback.")
else:
print("-> No data available from any source.")
except Exception as e:
print(f"-> Fallback also failed: {e}")
# 2. Summarize the news data into a simple text block
articles = intelligence_briefing.get('articles', [])
if articles:
news_summary = "\n".join([f"- {article['title']} (Source: {article['source']}, Sentiment: {article['sentiment']})" for article in articles[:10]]) # Limit to 10 articles
else:
news_summary = "No recent news or social media mentions found."
# 3. Initialize the LLM
try:
llm = ChatGoogleGenerativeAI(model="gemini-1.5-flash-latest", temperature=0.3)
except Exception as e:
print(f"Error initializing Gemini: {e}")
return {"error": f"Failed to initialize Gemini API: {str(e)}"}
# 4. Enhanced prompt that handles missing data gracefully
prompt = PromptTemplate(
input_variables=["ticker", "company_name", "historical_data", "news_summary"],
template="""
You are a Senior Financial Analyst for an Indian market-focused investment fund.
Your task is to provide a comprehensive analysis and a 30-day forecast for the stock: {ticker} ({company_name}).
**Important Instructions:**
- If historical data is limited or unavailable, focus your analysis on news sentiment and general market conditions
- Always provide a forecast range even with limited data, but adjust confidence accordingly
- Base your analysis ONLY on the provided data
**Data Provided:**
1. **Historical Price Data:**
```
{historical_data}
```
2. **Recent News & Social Media Headlines:**
{news_summary}
**Your Analysis Report (in Markdown format):**
## 30-Day Price Forecast
**Analysis:** Analyze available data (price trends if available, news sentiment, market conditions). If price data is limited, focus on sentiment analysis and sector trends.
**Predicted Range:** Provide a realistic price range for 30 days (e.g., ₹1500 - ₹1650). If no current price available, state "Unable to provide specific range due to data limitations."
**Justification:** Explain your forecast based on available information.
**Confidence:** High/Moderate/Low based on data quality and availability.
## Investment Thesis
**Bull Case:**
- Point 1 based on positive signals from available data
- Point 2 based on news sentiment or market conditions
- Point 3 if sufficient data available
**Bear Case:**
- Point 1 based on negative signals or risks
- Point 2 based on market concerns
- Point 3 if sufficient data available
## Actionable Strategy
**Signal:** Buy/Sell/Hold (choose one)
**Strategy:** Provide 1-2 sentences with specific actionable advice based on the analysis above.
**Risk Management:** Brief note on stop-loss or position sizing if relevant.
"""
)
# 5. Run the LangChain chain with error handling
chain = LLMChain(llm=llm, prompt=prompt)
try:
response = chain.run(
ticker=ticker,
company_name=company_name,
historical_data=historical_data_text,
news_summary=news_summary
)
print("Successfully generated analysis from Gemini.")
return {"llm_report": response}
except Exception as e:
error_msg = f"Failed to generate analysis from Gemini: {str(e)}"
print(f"Error calling Gemini API: {e}")
return {"error": error_msg} |