lucifer7210 commited on
Commit
53072ae
·
verified ·
1 Parent(s): b167eae

Update services/analyzer.py

Browse files
Files changed (1) hide show
  1. services/analyzer.py +174 -174
services/analyzer.py CHANGED
@@ -1,174 +1,174 @@
1
- # services/analyzer.py
2
- import numpy as np
3
- import pandas as pd
4
- from typing import Dict, Any, Optional
5
- from services.economic_data import EconomicDataFetcher
6
- from models.analysis import StockAnalysisResponse, InvestmentRecommendationResponse
7
- from models.market_data import StockDataResponse
8
-
9
- class IndianFinancialAnalyzer:
10
- """Analyze Indian market financial data"""
11
-
12
- def __init__(self):
13
- self.economic_data_fetcher = EconomicDataFetcher()
14
- self.rbi_repo_rate = self.economic_data_fetcher.get_rbi_repo_rate()
15
- self.indian_inflation_rate = self.economic_data_fetcher.get_indian_inflation_rate()
16
-
17
- def analyze_indian_stock(self, stock_data: StockDataResponse, company_name: str) -> StockAnalysisResponse:
18
- """Comprehensive analysis of Indian stock"""
19
- # Convert history dict back to DataFrame for analysis (basic reconstruction)
20
- # This assumes the dict keys are column names and values are lists of values
21
- try:
22
- hist_df = pd.DataFrame(stock_data.history)
23
- if 'Date' in hist_df.columns:
24
- hist_df['Date'] = pd.to_datetime(hist_df['Date'])
25
- hist_df.set_index('Date', inplace=True)
26
- except Exception as e:
27
- print(f"Error reconstructing DataFrame for analysis: {e}")
28
- hist_df = pd.DataFrame() # Return empty DataFrame on error
29
-
30
- if hist_df.empty:
31
- analysis_text = "No data available for analysis"
32
- else:
33
- # Current metrics
34
- current_price = hist_df['Close'].iloc[-1]
35
- year_high = hist_df['High'].max()
36
- year_low = hist_df['Low'].min()
37
-
38
- # Calculate returns
39
- returns_1m = ((current_price - hist_df['Close'].iloc[-21]) / hist_df['Close'].iloc[-21]) * 100 if len(hist_df) >= 21 else 0
40
- returns_3m = ((current_price - hist_df['Close'].iloc[-63]) / hist_df['Close'].iloc[-63]) * 100 if len(hist_df) >= 63 else 0
41
- returns_1y = ((current_price - hist_df['Close'].iloc[0]) / hist_df['Close'].iloc[0]) * 100
42
-
43
- # Volatility (standard deviation of daily returns)
44
- daily_returns = hist_df['Close'].pct_change().dropna()
45
- volatility = daily_returns.std() * np.sqrt(252) * 100 # Annualized volatility
46
-
47
- # Technical indicators
48
- sma_20 = hist_df['Close'].rolling(20).mean().iloc[-1] if len(hist_df) >= 20 else np.nan
49
- sma_50 = hist_df['Close'].rolling(50).mean().iloc[-1] if len(hist_df) >= 50 else np.nan
50
- ema_20 = hist_df['Close'].ewm(span=20, adjust=False).mean().iloc[-1] if len(hist_df) >= 20 else np.nan
51
- ema_50 = hist_df['Close'].ewm(span=50, adjust=False).mean().iloc[-1] if len(hist_df) >= 50 else np.nan
52
- rsi = hist_df['Close'].diff().ewm(span=14, adjust=False).mean() / hist_df['Close'].diff().ewm(span=14, adjust=False).std()
53
- macd = hist_df['Close'].ewm(span=12, adjust=False).mean() - hist_df['Close'].ewm(span=26, adjust=False).mean()
54
- macd_signal = macd.ewm(span=9, adjust=False).mean()
55
- macd_hist = macd - macd_signal
56
- macd_cross = 'Bullish' if macd.iloc[-1] > macd_signal.iloc[-1] else 'Bearish'
57
- stochastic_k = ((hist_df['Close'].iloc[-1] - hist_df['Low'].rolling(14).min().iloc[-1]) /
58
- (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
59
- stochastic_d = pd.Series(stochastic_k).rolling(3).mean().iloc[-1] if not pd.isna(stochastic_k) else np.nan
60
- stochastic_cross = 'Bullish' if stochastic_k > stochastic_d else 'Bearish'
61
- atr = hist_df['High'].combine(hist_df['Low'], max) - hist_df['Low'].combine(hist_df['Close'].shift(), min)
62
- atr = atr.rolling(14).mean().iloc[-1] if len(hist_df) >= 14 else np.nan
63
- obv = (np.sign(hist_df['Close'].diff()) * hist_df['Volume']).fillna(0).cumsum().iloc[-1]
64
- mfi = 100 - (100 / (1 + (hist_df['Close'].diff().fillna(0) * hist_df['Volume']).rolling(14).sum() /
65
- (-hist_df['Close'].diff().fillna(0) * hist_df['Volume']).rolling(14).sum())).iloc[-1] if len(hist_df) >= 14 else np.nan
66
- 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
67
-
68
-
69
- # Determine trend
70
- if pd.notna(current_price) and pd.notna(sma_20) and pd.notna(sma_50):
71
- if current_price > sma_20 > sma_50:
72
- trend = 'Bullish'
73
- elif current_price < sma_20 < sma_50:
74
- trend = 'Bearish'
75
- else:
76
- trend = 'Neutral'
77
- else:
78
- trend = 'Insufficient Data'
79
-
80
- # Indian market specific analysis
81
- analysis = f"""
82
- ## 🇮🇳 Indian Market Analysis for {company_name}
83
-
84
- ### Current Market Position
85
- - **Current Price**: ₹{current_price:.2f}
86
- - **52-Week High**: ₹{year_high:.2f}
87
- - **52-Week Low**: ₹{year_low:.2f}
88
- - **Distance from High**: {((current_price - year_high) / year_high * 100):.1f}%
89
-
90
- ### Returns Performance
91
- - **1 Month Return**: {returns_1m:.2f}%
92
- - **3 Month Return**: {returns_3m:.2f}%
93
- - **1 Year Return**: {returns_1y:.2f}%
94
- - **Annualized Volatility**: {volatility:.2f}%
95
-
96
- ### Technical Analysis
97
- - **20-Day SMA**: ₹{sma_20:.2f}
98
- - **50-Day SMA**: ₹{sma_50:.2f}
99
- - **20-Day EMA**: ₹{ema_20:.2f}
100
- - **50-Day EMA**: ₹{ema_50:.2f}
101
- - **RSI (14-day)**: {rsi.iloc[-1]:.2f}'
102
- - **MACD**: {macd.iloc[-1]:.2f} ({macd_cross})
103
- - **MACD HIST**: {macd_hist.iloc[-1]:.2f}
104
- - **Stochastic %K**: {stochastic_k:.2f} ({stochastic_cross})
105
- - **ATR (14-day)**: ₹{atr:.2f}'
106
- - **OBV**: {obv:.2f}
107
- - **MFI (14-day)**: {mfi:.2f}'
108
- - **ADX (14-day)**: {adx:.2f}'
109
- - **Trend**: {trend}
110
-
111
- ### Indian Market Context
112
- - **Relative to RBI Repo Rate ({self.rbi_repo_rate}%)**: {'Attractive' if returns_1y > self.rbi_repo_rate else 'Underperforming'}
113
- - **Inflation Adjusted Return**: {returns_1y - self.indian_inflation_rate:.2f}%
114
-
115
- ### Key Company Metrics
116
- """
117
-
118
- # Add company-specific info if available
119
- info = stock_data.info
120
- if info:
121
- market_cap = info.get('marketCap', 'N/A')
122
- pe_ratio = info.get('forwardPE', info.get('trailingPE', 'N/A'))
123
- dividend_yield = info.get('dividendYield', 0) * 100 if info.get('dividendYield') else 'N/A'
124
-
125
- market_cap_str = f"₹{market_cap/10000000:.0f} Cr." if isinstance(market_cap, (int, float)) else market_cap
126
- pe_str = f"{pe_ratio:.2f}" if isinstance(pe_ratio, (int, float)) else 'N/A'
127
- div_yield_str = f"{dividend_yield:.2f}%" if isinstance(dividend_yield, (int, float)) else 'N/A'
128
-
129
- analysis += f"""
130
- - **Market Cap**: {market_cap_str}
131
- - **P/E Ratio**: {pe_str}
132
- - **Dividend Yield**: {div_yield_str}
133
- """
134
- analysis_text = analysis
135
-
136
- return StockAnalysisResponse(basic_analysis=analysis_text)
137
-
138
- def generate_investment_recommendation(self) -> InvestmentRecommendationResponse:
139
- """Generate investment recommendation based on Indian market conditions"""
140
-
141
- recommendation = f"""
142
- ## 📊 Investment Recommendation (Indian Market Context)
143
-
144
- ### Risk Assessment
145
- - **Market Risk**: Indian equity markets are subject to high volatility
146
- - **Currency Risk**: INR fluctuations affect returns for foreign investors
147
- - **Regulatory Risk**: SEBI regulations and policy changes impact
148
-
149
- ### Recommendation Framework
150
- Based on current Indian market conditions:
151
-
152
- 1. **Conservative Investors**: Consider Large Cap stocks with dividend yield
153
- 2. **Moderate Risk**: Mid Cap stocks with strong fundamentals
154
- 3. **Aggressive**: Small Cap and sector-specific opportunities
155
-
156
- ### Indian Market Specific Factors
157
- - **Monsoon Impact**: Agricultural and rural demand dependency
158
- - **Festival Season**: Seasonal consumption patterns
159
- - **Government Policy**: Budget announcements and reforms
160
- - **FII/DII Flows**: Foreign and domestic institutional investor sentiment
161
-
162
- ### Economic Context
163
- - **RBI Repo Rate**: {self.rbi_repo_rate}%
164
- - **Inflation Rate**: {self.indian_inflation_rate}%
165
-
166
- ### Tax Implications (Indian Investors)
167
- - **Short Term Capital Gains**: 15% (< 1 year)
168
- - **Long Term Capital Gains**: 10% on gains > ₹1 Lakh (> 1 year)
169
- - **Dividend Tax**: TDS as per income tax slab
170
-
171
- **Disclaimer**: This is for educational purposes only. Please consult a SEBI registered investment advisor.
172
- """
173
-
174
- return InvestmentRecommendationResponse(recommendation=recommendation)
 
1
+ # services/analyzer.py
2
+ import numpy as np
3
+ import pandas as pd
4
+ from typing import Dict, Any, Optional
5
+ from services.economic_data import EconomicDataFetcher
6
+ from models.analysis import StockAnalysisResponse, InvestmentRecommendationResponse
7
+ from models.market_data import StockDataResponse
8
+
9
+ class IndianFinancialAnalyzer:
10
+ """Analyze Indian market financial data"""
11
+
12
+ def __init__(self):
13
+ self.economic_data_fetcher = EconomicDataFetcher()
14
+ self.rbi_repo_rate = self.economic_data_fetcher.get_rbi_repo_rate()
15
+ self.indian_inflation_rate = self.economic_data_fetcher.get_indian_inflation_rate()
16
+
17
+ def analyze_indian_stock(self, stock_data: StockDataResponse, company_name: str) -> StockAnalysisResponse:
18
+ """Comprehensive analysis of Indian stock"""
19
+ # Convert history dict back to DataFrame for analysis (basic reconstruction)
20
+ # This assumes the dict keys are column names and values are lists of values
21
+ try:
22
+ hist_df = pd.DataFrame(stock_data.history)
23
+ if 'Date' in hist_df.columns:
24
+ hist_df['Date'] = pd.to_datetime(hist_df['Date'])
25
+ hist_df.set_index('Date', inplace=True)
26
+ except Exception as e:
27
+ print(f"Error reconstructing DataFrame for analysis: {e}")
28
+ hist_df = pd.DataFrame() # Return empty DataFrame on error
29
+
30
+ if hist_df.empty:
31
+ analysis_text = "No data available for analysis"
32
+ else:
33
+ # Current metrics
34
+ current_price = hist_df['Close'].iloc[-1]
35
+ year_high = hist_df['High'].max()
36
+ year_low = hist_df['Low'].min()
37
+
38
+ # Calculate returns
39
+ returns_1m = ((current_price - hist_df['Close'].iloc[-21]) / hist_df['Close'].iloc[-21]) * 100 if len(hist_df) >= 21 else 0
40
+ returns_3m = ((current_price - hist_df['Close'].iloc[-63]) / hist_df['Close'].iloc[-63]) * 100 if len(hist_df) >= 63 else 0
41
+ returns_1y = ((current_price - hist_df['Close'].iloc[0]) / hist_df['Close'].iloc[0]) * 100
42
+
43
+ # Volatility (standard deviation of daily returns)
44
+ daily_returns = hist_df['Close'].pct_change().dropna()
45
+ volatility = daily_returns.std() * np.sqrt(252) * 100 # Annualized volatility
46
+
47
+ # Technical indicators
48
+ sma_20 = hist_df['Close'].rolling(20).mean().iloc[-1] if len(hist_df) >= 20 else np.nan
49
+ sma_50 = hist_df['Close'].rolling(50).mean().iloc[-1] if len(hist_df) >= 50 else np.nan
50
+ ema_20 = hist_df['Close'].ewm(span=20, adjust=False).mean().iloc[-1] if len(hist_df) >= 20 else np.nan
51
+ ema_50 = hist_df['Close'].ewm(span=50, adjust=False).mean().iloc[-1] if len(hist_df) >= 50 else np.nan
52
+ rsi = hist_df['Close'].diff().ewm(span=14, adjust=False).mean() / hist_df['Close'].diff().ewm(span=14, adjust=False).std()
53
+ macd = hist_df['Close'].ewm(span=12, adjust=False).mean() - hist_df['Close'].ewm(span=26, adjust=False).mean()
54
+ macd_signal = macd.ewm(span=9, adjust=False).mean()
55
+ macd_hist = macd - macd_signal
56
+ macd_cross = 'Bullish' if macd.iloc[-1] > macd_signal.iloc[-1] else 'Bearish'
57
+ stochastic_k = ((hist_df['Close'].iloc[-1] - hist_df['Low'].rolling(14).min().iloc[-1]) /
58
+ (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
59
+ stochastic_d = pd.Series(stochastic_k).rolling(3).mean().iloc[-1] if not pd.isna(stochastic_k) else np.nan
60
+ stochastic_cross = 'Bullish' if stochastic_k > stochastic_d else 'Bearish'
61
+ atr = hist_df['High'].combine(hist_df['Low'], max) - hist_df['Low'].combine(hist_df['Close'].shift(), min)
62
+ atr = atr.rolling(14).mean().iloc[-1] if len(hist_df) >= 14 else np.nan
63
+ obv = (np.sign(hist_df['Close'].diff()) * hist_df['Volume']).fillna(0).cumsum().iloc[-1]
64
+ mfi = 100 - (100 / (1 + (hist_df['Close'].diff().fillna(0) * hist_df['Volume']).rolling(14).sum() /
65
+ (-hist_df['Close'].diff().fillna(0) * hist_df['Volume']).rolling(14).sum())).iloc[-1] if len(hist_df) >= 14 else np.nan
66
+ 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
67
+
68
+
69
+ # Determine trend
70
+ if pd.notna(current_price) and pd.notna(sma_20) and pd.notna(sma_50):
71
+ if current_price > sma_20 > sma_50:
72
+ trend = 'Bullish'
73
+ elif current_price < sma_20 < sma_50:
74
+ trend = 'Bearish'
75
+ else:
76
+ trend = 'Neutral'
77
+ else:
78
+ trend = 'Insufficient Data'
79
+
80
+ # Indian market specific analysis
81
+ analysis = f"""
82
+ ## Indian Market Analysis for {company_name}
83
+
84
+ ### Current Market Position
85
+ - **Current Price**: ₹{current_price:.2f}
86
+ - **52-Week High**: ₹{year_high:.2f}
87
+ - **52-Week Low**: ₹{year_low:.2f}
88
+ - **Distance from High**: {((current_price - year_high) / year_high * 100):.1f}%
89
+
90
+ ### Returns Performance
91
+ - **1 Month Return**: {returns_1m:.2f}%
92
+ - **3 Month Return**: {returns_3m:.2f}%
93
+ - **1 Year Return**: {returns_1y:.2f}%
94
+ - **Annualized Volatility**: {volatility:.2f}%
95
+
96
+ ### Technical Analysis
97
+ - **20-Day SMA**: ₹{sma_20:.2f}
98
+ - **50-Day SMA**: ₹{sma_50:.2f}
99
+ - **20-Day EMA**: ₹{ema_20:.2f}
100
+ - **50-Day EMA**: ₹{ema_50:.2f}
101
+ - **RSI (14-day)**: {rsi.iloc[-1]:.2f}'
102
+ - **MACD**: {macd.iloc[-1]:.2f} ({macd_cross})
103
+ - **MACD HIST**: {macd_hist.iloc[-1]:.2f}
104
+ - **Stochastic %K**: {stochastic_k:.2f} ({stochastic_cross})
105
+ - **ATR (14-day)**: ₹{atr:.2f}'
106
+ - **OBV**: {obv:.2f}
107
+ - **MFI (14-day)**: {mfi:.2f}'
108
+ - **ADX (14-day)**: {adx:.2f}'
109
+ - **Trend**: {trend}
110
+
111
+ ### Indian Market Context
112
+ - **Relative to RBI Repo Rate ({self.rbi_repo_rate}%)**: {'Attractive' if returns_1y > self.rbi_repo_rate else 'Underperforming'}
113
+ - **Inflation Adjusted Return**: {returns_1y - self.indian_inflation_rate:.2f}%
114
+
115
+ ### Key Company Metrics
116
+ """
117
+
118
+ # Add company-specific info if available
119
+ info = stock_data.info
120
+ if info:
121
+ market_cap = info.get('marketCap', 'N/A')
122
+ pe_ratio = info.get('forwardPE', info.get('trailingPE', 'N/A'))
123
+ dividend_yield = info.get('dividendYield', 0) * 100 if info.get('dividendYield') else 'N/A'
124
+
125
+ market_cap_str = f"₹{market_cap/10000000:.0f} Cr." if isinstance(market_cap, (int, float)) else market_cap
126
+ pe_str = f"{pe_ratio:.2f}" if isinstance(pe_ratio, (int, float)) else 'N/A'
127
+ div_yield_str = f"{dividend_yield:.2f}%" if isinstance(dividend_yield, (int, float)) else 'N/A'
128
+
129
+ analysis += f"""
130
+ - **Market Cap**: {market_cap_str}
131
+ - **P/E Ratio**: {pe_str}
132
+ - **Dividend Yield**: {div_yield_str}
133
+ """
134
+ analysis_text = analysis
135
+
136
+ return StockAnalysisResponse(basic_analysis=analysis_text)
137
+
138
+ def generate_investment_recommendation(self) -> InvestmentRecommendationResponse:
139
+ """Generate investment recommendation based on Indian market conditions"""
140
+
141
+ recommendation = f"""
142
+ ## 📊 Investment Recommendation (Indian Market Context)
143
+
144
+ ### Risk Assessment
145
+ - **Market Risk**: Indian equity markets are subject to high volatility
146
+ - **Currency Risk**: INR fluctuations affect returns for foreign investors
147
+ - **Regulatory Risk**: SEBI regulations and policy changes impact
148
+
149
+ ### Recommendation Framework
150
+ Based on current Indian market conditions:
151
+
152
+ 1. **Conservative Investors**: Consider Large Cap stocks with dividend yield
153
+ 2. **Moderate Risk**: Mid Cap stocks with strong fundamentals
154
+ 3. **Aggressive**: Small Cap and sector-specific opportunities
155
+
156
+ ### Indian Market Specific Factors
157
+ - **Monsoon Impact**: Agricultural and rural demand dependency
158
+ - **Festival Season**: Seasonal consumption patterns
159
+ - **Government Policy**: Budget announcements and reforms
160
+ - **FII/DII Flows**: Foreign and domestic institutional investor sentiment
161
+
162
+ ### Economic Context
163
+ - **RBI Repo Rate**: {self.rbi_repo_rate}%
164
+ - **Inflation Rate**: {self.indian_inflation_rate}%
165
+
166
+ ### Tax Implications (Indian Investors)
167
+ - **Short Term Capital Gains**: 15% (< 1 year)
168
+ - **Long Term Capital Gains**: 10% on gains > ₹1 Lakh (> 1 year)
169
+ - **Dividend Tax**: TDS as per income tax slab
170
+
171
+ **Disclaimer**: This is for educational purposes only. Please consult a SEBI registered investment advisor.
172
+ """
173
+
174
+ return InvestmentRecommendationResponse(recommendation=recommendation)