mftool 2.9__tar.gz → 3.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mftool
3
- Version: 2.9
3
+ Version: 3.1
4
4
  Summary: Library for getting real time Mutual funds info
5
5
  Home-page: https://github.com/NayakwadiS/mftool
6
6
  Author: SujitN
@@ -1,7 +1,7 @@
1
1
  """
2
2
  The MIT License (MIT)
3
3
 
4
- Copyright (c) 2025 Sujit Nayakwadi
4
+ Copyright (c) 2026 Sujit Nayakwadi
5
5
 
6
6
  Permission is hereby granted, free of charge, to any person obtaining a copy
7
7
  of this software and associated documentation files (the "Software"), to deal
@@ -21,5 +21,5 @@
21
21
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22
22
  SOFTWARE.
23
23
  """
24
- __VERSION__='2.9'
24
+ __VERSION__='3.1'
25
25
  from .mftool import Mftool
@@ -2,58 +2,60 @@
2
2
  "get_quote_url": "https://www.amfiindia.com/spages/NAVAll.txt",
3
3
  "get_scheme_url": "https://api.mfapi.in/mf/",
4
4
  "get_amc_details_url": "https://www.amfiindia.com/modules/AMCProfileDetail",
5
- "get_open_ended_equity_scheme_url": "http://www.valueresearchonline.com/amfi/fund-performance-data/?end-type=1&primary-category=SEQ&category=CAT&amc=ALL",
5
+ "get_open_ended_equity_scheme_url": "https://www.amfiindia.com/gateway/pollingsebi/api/amfi/fundperformance",
6
6
  "get_avg_aum_url": "https://www.amfiindia.com/modules/AverageAUMDetails",
7
7
  "user_agent": {
8
8
  "User-Agent": "Chrome/95.0.4638.69"
9
9
  },
10
10
  "open_ended_equity_category": {
11
- "Large Cap": "SEQ_LC",
12
- "Large & Mid Cap": "SEQ_LMC",
13
- "Multi Cap": "SEQ_MLC",
14
- "Mid Cap": "SEQ_MC",
15
- "Small Cap": "SEQ_SC",
16
- "Value": "SEQ_VAL",
17
- "ELSS": "SEQ_ELSS",
18
- "Contra": "SEQ_CONT",
19
- "Dividend Yield": "SEQ_DIVY",
20
- "Focused": "SEQ_FOC"
11
+ "1": "Large Cap",
12
+ "2": "Large & Mid Cap",
13
+ "3": "Flexi Cap",
14
+ "4": "Multi Cap",
15
+ "5": "Mid Cap",
16
+ "6": "Small Cap",
17
+ "7": "Value",
18
+ "8": "ELSS",
19
+ "9": "Contra",
20
+ "10": "Dividend Yield",
21
+ "11": "Focused",
22
+ "12": "Sectoral / Thematic"
21
23
  },
22
24
  "open_ended_debt_category": {
23
- "Long Duration": "SDT_LND",
24
- "Medium to Long Duration": "SDT_MLD",
25
- "Medium Duration": "SDT_MD",
26
- "Short Duration": "SDT_SD",
27
- "Low Duration": "SDT_LWD",
28
- "Ultra Short Duration": "SDT_USD",
29
- "Liquid": "SDT_LIQ",
30
- "Money Market": "SDT_MM",
31
- "Overnight": "SDT_OVNT",
32
- "Dynamic Bond": "SDT_DB",
33
- "Corporate Bond": "SDT_CB",
34
- "Credit Risk": "SDT_CR",
35
- "Banking and PSU": "SDT_BPSU",
36
- "Floater": "SDT_FL",
37
- "FMP": "SDT_FMP",
38
- "Gilt": "SDT_GL",
39
- "Gilt with 10 year constant duration": "SDT_GL10CD"
25
+ "13":"Long Duration",
26
+ "14":"Medium to Long Duration",
27
+ "16":"Medium Duration",
28
+ "15":"Short Duration",
29
+ "18":"Low Duration",
30
+ "19":"Ultra Short Duration",
31
+ "20":"Liquid",
32
+ "17":"Money Market",
33
+ "21":"Overnight",
34
+ "22":"Dynamic Bond",
35
+ "23":"Corporate Bond",
36
+ "24":"Credit Risk",
37
+ "25":"Banking and PSU",
38
+ "26":"Floater",
39
+ "27":"FMP",
40
+ "28":"Gilt",
41
+ "29":"Gilt with 10 year constant duration"
40
42
  },
