financial-analyst / services /data_fetcher.py
lucifer7210's picture
Upload 16 files
c04ca05 verified
import yfinance as yf
import pandas as pd
from typing import Dict, Optional, Any, List
from config import NIFTY50_COMPANIES, MARKET_INDICES, SECTOR_ETFS
from models.market_data import StockDataResponse, IndexData, SectorPerformance
class IndianMarketDataFetcher:
"""Fetch real-time Indian market data using yfinance"""
def get_nifty50_companies(self) -> Dict[str, str]:
"""Get Nifty 50 company list"""
return NIFTY50_COMPANIES
def get_market_indices(self) -> Dict[str, IndexData]:
"""Fetch Indian market indices data"""
indices_data = {}
for symbol, name in MARKET_INDICES.items():
try:
ticker = yf.Ticker(symbol)
hist = ticker.history(period="5d")
if not hist.empty:
current_price = hist['Close'].iloc[-1]
previous_close = hist['Close'].iloc[-2] if len(hist) > 1 else current_price
change = current_price - previous_close
change_pct = (change / previous_close) * 100
indices_data[name] = IndexData(
current_price=current_price,
change=change,
change_pct=change_pct,
volume=hist['Volume'].iloc[-1] if 'Volume' in hist else 0
)
except Exception as e:
print(f"Could not fetch data for {name}: {e}")
return indices_data
def _process_financial_df(self, df: pd.DataFrame, date_col_name: str = 'Date') -> List[Dict[str, Any]]:
"""
Helper to process yfinance financial DataFrames (financials, balance_sheet, cashflow).
It transposes the DataFrame, resets the index, and converts the date column
to string format, then returns a list of dictionaries.
"""
if df.empty:
return []
# Transpose the DataFrame so dates become the index, and metrics become columns
df_transposed = df.T
# Reset the index to turn the date index into a regular column,
# then convert that column to string format.
df_processed = df_transposed.reset_index()
df_processed.rename(columns={'index': date_col_name}, inplace=True)
# Ensure the date column is explicitly converted to string type (YYYY-MM-DD)
if pd.api.types.is_datetime64_any_dtype(df_processed[date_col_name]):
df_processed[date_col_name] = df_processed[date_col_name].dt.strftime('%Y-%m-%d')
# Fill any remaining NaN values with 0 to ensure Pydantic doesn't complain about None
df_processed.fillna(0, inplace=True)
return df_processed.to_dict('records')
def get_stock_data(self, symbol: str="RELIANCE", period: str = "1y") -> Optional[StockDataResponse]:
"""Fetch stock data for analysis"""
if not symbol.endswith(".NS"):
symbol = f"{symbol}.NS"
try:
ticker = yf.Ticker(symbol)
# Get historical data
hist = ticker.history(period=period)
if not hist.empty:
# Reset index to make 'Date' a column, then convert to string
hist_processed = hist.reset_index()
hist_processed['Date'] = hist_processed['Date'].dt.strftime('%Y-%m-%d')
hist_dict = hist_processed.to_dict(orient='list')
else:
hist_dict = {}
# Get company info
info = ticker.info
# Process financials, balance sheet, and cash flow using the helper function
financials_list = self._process_financial_df(ticker.financials)
balance_sheet_list = self._process_financial_df(ticker.balance_sheet)
cash_flow_list = self._process_financial_df(ticker.cashflow)
return StockDataResponse(
history=hist_dict,
info=info,
financials=financials_list,
balance_sheet=balance_sheet_list,
cash_flow=cash_flow_list
)
except Exception as e:
print(f"Error fetching data for {symbol}: {e}")
return None
def get_sector_performance(self) -> Dict[str, SectorPerformance]:
"""Get sector-wise performance"""
sector_data = {}
for etf, sector in SECTOR_ETFS.items():
try:
ticker = yf.Ticker(etf)
hist = ticker.history(period="5d")
if not hist.empty:
current = hist['Close'].iloc[-1]
prev = hist['Close'].iloc[-2] if len(hist) > 1 else current
change_pct = ((current - prev) / prev) * 100
sector_data[sector] = SectorPerformance(sector=sector, change_pct=change_pct)
except Exception as e:
print(f"Could not fetch sector data for {etf} ({sector}): {e}")
return sector_data
def get_company_name(self, symbol: str) -> Optional[str]:
"""Get company name from symbol"""
return NIFTY50_COMPANIES.get(symbol)