Toymakerftw commited on
Commit
3994932
·
1 Parent(s): a07c1a8

feat: Enhance ticker resolution and data fetching for multiple exchanges

Browse files

- Improved `resolve_ticker_symbol` function:
- Added support for multiple exchanges (NSE and BSE).
- Ensured the correct exchange suffix is appended to the ticker symbol.
- Used fuzzy matching to find the best match for the query among company names.
- Defaulted to the first result if no best match is found, with the appropriate exchange suffix.

- Enhanced `fetch_yfinance_data` function:
- Added support for fetching data from multiple exchanges (NSE and BSE).
- Implemented a fallback mechanism to try the next exchange if data is not found on the initial exchange.
- Improved error handling to log errors and continue to the next exchange if necessary.
- Returned detailed financial data, technical indicators, and price charts if data is successfully fetched from any exchange.

These changes ensure that the application can handle stocks listed on multiple exchanges and provide accurate data and analysis.

Files changed (1) hide show
  1. app.py +65 -41
app.py CHANGED
@@ -120,38 +120,48 @@ def generate_price_chart(history):
120
  plt.tight_layout()
121
  return fig
122
 
123
- def resolve_ticker_symbol(query: str, exchange: str = "NSE") -> str:
124
  """
125
  Convert company names/partial symbols to valid Yahoo Finance tickers.
126
  Example: "Kalyan Jewellers" → "KALYANKJIL.NS"
127
  """
 
 
 
 
 
 
128
  url = "https://query2.finance.yahoo.com/v1/finance/search"
129
  headers = {"User-Agent": "Mozilla/5.0"} # Avoid blocking
130
  params = {"q": query, "quotesCount": 5, "country": "India"} # Adjust for regional markets
131
-
132
  response = requests.get(url, headers=headers, params=params)
133
  data = response.json()
134
-
135
  if data.get("quotes"):
136
  # Extract symbols and names
137
  tickers = [quote["symbol"] for quote in data["quotes"]]
138
  names = [quote["longname"] or quote["shortname"] for quote in data["quotes"]]
139
-
140
  # Fuzzy match the query with company names
141
  best_match = process.extractOne(query, names)
142
  if best_match:
143
  index = names.index(best_match[0])
144
  resolved_ticker = tickers[index]
145
-
146
  # Ensure the exchange suffix is only added if not already present
147
- if not resolved_ticker.endswith(EXCHANGE_SUFFIXES.get(exchange, "")):
148
- resolved_ticker += EXCHANGE_SUFFIXES.get(exchange, "")
 
 
149
  return resolved_ticker
150
  else:
151
  # Default to first result
152
  resolved_ticker = tickers[0]
153
- if not resolved_ticker.endswith(EXCHANGE_SUFFIXES.get(exchange, "")):
154
- resolved_ticker += EXCHANGE_SUFFIXES.get(exchange, "")
 
 
155
  return resolved_ticker
156
  else:
157
  raise ValueError(f"No ticker found for: {query}")
@@ -180,38 +190,52 @@ def analyze_article_sentiment(article):
180
  return article
181
 
182
  def fetch_yfinance_data(ticker):
183
- """Enhanced Yahoo Finance data fetching with technical analysis"""
184
- try:
185
- logging.info(f"Fetching Yahoo Finance data for: {ticker}")
186
- stock = yf.Ticker(ticker)
187
-
188
- # Get historical data for technical analysis
189
- history = stock.history(period="1y", interval="1d")
190
-
191
- # Calculate technical indicators
192
- ta_data = calculate_technical_indicators(history) if not history.empty else {}
193
-
194
- # Current price data
195
- current_price = history['Close'].iloc[-1] if not history.empty else 0
196
- prev_close = history['Close'].iloc[-2] if len(history) > 1 else 0
197
- price_change = current_price - prev_close
198
- percent_change = (price_change / prev_close) * 100 if prev_close != 0 else 0
199
-
200
- # Generate price chart
201
- chart = generate_price_chart(history[-120:]) # Last 120 days
202
-
203
- return {
204
- 'current_price': current_price,
205
- 'price_change': price_change,
206
- 'percent_change': percent_change,
207
- 'chart': chart,
208
- 'technical_indicators': ta_data,
209
- 'fundamentals': stock.info
210
- }
211
-
212
- except Exception as e:
213
- logging.error(f"Error fetching Yahoo Finance data: {str(e)}")
214
- return {"error": str(e)}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
215
 