41
43
  "open_ended_hybrid_category": {
42
- "Aggressive Hybrid": "SHY_AH",
43
- "Balanced Hybrid": "SHY_BH",
44
- "Conservative Hybrid": "SHY_CH",
45
- "Equity Savings": "SHY_EQS",
46
- "Arbitrage": "SHY_AR",
47
- "Multi Asset Allocation": "SHY_MAA"
44
+ "30":"Aggressive Hybrid",
45
+ "40":"Balanced Hybrid",
46
+ "31":"Conservative Hybrid",
47
+ "32":"Equity Savings",
48
+ "33":"Arbitrage",
49
+ "34":"Multi Asset Allocation"
48
50
  },
49
51
  "open_ended_solution_category": {
50
- "Children's": "SSO_CHILD",
51
- "Retirement": "SSO_RETR"
52
+ "36":"Children's",
53
+ "37":"Retirement"
52
54
 
53
55
  },
54
56
  "open_ended_other_category": {
55
- "Index Funds/ETFs": "SOTH_IXETF",
56
- "FoFs(Oversease/Domestic)": "SOTH_FOFS"
57
+ "38":"Index Funds/ETFs",
58
+ "39":"FoFs(Oversease/Domestic)"
57
59
 
58
60
  },
59
61
  "amc": [3, 53, 1, 4, 59, 46, 32, 6, 47, 54, 27, 9, 37, 20, 57, 48, 68, 62, 65, 63, 42, 70, 16, 17, 56, 18, 69, 45, 55, 21, 58, 64, 10, 13, 35,
@@ -20060,4 +20062,4 @@
20060
20062
  }, {
20061
20063
  "0P00019W5W": "Indiabulls Blue Chip Fund Direct Plan Quarterly Dividend Payout Option"
20062
20064
  }]
20063
- }
20065
+ }
@@ -1,6 +1,6 @@
1
1
  """
2
2
  The MIT License (MIT)
3
- Copyright (c) 2025 Sujit Nayakwadi
3
+ Copyright (c) 2026 Sujit Nayakwadi
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to deal
6
6
  in the Software without restriction, including without limitation the rights
@@ -25,7 +25,7 @@ import yfinance as yf
25
25
  import datetime
26
26
  from deprecated import deprecated
27
27
  from matplotlib import pyplot as plt
28
- from .utils import Utilities, is_holiday, get_today, get_friday, render_response
28
+ from .utils import Utilities, is_holiday, get_today, get_friday, render_response, get_52_week_friday
29
29
  import pandas as pd
30
30
 
31
31
 
@@ -175,7 +175,6 @@ class Mftool:
175
175
  scheme_info = {}
176
176
  url = self._get_scheme_url + code
177
177
  response = self._session.get(url).json()
178
-
179
178
  scheme_data = response['meta']
180
179
  scheme_info['fund_house'] = scheme_data['fund_house']
181
180
  scheme_info['scheme_type'] = scheme_data['scheme_type']
@@ -183,6 +182,9 @@ class Mftool:
183
182
  scheme_info['scheme_code'] = scheme_data['scheme_code']
184
183
  scheme_info['scheme_name'] = scheme_data['scheme_name']
185
184
  scheme_info['scheme_start_date'] = response['data'][int(len(response['data']) - 1)]
185
+ result = self.get_52_week_high_low(response['data'])
186
+ scheme_info['52_week_high'] = result['52_week_high']
187
+ scheme_info['52_week_low'] = result['52_week_low']
186
188
  if response['data']:
187
189
  scheme_info['data'] = response['data']
188
190
  else:
@@ -191,6 +193,14 @@ class Mftool:
191
193
  else:
192
194
  return None
193
195
 
196
+ def get_52_week_high_low(self, data):
197
+ friday = pd.to_datetime(get_52_week_friday())
198
+ df = pd.DataFrame.from_records(data)
199
+ df['date'] = pd.to_datetime(df['date'], dayfirst=True)
200
+ df_high = df[df['date'] >= friday].sort_values(by='nav', ascending=False).head(1)
201
+ df_low = df[df['date'] >= friday].sort_values(by='nav', ascending=True).head(1)
202
+ return {"52_week_high": df_high['nav'].values[0], "52_week_low": df_low['nav'].values[0]}
203
+
194
204
  def calculate_balance_units_value(self, code, balance_units, as_json=False):
195
205
  """
