lucifer7210's picture
Update services/analyzer.py
53072ae verified
# services/analyzer.py
import numpy as np
import pandas as pd
from typing import Dict, Any, Optional
from services.economic_data import EconomicDataFetcher
from models.analysis import StockAnalysisResponse, InvestmentRecommendationResponse
from models.market_data import StockDataResponse
class IndianFinancialAnalyzer:
"""Analyze Indian market financial data"""
def __init__(self):
self.economic_data_fetcher = EconomicDataFetcher()
self.rbi_repo_rate = self.economic_data_fetcher.get_rbi_repo_rate()
self.indian_inflation_rate = self.economic_data_fetcher.get_indian_inflation_rate()
def analyze_indian_stock(self, stock_data: StockDataResponse, company_name: str) -> StockAnalysisResponse:
"""Comprehensive analysis of Indian stock"""
# Convert history dict back to DataFrame for analysis (basic reconstruction)
# This assumes the dict keys are column names and values are lists of values
try:
hist_df = pd.DataFrame(stock_data.history)
if 'Date' in hist_df.columns:
hist_df['Date'] = pd.to_datetime(hist_df['Date'])
hist_df.set_index('Date', inplace=True)
except Exception as e:
print(f"Error reconstructing DataFrame for analysis: {e}")
hist_df = pd.DataFrame() # Return empty DataFrame on error
if hist_df.empty:
analysis_text = "No data available for analysis"
else:
# Current metrics
current_price = hist_df['Close'].iloc[-1]
year_high = hist_df['High'].max()
year_low = hist_df['Low'].min()
# Calculate returns
returns_1m = ((current_price - hist_df['Close'].iloc[-21]) / hist_df['Close'].iloc[-21]) * 100 if len(hist_df) >= 21 else 0
returns_3m = ((current_price - hist_df['Close'].iloc[-63]) / hist_df['Close'].iloc[-63]) * 100 if len(hist_df) >= 63 else 0
returns_1y = ((current_price - hist_df['Close'].iloc[0]) / hist_df['Close'].iloc[0]) * 100
# Volatility (standard deviation of daily returns)
daily_returns = hist_df['Close'].pct_change().dropna()
volatility = daily_returns.std() * np.sqrt(252) * 100 # Annualized volatility
# Technical indicators
sma_20 = hist_df['Close'].rolling(20).mean().iloc[-1] if len(hist_df) >= 20 else np.nan
sma_50 = hist_df['Close'].rolling(50).mean().iloc[-1] if len(hist_df) >= 50 else np.nan
ema_20 = hist_df['Close'].ewm(span=20, adjust=False).mean().iloc[-1] if len(hist_df) >= 20 else np.nan
ema_50 = hist_df['Close'].ewm(span=50, adjust=False).mean().iloc[-1] if len(hist_df) >= 50 else np.nan
rsi = hist_df['Close'].diff().ewm(span=14, adjust=False).mean() / hist_df['Close'].diff().ewm(span=14, adjust=False).std()
macd = hist_df['Close'].ewm(span=12, adjust=False).mean() - hist_df['Close'].ewm(span=26, adjust=False).mean()
macd_signal = macd.ewm(span=9, adjust=False).mean()
macd_hist = macd - macd_signal
macd_cross = 'Bullish' if macd.iloc[-1] > macd_signal.iloc[-1] else 'Bearish'
stochastic_k = ((hist_df['Close'].iloc[-1] - hist_df['Low'].rolling(14).min().iloc[-1]) /
(hist_df['High'].rolling(14).max().iloc[-1] - hist_df['Low'].rolling(14).min().iloc[-1]) * 100) if len(hist_df) >= 14 else np.nan
stochastic_d = pd.Series(stochastic_k).rolling(3).mean().iloc[-1] if not pd.isna(stochastic_k) else np.nan
stochastic_cross = 'Bullish' if stochastic_k > stochastic_d else 'Bearish'
atr = hist_df['High'].combine(hist_df['Low'], max) - hist_df['Low'].combine(hist_df['Close'].shift(), min)
atr = atr.rolling(14).mean().iloc[-1] if len(hist_df) >= 14 else np.nan
obv = (np.sign(hist_df['Close'].diff()) * hist_df['Volume']).fillna(0).cumsum().iloc[-1]
mfi = 100 - (100 / (1 + (hist_df['Close'].diff().fillna(0) * hist_df['Volume']).rolling(14).sum() /
(-hist_df['Close'].diff().fillna(0) * hist_df['Volume']).rolling(14).sum())).iloc[-1] if len(hist_df) >= 14 else np.nan
adx = pd.Series(np.abs(hist_df['High'].diff()) - np.abs(hist_df['Low'].diff())).rolling(14).mean().iloc[-1] if len(hist_df) >= 14 else np.nan
# Determine trend
if pd.notna(current_price) and pd.notna(sma_20) and pd.notna(sma_50):
if current_price > sma_20 > sma_50:
trend = 'Bullish'
elif current_price < sma_20 < sma_50:
trend = 'Bearish'
else:
trend = 'Neutral'
else:
trend = 'Insufficient Data'
# Indian market specific analysis
analysis = f"""
## Indian Market Analysis for {company_name}
### Current Market Position
- **Current Price**: ₹{current_price:.2f}
- **52-Week High**: ₹{year_high:.2f}
- **52-Week Low**: ₹{year_low:.2f}
- **Distance from High**: {((current_price - year_high) / year_high * 100):.1f}%
### Returns Performance
- **1 Month Return**: {returns_1m:.2f}%
- **3 Month Return**: {returns_3m:.2f}%
- **1 Year Return**: {returns_1y:.2f}%
- **Annualized Volatility**: {volatility:.2f}%
### Technical Analysis
- **20-Day SMA**: ₹{sma_20:.2f}
- **50-Day SMA**: ₹{sma_50:.2f}
- **20-Day EMA**: ₹{ema_20:.2f}
- **50-Day EMA**: ₹{ema_50:.2f}
- **RSI (14-day)**: {rsi.iloc[-1]:.2f}'
- **MACD**: {macd.iloc[-1]:.2f} ({macd_cross})
- **MACD HIST**: {macd_hist.iloc[-1]:.2f}
- **Stochastic %K**: {stochastic_k:.2f} ({stochastic_cross})
- **ATR (14-day)**: ₹{atr:.2f}'
- **OBV**: {obv:.2f}
- **MFI (14-day)**: {mfi:.2f}'
- **ADX (14-day)**: {adx:.2f}'
- **Trend**: {trend}
### Indian Market Context
- **Relative to RBI Repo Rate ({self.rbi_repo_rate}%)**: {'Attractive' if returns_1y > self.rbi_repo_rate else 'Underperforming'}
- **Inflation Adjusted Return**: {returns_1y - self.indian_inflation_rate:.2f}%
### Key Company Metrics
"""
# Add company-specific info if available
info = stock_data.info
if info:
market_cap = info.get('marketCap', 'N/A')
pe_ratio = info.get('forwardPE', info.get('trailingPE', 'N/A'))
dividend_yield = info.get('dividendYield', 0) * 100 if info.get('dividendYield') else 'N/A'
market_cap_str = f"₹{market_cap/10000000:.0f} Cr." if isinstance(market_cap, (int, float)) else market_cap
pe_str = f"{pe_ratio:.2f}" if isinstance(pe_ratio, (int, float)) else 'N/A'
div_yield_str = f"{dividend_yield:.2f}%" if isinstance(dividend_yield, (int, float)) else 'N/A'
analysis += f"""
- **Market Cap**: {market_cap_str}
- **P/E Ratio**: {pe_str}
- **Dividend Yield**: {div_yield_str}
"""
analysis_text = analysis
return StockAnalysisResponse(basic_analysis=analysis_text)
def generate_investment_recommendation(self) -> InvestmentRecommendationResponse:
"""Generate investment recommendation based on Indian market conditions"""
recommendation = f"""
## 📊 Investment Recommendation (Indian Market Context)
### Risk Assessment
- **Market Risk**: Indian equity markets are subject to high volatility
- **Currency Risk**: INR fluctuations affect returns for foreign investors
- **Regulatory Risk**: SEBI regulations and policy changes impact
### Recommendation Framework
Based on current Indian market conditions:
1. **Conservative Investors**: Consider Large Cap stocks with dividend yield
2. **Moderate Risk**: Mid Cap stocks with strong fundamentals
3. **Aggressive**: Small Cap and sector-specific opportunities
### Indian Market Specific Factors
- **Monsoon Impact**: Agricultural and rural demand dependency
- **Festival Season**: Seasonal consumption patterns
- **Government Policy**: Budget announcements and reforms
- **FII/DII Flows**: Foreign and domestic institutional investor sentiment
### Economic Context
- **RBI Repo Rate**: {self.rbi_repo_rate}%
- **Inflation Rate**: {self.indian_inflation_rate}%
### Tax Implications (Indian Investors)
- **Short Term Capital Gains**: 15% (< 1 year)
- **Long Term Capital Gains**: 10% on gains > ₹1 Lakh (> 1 year)
- **Dividend Tax**: TDS as per income tax slab
**Disclaimer**: This is for educational purposes only. Please consult a SEBI registered investment advisor.
"""
return InvestmentRecommendationResponse(recommendation=recommendation)