216
  def time_weighted_sentiment(articles):
217
  """Apply time-based weighting to sentiment scores"""
 
120
  plt.tight_layout()
121
  return fig
122
 
123
+ def resolve_ticker_symbol(query: str) -> str:
124
  """
125
  Convert company names/partial symbols to valid Yahoo Finance tickers.
126
  Example: "Kalyan Jewellers" → "KALYANKJIL.NS"
127
  """
128
+ exchanges = ["NSE", "BSE"]
129
+ exchange_suffixes = {
130
+ "NSE": ".NS",
131
+ "BSE": ".BO",
132
+ }
133
+
134
  url = "https://query2.finance.yahoo.com/v1/finance/search"
135
  headers = {"User-Agent": "Mozilla/5.0"} # Avoid blocking
136
  params = {"q": query, "quotesCount": 5, "country": "India"} # Adjust for regional markets
137
+
138
  response = requests.get(url, headers=headers, params=params)
139
  data = response.json()
140
+
141
  if data.get("quotes"):
142
  # Extract symbols and names
143
  tickers = [quote["symbol"] for quote in data["quotes"]]
144
  names = [quote["longname"] or quote["shortname"] for quote in data["quotes"]]
145
+
146
  # Fuzzy match the query with company names
147
  best_match = process.extractOne(query, names)
148
  if best_match:
149
  index = names.index(best_match[0])
150
  resolved_ticker = tickers[index]
151
+
152
  # Ensure the exchange suffix is only added if not already present
153
+ for exchange in exchanges:
154
+ if not resolved_ticker.endswith(exchange_suffixes[exchange]):
155
+ resolved_ticker += exchange_suffixes[exchange]
156
+ break
157
  return resolved_ticker
158
  else:
159
  # Default to first result
160
  resolved_ticker = tickers[0]
161
+ for exchange in exchanges:
162
+ if not resolved_ticker.endswith(exchange_suffixes[exchange]):
163
+ resolved_ticker += exchange_suffixes[exchange]
164
+ break
165
  return resolved_ticker
166
  else:
167
  raise ValueError(f"No ticker found for: {query}")
 
190
  return article
191
 
192
  def fetch_yfinance_data(ticker):
193
+ """Enhanced Yahoo Finance data fetching with technical analysis and fallback for multiple exchanges"""
194
+ exchanges = ["NSE", "BSE"]
195
+ exchange_suffixes = {
196
+ "NSE": ".NS",
197
+ "BSE": ".BO",
198
+ }
199
+
200
+ for exchange in exchanges:
201
+ try:
202
+ logging.info(f"Fetching Yahoo Finance data for: {ticker}{exchange_suffixes[exchange]}")
203
+ stock = yf.Ticker(f"{ticker}{exchange_suffixes[exchange]}")
204
+
205
+ # Get historical data for technical analysis
206
+ history = stock.history(period="1y", interval="1d")
207
+
208
+ if history.empty:
209
+ logging.error(f"No data found for {ticker}{exchange_suffixes[exchange]}, trying next exchange")
210
+ continue
211
+
212
+ # Calculate technical indicators
213
+ ta_data = calculate_technical_indicators(history)
214
+
215
+ # Current price data
216
+ current_price = history['Close'].iloc[-1]
217
+ prev_close = history['Close'].iloc[-2] if len(history) > 1 else 0
218
+ price_change = current_price - prev_close
219
+ percent_change = (price_change / prev_close) * 100 if prev_close != 0 else 0
220
+
221
+ # Generate price chart
222
+ chart = generate_price_chart(history[-120:]) # Last 120 days
223
+
224
+ return {
225
+ 'current_price': current_price,
226
+ 'price_change': price_change,
227
+ 'percent_change': percent_change,
228
+ 'chart': chart,
229
+ 'technical_indicators': ta_data,
230
+ 'fundamentals': stock.info
231
+ }
232
+
233
+ except Exception as e:
234
+ logging.error(f"Error fetching Yahoo Finance data for {ticker}{exchange_suffixes[exchange]}: {str(e)}")
235
+ continue
236
+
237
+ logging.error(f"Failed to fetch data for {ticker} from all exchanges")
238
+ return {"error": f"Failed to fetch data for {ticker} from all exchanges"}
239
 
240
  def time_weighted_sentiment(articles):
241
  """Apply time-based weighting to sentiment scores"""