Sujatha commited on
Commit
e22160e
·
verified ·
1 Parent(s): 92d1e00

Create data_sources.py

Browse files
Files changed (1) hide show
  1. data_sources.py +406 -0
data_sources.py ADDED
@@ -0,0 +1,406 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Real-time market data integration for Indian and global stocks
3
+ Optimized for Hugging Face free tier
4
+ """
5
+
6
+ import requests
7
+ import yfinance as yf
8
+ import pandas as pd
9
+ import numpy as np
10
+ from typing import Dict, List, Optional, Tuple
11
+ import time
12
+ from datetime import datetime, timedelta
13
+
14
+ class IndianStockAPI:
15
+ """
16
+ Free Indian Stock Market API client for NSE/BSE data
17
+ No API key required [citation:1]
18
+ """
19
+
20
+ BASE_URL = "https://military-jobye-haiqstudios-14f59639.koyeb.app"
21
+
22
+ def __init__(self):
23
+ self.session = requests.Session()
24
+ self.cache = {}
25
+ self.cache_timestamp = {}
26
+ self.cache_duration = 60 # Cache for 60 seconds to avoid rate limits
27
+
28
+ def search_stocks(self, query: str) -> List[Dict]:
29
+ """
30
+ Search for stocks by company name or symbol
31
+ Example: /search?q=reliance [citation:1]
32
+ """
33
+ try:
34
+ url = f"{self.BASE_URL}/search"
35
+ params = {'q': query}
36
+ response = self.session.get(url, params=params, timeout=10)
37
+
38
+ if response.status_code == 200:
39
+ data = response.json()
40
+ if data.get('status') == 'success':
41
+ return data.get('results', [])
42
+ return []
43
+ except Exception as e:
44
+ print(f"Search error: {e}")
45
+ return []
46
+
47
+ def get_stock_price(self, symbol: str, exchange: str = "NSE") -> Optional[Dict]:
48
+ """
49
+ Get real-time stock price and details
50
+ Examples:
51
+ - ITC → NSE (default) [citation:1]
52
+ - ITC.NS → NSE explicit
53
+ - ITC.BO → BSE
54
+ """
55
+ # Check cache first
56
+ cache_key = f"{symbol}_{exchange}"
57
+ if cache_key in self.cache:
58
+ if time.time() - self.cache_timestamp[cache_key] < self.cache_duration:
59
+ return self.cache[cache_key]
60
+
61
+ try:
62
+ # Format symbol with exchange suffix
63
+ if exchange.upper() == "NSE":
64
+ api_symbol = f"{symbol}.NS" if not symbol.endswith('.NS') else symbol
65
+ else: # BSE
66
+ api_symbol = f"{symbol}.BO" if not symbol.endswith('.BO') else symbol
67
+
68
+ url = f"{self.BASE_URL}/stock"
69
+ params = {
70
+ 'symbol': api_symbol,
71
+ 'res': 'val' # Get values with units [citation:1]
72
+ }
73
+
74
+ response = self.session.get(url, params=params, timeout=10)
75
+
76
+ if response.status_code == 200:
77
+ data = response.json()
78
+ if data.get('status') == 'success':
79
+ # Cache the result
80
+ self.cache[cache_key] = data
81
+ self.cache_timestamp[cache_key] = time.time()
82
+ return data
83
+ return None
84
+ except Exception as e:
85
+ print(f"Stock price error: {e}")
86
+ return None
87
+
88
+ def get_multiple_stocks(self, symbols: List[str], exchange: str = "NSE") -> Dict:
89
+ """
90
+ Get prices for multiple stocks efficiently
91
+ """
92
+ results = {}
93
+ for symbol in symbols:
94
+ data = self.get_stock_price(symbol, exchange)
95
+ if data:
96
+ results[symbol] = data
97
+ time.sleep(0.5) # Rate limiting protection
98
+ return results
99
+
100
+ def format_for_index(self, stock_data: Dict) -> Dict:
101
+ """
102
+ Convert stock API response to index calculator format
103
+ """
104
+ if not stock_data or 'data' not in stock_data:
105
+ return {}
106
+
107
+ d = stock_data['data']
108
+
109
+ # Handle both numeric and unit formats [citation:1]
110
+ def extract_value(field):
111
+ if isinstance(field, dict):
112
+ return field.get('value', 0)
113
+ return field
114
+
115
+ return {
116
+ 'company_name': d.get('company_name', ''),
117
+ 'last_price': extract_value(d.get('last_price', 0)),
118
+ 'change': extract_value(d.get('change', 0)),
119
+ 'percent_change': extract_value(d.get('percent_change', 0)),
120
+ 'previous_close': extract_value(d.get('previous_close', 0)),
121
+ 'open': extract_value(d.get('open', 0)),
122
+ 'day_high': extract_value(d.get('day_high', 0)),
123
+ 'day_low': extract_value(d.get('day_low', 0)),
124
+ 'year_high': extract_value(d.get('year_high', 0)),
125
+ 'year_low': extract_value(d.get('year_low', 0)),
126
+ 'volume': extract_value(d.get('volume', 0)),
127
+ 'market_cap': extract_value(d.get('market_cap', 0)),
128
+ 'pe_ratio': extract_value(d.get('pe_ratio', 0)),
129
+ 'dividend_yield': extract_value(d.get('dividend_yield', 0)),
130
+ 'sector': d.get('sector', ''),
131
+ 'currency': d.get('currency', 'INR')
132
+ }
133
+
134
+
135
+ class GlobalStockAPI:
136
+ """
137
+ Yahoo Finance client for global stocks and indices
138
+ Uses yfinance library [citation:2]
139
+ """
140
+
141
+ def __init__(self):
142
+ self.cache = {}
143
+ self.cache_timestamp = {}
144
+ self.cache_duration = 60 # Cache for 60 seconds
145
+
146
+ def get_stock_info(self, symbol: str) -> Optional[Dict]:
147
+ """
148
+ Get real-time stock data from Yahoo Finance
149
+ Examples: 'RELIANCE.NS', 'AAPL', '^NSEI' (Nifty 50)
150
+ """
151
+ # Check cache
152
+ if symbol in self.cache:
153
+ if time.time() - self.cache_timestamp[symbol] < self.cache_duration:
154
+ return self.cache[symbol]
155
+
156
+ try:
157
+ ticker = yf.Ticker(symbol)
158
+ info = ticker.info
159
+
160
+ # Get current price (fastest method)
161
+ hist = ticker.history(period="1d", interval="1m")
162
+
163
+ if not hist.empty:
164
+ current_price = hist['Close'].iloc[-1]
165
+ else:
166
+ current_price = info.get('regularMarketPrice', info.get('currentPrice', 0))
167
+
168
+ result = {
169
+ 'symbol': symbol,
170
+ 'company_name': info.get('longName', info.get('shortName', symbol)),
171
+ 'last_price': current_price,
172
+ 'previous_close': info.get('regularMarketPreviousClose', 0),
173
+ 'open': info.get('regularMarketOpen', 0),
174
+ 'day_high': info.get('dayHigh', info.get('regularMarketDayHigh', 0)),
175
+ 'day_low': info.get('dayLow', info.get('regularMarketDayLow', 0)),
176
+ 'volume': info.get('volume', info.get('regularMarketVolume', 0)),
177
+ 'market_cap': info.get('marketCap', 0),
178
+ 'pe_ratio': info.get('trailingPE', 0),
179
+ 'dividend_yield': info.get('dividendYield', 0) * 100 if info.get('dividendYield') else 0,
180
+ 'sector': info.get('sector', ''),
181
+ 'industry': info.get('industry', ''),
182
+ 'currency': info.get('currency', 'USD'),
183
+ 'timestamp': datetime.now().isoformat()
184
+ }
185
+
186
+ # Cache the result
187
+ self.cache[symbol] = result
188
+ self.cache_timestamp[symbol] = time.time()
189
+
190
+ return result
191
+
192
+ except Exception as e:
193
+ print(f"YFinance error for {symbol}: {e}")
194
+ return None
195
+
196
+ def get_nifty50_components(self) -> List[str]:
197
+ """
198
+ Get Nifty 50 constituent symbols
199
+ """
200
+ # Common Nifty 50 stocks with .NS suffix for Yahoo Finance
201
+ return [
202
+ 'RELIANCE.NS', 'TCS.NS', 'HDFCBANK.NS', 'INFY.NS', 'ICICIBANK.NS',
203
+ 'HINDUNILVR.NS', 'ITC.NS', 'SBIN.NS', 'BHARTIARTL.NS', 'KOTAKBANK.NS',
204
+ 'BAJFINANCE.NS', 'LT.NS', 'WIPRO.NS', 'AXISBANK.NS', 'TITAN.NS',
205
+ 'ASIANPAINT.NS', 'MARUTI.NS', 'SUNPHARMA.NS', 'HCLTECH.NS', 'ULTRACEMCO.NS'
206
+ ]
207
+
208
+ def get_multiple_stocks(self, symbols: List[str]) -> Dict:
209
+ """
210
+ Get data for multiple symbols efficiently
211
+ """
212
+ results = {}
213
+ for symbol in symbols:
214
+ data = self.get_stock_info(symbol)
215
+ if data:
216
+ results[symbol] = data
217
+ return results
218
+
219
+
220
+ class IndexBuilder:
221
+ """
222
+ Build custom indices from real-time stock data
223
+ """
224
+
225
+ def __init__(self):
226
+ self.indian_api = IndianStockAPI()
227
+ self.global_api = GlobalStockAPI()
228
+
229
+ def create_price_weighted_index(self, symbols: List[str], exchange: str = "NSE") -> Dict:
230
+ """
231
+ Create a price-weighted index (like Dow Jones)
232
+ Index = (Sum of stock prices) / Divisor
233
+ """
234
+ stocks_data = []
235
+ total_price = 0
236
+
237
+ for symbol in symbols:
238
+ if exchange in ["NSE", "BSE"]:
239
+ data = self.indian_api.get_stock_price(symbol, exchange)
240
+ if data and 'data' in data:
241
+ price_data = self.indian_api.format_for_index(data)
242
+ stocks_data.append(price_data)
243
+ total_price += price_data['last_price']
244
+ else:
245
+ data = self.global_api.get_stock_info(symbol)
246
+ if data:
247
+ stocks_data.append(data)
248
+ total_price += data['last_price']
249
+
250
+ # Simple divisor (number of stocks for demo)
251
+ divisor = len(stocks_data)
252
+ index_value = total_price / divisor if divisor > 0 else 0
253
+
254
+ return {
255
+ 'index_name': f"Price-Weighted Index ({exchange})",
256
+ 'index_value': round(index_value, 2),
257
+ 'components': len(stocks_data),
258
+ 'total_price': round(total_price, 2),
259
+ 'divisor': divisor,
260
+ 'stocks': stocks_data,
261
+ 'timestamp': datetime.now().isoformat()
262
+ }
263
+
264
+ def create_market_cap_weighted_index(self, symbols: List[str], exchange: str = "NSE") -> Dict:
265
+ """
266
+ Create a market capitalization-weighted index (like S&P 500, Nifty 50)
267
+ Index = (Sum of market caps) / Base market cap * Base index value
268
+ """
269
+ stocks_data = []
270
+ total_market_cap = 0
271
+
272
+ for symbol in symbols:
273
+ if exchange in ["NSE", "BSE"]:
274
+ data = self.indian_api.get_stock_price(symbol, exchange)
275
+ if data and 'data' in data:
276
+ price_data = self.indian_api.format_for_index(data)
277
+ stocks_data.append(price_data)
278
+ total_market_cap += price_data['market_cap']
279
+ else:
280
+ data = self.global_api.get_stock_info(symbol)
281
+ if data:
282
+ stocks_data.append(data)
283
+ total_market_cap += data['market_cap']
284
+
285
+ # Use first stock's market cap as base for demo
286
+ base_market_cap = stocks_data[0]['market_cap'] if stocks_data else 1
287
+ index_value = (total_market_cap / base_market_cap) * 100 if base_market_cap > 0 else 0
288
+
289
+ return {
290
+ 'index_name': f"Market Cap-Weighted Index ({exchange})",
291
+ 'index_value': round(index_value, 2),
292
+ 'components': len(stocks_data),
293
+ 'total_market_cap': total_market_cap,
294
+ 'base_market_cap': base_market_cap,
295
+ 'stocks': stocks_data,
296
+ 'timestamp': datetime.now().isoformat()
297
+ }
298
+
299
+ def calculate_sector_index(self, sector: str, exchange: str = "NSE") -> Dict:
300
+ """
301
+ Create an index for a specific sector
302
+ """
303
+ # This would normally filter stocks by sector
304
+ # For demo, we'll use a few known stocks
305
+ sector_stocks = {
306
+ 'technology': ['TCS.NS', 'INFY.NS', 'WIPRO.NS', 'HCLTECH.NS'],
307
+ 'banking': ['HDFCBANK.NS', 'ICICIBANK.NS', 'SBIN.NS', 'KOTAKBANK.NS'],
308
+ 'energy': ['RELIANCE.NS', 'ONGC.NS', 'POWERGRID.NS']
309
+ }
310
+
311
+ symbols = sector_stocks.get(sector.lower(), [])
312
+ if not symbols:
313
+ return {'error': f'Sector {sector} not found'}
314
+
315
+ return self.create_market_cap_weighted_index(symbols, exchange)
316
+
317
+
318
+ class RealTimeIndexCalculator:
319
+ """
320
+ Main class that combines real-time data with index calculations
321
+ """
322
+
323
+ def __init__(self):
324
+ self.index_builder = IndexBuilder()
325
+ self.indian_api = IndianStockAPI()
326
+ self.global_api = GlobalStockAPI()
327
+ from index_calculator import IndexCalculator
328
+ self.calculator = IndexCalculator()
329
+
330
+ def calculate_live_laspeyres(self, symbols: List[str], base_prices: List[float],
331
+ base_quantities: List[float], exchange: str = "NSE") -> Dict:
332
+ """
333
+ Calculate Laspeyres index using live prices
334
+ """
335
+ # Get live prices
336
+ live_prices = []
337
+ stock_details = []
338
+
339
+ for i, symbol in enumerate(symbols):
340
+ if exchange in ["NSE", "BSE"]:
341
+ data = self.indian_api.get_stock_price(symbol, exchange)
342
+ if data and 'data' in data:
343
+ price_data = self.indian_api.format_for_index(data)
344
+ live_prices.append(price_data['last_price'])
345
+ stock_details.append(price_data)
346
+ else:
347
+ data = self.global_api.get_stock_info(symbol)
348
+ if data:
349
+ live_prices.append(data['last_price'])
350
+ stock_details.append(data)
351
+
352
+ # Create DataFrame for index calculator
353
+ df = pd.DataFrame({
354
+ 'item': symbols,
355
+ 'p0': base_prices[:len(live_prices)],
356
+ 'q0': base_quantities[:len(live_prices)],
357
+ 'p1': live_prices
358
+ })
359
+
360
+ self.calculator.load_data(df)
361
+ laspeyres = self.calculator.calculate_laspeyres()
362
+
363
+ return {
364
+ 'index_value': laspeyres['value'],
365
+ 'interpretation': laspeyres['interpretation'],
366
+ 'live_prices': live_prices,
367
+ 'stock_details': stock_details,
368
+ 'timestamp': datetime.now().isoformat()
369
+ }
370
+
371
+ def compare_indices(self, symbols: List[str], exchange: str = "NSE") -> pd.DataFrame:
372
+ """
373
+ Compare multiple index calculations on the same basket
374
+ """
375
+ # Get live data
376
+ stocks_data = []
377
+ for symbol in symbols:
378
+ if exchange in ["NSE", "BSE"]:
379
+ data = self.indian_api.get_stock_price(symbol, exchange)
380
+ if data and 'data' in data:
381
+ stocks_data.append(self.indian_api.format_for_index(data))
382
+ else:
383
+ data = self.global_api.get_stock_info(symbol)
384
+ if data:
385
+ stocks_data.append(data)
386
+
387
+ if not stocks_data:
388
+ return pd.DataFrame()
389
+
390
+ # Prepare data for index calculator
391
+ df = pd.DataFrame({
392
+ 'item': [s.get('company_name', f'Stock{i}') for i, s in enumerate(stocks_data)],
393
+ 'p0': [100.0] * len(stocks_data), # Assume base price 100
394
+ 'q0': [1.0] * len(stocks_data), # Equal quantities
395
+ 'p1': [s['last_price'] for s in stocks_data]
396
+ })
397
+
398
+ self.calculator.load_data(df)
399
+ results = self.calculator.calculate_all()
400
+
401
+ # Add live data
402
+ results['live_data'] = stocks_data
403
+ results['symbols'] = symbols
404
+ results['exchange'] = exchange
405
+
406
+ return results