196
206
  gets the market value of your balance units for a given scheme code
@@ -238,39 +248,7 @@ class Mftool:
238
248
  else:
239
249
  return None
240
250
 
241
- @deprecated(version='2.8',
242
- reason="This function will be in deprecated from next release, use mf.history() to get data")
243
- def get_scheme_historical_nav_year(self, code, year, as_json=False, as_dataframe=False):
244
- """
245
- gets the scheme historical data of given year for a given scheme code
246
- :param code: scheme-code
247
- :param year: year
248
- :param as_json: default false
249
- :param as_dataframe: default false
250
- :return: dict or None
251
- :raises: HTTPError, URLError
252
- """
253
- code = str(code)
254
- if self.is_valid_code(code):
255
- data = []
256
- # url = self._get_scheme_url + code
257
- # response = self._session.get(url).json()
258
- nav = self.get_scheme_historical_nav(code)
259
- scheme_info = self.get_scheme_details(code)
260
- for dat in nav['data']:
261
- navDate = dat['date']
262
- d = datetime.datetime.strptime(navDate, '%d-%m-%Y')
263
- if d.year == int(year):
264
- data.append(dat)
265
- if len(data) == 0:
266
- data.append({'Error': 'For Year '+str(year)+' Data is NOT available'})
267
-
268
- scheme_info.update(data=data)
269
- return render_response(scheme_info, as_json, as_dataframe)
270
- else:
271
- return None
272
-
273
- @deprecated(version='2.7',
251
+ @deprecated(version='3.1',
274
252
  reason="This function will be in deprecated from next release, use mf.history() to get data")
275
253
  def get_scheme_historical_nav_for_dates(self, code, start_date, end_date, as_json=False, as_dataframe=False):
276
254
  """
@@ -289,8 +267,6 @@ class Mftool:
289
267
  data = []
290
268
  start_date = datetime.datetime.strptime(start_date, '%d-%m-%Y').date()
291
269
  end_date = datetime.datetime.strptime(end_date, '%d-%m-%Y').date()
292
- # url = self._get_scheme_url + code
293
- # response = self._session.get(url).json()
294
270
  nav = self.get_scheme_historical_nav(code)
295
271
  scheme_info = self.get_scheme_details(code)
296
272
  for dat in nav['data']:
@@ -306,110 +282,103 @@ class Mftool:
306
282
  else:
307
283
  return None
308
284
 
309
- def get_open_ended_equity_scheme_performance(self, as_json=False):
285
+ def get_open_ended_equity_scheme_performance(self, report_date=None,as_json=False):
310
286
  """
311
287
  gets the daily performance of open-ended equity schemes for all AMCs
288
+ :param report_date: date in 'DD-MMM-YYYY' format, if None then it will take last working day
312
289
  :return: json format
313
290
  :raises: HTTPError, URLError
314
291
  """
315
292
  scheme_performance = {}
316
- for key in self._open_ended_equity_category.keys():
317
- scheme_performance_url = self._get_open_ended_equity_scheme_url.replace('CAT', self._open_ended_equity_category[key])
318
- scheme_performance[key] = self._get_daily_scheme_performance(scheme_performance_url)
293
+ subCategory = self._open_ended_equity_category
294
+ for key in subCategory:
295
+ scheme_performance[subCategory[key]] = self._get_daily_scheme_performance(self._get_open_ended_equity_scheme_url,report_date,1, key)
319
296
  return render_response(scheme_performance, as_json)
320
297
 
321
- def get_open_ended_debt_scheme_performance(self, as_json=False):
298
+ def get_open_ended_debt_scheme_performance(self, report_date=None, as_json=False):
322
299
  """
323
300
  gets the daily performance of open-ended debt schemes for all AMCs
301
+ :param report_date: date in 'DD-MMM-YYYY' format, if None then it will take last working day
324
302
  :return: json format
325
303
  :raises: HTTPError, URLError
326
304
  """
327
- get_open_ended_debt_scheme_url = self._get_open_ended_equity_scheme_url.replace('SEQ', 'SDT')
305
+ subCategory = self._open_ended_debt_category
328
306
  scheme_performance = {}
329
- for key in self._open_ended_debt_category.keys():
330
- scheme_performance_url = get_open_ended_debt_scheme_url.replace('CAT', self._open_ended_debt_category[key])
331
- scheme_performance[key] = self._get_daily_scheme_performance(scheme_performance_url)
307
+ for key in subCategory:
308
+ scheme_performance[subCategory[key]] = self._get_daily_scheme_performance(self._get_open_ended_equity_scheme_url,report_date,2,key)
332
309
  return render_response(scheme_performance, as_json)
333
310
 
334
- def get_open_ended_hybrid_scheme_performance(self, as_json=False):
311
+ def get_open_ended_hybrid_scheme_performance(self, report_date=None, as_json=False):
335
312
  """
336
313
  gets the daily performance of open-ended hybrid schemes for all AMCs
314
+ :param report_date: date in 'DD-MMM-YYYY' format, if None then it will take last working day
337
315
  :return: json format
338
316
  :raises: HTTPError, URLError
339
317
  """
340
- get_open_ended_debt_scheme_url = self._get_open_ended_equity_scheme_url.replace('SEQ', 'SHY')
318
+ subCategory = self._open_ended_hybrid_category
341
319
  scheme_performance = {}
342
- for key in self._open_ended_hybrid_category.keys():
343
- scheme_performance_url = get_open_ended_debt_scheme_url.replace('CAT',self._open_ended_hybrid_category[key])
344
- scheme_performance[key] = self._get_daily_scheme_performance(scheme_performance_url)
320
+ for key in subCategory:
321
+ scheme_performance[subCategory[key]] = self._get_daily_scheme_performance(
322
+ self._get_open_ended_equity_scheme_url,report_date, 3, key)
345
323
  return render_response(scheme_performance, as_json)
346
324
 
347
- def get_open_ended_solution_scheme_performance(self, as_json=False):
325
+ def get_open_ended_solution_scheme_performance(self, report_date=None, as_json=False):
348
326
  """
349
327
  gets the daily performance of open-ended Solution-Oriented schemes for all AMCs
328
+ :param report_date: date in 'DD-MMM-YYYY' format, if None then it will take last working day
350
329
  :return: json format
351
330
  :raises: HTTPError, URLError
352
331
  """
353
- get_open_ended_solution_scheme_url = self._get_open_ended_equity_scheme_url.replace('SEQ', 'SSO')
332
+ subCategory = self._open_ended_solution_category
354
333
  scheme_performance = {}
355
- for key in self._open_ended_solution_category.keys():
356
- scheme_performance_url = get_open_ended_solution_scheme_url.replace('CAT',
357
- self._open_ended_solution_category[key])
358
- scheme_performance[key] = self._get_daily_scheme_performance(scheme_performance_url)
334
+ for key in subCategory:
335
+ scheme_performance[subCategory[key]] = self._get_daily_scheme_performance(
336
+ self._get_open_ended_equity_scheme_url, report_date,4, key)
359
337
  return render_response(scheme_performance, as_json)
360
338
 
361
- def get_open_ended_other_scheme_performance(self, as_json=False):
339
+ def get_open_ended_other_scheme_performance(self, report_date=None, as_json=False):
362
340
  """
363
341
  gets the daily performance of open-ended index and FoF schemes for all AMCs
342
+ :param report_date: date in 'DD-MMM-YYYY' format, if None then it will take last working day
364
343
  :return: json format
365
344
  :raises: HTTPError, URLError
366
345
  """
367
- get_open_ended_other_scheme_url = self._get_open_ended_equity_scheme_url.replace('SEQ', 'SOTH')
346
+ subCategory = self._open_ended_other_category
368
347
  scheme_performance = {}
369
- for key in self._open_ended_other_category.keys():
370
- scheme_performance_url = get_open_ended_other_scheme_url.replace('CAT', self._open_ended_other_category[key])
371
- scheme_performance[key] = self._get_daily_scheme_performance(scheme_performance_url)
348
+ for key in subCategory:
349
+ scheme_performance[subCategory[key]] = self._get_daily_scheme_performance(
350
+ self._get_open_ended_equity_scheme_url, report_date,5, key)
372
351
  return render_response(scheme_performance, as_json)
373
352
 
374
- def _get_daily_scheme_performance(self, performance_url, as_json=False):
353
+ def _get_daily_scheme_performance(self, performance_url,report_date, category,key, as_json=False):
375
354
  fund_performance = []
376
- if is_holiday():
377
- url = performance_url + '&nav-date=' + get_friday()
378
- else:
379
- url = performance_url + '&nav-date=' + get_today()
380
- # html = requests.get(url, headers=self._user_agent)
381
- html = httpx.get(url, headers=self._user_agent,timeout=25)
382
- soup = BeautifulSoup(html.text, 'html.parser')
383
- rows = soup.select("table tbody tr")
355
+ if not report_date:
356
+ if is_holiday():
357
+ report_date = get_friday()
358
+ else:
359
+ report_date = get_today()
384
360
  try:
385
- for tr in rows:
361
+ data = {"maturityType": 1,"category": category,"subCategory": int(key),"mfid": 0,"reportDate": report_date}
362
+ html = httpx.post(performance_url,headers={"User-Agent":"Mozilla/5.0"},timeout=25, json=data)
363
+ for result in html.json()['data']:
386
364
  scheme_details = {}
387
- cols = tr.select("td.nav.text-right")
388
- scheme_details['scheme_name'] = tr.select("td")[0].get_text()
389
- scheme_details['benchmark'] = tr.select("td")[1].get_text()
390
-
391
- scheme_details['latest NAV- Regular'] = tr.select("td")[2].get_text().strip()
392
- scheme_details['latest NAV- Direct'] = tr.select("td")[3].get_text().strip()
393
-
394
- regData = tr.find_all("td", recursive=False,class_="text-right period-return-reg", limit=1)
395
- dirData = tr.find_all("td", recursive=False, class_="text-right period-return-dir", limit=1)
396
-
397
- scheme_details['1-Year Return(%)- Regular'] = regData[0]['data-1y']
398
- scheme_details['1-Year Return(%)- Direct'] = dirData[0]['data-1y']
399
-
400
- scheme_details['3-Year Return(%)- Regular'] = regData[0]['data-3y']
401
- scheme_details['3-Year Return(%)- Direct'] = dirData[0]['data-3y']
402
-
403
- scheme_details['5-Year Return(%)- Regular'] = regData[0]['data-5y']
404
- scheme_details['5-Year Return(%)- Direct'] = dirData[0]['data-5y']
405
-
365
+ scheme_details['scheme_name'] = result['schemeName']
366
+ scheme_details['benchmark'] = result['benchmark']
367
+ scheme_details['latest NAV- Regular'] = result['navRegular']
368
+ scheme_details['latest NAV- Direct'] = result['navDirect']
369
+ scheme_details['1-Year Return(%)- Regular'] = result['return1YearRegular']
370
+ scheme_details['1-Year Return(%)- Direct'] = result['return1YearDirect']
371
+ scheme_details['3-Year Return(%)- Regular'] = result['return3YearRegular']
372
+ scheme_details['3-Year Return(%)- Direct'] = result['return3YearDirect']
373
+ scheme_details['5-Year Return(%)- Regular'] = result['return5YearRegular']
374
+ scheme_details['5-Year Return(%)- Direct'] = result['return5YearDirect']
406
375
  fund_performance.append(scheme_details)
407
-
408
376
  except Exception:
409
377
  return render_response(['The underlying data is unavailable for Today'], as_json)
410
-
411
378
  return render_response(fund_performance, as_json)
412
379
 
380
+ @deprecated(version='3.1',
381
+ reason="This function will be in deprecated from next release, use mf.history() to get data")
413
382
  def get_all_amc_profiles(self, as_json=True):
414
383
  """
415
384
  gets profiles for all Fund houses
@@ -479,7 +448,7 @@ class Mftool:
479
448
  code = str(code)
480
449
  if self.is_code(code):
481
450
  def get_Dataframe(df, as_dataframe):
482
- df = df.drop(columns=['Open', 'High', 'Low','Adj Close','Volume'])
451
+ df = df.drop(columns=['Open', 'High', 'Low','Volume'])
483
452
  df = df.rename(columns={'Close': 'nav'})
484
453
  df['dayChange'] = df['nav'].diff()
485
454
  df = df.rename_axis('date')
@@ -22,6 +22,10 @@ def get_today():
22
22
  return (date.today() - timedelta(days=1)).strftime("%d-%b-%Y")
23
23
 
24
24
 
25
+ def get_52_week_friday():
26
+ return (date.today() - timedelta(weeks=52)).strftime("%d-%m-%Y")
27
+
28
+
25
29
  def render_response(data, as_json=False, as_Dataframe=False):
26
30
  if as_json is True:
27
31
  return json.dumps(data)
@@ -40,4 +44,4 @@ class Utilities:
40
44
  def __init__(self):
41
45
  self._filepath = str(os.path.dirname(os.path.abspath(__file__))) + '/const.json'
42
46
  with open(self._filepath, 'r') as f:
43
- self.values = json.load(f)
47
+ self.values = json.load(f)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: mftool
3
- Version: 2.9
3
+ Version: 3.1
4
4
  Summary: Library for getting real time Mutual funds info
5
5
  Home-page: https://github.com/NayakwadiS/mftool
6
6
  Author: SujitN
@@ -3,4 +3,5 @@ bs4
3
3
  httpx
4
4
  pandas
5
5
  yfinance
6
- Deprecated
6
+ matplotlib
7
+ deprecated
@@ -7,13 +7,13 @@ with open(path.join(this_directory, 'README.md'), encoding='utf-8') as f:
7
7
 
8
8
  setup(
9
9
  name="mftool",
10
- version="2.9",
10
+ version="3.1",
11
11
  author="SujitN",
12
12
  author_email="nayakwadi_sujit@rediffmail.com",
13
13
  description="Library for getting real time Mutual funds info",
14
14
  license="MIT",
15
15
  keywords="amfi, quote, mutual-funds, funds, bse, nse, market, stock, stocks",
16
- install_requires=['requests', 'bs4', 'httpx','pandas','yfinance','Deprecated'],
16
+ install_requires=['requests', 'bs4', 'httpx', 'pandas', 'yfinance', 'matplotlib','deprecated'],
17
17
  url="https://github.com/NayakwadiS/mftool",
18
18
  packages=find_packages(),
19
19
  long_description=long_description,
File without changes
File without changes
File without changes
File without changes