From 01f4afbdf7f8f1330c7d838252c3a1ce8ad429ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ma=C3=ABl?= Date: Sat, 3 Feb 2024 19:55:48 +0100 Subject: [PATCH] format with black --- mstarpy/error.py | 13 +- mstarpy/funds.py | 700 +++++++++++++++------------ mstarpy/search.py | 799 ++++++++++++++++--------------- mstarpy/security.py | 257 +++++----- mstarpy/stock.py | 291 +++++++----- mstarpy/utils.py | 1097 +++++++++++++++++++------------------------ 6 files changed, 1613 insertions(+), 1544 deletions(-) diff --git a/mstarpy/error.py b/mstarpy/error.py index d85b08f..e7b0ae4 100644 --- a/mstarpy/error.py +++ b/mstarpy/error.py @@ -1,19 +1,22 @@ """module to raise error""" -def not_200_response(url,response): + +def not_200_response(url, response): """ This function raise a ConnectionError if the status code a requests is not 200. """ if not response.status_code == 200: - raise ConnectionError(f"""Error {response.status_code} - for the api {url}. Message : {response.reason}.""") + raise ConnectionError( + f"""Error {response.status_code} + for the api {url}. Message : {response.reason}.""" + ) def no_site_error(code, name, country, site): - """ + """ This function raise a ValueError if the selected country is "us" or a site is not selected. """ - if not site or country == 'us': + if not site or country == "us": if country: raise ValueError(f"The funds of the country {country} cannot be scraped.") raise ValueError(f"The funds {name} ({code}) cannot be scraped.") diff --git a/mstarpy/funds.py b/mstarpy/funds.py index 4b010bc..af69f1a 100644 --- a/mstarpy/funds.py +++ b/mstarpy/funds.py @@ -10,7 +10,6 @@ from .security import Security - class Funds(Security): """ Main class to access data about funds and etf, inherit from Security class @@ -20,7 +19,8 @@ class Funds(Security): country (str) : text for code ISO 3166-1 alpha-2 of country, should be '' for etf pageSize (int): number of funds to return itemRange (int) : index of funds to return (must be inferior to PageSize) - proxies = (dict) : set the proxy if needed , example : {"http": "http://host:port","https": "https://host:port"} + proxies = (dict) : set the proxy if needed , + example : {"http": "http://host:port","https": "https://host:port"} Examples: >>> Funds('0P0000712R', "ca", 9, 0) @@ -32,14 +32,28 @@ class Funds(Security): """ - def __init__(self, term = None, country: str = "", pageSize : int =1, itemRange: int = 0, filters: dict = {}, proxies: dict = {}): - - super().__init__(term=term,asset_type='fund',country=country,pageSize=pageSize,itemRange=itemRange,filters=filters,proxies=proxies) - - + def __init__( + self, + term=None, + country: str = "", + pageSize: int = 1, + itemRange: int = 0, + filters: dict = {}, + proxies: dict = {}, + ): + + super().__init__( + term=term, + asset_type="fund", + country=country, + pageSize=pageSize, + itemRange=itemRange, + filters=filters, + proxies=proxies, + ) def allocationMap(self): - """ + """ This function retrieves the asset allocation of the funds, index and category. Returns: @@ -53,14 +67,15 @@ def allocationMap(self): def allocationWeighting(self): """ - This function retrieves the Growth/Blend/Value and market capitalizaton allocation size of the funds. + This function retrieves the Growth/Blend/Value and + market capitalizaton allocation size of the funds. Returns: dict with allocation Examples: >>> Funds("myria", "fr").allocationWeighting() - + """ return self.GetData("process/weighting") @@ -74,7 +89,6 @@ def analystRating(self): >>> Funds("RMAGX", "us").analystRating() """ - return self.GetData("parent/analystRating") @@ -86,11 +100,10 @@ def analystRatingTopFunds(self): dict with ratings >>> Funds("RMAGX", "us").analystRatingTopFunds() - + """ - - return self.GetData("parent/analystRating/topfunds") + return self.GetData("parent/analystRating/topfunds") def analystRatingTopFundsUpDown(self): """ @@ -100,37 +113,37 @@ def analystRatingTopFundsUpDown(self): dict with ratings >>> Funds("RMAGX", "us").analystRatingTopFundsUpDown() - + """ return self.GetData("parent/analystRating/topfundsUpDown") - - def keyStats(self): """ - This function retrieves the key status information of the funds, index, category or the annual rank of the funds. + This function retrieves the key status information of the funds, + index, category or the annual rank of the funds. Returns: dict annual performance or rank Raises: - ValueError : raised whenever parameter cat is not category, funds, index, or rank + ValueError : raised whenever parameter cat is not category, + funds, index, or rank Examples: >>> Funds("myria", "fr").key_stats() """ - #headers random agent - headers = {'user-agent' : random_user_agent()} - #page 1 - performance + # headers random agent + headers = {"user-agent": random_user_agent()} + # page 1 - performance url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}" - + response = requests.get(url, headers=headers, proxies=self.proxies) - not_200_response(url,response) - soup = BeautifulSoup(response.text, 'html.parser') - - table_rows = soup.find(id="overviewQuickstatsDiv").find_all('tr') + not_200_response(url, response) + soup = BeautifulSoup(response.text, "html.parser") + + table_rows = soup.find(id="overviewQuickstatsDiv").find_all("tr") details = [] for row in table_rows: key, value, date_stamp = None, None, None @@ -138,7 +151,7 @@ def keyStats(self): if cell.has_attr("class"): if "line" in cell["class"]: if "heading" in cell["class"]: - key = cell.get_text(separator = "\n", strip = True) + key = cell.get_text(separator="\n", strip=True) if "\n" in key: key, date_stamp = key.split("\n") if "text" in cell["class"]: @@ -150,13 +163,13 @@ def keyStats(self): return details - def AnnualPerformance(self, cat): """ - This function retrieves the annual performance of the funds, index, category or the annual rank of the funds. + This function retrieves the annual performance of the funds, + index, category or the annual rank of the funds. Args: - cat (str) : possible values are category, funds, index, rank + cat (str) : possible values are category, funds, index, rank Returns: dict annual performance or rank @@ -170,41 +183,49 @@ def AnnualPerformance(self, cat): >>> Funds("myria", "fr").AnnualPerformance("rank") """ - no_site_error(self.code,self.name,self.country,self.site) + no_site_error(self.code, self.name, self.country, self.site) - cat_row = {'funds' : 0,'category' : 1, 'index' : 2, 'rank' : 3} + cat_row = {"funds": 0, "category": 1, "index": 2, "rank": 3} if cat not in cat_row: - raise ValueError(f"cat parameter must take one of the following value : { ', '.join(cat_row.keys())}") + raise ValueError( + f"cat parameter must take one of the following value : { ', '.join(cat_row.keys())}" + ) result = {} - #headers random agent - headers = {'user-agent' : random_user_agent()} - #page 1 - performance + # headers random agent + headers = {"user-agent": random_user_agent()} + # page 1 - performance url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}&tab=1" - + response = requests.get(url, headers=headers, proxies=self.proxies) - not_200_response(url,response) - soup = BeautifulSoup(response.text, 'html.parser') - #label are dates - regex = re.compile('.*heading number') - label_list = soup.find(id='returnsCalenderYearDiv').find_all('td', {"class": regex}) - #funds performance, category performance, index performance, rank in category - regex = re.compile('.*value number') - #values - value_list = soup.find(id='returnsCalenderYearDiv').find_all('td', {"class": regex}) - - regex = re.compile('-|\/') - #first col is nothing + not_200_response(url, response) + soup = BeautifulSoup(response.text, "html.parser") + # label are dates + regex = re.compile(".*heading number") + label_list = soup.find(id="returnsCalenderYearDiv").find_all( + "td", {"class": regex} + ) + # funds performance, category performance, index performance, rank in category + regex = re.compile(".*value number") + # values + value_list = soup.find(id="returnsCalenderYearDiv").find_all( + "td", {"class": regex} + ) + + regex = re.compile("-|\/") + # first col is nothing for i in range(1, len(label_list)): label = label_list[i].text - #if today + # if today if regex.search(label): - label = 'current' - #add category to label + label = "current" + # add category to label if label: - - result[f'{cat}_annual_performance_{label}'] = value_list[i+(cat_row[cat])*(len(label_list)-1)-1].text + + result[f"{cat}_annual_performance_{label}"] = value_list[ + i + (cat_row[cat]) * (len(label_list) - 1) - 1 + ].text return result @@ -251,7 +272,7 @@ def category(self): def categoryAnnualPerformance(self): """ This function retrieves the annual performance of the category. - + Returns: dict annual performance of the category @@ -260,12 +281,13 @@ def categoryAnnualPerformance(self): >>> Funds("myria", "fr").categoryAnnualPerformance() """ - return self.AnnualPerformance('category') + + return self.AnnualPerformance("category") def categoryCumulativePerformance(self): """ This function retrieves the cumulative performance of the category. - + Returns: dict cumulative performance of the category @@ -273,13 +295,13 @@ def categoryCumulativePerformance(self): >>> Funds("myria", "fr").categoryCumulativePerformance() """ - - return self.CumulativePerformance('category') + + return self.CumulativePerformance("category") def contact(self): """ This function retrieves information about the asset manager. - + Returns: dict contact @@ -287,23 +309,27 @@ def contact(self): >>> Funds("myria", "fr").contact() """ - no_site_error(self.code,self.name,self.country,self.site) + no_site_error(self.code, self.name, self.country, self.site) result = {} - #headers random agent - headers = {'user-agent' : random_user_agent()} - #page 1 - performance - #page 4 - info about found + # headers random agent + headers = {"user-agent": random_user_agent()} + # page 1 - performance + # page 4 - info about found url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}&tab=4" - response = requests.get(url, headers=headers,proxies=self.proxies) - not_200_response(url,response) - soup = BeautifulSoup(response.text, 'html.parser') - #label - label_list = soup.find(id='managementManagementDiv').find_all('td', {"class": "col1 label"}) - #value - value_list = soup.find(id='managementManagementDiv').find_all('td', {"class": "col2 value number"}) + response = requests.get(url, headers=headers, proxies=self.proxies) + not_200_response(url, response) + soup = BeautifulSoup(response.text, "html.parser") + # label + label_list = soup.find(id="managementManagementDiv").find_all( + "td", {"class": "col1 label"} + ) + # value + value_list = soup.find(id="managementManagementDiv").find_all( + "td", {"class": "col2 value number"} + ) for i in range(0, len(value_list)): label = label_list[i].text - + result[label] = value_list[i].text return result @@ -311,7 +337,7 @@ def contact(self): def costIllustration(self): """ This function retrieves the cost of the funds. - + Returns: dict cost of funds @@ -324,7 +350,7 @@ def costIllustration(self): def couponRange(self): """ This function retrieves the coupon of the funds, index and category. - + Returns: dict coupon @@ -334,11 +360,10 @@ def couponRange(self): """ return self.GetData("process/couponRange") - def creditQuality(self): """ This function retrieves the credit notation of the funds, index and category. - + Returns: dict credit notation @@ -348,9 +373,10 @@ def creditQuality(self): """ return self.GetData("portfolio/creditQuality") - def dataPoint(self, field, currency ='EUR'): + def dataPoint(self, field, currency="EUR"): """ - This function retrieves infos about funds such as name, performance, risk metrics... + This function retrieves infos about funds such as name, + performance, risk metrics... Args: field (str or list) : field to find @@ -364,15 +390,17 @@ def dataPoint(self, field, currency ='EUR'): >>> Funds("myria", "fr").dataPoint('SharpeM36') """ - return search_funds(self.code, field,self.country,10,currency, proxies=self.proxies) + return search_funds( + self.code, field, self.country, 10, currency, proxies=self.proxies + ) - def distribution(self, period = "annual"): + def distribution(self, period="annual"): """ This function retrieves the coupon distributed by the funds. Args: period (str) : annual or latest - + Raises: ValueError whenever the pariod parameter is not annual or latest @@ -381,18 +409,18 @@ def distribution(self, period = "annual"): Example: >>> Funds("rmagx", "us").distribution("annual") - - """ + """ period_choice = ["annual", "latest"] if period not in period_choice: - raise ValueError(f'period parameter can only take one of the values: {", ".join(period_choice)}') + raise ValueError( + f"""period parameter can only take one of + the values: {", ".join(period_choice)}""" + ) return self.GetData(f"distribution/{period}") - - def CumulativePerformance(self, cat): """ This function retrieves the cumulative performance of funds, index and category. @@ -409,36 +437,45 @@ def CumulativePerformance(self, cat): >>> Funds("myria", "fr").CumulativePerformance("category") """ - no_site_error(self.code,self.name,self.country,self.site) - cat_row = {'funds' : 2,'category' : 3, 'index' : 4} + no_site_error(self.code, self.name, self.country, self.site) + cat_row = {"funds": 2, "category": 3, "index": 4} if cat not in cat_row: - raise ValueError(f"cat parameter must take one of the following value : { ', '.join(cat_row.keys())}") + raise ValueError( + f"""cat parameter must take + one of the following value : { ', '.join(cat_row.keys())}""" + ) result = {} - #headers random agent - headers = {'user-agent' : random_user_agent()} - #page 1 - performance + # headers random agent + headers = {"user-agent": random_user_agent()} + # page 1 - performance url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}&tab=1" - + response = requests.get(url, headers=headers, proxies=self.proxies) - not_200_response(url,response) - soup = BeautifulSoup(response.text, 'html.parser') - cumulative_performance_date = soup.find(id='returnsTrailingDiv').find('td', {"class": "titleBarNote"}).text - result['cumulative_performance_date'] = cumulative_performance_date - #days - regex = re.compile('.*label') - label_list = soup.find(id='returnsTrailingDiv').find_all('td', {"class": regex}) - - #cumulative performance cat - regex = re.compile(f'.*col{str(cat_row[cat])} value number') - value_list = soup.find(id='returnsTrailingDiv').find_all('td', {"class": regex}) - #loop on label + not_200_response(url, response) + soup = BeautifulSoup(response.text, "html.parser") + cumulative_performance_date = ( + soup.find(id="returnsTrailingDiv") + .find("td", {"class": "titleBarNote"}) + .text + ) + result["cumulative_performance_date"] = cumulative_performance_date + # days + regex = re.compile(".*label") + label_list = soup.find(id="returnsTrailingDiv").find_all("td", {"class": regex}) + + # cumulative performance cat + regex = re.compile(f".*col{str(cat_row[cat])} value number") + value_list = soup.find(id="returnsTrailingDiv").find_all("td", {"class": regex}) + # loop on label for i in range(0, len(label_list)): - #label + # label label = label_list[i].text - #perf funds - result[f'{cat}_cumulative_performance_{label}'] = re.sub('[^0-9,-\.]','',value_list[i].text) - + # perf funds + result[f"{cat}_cumulative_performance_{label}"] = re.sub( + "[^0-9,-\.]", "", value_list[i].text + ) + return result def equityStyle(self): @@ -453,7 +490,7 @@ def equityStyle(self): """ return self.GetData("process/stockStyle/v2") - + def equityStyleBoxHistory(self): """ This function retrieves the equity style history of the funds @@ -495,7 +532,7 @@ def factorProfile(self): return self.GetData("factorProfile") def feeLevel(self): - """ + """ This function retrieves the fees of the fund compare to its category. Returns: @@ -506,9 +543,9 @@ def feeLevel(self): """ return self.GetData("price/feeLevel") - - def feeMifid(self,currency="EUR"): - """ + + def feeMifid(self, currency="EUR"): + """ This function retrieves the fees of the fund. Returns: @@ -518,11 +555,10 @@ def feeMifid(self,currency="EUR"): >>> Funds("myria", "fr").feeMifid() """ - return self.ltData("Mifid",currency=currency) - + return self.ltData("Mifid", currency=currency) def fees(self): - """ + """ This function retrieves the fees of the fund (by scraping pages); Returns: @@ -532,28 +568,34 @@ def fees(self): >>> Funds("myria", "fr").fees() """ - no_site_error(self.code,self.name,self.country,self.site) + no_site_error(self.code, self.name, self.country, self.site) result = {} - #headers random agent - headers = {'user-agent' : random_user_agent()} + # headers random agent + headers = {"user-agent": random_user_agent()} url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}&tab=5" response = requests.get(url, headers=headers, proxies=self.proxies) - not_200_response(url,response) - soup = BeautifulSoup(response.text, 'html.parser') - if soup.find(id='managementFeesDiv') == None: + not_200_response(url, response) + soup = BeautifulSoup(response.text, "html.parser") + if soup.find(id="managementFeesDiv") == None: return {} - #label - label_list =soup.find(id='managementFeesDiv').find_all('td', {"class": "label"}) - #value - value_list = soup.find(id='managementFeesDiv').find_all('td', {"class": "value number"}) + soup.find(id='managementFeesDiv').find_all('td', {"class": "value number jdpa"}) + # label + label_list = soup.find(id="managementFeesDiv").find_all( + "td", {"class": "label"} + ) + # value + value_list = soup.find(id="managementFeesDiv").find_all( + "td", {"class": "value number"} + ) + soup.find(id="managementFeesDiv").find_all( + "td", {"class": "value number jdpa"} + ) for i in range(0, len(value_list)): label = label_list[i].text - result[label] = re.sub('(\\n +)|(\\n)','',value_list[i].text) + result[label] = re.sub("(\\n +)|(\\n)", "", value_list[i].text) return result def financialMetrics(self): - """ + """ This function retrieves the final metrics of the funds and category. Returns: @@ -565,9 +607,8 @@ def financialMetrics(self): """ return self.GetData("process/financialMetrics") - def fixedIncomeStyle(self): - """ + """ This function retrieves the fixed income style of the funds and category. Returns: @@ -581,7 +622,7 @@ def fixedIncomeStyle(self): return self.GetData("process/fixedIncomeStyle") def fixedincomeStyleBoxHistory(self): - """ + """ This function retrieves the fixed income style history of the funds. Returns: @@ -594,20 +635,20 @@ def fixedincomeStyleBoxHistory(self): return self.GetData("process/fixedincomeStyleBoxHistory") def fundsAnnualPerformance(self): - """ + """ This function retrieves the annual performance of the funds. Returns: - dict funds annual performance + dict funds annual performance Examples: >>> Funds("myria", "fr").fundsAnnualPerformance() """ - return self.AnnualPerformance('funds') + return self.AnnualPerformance("funds") def fundsAnnualRank(self): - """ + """ This function retrieves the annual rank of the funds in percentile. Returns: @@ -617,10 +658,10 @@ def fundsAnnualRank(self): >>> Funds("myria", "fr").fundsAnnualRank() """ - return self.AnnualPerformance('rank') + return self.AnnualPerformance("rank") def fundsCumulativePerformance(self): - """ + """ This function retrieves the cumulative performance of the funds. Returns: @@ -630,10 +671,10 @@ def fundsCumulativePerformance(self): >>> Funds("myria", "fr").fundsCumulativePerformance() """ - return self.CumulativePerformance('funds') + return self.CumulativePerformance("funds") def fundsQuarterlyPerformance(self): - """ + """ This function retrieves the quarterly performance of the funds. Returns: @@ -643,48 +684,62 @@ def fundsQuarterlyPerformance(self): >>> Funds("myria", "fr").fundsCumulativePerformance() """ - no_site_error(self.code,self.name,self.country,self.site) + no_site_error(self.code, self.name, self.country, self.site) result = {} - #headers random agent - headers = {'user-agent' : random_user_agent()} - #page 1 - performance + # headers random agent + headers = {"user-agent": random_user_agent()} + # page 1 - performance url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}&tab=1" - + response = requests.get(url, headers=headers, proxies=self.proxies) - not_200_response(url,response) - soup = BeautifulSoup(response.text, 'html.parser') - quarterly_performance_date = soup.find(id='returnsTrailingDiv').find('td', {"class": "titleBarNote"}).text - result['quarterly_performance_date'] = quarterly_performance_date - - #quarter label - regex = re.compile('.*heading number') - quarter_list = soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex}) - #year label - regex = re.compile('.*label') - year_list =soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex}) - #1st Quarter - regex = re.compile('.*col2 value number') - quarter_1_list = soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex}) - #2nd Quarter - regex = re.compile('.*col3 value number') - quarter_2_list = soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex}) - #3rd Quarter - regex = re.compile('.*col4 value number') - quarter_3_list = soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex}) - #4th Quarter - regex = re.compile('.*col5 value number') - quarter_4_list = soup.find(id='returnsQuarterlyDiv').find_all('td', {"class": regex}) - #loop on year + not_200_response(url, response) + soup = BeautifulSoup(response.text, "html.parser") + quarterly_performance_date = ( + soup.find(id="returnsTrailingDiv") + .find("td", {"class": "titleBarNote"}) + .text + ) + result["quarterly_performance_date"] = quarterly_performance_date + + # quarter label + regex = re.compile(".*heading number") + quarter_list = soup.find(id="returnsQuarterlyDiv").find_all( + "td", {"class": regex} + ) + # year label + regex = re.compile(".*label") + year_list = soup.find(id="returnsQuarterlyDiv").find_all("td", {"class": regex}) + # 1st Quarter + regex = re.compile(".*col2 value number") + quarter_1_list = soup.find(id="returnsQuarterlyDiv").find_all( + "td", {"class": regex} + ) + # 2nd Quarter + regex = re.compile(".*col3 value number") + quarter_2_list = soup.find(id="returnsQuarterlyDiv").find_all( + "td", {"class": regex} + ) + # 3rd Quarter + regex = re.compile(".*col4 value number") + quarter_3_list = soup.find(id="returnsQuarterlyDiv").find_all( + "td", {"class": regex} + ) + # 4th Quarter + regex = re.compile(".*col5 value number") + quarter_4_list = soup.find(id="returnsQuarterlyDiv").find_all( + "td", {"class": regex} + ) + # loop on year for i in range(0, len(year_list)): - label = 'performance_%s_' % (year_list[i].text) - result[label + 'quarter_1'] = quarter_1_list[i].text - result[label + 'quarter_2'] = quarter_2_list[i].text - result[label + 'quarter_3'] = quarter_3_list[i].text - result[label + 'quarter_4'] = quarter_4_list[i].text + label = "performance_%s_" % (year_list[i].text) + result[label + "quarter_1"] = quarter_1_list[i].text + result[label + "quarter_2"] = quarter_2_list[i].text + result[label + "quarter_3"] = quarter_3_list[i].text + result[label + "quarter_4"] = quarter_4_list[i].text return result def graphData(self): - """ + """ This function retrieves historical data of the funds. Returns: @@ -692,7 +747,7 @@ def graphData(self): Examples: >>> Funds("myria", "fr").graphData() - + """ return self.GetData("parent/graphData") @@ -708,12 +763,10 @@ def historicalData(self): >>> Funds("myria", "fr").historicalData() """ - return self.GetData("performance/v3", url_suffixe='') - - + return self.GetData("performance/v3", url_suffixe="") def historicalExpenses(self): - """ + """ This function retrieves historical expenses of the funds. Returns: @@ -723,12 +776,12 @@ def historicalExpenses(self): >>> Funds("rmagx", "us").historicalExpenses() """ - if self.asset_type == 'etf': + if self.asset_type == "etf": return {} return self.GetData("price/historicalExpenses") - def holdings(self, holdingType: str = 'all'): - """ + def holdings(self, holdingType: str = "all"): + """ This function retrieves holdings of the funds. Args: @@ -747,20 +800,32 @@ def holdings(self, holdingType: str = 'all'): >>> Funds("myria", "fr").holdings("other") """ - holdingType_to_holdingPage = {"all" : "all", "bond" : "boldHoldingPage","equity" : "equityHoldingPage", "other": "otherHoldingPage"} + holdingType_to_holdingPage = { + "all": "all", + "bond": "boldHoldingPage", + "equity": "equityHoldingPage", + "other": "otherHoldingPage", + } if holdingType not in holdingType_to_holdingPage: - raise ValueError(f'parameter holdingType must take one of the following value : {", ".join(holdingType_to_holdingPage.keys())} ') - + raise ValueError( + f'parameter holdingType must take one of the following value : {", ".join(holdingType_to_holdingPage.keys())} ' + ) + if holdingType == "all": - return pd.DataFrame(self.position()["equityHoldingPage"]["holdingList"] + - self.position()["boldHoldingPage"]["holdingList"] + self.position()["otherHoldingPage"]["holdingList"] ) + return pd.DataFrame( + self.position()["equityHoldingPage"]["holdingList"] + + self.position()["boldHoldingPage"]["holdingList"] + + self.position()["otherHoldingPage"]["holdingList"] + ) else: - return pd.DataFrame(self.position()[holdingType_to_holdingPage[holdingType]]["holdingList"]) + return pd.DataFrame( + self.position()[holdingType_to_holdingPage[holdingType]]["holdingList"] + ) def indexAnnualPerformance(self): """ This function retrieves the annual performance of the index. - + Returns: dict annual performance of the index @@ -769,12 +834,12 @@ def indexAnnualPerformance(self): >>> Funds("myria", "fr").indexAnnualPerformance() """ - return self.AnnualPerformance('index') + return self.AnnualPerformance("index") def indexCumulativePerformance(self): """ This function retrieves the cumulative performance of the index. - + Returns: dict cumulative performance of the index @@ -783,13 +848,13 @@ def indexCumulativePerformance(self): >>> Funds("myria", "fr").indexCumulativePerformance() """ - - return self.CumulativePerformance('index') + + return self.CumulativePerformance("index") def investmentStrategy(self): """ This function retrieves the investment strategy. - + Returns: dict investment strategy @@ -798,14 +863,14 @@ def investmentStrategy(self): """ bearer_token = token_investment_strategy() - #header with bearer token + # header with bearer token headers = { - 'authorization': f'Bearer {bearer_token}', - } - return self.GetData("morningstarTake/investmentStrategy",headers=headers) - - def investmentLookup(self,currency="EUR"): - """ + "authorization": f"Bearer {bearer_token}", + } + return self.GetData("morningstarTake/investmentStrategy", headers=headers) + + def investmentLookup(self, currency="EUR"): + """ This function gives details about fund investment. Returns: @@ -815,12 +880,12 @@ def investmentLookup(self,currency="EUR"): >>> Funds("myria", "fr").investmentLookup() """ - return self.ltData("investmentTypeLookup",currency=currency) - + return self.ltData("investmentTypeLookup", currency=currency) + def marketCapitalization(self): """ This function retrieves the marketCapitalization breakdown of the funds, category and index. - + Returns: dict market capitalization @@ -830,11 +895,10 @@ def marketCapitalization(self): """ return self.GetData("process/marketCap") - def maturitySchedule(self): """ This function retrieves the maturity breakdown of the funds and category. - + Returns: dict maturity @@ -844,11 +908,10 @@ def maturitySchedule(self): """ return self.GetData("process/maturitySchedule") - - def maxDrawDown(self, year = 3): + def maxDrawDown(self, year=3): """ This function retrieves the max drawdown of the funds, index and category. - + Args: year (int) : period of calculation in year @@ -864,26 +927,29 @@ def maxDrawDown(self, year = 3): """ if not isinstance(year, int): - raise TypeError('year parameter should be an integer') + raise TypeError("year parameter should be an integer") - - return self.GetData("performance/marketVolatilityMeasure", params = {"year": year}) + return self.GetData( + "performance/marketVolatilityMeasure", params={"year": year} + ) def morningstarAnalyst(self): """ This function retrieves the raiting of MorningStar analyst. - + Returns: dict rating Examples: >>> Funds("rmagx", "us").morningstarAnalyst() - + """ return self.GetData("morningstarAnalyst") - def multiLevelFixedIncomeData(self, primary = "superEffectiveDuration", secondary = "superSector.weight"): + def multiLevelFixedIncomeData( + self, primary="superEffectiveDuration", secondary="superSector.weight" + ): """ This function retrieves the exposures of fixed income @@ -902,21 +968,38 @@ def multiLevelFixedIncomeData(self, primary = "superEffectiveDuration", secondar """ - primary_choice = ["superEffectiveDuration","superYieldToWorst", "creditQuality"] - secondary_choice = ["superSector.weight", "region.weight", "creditQuality.weight"] + primary_choice = [ + "superEffectiveDuration", + "superYieldToWorst", + "creditQuality", + ] + secondary_choice = [ + "superSector.weight", + "region.weight", + "creditQuality.weight", + ] if primary not in primary_choice: - raise ValueError(f'primary parameter can only take one of the values: {", ".join(primary_choice)}') - + raise ValueError( + f'primary parameter can only take one of the values: {", ".join(primary_choice)}' + ) + if secondary not in secondary_choice: - raise ValueError(f'secondary parameter can only take one of the values: {", ".join(secondary_choice)}') + raise ValueError( + f'secondary parameter can only take one of the values: {", ".join(secondary_choice)}' + ) if primary == "creditQuality" and secondary == "creditQuality.weight": - raise ValueError(f'primary and secondary parameters cannot be both credit quality') + raise ValueError( + f"primary and secondary parameters cannot be both credit quality" + ) - return self.GetData("multiLevelFixedIncomeData", params = {"primary": primary,"secondary": secondary}) + return self.GetData( + "multiLevelFixedIncomeData", + params={"primary": primary, "secondary": secondary}, + ) - def nav(self,start_date,end_date,frequency="daily"): + def nav(self, start_date, end_date, frequency="daily"): """ This function retrieves the NAV of the funds @@ -936,15 +1019,15 @@ def nav(self,start_date,end_date,frequency="daily"): """ - return self.TimeSeries(["nav","totalReturn"], - start_date=start_date,end_date=end_date,frequency=frequency) - - - - + return self.TimeSeries( + ["nav", "totalReturn"], + start_date=start_date, + end_date=end_date, + frequency=frequency, + ) def objectiveInvestment(self): - """ + """ This function retrieves the objective of investment of the fund (by scraping pages); Returns: @@ -954,22 +1037,26 @@ def objectiveInvestment(self): >>> Funds("myria", "fr").objectiveInvestment() """ - - no_site_error(self.code,self.name,self.country,self.site) - #headers random agent - headers = {'user-agent' : random_user_agent()} - #Page 1 - overview - #url page overview + + no_site_error(self.code, self.name, self.country, self.site) + # headers random agent + headers = {"user-agent": random_user_agent()} + # Page 1 - overview + # url page overview url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}" - #get HTML page overview + # get HTML page overview response = requests.get(url, headers=headers, proxies=self.proxies) - #if page not found - - not_200_response(url,response) - #html page as soup - soup = BeautifulSoup(response.text, 'html.parser') - #investment objective funds - return soup.find(id='overviewObjectiveDiv').find('td', {"class": "value text"}).text + # if page not found + + not_200_response(url, response) + # html page as soup + soup = BeautifulSoup(response.text, "html.parser") + # investment objective funds + return ( + soup.find(id="overviewObjectiveDiv") + .find("td", {"class": "value text"}) + .text + ) def otherFee(self): """ @@ -980,15 +1067,14 @@ def otherFee(self): Examples: >>> Funds("American Century Foc Dynmc Gr ETF").otherFee() - + {'expenseWaiver': False, 'expenseReimbursement': None, 'expirationDate': None, 'expenseWaivers': None} - + """ return self.GetData("price/otherFee") - def ownershipZone(self): - """ + """ This function retrieves ownershipZone of the funds, index and category. Returns: @@ -1002,10 +1088,9 @@ def ownershipZone(self): return self.GetData("process/ownershipZone") def parentMstarRating(self): - """ This function retrieves the raiting of parent by MorningStar analyst. - + Returns: list of dict rating @@ -1019,7 +1104,7 @@ def parentMstarRating(self): def parentSummary(self): """ This function retrieves info about the parent. - + Returns: dict parent info @@ -1032,7 +1117,7 @@ def parentSummary(self): def people(self): """ This function retrieves info about people who works in the company. - + Returns: dict people info @@ -1045,21 +1130,23 @@ def people(self): def position(self): """ This function retrieves the hodings of the funds. - + Returns: dict holdings Examples: >>> Funds("myria", "fr").position() - + """ - return self.GetData("portfolio/holding/v2", params = {"premiumNum" : 10000, "freeNum" : 10000}) + return self.GetData( + "portfolio/holding/v2", params={"premiumNum": 10000, "freeNum": 10000} + ) def proxyVotingManagement(self): """ This function retrieves the vote of management. - + Returns: dict vote @@ -1068,12 +1155,11 @@ def proxyVotingManagement(self): """ return self.GetData("people/proxyVoting/management") - def proxyVotingShareHolder(self): """ This function retrieves the vote of shareholders. - + Returns: dict vote @@ -1086,7 +1172,7 @@ def proxyVotingShareHolder(self): def productInvolvement(self): """ This function retrieves the involvement of the funds - + Returns: dict involvement @@ -1097,13 +1183,12 @@ def productInvolvement(self): return self.GetData("esg/productInvolvement") - def referenceIndex(self, index): """ This function retrieves the name of the category or the benchmark Args: - index (str) : possible values are benchmark, category + index (str) : possible values are benchmark, category Returns: str category or benchmark @@ -1116,32 +1201,36 @@ def referenceIndex(self, index): >>> Funds("myria", "fr").referenceIndex("benchmark") """ - no_site_error(self.code,self.name,self.country,self.site) + no_site_error(self.code, self.name, self.country, self.site) - index_row = {'benchmark' : 0,'category' : 1} + index_row = {"benchmark": 0, "category": 1} if index not in index_row: - raise ValueError(f"index parameter must take one of the following value : { ', '.join(index_row.keys())}") - - #headers random agent - headers = {'user-agent' : random_user_agent()} - #Page 1 - overview - #url page overview + raise ValueError( + f"index parameter must take one of the following value : { ', '.join(index_row.keys())}" + ) + + # headers random agent + headers = {"user-agent": random_user_agent()} + # Page 1 - overview + # url page overview url = f"{self.site}funds/snapshot/snapshot.aspx?id={self.code}" - #get HTML page overview + # get HTML page overview response = requests.get(url, headers=headers, proxies=self.proxies) - #if page not found - - not_200_response(url,response) + # if page not found - #html page as soup - soup = BeautifulSoup(response.text, 'html.parser') - benchmark_soup = soup.find(id='overviewBenchmarkDiv2Cols').find_all('td', {"class": "value text"}) + not_200_response(url, response) + + # html page as soup + soup = BeautifulSoup(response.text, "html.parser") + benchmark_soup = soup.find(id="overviewBenchmarkDiv2Cols").find_all( + "td", {"class": "value text"} + ) return benchmark_soup[index_row[index]].text def regionalSector(self): """ This function retrieves the breakdown of the funds, category and index by region - + Returns: dict regional breakdown @@ -1154,7 +1243,7 @@ def regionalSector(self): def regionalSectorIncludeCountries(self): """ This function retrieves the breakdown of the funds, category and index by region and country - + Returns: dict regional breakdown @@ -1164,12 +1253,10 @@ def regionalSectorIncludeCountries(self): """ return self.GetData("portfolio/regionalSectorIncludeCountries") - - def riskReturnScatterplot(self): """ This function retrieves the return and standard deviation of the funds and category - + Returns: dict risk return @@ -1182,7 +1269,7 @@ def riskReturnScatterplot(self): def riskReturnSummary(self): """ This function retrieves the return and risk summary of the funds compare to the category - + Returns: dict risk return @@ -1215,16 +1302,16 @@ def salesFees(self): Examples: >>> Funds("myria", "fr").salesFees() - + """ - if self.asset_type == 'etf': + if self.asset_type == "etf": return {} return self.GetData("price/salesFees") def sector(self): """ This function retrieves the sector breakdown of the funds, category and index - + Returns: dict sector breakdown @@ -1233,9 +1320,9 @@ def sector(self): """ return self.GetData("portfolio/v2/sector") - - def snapshot(self,currency="EUR"): - """ + + def snapshot(self, currency="EUR"): + """ This function returns a snapshot of the fund and asset manager. Returns: @@ -1245,26 +1332,26 @@ def snapshot(self,currency="EUR"): >>> Funds("myria", "fr").snapshot() """ - return self.ltData("MFsnapshot",currency=currency) + return self.ltData("MFsnapshot", currency=currency) def starRatingFundAsc(self): """ This function retrieves the MorningStar rating of the funds of the company by ascending order - + Returns: dict rating Examples: >>> Funds("myria", "fr").starRatingFundAsc() - + """ - + return self.GetData("parent/mstarRating/StarRatingFundAsc") def starRatingFundDesc(self): """ This function retrieves the MorningStar rating of the funds of the company by descending order - + Returns: dict rating @@ -1272,11 +1359,11 @@ def starRatingFundDesc(self): >>> Funds("myria", "fr").starRatingFundDesc() """ - + return self.GetData("parent/mstarRating/StarRatingFundDesc") - def sustainability(self,currency="EUR"): - """ + def sustainability(self, currency="EUR"): + """ This function retrieves the sustainability data of the fund. Returns: @@ -1286,8 +1373,8 @@ def sustainability(self,currency="EUR"): >>> Funds("myria", "fr").sustainability() """ - return self.ltData("sustainability",currency=currency) - + return self.ltData("sustainability", currency=currency) + def taxes(self): """ This function retrieves the other fee of the etf @@ -1301,13 +1388,13 @@ def taxes(self): """ return self.GetData("price/taxes") - def trailingReturn(self, duration ='daily'): + def trailingReturn(self, duration="daily"): """ This function retrieves the trailing return of the funds of the company. Args: duration (str) : frequency of return can be daily, monthly or quarterly - + Returns: dict trailing return @@ -1321,7 +1408,8 @@ def trailingReturn(self, duration ='daily'): duration_choice = ["daily", "monthly", "quarterly"] if duration not in duration_choice: - raise ValueError(f'duration parameter can only take one of the values: {", ".join(duration_choice)}') - + raise ValueError( + f'duration parameter can only take one of the values: {", ".join(duration_choice)}' + ) - return self.GetData("trailingReturn/v2",{"duration" : duration}) \ No newline at end of file + return self.GetData("trailingReturn/v2", {"duration": duration}) diff --git a/mstarpy/search.py b/mstarpy/search.py index 93e0826..4c049b9 100644 --- a/mstarpy/search.py +++ b/mstarpy/search.py @@ -6,440 +6,463 @@ from .utils import ASSET_TYPE, EXCHANGE, FIELDS, FILTER_FUND, FILTER_STOCK, SITE from .error import not_200_response -def filter_universe(field = FILTER_FUND, proxies = {}): - """ - This function will use the screener of morningstar.co.uk to find the possible filters and their values. - - Args: - - field (str | list) : field to find - - Returns: - dict of filter - {'LargestRegion': ['', 'RE_AfricaDeveloped', 'RE_AfricaEmerging', 'RE_Asia4TigersEmerging', 'RE_Asiaex4TigersEmerging', 'RE_Australasia', 'RE_Canada', 'RE_CentralandEasternEurope', 'RE_CentralandLatinAmericaEmerging', 'RE_Japan', 'RE_UnitedKingdom', 'RE_UnitedStates', 'RE_WesternEuropeEuro', 'RE_WesternEuropeNonEuroexUK'], 'SustainabilityRank': ['1', '2', '3', '4', '5']} - - Examples: - >>> filter_universe(['LargestRegion','SustainabilityRank']) - >>> filter_universe('FeeLevel') - >>> filter_universe(FILTER_STOCK) - - """ - - if not isinstance(field, (str, list)): - raise TypeError('field parameter should be a string or a list') - - if 'StarRatingM255' in field: - raise ValueError('StarRatingM255 cannot be a field') - - if isinstance(field, list): - filterDataPoints = '|'.join(field) - else: - filterDataPoints = field - - if not isinstance(proxies, dict): - raise TypeError('proxies parameter should be dict') - - params = { - 'outputType' : 'json', - 'filterDataPoints' : filterDataPoints, - } - - result = general_search(params, proxies=proxies) - - all_filter ={} - - if "filters" not in result: - return all_filter - if not result['filters']: - return all_filter - for r in result['filters'][0]: - all_filter[list(r)[0]] = r[list(r)[0]] - - return all_filter +def filter_universe(field=FILTER_FUND, proxies={}): + """ + This function will use the screener of morningstar.co.uk to find the possible filters and their values. + + Args: + + field (str | list) : field to find + + Returns: + dict of filter + {'LargestRegion': ['', 'RE_AfricaDeveloped', 'RE_AfricaEmerging', 'RE_Asia4TigersEmerging', 'RE_Asiaex4TigersEmerging', 'RE_Australasia', 'RE_Canada', 'RE_CentralandEasternEurope', 'RE_CentralandLatinAmericaEmerging', 'RE_Japan', 'RE_UnitedKingdom', 'RE_UnitedStates', 'RE_WesternEuropeEuro', 'RE_WesternEuropeNonEuroexUK'], 'SustainabilityRank': ['1', '2', '3', '4', '5']} + + Examples: + >>> filter_universe(['LargestRegion','SustainabilityRank']) + >>> filter_universe('FeeLevel') + >>> filter_universe(FILTER_STOCK) + + """ + + if not isinstance(field, (str, list)): + raise TypeError("field parameter should be a string or a list") + + if "StarRatingM255" in field: + raise ValueError("StarRatingM255 cannot be a field") + + if isinstance(field, list): + filterDataPoints = "|".join(field) + else: + filterDataPoints = field + + if not isinstance(proxies, dict): + raise TypeError("proxies parameter should be dict") + + params = { + "outputType": "json", + "filterDataPoints": filterDataPoints, + } + + result = general_search(params, proxies=proxies) + + all_filter = {} + + if "filters" not in result: + return all_filter + if not result["filters"]: + return all_filter + + for r in result["filters"][0]: + all_filter[list(r)[0]] = r[list(r)[0]] + + return all_filter def general_search(params, proxies={}): - """ - This function will use the screener of morningstar.co.uk to find informations about funds or classification + """ + This function will use the screener of morningstar.co.uk to find informations about funds or classification - Args: - params (dict) : paramaters of the request - proxies (dict) : set the proxy if needed , example : {"http": "http://host:port","https": "https://host:port"} - - Returns: - list of information - - Examples: - >>> general_search(params = { - 'page' : 1, - 'pageSize' : 10, - 'sortOrder' : 'LegalName asc', - 'outputType' : 'json', - 'version' : 1, - 'universeIds' : 'FOFRA$$ALL', - 'currencyId': 'EUR', - 'securityDataPoints' : ['FundTNAV','GBRReturnD1'], - 'term' : 'myria', - }) - - - """ - if not isinstance(params, dict): - raise TypeError('params parameter should be dict') - - if not isinstance(proxies, dict): - raise TypeError('proxies parameter should be dict') - #url - url = "https://tools.morningstar.co.uk/api/rest.svc/klr5zyak8x/security/screener" - #headers - headers = { - 'user-agent': random_user_agent(), - } - - response = requests.get(url,params=params, headers=headers,proxies=proxies) - - not_200_response(url, response) - - return response.json() - - -def search_field(pattern = ''): - """ - This function retrieves the possible fields for the function dataPoint - - Args: - pattern (str) : text contained in the field - - Returns: - list of possible fields for the method dataPoint - - Example: - >>> search_field('feE') - >>> search_field('reTurn') - - """ - - regex = re.compile(f'(?i){pattern}') - filtered_list = list(filter(lambda field : regex.search(field),FIELDS)) - print(f"possible fields for function dataPoint can be : {', '.join(filtered_list)}") - - return filtered_list - - -def search_filter(pattern = '', asset_type ='fund'): - """ - This function retrieves the possible filters for the parameter filters of the function search_funds - - - Args: - pattern (str) : text contained in the filter - - Returns: - list of possible filters - - Example: - >>> search_filter('RetUrn') - >>> search_filter('id') - - """ - if not isinstance(pattern, (str)): - raise TypeError('pattern parameter should be a string') - - if not isinstance(asset_type, (str)): - raise TypeError('asset_type parameter should be a string') - - if asset_type not in ASSET_TYPE: - raise TypeError(f"asset_type parameter can only take one of the values : {','.join(ASSET_TYPE)}") - - if asset_type == 'stock': - filter_type = FILTER_STOCK - else: - filter_type = FILTER_FUND - - regex = re.compile(f'(?i){pattern}') - filtered_list = list(filter(lambda field : regex.search(field),filter_type)) - if asset_type == 'stock': - print(f"possible keys for the parameter filters of the method search_stock can be : {', '.join(filtered_list)}") - else: - print(f"possible keys for the parameter filters of the method seach_funds can be : {', '.join(filtered_list)}") - - return filtered_list - -def search_funds(term, field, country = "", pageSize=10, currency ='EUR', filters={}, proxies = {}): - """ - This function will use the screener of morningstar.co.uk to find funds which include the term. - - Args: - term (str): text to find a funds can be a the name, part of a name or the isin of the funds - field (str | list) : field to find - country (str) : text for code ISO 3166-1 alpha-2 of country - pageSize (int): number of funds to return - currency (str) : currency in 3 letters - filters (dict) : filter funds, use the method filter_universe to find the different possible filter keys and values - proxies (dict) : set the proxy if needed , example : {"http": "http://host:port","https": "https://host:port"} + Args: + params (dict) : paramaters of the request + proxies (dict) : set the proxy if needed , example : {"http": "http://host:port","https": "https://host:port"} + + Returns: + list of information + + Examples: + >>> general_search(params = { + 'page' : 1, + 'pageSize' : 10, + 'sortOrder' : 'LegalName asc', + 'outputType' : 'json', + 'version' : 1, + 'universeIds' : 'FOFRA$$ALL', + 'currencyId': 'EUR', + 'securityDataPoints' : ['FundTNAV','GBRReturnD1'], + 'term' : 'myria', + }) + + + """ + if not isinstance(params, dict): + raise TypeError("params parameter should be dict") + + if not isinstance(proxies, dict): + raise TypeError("proxies parameter should be dict") + # url + url = "https://tools.morningstar.co.uk/api/rest.svc/klr5zyak8x/security/screener" + # headers + headers = { + "user-agent": random_user_agent(), + } + + response = requests.get(url, params=params, headers=headers, proxies=proxies) + + not_200_response(url, response) + + return response.json() + + +def search_field(pattern=""): + """ + This function retrieves the possible fields for the function dataPoint + + Args: + pattern (str) : text contained in the field + + Returns: + list of possible fields for the method dataPoint + + Example: + >>> search_field('feE') + >>> search_field('reTurn') + + """ + + regex = re.compile(f"(?i){pattern}") + filtered_list = list(filter(lambda field: regex.search(field), FIELDS)) + print(f"possible fields for function dataPoint can be : {', '.join(filtered_list)}") + + return filtered_list - Returns: - list of dict with fund information - [{'SecId': 'F00000270E', 'TenforeId': '52.8.FR0010342600', 'LegalName': '21 Gestion Active'}, {'SecId': 'F000013BGI', 'TenforeId': '52.8.MT7000022612', 'LegalName': '24 Capital Management SICAV plc - 24 Global Currency Fund Share Class A USD Accumulation'}, {'SecId': 'F00000PZHI', 'TenforeId': '52.8.FR0011443225', 'LegalName': '29 Haussmann Actions Europe C'}, {'SecId': 'F0GBR06QS1', 'TenforeId': '52.8.FR0007057427', 'LegalName': '29 Haussmann Actions Europe D'}, {'SecId': 'F0000101BL', 'TenforeId': '52.8.FR0013266590', 'LegalName': '29 Haussmann Actions Europe - I'}, {'SecId': 'F00000JW7U', 'TenforeId': '52.8.LU0532306957', 'LegalName': '3F Generation Acc'}, {'SecId': 'F00000UDVR', 'TenforeId': '52.8.FR0011911189', 'LegalName': 'AAM Family Values E1'}, {'SecId': 'F00000UDVS', 'TenforeId': '52.8.FR0011911197', 'LegalName': 'AAM Family Values - I'}, {'SecId': 'F0GBR04RG5', 'TenforeId': '52.8.FR0007022025', 'LegalName': 'AAZ Capitalisation'}, {'SecId': 'F000000ITD', 'TenforeId': '52.8.FR0010361600', 'LegalName': 'AAZ Prestige Or'}] - - Examples: - >>> search_funds("Myria",['SecId','TenforeId','LegalName'],country="fr", pageSize=25) - >>> search_funds("FR0011399914", 'LegalName', country="fr", pageSize=25) - - """ - - if not isinstance(field, (str, list)): - raise TypeError('field parameter should be a string or a list') - - if not isinstance(country, str): - raise TypeError('country parameter should be a string') - - if country and not country.lower() in SITE.keys(): - raise ValueError(f'country parameter can only take one of the values: {", ".join(SITE.keys())}') - - if not isinstance(pageSize, int): - raise TypeError('pageSize parameter should be an integer') - - if not isinstance(currency, str): - raise TypeError('currency parameter should be a string') - - if not isinstance(filters, dict): - raise TypeError('filters parameter should be a dict') - - if not isinstance(proxies, dict): - raise TypeError('proxies parameter should be dict') - - - if isinstance(field, list): - securityDataPoints = '|'.join(field) - else: - securityDataPoints = field - #if country find iso - if country: - iso = SITE[country.lower()]["iso3"] - universeIds = f'FO{iso}$$ALL' - else: - universeIds = "" - - filter_list = [] - #loop on filter dict - for f in filters: - if f not in FILTER_FUND: - print(f"{f} is not a valid filter and will be ignored. You can find the possible filters with the method search_filter().") + +def search_filter(pattern="", asset_type="fund"): + """ + This function retrieves the possible filters for the parameter filters of the function search_funds + + + Args: + pattern (str) : text contained in the filter + + Returns: + list of possible filters + + Example: + >>> search_filter('RetUrn') + >>> search_filter('id') + + """ + if not isinstance(pattern, (str)): + raise TypeError("pattern parameter should be a string") + + if not isinstance(asset_type, (str)): + raise TypeError("asset_type parameter should be a string") + + if asset_type not in ASSET_TYPE: + raise TypeError( + f"asset_type parameter can only take one of the values : {','.join(ASSET_TYPE)}" + ) + + if asset_type == "stock": + filter_type = FILTER_STOCK else: - #if list, IN condition - if isinstance(filters[f], list): - filter_list.append(f'{f}:IN:{":".join(filters[f])}') - #if tuple, either, BTW, LT or GT condition - elif isinstance(filters[f], tuple): - if len(filters[f]) == 2: - if isinstance(filters[f][0], (int,float)): - filter_list.append(f'{f}:BTW:{filters[f][0]}:{filters[f][1]}') - elif filters[f][0] =="<": - filter_list.append(f'{f}:LT:{filters[f][1]}') - elif filters[f][0] ==">": - filter_list.append(f'{f}:GT:{filters[f][1]}') - #else IN condition - else: - filter_list.append(f'{f}:IN:{filters[f]}') - - params = { - 'page' : 1, - 'pageSize' : pageSize, - 'sortOrder' : 'LegalName asc', - 'outputType' : 'json', - 'version' : 1, - 'universeIds' : universeIds, - 'currencyId': currency, - 'securityDataPoints' : securityDataPoints, - 'term' : term, - 'filters' : '|'.join(filter_list), - } - - result = general_search(params, proxies=proxies) - - if result['rows']: - return result['rows'] - else: - print('0 fund found whith the term %s' % (term)) - return {} - -def search_stock(term,field,exchange, pageSize =10,currency ='EUR', filters={}, proxies={}): - """ - This function will use the screener of morningstar.co.uk to find stocks which include the term. - - Args: - term (str): text to find a funds can be a the name, part of a name or the isin of the funds - field (str | list) : field to find - exchange (str) : stock echange closed list (.utils EXCHANGE) - pageSize (int): number of funds to return - currency (str) : currency in 3 letters - filters (dict) : filter funds, use the method filter_universe to find the different possible filter keys and values - proxies (dict) : set the proxy if needed , example : {"http": "http://host:port","https": "https://host:port"} + filter_type = FILTER_FUND + + regex = re.compile(f"(?i){pattern}") + filtered_list = list(filter(lambda field: regex.search(field), filter_type)) + if asset_type == "stock": + print( + f"possible keys for the parameter filters of the method search_stock can be : {', '.join(filtered_list)}" + ) + else: + print( + f"possible keys for the parameter filters of the method seach_funds can be : {', '.join(filtered_list)}" + ) + + return filtered_list + - Returns: - list of dict with stocks information - [{'SecId': '0P0001OMLZ', 'TenforeId': '126.1.VCXB', 'LegalName': '10X Capital Venture Acquisition Corp III Ordinary Shares - - Class A'}, {'SecId': '0P0001O9WE', 'TenforeId': '126.1.VCXB/U', 'LegalName': '10X Capital Venture Acquisition Corp III Units (1 Ord Share Class A & 1/2 War)'}, {'SecId': '0P000184MI', 'TenforeId': '126.1.COE', 'LegalName': '51Talk无忧英语 ADR'}, {'SecId': '0P0001NAQE', 'TenforeId': '126.1.AKA', 'LegalName': 'a.k.a. Brands Holding Corp'}] - - Examples: - >>> search_stock("visa",['SecId','TenforeId','LegalName'],exchange="NYSE", pageSize=25) - >>> search_stock("FR0000125486", 'LegalName', exchange="PARIS", pageSize=10) - - """ +def search_funds( + term, field, country="", pageSize=10, currency="EUR", filters={}, proxies={} +): + """ + This function will use the screener of morningstar.co.uk to find funds which include the term. - if not isinstance(field, (str, list)): - raise TypeError('field parameter should be a string or a list') + Args: + term (str): text to find a funds can be a the name, part of a name or the isin of the funds + field (str | list) : field to find + country (str) : text for code ISO 3166-1 alpha-2 of country + pageSize (int): number of funds to return + currency (str) : currency in 3 letters + filters (dict) : filter funds, use the method filter_universe to find the different possible filter keys and values + proxies (dict) : set the proxy if needed , example : {"http": "http://host:port","https": "https://host:port"} - if not isinstance(exchange, str): - raise TypeError('exchange parameter should be a string') + Returns: + list of dict with fund information + [{'SecId': 'F00000270E', 'TenforeId': '52.8.FR0010342600', 'LegalName': '21 Gestion Active'}, {'SecId': 'F000013BGI', 'TenforeId': '52.8.MT7000022612', 'LegalName': '24 Capital Management SICAV plc - 24 Global Currency Fund Share Class A USD Accumulation'}, {'SecId': 'F00000PZHI', 'TenforeId': '52.8.FR0011443225', 'LegalName': '29 Haussmann Actions Europe C'}, {'SecId': 'F0GBR06QS1', 'TenforeId': '52.8.FR0007057427', 'LegalName': '29 Haussmann Actions Europe D'}, {'SecId': 'F0000101BL', 'TenforeId': '52.8.FR0013266590', 'LegalName': '29 Haussmann Actions Europe + I'}, {'SecId': 'F00000JW7U', 'TenforeId': '52.8.LU0532306957', 'LegalName': '3F Generation Acc'}, {'SecId': 'F00000UDVR', 'TenforeId': '52.8.FR0011911189', 'LegalName': 'AAM Family Values E1'}, {'SecId': 'F00000UDVS', 'TenforeId': '52.8.FR0011911197', 'LegalName': 'AAM Family Values + I'}, {'SecId': 'F0GBR04RG5', 'TenforeId': '52.8.FR0007022025', 'LegalName': 'AAZ Capitalisation'}, {'SecId': 'F000000ITD', 'TenforeId': '52.8.FR0010361600', 'LegalName': 'AAZ Prestige Or'}] - if not exchange.upper() in EXCHANGE.keys(): - raise ValueError(f'exchange parameter can only take one of the values : {", ".join(EXCHANGE.keys())}') + Examples: + >>> search_funds("Myria",['SecId','TenforeId','LegalName'],country="fr", pageSize=25) + >>> search_funds("FR0011399914", 'LegalName', country="fr", pageSize=25) - if not isinstance(pageSize, int): - raise TypeError('pageSize parameter should be an integer') + """ - if not isinstance(currency, str): - raise TypeError('currency parameter should be a string') + if not isinstance(field, (str, list)): + raise TypeError("field parameter should be a string or a list") - if not isinstance(filters, dict): - raise TypeError('filters parameter should be a dict') + if not isinstance(country, str): + raise TypeError("country parameter should be a string") - if not isinstance(proxies, dict): - raise TypeError('proxies parameter should be dict') + if country and not country.lower() in SITE.keys(): + raise ValueError( + f'country parameter can only take one of the values: {", ".join(SITE.keys())}' + ) + if not isinstance(pageSize, int): + raise TypeError("pageSize parameter should be an integer") - if isinstance(field, list): - securityDataPoints = '|'.join(field) - else: - securityDataPoints = field + if not isinstance(currency, str): + raise TypeError("currency parameter should be a string") - universeIds = EXCHANGE[exchange.upper()]["id"] + if not isinstance(filters, dict): + raise TypeError("filters parameter should be a dict") - filter_list = [] - #loop on filter dict - for f in filters: - if f not in FILTER_STOCK: - print(f"{f} is not a valid filter and will be ignored. You can find the possible filters with the method search_filter().") + if not isinstance(proxies, dict): + raise TypeError("proxies parameter should be dict") + + if isinstance(field, list): + securityDataPoints = "|".join(field) + else: + securityDataPoints = field + # if country find iso + if country: + iso = SITE[country.lower()]["iso3"] + universeIds = f"FO{iso}$$ALL" else: - #if list, IN condition - if isinstance(filters[f], list): - filter_list.append(f'{f}:IN:{":".join(filters[f])}') - #if tuple, either, BTW, LT or GT condition - elif isinstance(filters[f], tuple): - if len(filters[f]) == 2: - if isinstance(filters[f][0], (int,float)): - filter_list.append(f'{f}:BTW:{filters[f][0]}:{filters[f][1]}') - elif filters[f][0] =="<": - filter_list.append(f'{f}:LT:{filters[f][1]}') - elif filters[f][0] ==">": - filter_list.append(f'{f}:GT:{filters[f][1]}') - #else IN condition - else: - filter_list.append(f'{f}:IN:{filters[f]}') - - params = { - 'page' : 1, - 'pageSize' : pageSize, - 'sortOrder' : 'LegalName asc', - 'outputType' : 'json', - 'version' : 1, - 'universeIds' : universeIds, - 'currencyId': currency, - 'securityDataPoints' : securityDataPoints, - 'term' : term, - 'filters' : '|'.join(filter_list), - } - - result = general_search(params, proxies=proxies) - - if result['rows']: - return result['rows'] - else: - print('0 stock found whith the term %s' % (term)) - return {} + universeIds = "" + + filter_list = [] + # loop on filter dict + for f in filters: + if f not in FILTER_FUND: + print( + f"{f} is not a valid filter and will be ignored. You can find the possible filters with the method search_filter()." + ) + else: + # if list, IN condition + if isinstance(filters[f], list): + filter_list.append(f'{f}:IN:{":".join(filters[f])}') + # if tuple, either, BTW, LT or GT condition + elif isinstance(filters[f], tuple): + if len(filters[f]) == 2: + if isinstance(filters[f][0], (int, float)): + filter_list.append(f"{f}:BTW:{filters[f][0]}:{filters[f][1]}") + elif filters[f][0] == "<": + filter_list.append(f"{f}:LT:{filters[f][1]}") + elif filters[f][0] == ">": + filter_list.append(f"{f}:GT:{filters[f][1]}") + # else IN condition + else: + filter_list.append(f"{f}:IN:{filters[f]}") + + params = { + "page": 1, + "pageSize": pageSize, + "sortOrder": "LegalName asc", + "outputType": "json", + "version": 1, + "universeIds": universeIds, + "currencyId": currency, + "securityDataPoints": securityDataPoints, + "term": term, + "filters": "|".join(filter_list), + } + + result = general_search(params, proxies=proxies) + + if result["rows"]: + return result["rows"] + else: + print("0 fund found whith the term %s" % (term)) + return {} + + +def search_stock( + term, field, exchange, pageSize=10, currency="EUR", filters={}, proxies={} +): + """ + This function will use the screener of morningstar.co.uk to find stocks which include the term. + + Args: + term (str): text to find a funds can be a the name, part of a name or the isin of the funds + field (str | list) : field to find + exchange (str) : stock echange closed list (.utils EXCHANGE) + pageSize (int): number of funds to return + currency (str) : currency in 3 letters + filters (dict) : filter funds, use the method filter_universe to find the different possible filter keys and values + proxies (dict) : set the proxy if needed , example : {"http": "http://host:port","https": "https://host:port"} + + Returns: + list of dict with stocks information + [{'SecId': '0P0001OMLZ', 'TenforeId': '126.1.VCXB', 'LegalName': '10X Capital Venture Acquisition Corp III Ordinary Shares + - Class A'}, {'SecId': '0P0001O9WE', 'TenforeId': '126.1.VCXB/U', 'LegalName': '10X Capital Venture Acquisition Corp III Units (1 Ord Share Class A & 1/2 War)'}, {'SecId': '0P000184MI', 'TenforeId': '126.1.COE', 'LegalName': '51Talk无忧英语 ADR'}, {'SecId': '0P0001NAQE', 'TenforeId': '126.1.AKA', 'LegalName': 'a.k.a. Brands Holding Corp'}] + + Examples: + >>> search_stock("visa",['SecId','TenforeId','LegalName'],exchange="NYSE", pageSize=25) + >>> search_stock("FR0000125486", 'LegalName', exchange="PARIS", pageSize=10) + + """ + + if not isinstance(field, (str, list)): + raise TypeError("field parameter should be a string or a list") + + if not isinstance(exchange, str): + raise TypeError("exchange parameter should be a string") + + if not exchange.upper() in EXCHANGE.keys(): + raise ValueError( + f'exchange parameter can only take one of the values : {", ".join(EXCHANGE.keys())}' + ) + + if not isinstance(pageSize, int): + raise TypeError("pageSize parameter should be an integer") + + if not isinstance(currency, str): + raise TypeError("currency parameter should be a string") + + if not isinstance(filters, dict): + raise TypeError("filters parameter should be a dict") + + if not isinstance(proxies, dict): + raise TypeError("proxies parameter should be dict") + + if isinstance(field, list): + securityDataPoints = "|".join(field) + else: + securityDataPoints = field + + universeIds = EXCHANGE[exchange.upper()]["id"] + + filter_list = [] + # loop on filter dict + for f in filters: + if f not in FILTER_STOCK: + print( + f"{f} is not a valid filter and will be ignored. You can find the possible filters with the method search_filter()." + ) + else: + # if list, IN condition + if isinstance(filters[f], list): + filter_list.append(f'{f}:IN:{":".join(filters[f])}') + # if tuple, either, BTW, LT or GT condition + elif isinstance(filters[f], tuple): + if len(filters[f]) == 2: + if isinstance(filters[f][0], (int, float)): + filter_list.append(f"{f}:BTW:{filters[f][0]}:{filters[f][1]}") + elif filters[f][0] == "<": + filter_list.append(f"{f}:LT:{filters[f][1]}") + elif filters[f][0] == ">": + filter_list.append(f"{f}:GT:{filters[f][1]}") + # else IN condition + else: + filter_list.append(f"{f}:IN:{filters[f]}") + + params = { + "page": 1, + "pageSize": pageSize, + "sortOrder": "LegalName asc", + "outputType": "json", + "version": 1, + "universeIds": universeIds, + "currencyId": currency, + "securityDataPoints": securityDataPoints, + "term": term, + "filters": "|".join(filter_list), + } + + result = general_search(params, proxies=proxies) + + if result["rows"]: + return result["rows"] + else: + print("0 stock found whith the term %s" % (term)) + return {} + + def token_chart(proxies={}): - """ - This function will scrape the Bearer Token needed to access MS API chart data + """ + This function will scrape the Bearer Token needed to access MS API chart data - Args: - proxies (dict) : set the proxy if needed , example : {"http": "http://host:port","https": "https://host:port"} + Args: + proxies (dict) : set the proxy if needed , example : {"http": "http://host:port","https": "https://host:port"} - Returns: - str bearer token + Returns: + str bearer token - """ + """ - if not isinstance(proxies, dict): - raise TypeError('proxies parameter should be dict') + if not isinstance(proxies, dict): + raise TypeError("proxies parameter should be dict") - url = 'https://www.morningstar.com/funds/xnas/afozx/chart' + url = "https://www.morningstar.com/funds/xnas/afozx/chart" - headers = {'user-agent' : random_user_agent()} + headers = {"user-agent": random_user_agent()} - response = requests.get(url, headers=headers,proxies=proxies) + response = requests.get(url, headers=headers, proxies=proxies) - all_text = response.text - if all_text.find("token") ==-1: - return None + all_text = response.text + if all_text.find("token") == -1: + return None - token_start =all_text[all_text.find("token"):] - return token_start[7:token_start.find("}")-1] + token_start = all_text[all_text.find("token") :] + return token_start[7 : token_start.find("}") - 1] def token_fund_information(proxies={}): - """ - This function will scrape the Bearer Token needed to access MS API funds information + """ + This function will scrape the Bearer Token needed to access MS API funds information - Args: - proxies (dict) : set the proxy if needed , example : {"http": "http://host:port","https": "https://host:port"} + Args: + proxies (dict) : set the proxy if needed , example : {"http": "http://host:port","https": "https://host:port"} - Returns: - str bearer token + Returns: + str bearer token - """ - if not isinstance(proxies, dict): - raise TypeError('proxies parameter should be dict') + """ + if not isinstance(proxies, dict): + raise TypeError("proxies parameter should be dict") - url = 'https://www.morningstar.co.uk/Common/funds/snapshot/PortfolioSAL.aspx' + url = "https://www.morningstar.co.uk/Common/funds/snapshot/PortfolioSAL.aspx" - headers = {'user-agent' : random_user_agent()} + headers = {"user-agent": random_user_agent()} + response = requests.get(url, headers=headers, proxies=proxies) + soup = BeautifulSoup(response.text, "html.parser") + script = soup.find_all("script", {"type": "text/javascript"}) + bearerToken = ( + str(script).split("tokenMaaS:")[-1].split("}")[0].replace('"', "").strip() + ) + return bearerToken - response = requests.get(url, headers=headers,proxies=proxies) - soup = BeautifulSoup(response.text, 'html.parser') - script = soup.find_all('script', {'type':'text/javascript'}) - bearerToken = str(script).split('tokenMaaS:')[-1].split('}')[0].replace('"','').strip() - return bearerToken def token_investment_strategy(proxies={}): - """ - This function will scrape the Bearer Token needed to access the investment strategy + """ + This function will scrape the Bearer Token needed to access the investment strategy - Args: - proxies (dict) : set the proxy if needed , example : {"http": "http://host:port","https": "https://host:port"} + Args: + proxies (dict) : set the proxy if needed , example : {"http": "http://host:port","https": "https://host:port"} - Returns: - str bearer token + Returns: + str bearer token - """ - if not isinstance(proxies, dict): - raise TypeError('proxies parameter should be dict') + """ + if not isinstance(proxies, dict): + raise TypeError("proxies parameter should be dict") - url = 'https://www.morningstar.com.au/investments/security/ASX/VHY' + url = "https://www.morningstar.com.au/investments/security/ASX/VHY" - headers = {'user-agent' : random_user_agent()} + headers = {"user-agent": random_user_agent()} - response = requests.get(url, headers=headers, proxies = proxies) + response = requests.get(url, headers=headers, proxies=proxies) - all_text = response.text - if all_text.find("token") ==-1: - return None - start_flag =',"' - end_flag = '"www.morningstar.com.au"' - token_end = all_text[:all_text.find(end_flag)-3] - return token_end[token_end.rfind(start_flag)+2:] \ No newline at end of file + all_text = response.text + if all_text.find("token") == -1: + return None + start_flag = ',"' + end_flag = '"www.morningstar.com.au"' + token_end = all_text[: all_text.find(end_flag) - 3] + return token_end[token_end.rfind(start_flag) + 2 :] diff --git a/mstarpy/security.py b/mstarpy/security.py index 4bbc64a..05d32e6 100644 --- a/mstarpy/security.py +++ b/mstarpy/security.py @@ -7,9 +7,10 @@ from .utils import APIKEY, SITE, random_user_agent -#with Universe field, we can detect the asset class -#done find all stock echange and create a search_equity method -#add parameter exchange to Security class and search stocks if stock and exchange +# with Universe field, we can detect the asset class +# done find all stock echange and create a search_equity method +# add parameter exchange to Security class and search stocks if stock and exchange + class Security: """ @@ -32,49 +33,76 @@ class Security: """ - def __init__(self, term = None, asset_type: str ="", country: str = "", exchange: str = "", pageSize: int =1, itemRange: int = 0, filters: dict={}, proxies: dict ={}): + def __init__( + self, + term=None, + asset_type: str = "", + country: str = "", + exchange: str = "", + pageSize: int = 1, + itemRange: int = 0, + filters: dict = {}, + proxies: dict = {}, + ): if not isinstance(country, str): - raise TypeError('country parameter should be a string') + raise TypeError("country parameter should be a string") if country and not country.lower() in SITE.keys(): - raise ValueError(f'country parameter can only take one of the values: {", ".join(SITE.keys())}') + raise ValueError( + f'country parameter can only take one of the values: {", ".join(SITE.keys())}' + ) if not isinstance(pageSize, int): - raise TypeError('pageSize parameter should be an integer') + raise TypeError("pageSize parameter should be an integer") if not isinstance(itemRange, int): - raise TypeError('itemRange parameter should be an integer') + raise TypeError("itemRange parameter should be an integer") - if pageSize <= itemRange : - raise ValueError('itemRange parameter should be strictly inferior to pageSize parameter') + if pageSize <= itemRange: + raise ValueError( + "itemRange parameter should be strictly inferior to pageSize parameter" + ) if not isinstance(filters, dict): - raise TypeError('filters parameter should be dict') - + raise TypeError("filters parameter should be dict") + if not isinstance(proxies, dict): - raise TypeError('proxies parameter should be dict') + raise TypeError("proxies parameter should be dict") self.proxies = proxies if country: self.site = SITE[country.lower()]["site"] else: - self.site ="" - + self.site = "" self.country = country - + self.exchange = exchange - self.asset_type = 'security' + self.asset_type = "security" code_list = [] - + if exchange: - code_list = search_stock(term,['fundShareClassId','SecId','TenforeId','LegalName','Universe'],exchange=exchange,pageSize=pageSize,filters =filters, proxies=self.proxies) + code_list = search_stock( + term, + ["fundShareClassId", "SecId", "TenforeId", "LegalName", "Universe"], + exchange=exchange, + pageSize=pageSize, + filters=filters, + proxies=self.proxies, + ) else: - code_list = search_funds(term,['fundShareClassId','SecId','TenforeId','LegalName','Universe'], country, pageSize,filters =filters, proxies = self.proxies) + code_list = search_funds( + term, + ["fundShareClassId", "SecId", "TenforeId", "LegalName", "Universe"], + country, + pageSize, + filters=filters, + proxies=self.proxies, + ) if code_list: if itemRange < len(code_list): @@ -83,45 +111,51 @@ def __init__(self, term = None, asset_type: str ="", country: str = "", exchange if "TenforeId" in code_list[itemRange]: tenforeId = code_list[itemRange]["TenforeId"] regex = re.compile("[0-9]*\.[0-9]\.") - self.isin = regex.sub('',tenforeId) + self.isin = regex.sub("", tenforeId) else: self.isin = None - - - universe = code_list[itemRange]["Universe"] - - if universe[:2] == 'E0': - self.asset_type = 'stock' - elif universe[:2] == 'ET': - self.asset_type = 'etf' - elif universe[:2] == 'FO': - self.asset_type = 'fund' + universe = code_list[itemRange]["Universe"] - if universe[:2] == 'E0' and asset_type in ['etf', 'fund']: - raise ValueError(f'The security found with the term {term} is a stock and the parameter asset_type is equal to {asset_type}, the class Stock should be used with this security.') - - if universe[:2] in ['FO', 'ET'] and asset_type =='stock': - if universe[:2] == 'FO': - raise ValueError(f'The security found with the term {term} is a fund and the parameter asset_type is equal to {asset_type}, the class Fund should be used with this security.') + if universe[:2] == "E0": + self.asset_type = "stock" + elif universe[:2] == "ET": + self.asset_type = "etf" + elif universe[:2] == "FO": + self.asset_type = "fund" + + if universe[:2] == "E0" and asset_type in ["etf", "fund"]: + raise ValueError( + f"The security found with the term {term} is a stock and the parameter asset_type is equal to {asset_type}, the class Stock should be used with this security." + ) + + if universe[:2] in ["FO", "ET"] and asset_type == "stock": + if universe[:2] == "FO": + raise ValueError( + f"The security found with the term {term} is a fund and the parameter asset_type is equal to {asset_type}, the class Fund should be used with this security." + ) else: - raise ValueError(f'The security found with the term {term} is an ETF and the parameter asset_type is equal to {asset_type}, the class Fund should be used with this security.') - - + raise ValueError( + f"The security found with the term {term} is an ETF and the parameter asset_type is equal to {asset_type}, the class Fund should be used with this security." + ) else: - raise ValueError(f'Found only {len(code_list)} {self.asset_type} with the term {term}. The paramater itemRange must maximum equal to {len(code_list)-1}') + raise ValueError( + f"Found only {len(code_list)} {self.asset_type} with the term {term}. The paramater itemRange must maximum equal to {len(code_list)-1}" + ) else: if country: - raise ValueError(f'0 {self.asset_type} found with the term {term} and country {country}') + raise ValueError( + f"0 {self.asset_type} found with the term {term} and country {country}" + ) elif exchange: - raise ValueError(f'0 {self.asset_type} found with the term {term} and exchange {exchange}') + raise ValueError( + f"0 {self.asset_type} found with the term {term} and exchange {exchange}" + ) else: - raise ValueError(f'0 {self.asset_type} found with the term {term}') - - + raise ValueError(f"0 {self.asset_type} found with the term {term}") - def GetData(self,field,params={},headers={}, url_suffixe='data'): + def GetData(self, field, params={}, headers={}, url_suffixe="data"): """ Generic function to use MorningStar global api. @@ -143,37 +177,36 @@ def GetData(self,field,params={},headers={}, url_suffixe='data'): """ if not isinstance(field, str): - raise TypeError('field parameter should be a string') + raise TypeError("field parameter should be a string") if not isinstance(params, dict): - raise TypeError('params parameter should be a dict') - + raise TypeError("params parameter should be a dict") + if not isinstance(url_suffixe, str): - raise TypeError('url_suffixe parameter should be a string') + raise TypeError("url_suffixe parameter should be a string") - #url of API + # url of API url = f"""https://api-global.morningstar.com/sal-service/v1/{self.asset_type}/{field}/{self.code}""" if url_suffixe: url += f"""/{url_suffixe}""" - - #headers + # headers default_headers = { - "apikey" : APIKEY, + "apikey": APIKEY, } all_headers = default_headers | headers - - response = requests.get(url,params=params, headers=all_headers,proxies=self.proxies) + response = requests.get( + url, params=params, headers=all_headers, proxies=self.proxies + ) - - not_200_response(url,response) + not_200_response(url, response) return response.json() - - def ltData(self,field,currency="EUR"): + + def ltData(self, field, currency="EUR"): """ Generic function to use MorningStar lt api. @@ -192,107 +225,103 @@ def ltData(self,field,currency="EUR"): """ if not isinstance(field, str): - raise TypeError('field parameter should be a string') - - #url of API + raise TypeError("field parameter should be a string") + + # url of API url = f"""https://lt.morningstar.com/api/rest.svc/klr5zyak8x/security_details/{self.code}""" params = { "viewId": field, - "currencyId":currency, - "itype" :"msid", + "currencyId": currency, + "itype": "msid", "languageId": "en", - "responseViewFormat":"json", + "responseViewFormat": "json", } - response = requests.get(url,params=params,proxies=self.proxies) + response = requests.get(url, params=params, proxies=self.proxies) + not_200_response(url, response) - not_200_response(url,response) - - #responseis a list - response_list = response.json() + # responseis a list + response_list = response.json() if response_list: return response_list[0] else: return {} - - - - - def TimeSeries(self,field,start_date,end_date,frequency="daily"): + def TimeSeries(self, field, start_date, end_date, frequency="daily"): """ This function retrieves historical data of the specified fields - + Args: start_date (datetime) : start date to get history end_date (datetime) : end date to get history - frequency (str) : can be daily, weekly, monthly + frequency (str) : can be daily, weekly, monthly Returns: list of dict time series - + Examples: >>> Funds("RMAGX", "us").TimeSeries(["nav","totalReturn"],datetime.datetime.today()- datetime.timedelta(30),datetime.datetime.today()) Raises: TypeError: raised whenever the parameter type is not the type expected ValueError : raised whenever the parameter is not valid or no funds found - + """ - #error raised if field is not a string or a list + # error raised if field is not a string or a list if not isinstance(field, (str, list)): - raise TypeError('field parameter should be a string or a list') - - #error raised if start_date is note a datetime.date - if not isinstance(start_date,datetime.date): + raise TypeError("field parameter should be a string or a list") + + # error raised if start_date is note a datetime.date + if not isinstance(start_date, datetime.date): raise TypeError("start_date parameter should be a datetime.date") - #error raised if end_date is note a datetime.date - if not isinstance(end_date,datetime.date): + # error raised if end_date is note a datetime.date + if not isinstance(end_date, datetime.date): raise TypeError("end_date parameter should be a datetime.date") - #error if end_date < start_date + # error if end_date < start_date if end_date < start_date: raise ValueError("end_date must be more recent than start_date") - - - #error raised if frequency is not a string + + # error raised if frequency is not a string if not isinstance(frequency, str): - raise TypeError('frequency parameter should be a string') + raise TypeError("frequency parameter should be a string") - #dict of frequency - frequency_row = {'daily' : 'd','weekly' : 'w', 'monthly' : 'm'} + # dict of frequency + frequency_row = {"daily": "d", "weekly": "w", "monthly": "m"} - #raise an error if frequency is not daily, wekly or monthly + # raise an error if frequency is not daily, wekly or monthly if frequency not in frequency_row: - raise ValueError(f"frequency parameter must take one of the following value : { ', '.join(frequency_row.keys())}") - + raise ValueError( + f"frequency parameter must take one of the following value : { ', '.join(frequency_row.keys())}" + ) + if isinstance(field, list): - queryField = ','.join(field) + queryField = ",".join(field) else: queryField = field - - #bearer token + + # bearer token bearer_token = token_chart() - #url for nav - url =f"https://www.us-api.morningstar.com/QS-markets/chartservice/v2/timeseries?query={self.code}:{queryField}&frequency={frequency_row[frequency]}&startDate={start_date.strftime('%Y-%m-%d')}&endDate={end_date.strftime('%Y-%m-%d')}&trackMarketData=3.6.3&instid=DOTCOM" - #header with bearer token + # url for nav + url = f"https://www.us-api.morningstar.com/QS-markets/chartservice/v2/timeseries?query={self.code}:{queryField}&frequency={frequency_row[frequency]}&startDate={start_date.strftime('%Y-%m-%d')}&endDate={end_date.strftime('%Y-%m-%d')}&trackMarketData=3.6.3&instid=DOTCOM" + # header with bearer token headers = { - 'user-agent' : random_user_agent(), - 'authorization': f'Bearer {bearer_token}', - } - #response + "user-agent": random_user_agent(), + "authorization": f"Bearer {bearer_token}", + } + # response response = requests.get(url, headers=headers, proxies=self.proxies) - #manage response - not_200_response(url,response) - #result + # manage response + not_200_response(url, response) + # result result = response.json() - #return empty list if we don't get data + # return empty list if we don't get data if not result: return [] if "series" in result[0]: return result[0]["series"] - - return [] \ No newline at end of file + + return [] diff --git a/mstarpy/stock.py b/mstarpy/stock.py index bc66737..afaa3a7 100644 --- a/mstarpy/stock.py +++ b/mstarpy/stock.py @@ -20,11 +20,25 @@ class Stock(Security): """ - def __init__(self, term = None, exchange: str = "", pageSize : int =1, itemRange: int = 0, filters: dict = {}, proxies: dict = {}): - - super().__init__(term=term,asset_type='stock',exchange=exchange,pageSize=pageSize,itemRange=itemRange,filters=filters,proxies=proxies) - - + def __init__( + self, + term=None, + exchange: str = "", + pageSize: int = 1, + itemRange: int = 0, + filters: dict = {}, + proxies: dict = {}, + ): + + super().__init__( + term=term, + asset_type="stock", + exchange=exchange, + pageSize=pageSize, + itemRange=itemRange, + filters=filters, + proxies=proxies, + ) def analysisData(self): """ @@ -37,8 +51,8 @@ def analysisData(self): >>> Stock("visa", exchange="nyse").analysisData() """ - return self.GetData("morningstarTake/v3",url_suffixe='analysisData') - + return self.GetData("morningstarTake/v3", url_suffixe="analysisData") + def analysisReport(self): """ This function retrieves the analysis of the stock. @@ -50,9 +64,9 @@ def analysisReport(self): >>> Stock("visa", exchange="nyse").analysisReport() """ - return self.GetData("morningstarTake/v4",url_suffixe='analysisReport') - - def balanceSheet(self, period='annual', reportType='original'): + return self.GetData("morningstarTake/v4", url_suffixe="analysisReport") + + def balanceSheet(self, period="annual", reportType="original"): """ This function retrieves the balance sheet. @@ -69,8 +83,10 @@ def balanceSheet(self, period='annual', reportType='original'): """ - return self.financialStatement("balancesheet",period=period, reportType=reportType) - + return self.financialStatement( + "balancesheet", period=period, reportType=reportType + ) + def boardOfDirectors(self): """ This function retrieves information about the board of directors. @@ -80,11 +96,11 @@ def boardOfDirectors(self): Examples: >>> Stock("Alphabet Inc Class A").boardOfDirectors() - + """ return self.GetData("insiders/boardOfDirectors") - def cashFlow(self, period='annual', reportType='original'): + def cashFlow(self, period="annual", reportType="original"): """ This function retrieves the cash flow. @@ -101,8 +117,8 @@ def cashFlow(self, period='annual', reportType='original'): """ - return self.financialStatement("cashflow",period=period, reportType=reportType) - + return self.financialStatement("cashflow", period=period, reportType=reportType) + def dividends(self): """ This function retrieves the dividends of the stock. @@ -115,7 +131,7 @@ def dividends(self): """ return self.GetData("dividends/v4") - + def esgRisk(self): """ This function retrieves the esg risk of the stock. @@ -128,7 +144,7 @@ def esgRisk(self): """ return self.GetData("esgRisk") - + def financialHealth(self): """ This function retrieves the financial health of the stock. @@ -140,9 +156,11 @@ def financialHealth(self): >>> Stock("visa", exchange="nyse").financialHealth() """ - return self.GetData("keyStats/financialHealth", url_suffixe='') - - def financialStatement(self,statement ='summary', period='annual', reportType='original'): + return self.GetData("keyStats/financialHealth", url_suffixe="") + + def financialStatement( + self, statement="summary", period="annual", reportType="original" + ): """ This function retrieves the financial statement. @@ -152,7 +170,7 @@ def financialStatement(self,statement ='summary', period='annual', reportType='o reportType (str) : possible values are original, restated Returns: - dict with financial statement + dict with financial statement Examples: >>> Stock("visa", exchange="nyse").financialStatement('summary', 'quarterly', 'original') @@ -163,38 +181,55 @@ def financialStatement(self,statement ='summary', period='annual', reportType='o """ if not isinstance(statement, str): - raise TypeError('statement parameter should be a string') - + raise TypeError("statement parameter should be a string") + if not isinstance(period, str): - raise TypeError('period parameter should be a string') - + raise TypeError("period parameter should be a string") + if not isinstance(reportType, str): - raise TypeError('reportType parameter should be a string') - - statement_choice = {"balancesheet" : "balanceSheet", "cashflow" : "cashFlow", "incomestatement" : "incomeStatement", "summary" : "summary"} - - period_choice = {"annual" : "A", "quarterly" : "Q"} + raise TypeError("reportType parameter should be a string") + + statement_choice = { + "balancesheet": "balanceSheet", + "cashflow": "cashFlow", + "incomestatement": "incomeStatement", + "summary": "summary", + } - reportType_choice = {"original" : "A", "restated" : "R"} + period_choice = {"annual": "A", "quarterly": "Q"} + + reportType_choice = {"original": "A", "restated": "R"} if statement not in statement_choice: - raise ValueError(f"statement parameter must take one of the following value : { ', '.join(statement_choice)}") + raise ValueError( + f"statement parameter must take one of the following value : { ', '.join(statement_choice)}" + ) if period not in period_choice: - raise ValueError(f"period parameter must take one of the following value : { ', '.join(period_choice)}") + raise ValueError( + f"period parameter must take one of the following value : { ', '.join(period_choice)}" + ) if reportType not in reportType_choice: - raise ValueError(f"reportType parameter must take one of the following value : { ', '.join(reportType_choice.keys())}") - - params = {"reportType" : reportType_choice[reportType]} + raise ValueError( + f"reportType parameter must take one of the following value : { ', '.join(reportType_choice.keys())}" + ) + + params = {"reportType": reportType_choice[reportType]} if statement == "summary": - return self.GetData("newfinancials",params=params, url_suffixe=f"{period}/summary") - + return self.GetData( + "newfinancials", params=params, url_suffixe=f"{period}/summary" + ) + params["dataType"] = period_choice[period] - return self.GetData("newfinancials",params=params, url_suffixe=f"{statement_choice[statement]}/detail") - - def financialSummary(self, period='annual', reportType='original'): + return self.GetData( + "newfinancials", + params=params, + url_suffixe=f"{statement_choice[statement]}/detail", + ) + + def financialSummary(self, period="annual", reportType="original"): """ This function retrieves the financial statement summary. @@ -211,8 +246,8 @@ def financialSummary(self, period='annual', reportType='original'): """ - return self.financialStatement("summary",period=period, reportType=reportType) - + return self.financialStatement("summary", period=period, reportType=reportType) + def freeCashFlow(self): """ This function retrieves the free cash flow. @@ -222,11 +257,11 @@ def freeCashFlow(self): Examples: >>> Stock("visa", exchange="nyse").freeCashFlow() - + """ - return self.GetData("keyStats/cashFlow", url_suffixe='') - - def historical(self, start_date,end_date,frequency="daily"): + return self.GetData("keyStats/cashFlow", url_suffixe="") + + def historical(self, start_date, end_date, frequency="daily"): """ This function retrieves the historical price, volume and divide of the stock. @@ -242,10 +277,14 @@ def historical(self, start_date,end_date,frequency="daily"): >>> Stock("visa", exchange="nyse").history(datetime.datetime.today()- datetime.timedelta(30),datetime.datetime.today()) """ - return self.TimeSeries(["open","high","low","close", - "volume","previousClose","dividend"], - start_date=start_date,end_date=end_date,frequency=frequency) - def incomeStatement(self, period='annual', reportType='original'): + return self.TimeSeries( + ["open", "high", "low", "close", "volume", "previousClose", "dividend"], + start_date=start_date, + end_date=end_date, + frequency=frequency, + ) + + def incomeStatement(self, period="annual", reportType="original"): """ This function retrieves the income statement. @@ -261,9 +300,11 @@ def incomeStatement(self, period='annual', reportType='original'): >>> Stock("visa", exchange="nyse").incomeStatement('annual', 'restated') """ - - return self.financialStatement("incomestatement",period=period, reportType=reportType) - + + return self.financialStatement( + "incomestatement", period=period, reportType=reportType + ) + def institutionBuyers(self, top=20): """ This function retrieves the institutions which buy the stock. @@ -275,14 +316,16 @@ def institutionBuyers(self, top=20): Examples: >>> Stock("visa", exchange="nyse").institutionBuyers(top=50) - + """ - + if not isinstance(top, int): - raise TypeError('top parameter should be an integer') - - return self.GetData("ownership/v1", url_suffixe= f"Buyers/institution/{top}/data") - + raise TypeError("top parameter should be an integer") + + return self.GetData( + "ownership/v1", url_suffixe=f"Buyers/institution/{top}/data" + ) + def institutionConcentratedOwners(self, top=20): """ This function retrieves the institutions which are concentrated on the stock. @@ -296,12 +339,14 @@ def institutionConcentratedOwners(self, top=20): >>> Stock("visa", exchange="nyse").institutionConcentratedOwners(top=50) """ - + if not isinstance(top, int): - raise TypeError('top parameter should be an integer') - - return self.GetData("ownership/v1", url_suffixe= f"ConcentratedOwners/institution/{top}/data") - + raise TypeError("top parameter should be an integer") + + return self.GetData( + "ownership/v1", url_suffixe=f"ConcentratedOwners/institution/{top}/data" + ) + def institutionOwnership(self, top=20): """ This function retrieves the main institutions which own the stock. @@ -313,14 +358,16 @@ def institutionOwnership(self, top=20): Examples: >>> Stock("visa", exchange="nyse").institutionOwnership(top=50) - + """ - + if not isinstance(top, int): - raise TypeError('top parameter should be an integer') - - return self.GetData("ownership/v1", url_suffixe= f"OwnershipData/institution/{top}/data") - + raise TypeError("top parameter should be an integer") + + return self.GetData( + "ownership/v1", url_suffixe=f"OwnershipData/institution/{top}/data" + ) + def institutionSellers(self, top=20): """ This function retrieves the institutions which sell on the stock. @@ -332,13 +379,15 @@ def institutionSellers(self, top=20): Examples: >>> Stock("visa", exchange="nyse").institutionSellers(top=50) - + """ if not isinstance(top, int): - raise TypeError('top parameter should be an integer') - - return self.GetData("ownership/v1", url_suffixe= f"Sellers/institution/{top}/data") - + raise TypeError("top parameter should be an integer") + + return self.GetData( + "ownership/v1", url_suffixe=f"Sellers/institution/{top}/data" + ) + def keyExecutives(self): """ This function retrieves information oabout key excutives of the company. @@ -351,6 +400,7 @@ def keyExecutives(self): """ return self.GetData("insiders/keyExecutives") + def keyRatio(self): """ This function retrieves the key ratio of the stock. @@ -363,7 +413,7 @@ def keyRatio(self): """ return self.GetData("keyratios") - + def mutualFundBuyers(self, top=20): """ This function retrieves the mutual funds which buy the stock. @@ -377,12 +427,12 @@ def mutualFundBuyers(self, top=20): >>> Stock("visa", exchange="nyse").mutualFundBuyers(top=50) """ - + if not isinstance(top, int): - raise TypeError('top parameter should be an integer') - - return self.GetData("ownership/v1", url_suffixe= f"Buyers/mutualfund/{top}/data") - + raise TypeError("top parameter should be an integer") + + return self.GetData("ownership/v1", url_suffixe=f"Buyers/mutualfund/{top}/data") + def mutualFundConcentratedOwners(self, top=20): """ This function retrieves the mutual funds which are concentrated on the stock. @@ -394,14 +444,16 @@ def mutualFundConcentratedOwners(self, top=20): Examples: >>> Stock("visa", exchange="nyse").mutualFundConcentratedOwners(top=50) - + """ - + if not isinstance(top, int): - raise TypeError('top parameter should be an integer') - - return self.GetData("ownership/v1", url_suffixe= f"ConcentratedOwners/mutualfund/{top}/data") - + raise TypeError("top parameter should be an integer") + + return self.GetData( + "ownership/v1", url_suffixe=f"ConcentratedOwners/mutualfund/{top}/data" + ) + def mutualFundOwnership(self, top=20): """ This function retrieves the main mutual funds which own the stock. @@ -413,14 +465,16 @@ def mutualFundOwnership(self, top=20): Examples: >>> Stock("visa", exchange="nyse").mutualFundOwnership(top=50) - + """ - + if not isinstance(top, int): - raise TypeError('top parameter should be an integer') - - return self.GetData("ownership/v1", url_suffixe= f"OwnershipData/mutualfund/{top}/data") - + raise TypeError("top parameter should be an integer") + + return self.GetData( + "ownership/v1", url_suffixe=f"OwnershipData/mutualfund/{top}/data" + ) + def mutualFundSellers(self, top=20): """ This function retrieves the mutual funds which sell on the stock. @@ -432,14 +486,16 @@ def mutualFundSellers(self, top=20): Examples: >>> Stock("visa", exchange="nyse").mutualFundSellers(top=50) - + """ - + if not isinstance(top, int): - raise TypeError('top parameter should be an integer') - - return self.GetData("ownership/v1", url_suffixe= f"Sellers/mutualfund/{top}/data") - + raise TypeError("top parameter should be an integer") + + return self.GetData( + "ownership/v1", url_suffixe=f"Sellers/mutualfund/{top}/data" + ) + def operatingGrowth(self): """ This function retrieves the operating growth of the stock. @@ -451,8 +507,8 @@ def operatingGrowth(self): >>> Stock("visa", exchange="nyse").operatingGrowth() """ - return self.GetData("keyStats/growthTable", url_suffixe='') - + return self.GetData("keyStats/growthTable", url_suffixe="") + def operatingMargin(self): """ This function retrieves the operating margin of the stock. @@ -462,10 +518,10 @@ def operatingMargin(self): Examples: >>> Stock("visa", exchange="nyse").operatingMargin() - + """ - return self.GetData("keyStats/OperatingAndEfficiency", url_suffixe='') - + return self.GetData("keyStats/OperatingAndEfficiency", url_suffixe="") + def operatingPerformance(self): """ This function retrieves the operating performance the stock. @@ -477,8 +533,8 @@ def operatingPerformance(self): >>> Stock("visa", exchange="nyse").operatingPerformance() """ - return self.GetData("operatingPerformance/v2", url_suffixe='') - + return self.GetData("operatingPerformance/v2", url_suffixe="") + def split(self): """ This function retrieves the split history of the stock. @@ -488,10 +544,10 @@ def split(self): Examples: >>> Stock("visa", exchange="nyse").split() - + """ return self.GetData("split") - + def trailingTotalReturn(self): """ This function retrieves the performance of the stock and its index. @@ -501,10 +557,10 @@ def trailingTotalReturn(self): Examples: >>> Stock("visa", exchange="nyse").trailingTotalReturn() - + """ return self.GetData("trailingTotalReturns") - + def transactionHistory(self): """ This function retrieves the transaction of key people. @@ -517,8 +573,7 @@ def transactionHistory(self): """ return self.GetData("insiders/transactionHistory") - - + def transactionSummary(self): """ This function retrieves the summuary of transactions of key people. @@ -528,10 +583,10 @@ def transactionSummary(self): Examples: >>> Stock("visa", exchange="nyse").transactionSummary() - + """ return self.GetData("insiders/transactionChart") - + def valuation(self): """ This function retrieves the valution of the stock. @@ -541,6 +596,6 @@ def valuation(self): Examples: >>> Stock("visa", exchange="nyse").valuation() - + """ - return self.GetData("valuation", url_suffixe='') + return self.GetData("valuation", url_suffixe="") diff --git a/mstarpy/utils.py b/mstarpy/utils.py index b8a3098..84debc7 100644 --- a/mstarpy/utils.py +++ b/mstarpy/utils.py @@ -1,619 +1,497 @@ import random -APIKEY = 'lstzFDEOhfFNMLikKa0am9mgEKLBl49T' +APIKEY = "lstzFDEOhfFNMLikKa0am9mgEKLBl49T" -ASSET_TYPE = ['etf', 'fund', 'stock'] +ASSET_TYPE = ["etf", "fund", "stock"] EXCHANGE = { - "NYSE": { - "name": "exchanges_equity_nyse", - "id": "E0EXG$XNYS" - }, - - "NASDAQ" : { - "name": "exchanges_equity_nasdaq", - "id": "E0EXG$XNAS" - }, - - "LSE" : { - "name": "exchanges_london_stock_exchange", - "id": "E0EXG$XLON" - }, - - "AMSTERDAM" : { - "name": "exchanges_equity_nyse_euronext_amsterdam", - "id": "E0EXG$XAMS" - }, - - "ATHENS" : { - "name": "exchanges_equity_athens_stock_exchange", - "id": "E0EXG$XATH" - }, - - "BOLSA_DE_VALORES" : { - "name": "exchanges_equity_bolsa_de_valores", - "id": "E0EXG$XMEX" - }, - - "BOMBAY" : { - "name": "exchanges_equity_bombay_stock_exchange", - "id": "E0EXG$XBOM" - }, - - "BORSA_ITALIANA" : { - "name": "exchanges_equity_borsa_italiana", - "id": "E0EXG$XMIL" - }, - - "BRUSSELS" : { - "name": "exchanges_equity_nyse_brussels", - "id": "E0EXG$XBRU" - }, - - "COPENHAGEN" : { - "name": "exchanges_equity_omx_exchange_copenhagen", - "id": "E0EXG$XCSE" - }, - - "HELSINKI" : { - "name": "exchanges_equity_omx_exchange_helsinki", - "id": "E0EXG$XHEL" - }, - - "HONG-KONG" : { - "name": "exchanges_HONG_KONG", - "id": "E0EXG$XHKG" - }, - "ICELAND" : { - "name": "exchanges_equity_omx_exchange_iceland", - "id": "E0EXG$XICE" - }, - - "INDIA" : { - "name": "exchanges_equity_india_stock_exchange", - "id": "E0EXG$XNSE" - }, - - "IPSX" : { - "name": "exchanges_equity_exchange_IPSX", - "id": "E0EXG$IPSX" - }, - - "IRELAND" : { - "name": "exchanges_equity_irish_stock_exchange", - "id": "E0EXG$XDUB" - }, - - "ISTANBUL" : { - "name": "exchanges_equity_istanbul_stock_exchange", - "id": "E0EXG$XIST" - }, - - "LISBON" : { - "name": "exchanges_equity_nyse_euronext_lisbon", - "id": "E0EXG$XLIS" - }, - - "LUXEMBOURG" : { - "name": "exchanges_equity_luxembourg_stock_exchange", - "id": "E0EXG$XLUX" - }, - - "OSLO_BORS" : { - "name": "exchanges_equity_oslo_bors", - "id": "E0EXG$XOSL" - }, - - "PARIS" : { - "name": "exchanges_equity_nyse_paris", - "id": "E0EXG$XPAR" - }, - - "RIGA" : { - "name": "exchanges_equity_omx_exchange_riga", - "id": "E0EXG$XRIS" - }, - - "SHANGAI" : { - "name": "exchanges_equity_shanghai_stock_exchange", - "id": "E0EXG$XSHG" - }, - - "SHENZHEN" : { - "name": "exchanges_equity_shenzhen_stock_exchange", - "id": "E0EXG$XSHE" - }, - - "SINGAPORE" : { - "name": "exchanges_equity_singapore", - "id": "E0EXG$XSES" - }, - - "STOCKHOLM" : { - "name": "exchanges_equity_nasdaq_omx_stockholm", - "id": "E0EXG$XSTO" - }, - - "SWISS" : { - "name": "exchanges_equity_six__swiss_exchange", - "id": "E0EXG$XSWX" - }, - - "TAIWAN" : { - "name": "exchanges_equity_taiwan_stock_exchange", - "id": "E0EXG$XTAI" - }, - - "TALLIN" : { - "name": "exchanges_equity_tallin_stock_exchange", - "id": "E0EXG$XTAL" - }, - - "THAILAND" : { - "name": "exchanges_equity_thailand_stock_exchange", - "id": "E0EXG$XBKK" - }, - - "TOKYO" : { - "name": "exchanges_equity_tokyo_stock_exchange", - "id": "E0EXG$XTKS" - }, - - "VILNIUS" : { - "name": "exchanges_equity_omx_exchange_vilnius", - "id": "E0EXG$XLIT" - }, - - "WARSAW" :{ - "name": "exchanges_equity_warsaw_stock_exchange", - "id": "E0EXG$XWAR" - }, - - "WIENER_BOERSE" : { - "name": "exchanges_equity_wiener_boerse", - "id": "E0EXG$XWBO" - }, - - "WORLDWIDE_EQUITY" : { - "name": "exchanges_worldwide_equity", - "id": "E0WWE$$ALL" - }, - } + "NYSE": {"name": "exchanges_equity_nyse", "id": "E0EXG$XNYS"}, + "NASDAQ": {"name": "exchanges_equity_nasdaq", "id": "E0EXG$XNAS"}, + "LSE": {"name": "exchanges_london_stock_exchange", "id": "E0EXG$XLON"}, + "AMSTERDAM": { + "name": "exchanges_equity_nyse_euronext_amsterdam", + "id": "E0EXG$XAMS", + }, + "ATHENS": {"name": "exchanges_equity_athens_stock_exchange", "id": "E0EXG$XATH"}, + "BOLSA_DE_VALORES": { + "name": "exchanges_equity_bolsa_de_valores", + "id": "E0EXG$XMEX", + }, + "BOMBAY": {"name": "exchanges_equity_bombay_stock_exchange", "id": "E0EXG$XBOM"}, + "BORSA_ITALIANA": {"name": "exchanges_equity_borsa_italiana", "id": "E0EXG$XMIL"}, + "BRUSSELS": {"name": "exchanges_equity_nyse_brussels", "id": "E0EXG$XBRU"}, + "COPENHAGEN": { + "name": "exchanges_equity_omx_exchange_copenhagen", + "id": "E0EXG$XCSE", + }, + "HELSINKI": {"name": "exchanges_equity_omx_exchange_helsinki", "id": "E0EXG$XHEL"}, + "HONG-KONG": {"name": "exchanges_HONG_KONG", "id": "E0EXG$XHKG"}, + "ICELAND": {"name": "exchanges_equity_omx_exchange_iceland", "id": "E0EXG$XICE"}, + "INDIA": {"name": "exchanges_equity_india_stock_exchange", "id": "E0EXG$XNSE"}, + "IPSX": {"name": "exchanges_equity_exchange_IPSX", "id": "E0EXG$IPSX"}, + "IRELAND": {"name": "exchanges_equity_irish_stock_exchange", "id": "E0EXG$XDUB"}, + "ISTANBUL": { + "name": "exchanges_equity_istanbul_stock_exchange", + "id": "E0EXG$XIST", + }, + "LISBON": {"name": "exchanges_equity_nyse_euronext_lisbon", "id": "E0EXG$XLIS"}, + "LUXEMBOURG": { + "name": "exchanges_equity_luxembourg_stock_exchange", + "id": "E0EXG$XLUX", + }, + "OSLO_BORS": {"name": "exchanges_equity_oslo_bors", "id": "E0EXG$XOSL"}, + "PARIS": {"name": "exchanges_equity_nyse_paris", "id": "E0EXG$XPAR"}, + "RIGA": {"name": "exchanges_equity_omx_exchange_riga", "id": "E0EXG$XRIS"}, + "SHANGAI": {"name": "exchanges_equity_shanghai_stock_exchange", "id": "E0EXG$XSHG"}, + "SHENZHEN": { + "name": "exchanges_equity_shenzhen_stock_exchange", + "id": "E0EXG$XSHE", + }, + "SINGAPORE": {"name": "exchanges_equity_singapore", "id": "E0EXG$XSES"}, + "STOCKHOLM": {"name": "exchanges_equity_nasdaq_omx_stockholm", "id": "E0EXG$XSTO"}, + "SWISS": {"name": "exchanges_equity_six__swiss_exchange", "id": "E0EXG$XSWX"}, + "TAIWAN": {"name": "exchanges_equity_taiwan_stock_exchange", "id": "E0EXG$XTAI"}, + "TALLIN": {"name": "exchanges_equity_tallin_stock_exchange", "id": "E0EXG$XTAL"}, + "THAILAND": { + "name": "exchanges_equity_thailand_stock_exchange", + "id": "E0EXG$XBKK", + }, + "TOKYO": {"name": "exchanges_equity_tokyo_stock_exchange", "id": "E0EXG$XTKS"}, + "VILNIUS": {"name": "exchanges_equity_omx_exchange_vilnius", "id": "E0EXG$XLIT"}, + "WARSAW": {"name": "exchanges_equity_warsaw_stock_exchange", "id": "E0EXG$XWAR"}, + "WIENER_BOERSE": {"name": "exchanges_equity_wiener_boerse", "id": "E0EXG$XWBO"}, + "WORLDWIDE_EQUITY": {"name": "exchanges_worldwide_equity", "id": "E0WWE$$ALL"}, +} FIELDS = [ - 'AdministratorCompanyId', - 'AlphaM36', - 'AnalystRatingScale', - 'AverageCreditQualityCode', - 'AverageMarketCapital', - 'BetaM36', - 'BondStyleBox', - 'brandingCompanyId', - 'categoryId', - 'CategoryName', - 'ClosePrice', - 'currency', - 'DebtEquityRatio', - 'distribution', - 'DividendYield', - 'EBTMarginYear1', - 'EffectiveDuration', - 'EPSGrowth3YYear1', - 'equityStyle', - 'EquityStyleBox', - 'exchangeCode', - 'ExchangeId', - 'ExpertiseAdvanced', - 'ExpertiseBasic', - 'ExpertiseInformed', - 'FeeLevel', - 'fundShareClassId', - 'fundSize', - 'fundStyle', - 'FundTNAV', - 'GBRReturnD1', - 'GBRReturnM0', - 'GBRReturnM1', - 'GBRReturnM12', - 'GBRReturnM120', - 'GBRReturnM3', - 'GBRReturnM36', - 'GBRReturnM6', - 'GBRReturnM60', - 'GBRReturnW1', - 'geoRegion', - 'globalAssetClassId', - 'globalCategoryId', - 'iMASectorId', - 'IndustryName', - 'InitialPurchase', - 'instrumentName', - 'investment', - 'investmentExpertise', - 'investmentObjective', - 'investmentType', - 'investorType', - 'InvestorTypeEligibleCounterparty', - 'InvestorTypeProfessional', - 'InvestorTypeRetail', - 'LargestSector', - 'LegalName', - 'managementStyle', - 'ManagerTenure', - 'MarketCap', - 'MarketCountryName', - 'MaxDeferredLoad', - 'MaxFrontEndLoad', - 'MaximumExitCostAcquired', - 'MorningstarRiskM255', - 'Name', - 'NetMargin', - 'ongoingCharge', - 'OngoingCostActual', - 'PEGRatio', - 'PERatio', - 'PerformanceFeeActual', - 'PriceCurrency', - 'QuantitativeRating', - 'R2M36', - 'ReturnD1', - 'ReturnM0', - 'ReturnM1', - 'ReturnM12', - 'ReturnM120', - 'ReturnM3', - 'ReturnM36', - 'ReturnM6', - 'ReturnM60', - 'ReturnProfileGrowth', - 'ReturnProfileHedging', - 'ReturnProfileIncome', - 'ReturnProfileOther', - 'ReturnProfilePreservation', - 'ReturnW1', - 'RevenueGrowth3Y', - 'riskSrri', - 'ROATTM', - 'ROETTM', - 'ROEYear1', - 'ROICYear1', - 'SecId', - 'SectorName', - 'shareClassType', - 'SharpeM36', - 'StandardDeviationM36', - 'starRating', - 'StarRatingM255', - 'SustainabilityRank', - 'sustainabilityRating', - 'TenforeId', - 'Ticker', - 'totalReturn', - 'totalReturnTimeFrame', - 'TrackRecordExtension', - 'TransactionFeeActual', - 'umbrellaCompanyId', - 'Universe', - 'Yield_M12', - 'yieldPercent', - + "AdministratorCompanyId", + "AlphaM36", + "AnalystRatingScale", + "AverageCreditQualityCode", + "AverageMarketCapital", + "BetaM36", + "BondStyleBox", + "brandingCompanyId", + "categoryId", + "CategoryName", + "ClosePrice", + "currency", + "DebtEquityRatio", + "distribution", + "DividendYield", + "EBTMarginYear1", + "EffectiveDuration", + "EPSGrowth3YYear1", + "equityStyle", + "EquityStyleBox", + "exchangeCode", + "ExchangeId", + "ExpertiseAdvanced", + "ExpertiseBasic", + "ExpertiseInformed", + "FeeLevel", + "fundShareClassId", + "fundSize", + "fundStyle", + "FundTNAV", + "GBRReturnD1", + "GBRReturnM0", + "GBRReturnM1", + "GBRReturnM12", + "GBRReturnM120", + "GBRReturnM3", + "GBRReturnM36", + "GBRReturnM6", + "GBRReturnM60", + "GBRReturnW1", + "geoRegion", + "globalAssetClassId", + "globalCategoryId", + "iMASectorId", + "IndustryName", + "InitialPurchase", + "instrumentName", + "investment", + "investmentExpertise", + "investmentObjective", + "investmentType", + "investorType", + "InvestorTypeEligibleCounterparty", + "InvestorTypeProfessional", + "InvestorTypeRetail", + "LargestSector", + "LegalName", + "managementStyle", + "ManagerTenure", + "MarketCap", + "MarketCountryName", + "MaxDeferredLoad", + "MaxFrontEndLoad", + "MaximumExitCostAcquired", + "MorningstarRiskM255", + "Name", + "NetMargin", + "ongoingCharge", + "OngoingCostActual", + "PEGRatio", + "PERatio", + "PerformanceFeeActual", + "PriceCurrency", + "QuantitativeRating", + "R2M36", + "ReturnD1", + "ReturnM0", + "ReturnM1", + "ReturnM12", + "ReturnM120", + "ReturnM3", + "ReturnM36", + "ReturnM6", + "ReturnM60", + "ReturnProfileGrowth", + "ReturnProfileHedging", + "ReturnProfileIncome", + "ReturnProfileOther", + "ReturnProfilePreservation", + "ReturnW1", + "RevenueGrowth3Y", + "riskSrri", + "ROATTM", + "ROETTM", + "ROEYear1", + "ROICYear1", + "SecId", + "SectorName", + "shareClassType", + "SharpeM36", + "StandardDeviationM36", + "starRating", + "StarRatingM255", + "SustainabilityRank", + "sustainabilityRating", + "TenforeId", + "Ticker", + "totalReturn", + "totalReturnTimeFrame", + "TrackRecordExtension", + "TransactionFeeActual", + "umbrellaCompanyId", + "Universe", + "Yield_M12", + "yieldPercent", ] -FILTER_FUND = [ - 'AdministratorCompanyId', - 'AnalystRatingScale', - 'BondStyleBox', - 'BrandingCompanyId', - 'CategoryId', - 'CollectedSRRI', - 'distribution', - 'EquityStyleBox', - 'ExpertiseInformed', - 'FeeLevel', - 'FundTNAV', - 'GBRReturnM0', - 'GBRReturnM12', - 'GBRReturnM120', - 'GBRReturnM36', - 'GBRReturnM60', - 'GlobalAssetClassId', - 'GlobalCategoryId', - 'IMASectorID', - 'IndexFund', - 'InvestorTypeProfessional', - 'LargestRegion', - 'LargestSector', - 'OngoingCharge', - 'QuantitativeRating', - 'ReturnProfilePreservation', - 'ShareClassTypeId', - 'starRating', - 'SustainabilityRank', - 'UmbrellaCompanyId', - 'Yield_M12', - - ] - -FILTER_STOCK = [ - 'debtEquityRatio', - 'DividendYield', - 'epsGrowth3YYear1', - 'EquityStyleBox', - 'GBRReturnM0', - 'GBRReturnM12', - 'GBRReturnM36', - 'GBRReturnM60', - 'GBRReturnM120', - 'IndustryId', - 'MarketCap', - 'netMargin', - 'PBRatio', - 'PEGRatio', - 'PERatio', - 'PSRatio', - 'revenueGrowth3Y', - 'roattm', - 'roettm', - 'SectorId', - ] +FILTER_FUND = [ + "AdministratorCompanyId", + "AnalystRatingScale", + "BondStyleBox", + "BrandingCompanyId", + "CategoryId", + "CollectedSRRI", + "distribution", + "EquityStyleBox", + "ExpertiseInformed", + "FeeLevel", + "FundTNAV", + "GBRReturnM0", + "GBRReturnM12", + "GBRReturnM120", + "GBRReturnM36", + "GBRReturnM60", + "GlobalAssetClassId", + "GlobalCategoryId", + "IMASectorID", + "IndexFund", + "InvestorTypeProfessional", + "LargestRegion", + "LargestSector", + "OngoingCharge", + "QuantitativeRating", + "ReturnProfilePreservation", + "ShareClassTypeId", + "starRating", + "SustainabilityRank", + "UmbrellaCompanyId", + "Yield_M12", +] +FILTER_STOCK = [ + "debtEquityRatio", + "DividendYield", + "epsGrowth3YYear1", + "EquityStyleBox", + "GBRReturnM0", + "GBRReturnM12", + "GBRReturnM36", + "GBRReturnM60", + "GBRReturnM120", + "IndustryId", + "MarketCap", + "netMargin", + "PBRatio", + "PEGRatio", + "PERatio", + "PSRatio", + "revenueGrowth3Y", + "roattm", + "roettm", + "SectorId", +] SITE = { - - 'af' : {'iso3' : 'AFG', 'site' : ''}, - 'ax' : {'iso3' : 'ALA', 'site' : ''}, - 'al' : {'iso3' : 'ALB', 'site' : ''}, - 'dz' : {'iso3' : 'DZA', 'site' : ''}, - 'as' : {'iso3' : 'ASM', 'site' : ''}, - 'ad' : {'iso3' : 'AND', 'site' : ''}, - 'ao' : {'iso3' : 'AGO', 'site' : ''}, - 'ai' : {'iso3' : 'AIA', 'site' : ''}, - 'aq' : {'iso3' : 'ATA', 'site' : ''}, - 'ag' : {'iso3' : 'ATG', 'site' : ''}, - 'ar' : {'iso3' : 'ARG', 'site' : ''}, - 'am' : {'iso3' : 'ARM', 'site' : ''}, - 'aw' : {'iso3' : 'ABW', 'site' : ''}, - 'au' : {'iso3' : 'AUS', 'site' : ''}, - 'at' : {'iso3' : 'AUT', 'site' : ''}, - 'az' : {'iso3' : 'AZE', 'site' : ''}, - 'bs' : {'iso3' : 'BHS', 'site' : ''}, - 'bh' : {'iso3' : 'BHR', 'site' : ''}, - 'bd' : {'iso3' : 'BGD', 'site' : ''}, - 'bb' : {'iso3' : 'BRB', 'site' : ''}, - 'by' : {'iso3' : 'BLR', 'site' : ''}, - 'be' : {'iso3' : 'BEL', 'site' : 'https://www.morningstar.be/be/'}, - 'bz' : {'iso3' : 'BLZ', 'site' : ''}, - 'bj' : {'iso3' : 'BEN', 'site' : ''}, - 'bm' : {'iso3' : 'BMU', 'site' : ''}, - 'bt' : {'iso3' : 'BTN', 'site' : ''}, - 'bo' : {'iso3' : 'BOL', 'site' : ''}, - 'bq' : {'iso3' : 'BES', 'site' : ''}, - 'ba' : {'iso3' : 'BIH', 'site' : ''}, - 'bw' : {'iso3' : 'BWA', 'site' : ''}, - 'bv' : {'iso3' : 'BVT', 'site' : ''}, - 'br' : {'iso3' : 'BRA', 'site' : 'https://www.morningstarbr.com/br/'}, - 'io' : {'iso3' : 'IOT', 'site' : ''}, - 'bn' : {'iso3' : 'BRN', 'site' : ''}, - 'bg' : {'iso3' : 'BGR', 'site' : ''}, - 'bf' : {'iso3' : 'BFA', 'site' : ''}, - 'bi' : {'iso3' : 'BDI', 'site' : ''}, - 'cv' : {'iso3' : 'CPV', 'site' : ''}, - 'kh' : {'iso3' : 'KHM', 'site' : ''}, - 'cm' : {'iso3' : 'CMR', 'site' : ''}, - 'ca' : {'iso3' : 'CAN', 'site' : 'https://www.morningstar.ca/ca/'}, - 'ky' : {'iso3' : 'CYM', 'site' : ''}, - 'cf' : {'iso3' : 'CAF', 'site' : ''}, - 'td' : {'iso3' : 'TCD', 'site' : ''}, - 'cl' : {'iso3' : 'CHL', 'site' : 'https://www.morningstar.cl/cl/'}, - 'cn' : {'iso3' : 'CHN', 'site' : ''}, - 'cx' : {'iso3' : 'CXR', 'site' : ''}, - 'cc' : {'iso3' : 'CCK', 'site' : ''}, - 'co' : {'iso3' : 'COL', 'site' : ''}, - 'km' : {'iso3' : 'COM', 'site' : ''}, - 'cd' : {'iso3' : 'COD', 'site' : ''}, - 'cg' : {'iso3' : 'COG', 'site' : ''}, - 'ck' : {'iso3' : 'COK', 'site' : ''}, - 'cr' : {'iso3' : 'CRI', 'site' : ''}, - 'ci' : {'iso3' : 'CIV', 'site' : ''}, - 'hr' : {'iso3' : 'HRV', 'site' : ''}, - 'cu' : {'iso3' : 'CUB', 'site' : ''}, - 'cw' : {'iso3' : 'CUW', 'site' : ''}, - 'cy' : {'iso3' : 'CYP', 'site' : ''}, - 'cz' : {'iso3' : 'CZE', 'site' : ''}, - 'dk' : {'iso3' : 'DNK', 'site' : 'https://www.morningstar.dk/dk/'}, - 'dj' : {'iso3' : 'DJI', 'site' : ''}, - 'dm' : {'iso3' : 'DMA', 'site' : ''}, - 'do' : {'iso3' : 'DOM', 'site' : ''}, - 'ec' : {'iso3' : 'ECU', 'site' : ''}, - 'eg' : {'iso3' : 'EGY', 'site' : ''}, - 'sv' : {'iso3' : 'SLV', 'site' : ''}, - 'gq' : {'iso3' : 'GNQ', 'site' : ''}, - 'er' : {'iso3' : 'ERI', 'site' : ''}, - 'ee' : {'iso3' : 'EST', 'site' : ''}, - 'sz' : {'iso3' : 'SWZ', 'site' : ''}, - 'et' : {'iso3' : 'ETH', 'site' : ''}, - 'fk' : {'iso3' : 'FLK', 'site' : ''}, - 'fo' : {'iso3' : 'FRO', 'site' : ''}, - 'fj' : {'iso3' : 'FJI', 'site' : ''}, - 'fi' : {'iso3' : 'FIN', 'site' : 'https://www.morningstar.fi/fi/'}, - 'fr' : {'iso3' : 'FRA', 'site' : 'https://www.morningstar.fr/fr/'}, - 'gf' : {'iso3' : 'GUF', 'site' : ''}, - 'pf' : {'iso3' : 'PYF', 'site' : ''}, - 'tf' : {'iso3' : 'ATF', 'site' : ''}, - 'ga' : {'iso3' : 'GAB', 'site' : ''}, - 'gm' : {'iso3' : 'GMB', 'site' : ''}, - 'ge' : {'iso3' : 'GEO', 'site' : ''}, - 'de' : {'iso3' : 'DEU', 'site' : 'https://www.morningstar.de/de/'}, - 'gh' : {'iso3' : 'GHA', 'site' : ''}, - 'gi' : {'iso3' : 'GIB', 'site' : ''}, - 'gr' : {'iso3' : 'GRC', 'site' : ''}, - 'gl' : {'iso3' : 'GRL', 'site' : ''}, - 'gd' : {'iso3' : 'GRD', 'site' : ''}, - 'gp' : {'iso3' : 'GLP', 'site' : ''}, - 'gu' : {'iso3' : 'GUM', 'site' : ''}, - 'gt' : {'iso3' : 'GTM', 'site' : ''}, - 'gg' : {'iso3' : 'GGY', 'site' : ''}, - 'gn' : {'iso3' : 'GIN', 'site' : ''}, - 'gw' : {'iso3' : 'GNB', 'site' : ''}, - 'gy' : {'iso3' : 'GUY', 'site' : ''}, - 'ht' : {'iso3' : 'HTI', 'site' : ''}, - 'hm' : {'iso3' : 'HMD', 'site' : ''}, - 'va' : {'iso3' : 'VAT', 'site' : ''}, - 'hn' : {'iso3' : 'HND', 'site' : ''}, - 'hk' : {'iso3' : 'HKG', 'site' : 'https://www.morningstar.hk/hk/'}, - 'hu' : {'iso3' : 'HUN', 'site' : ''}, - 'is' : {'iso3' : 'ISL', 'site' : ''}, - 'in' : {'iso3' : 'IND', 'site' : ''}, - 'id' : {'iso3' : 'IDN', 'site' : ''}, - 'ir' : {'iso3' : 'IRN', 'site' : ''}, - 'iq' : {'iso3' : 'IRQ', 'site' : ''}, - 'ie' : {'iso3' : 'IRL', 'site' : 'https://www.morningstarfunds.ie/ie/'}, - 'im' : {'iso3' : 'IMN', 'site' : ''}, - 'il' : {'iso3' : 'ISR', 'site' : ''}, - 'it' : {'iso3' : 'ITA', 'site' : 'https://www.morningstar.it/it/'}, - 'jm' : {'iso3' : 'JAM', 'site' : ''}, - 'jp' : {'iso3' : 'JPN', 'site' : ''}, - 'je' : {'iso3' : 'JEY', 'site' : ''}, - 'jo' : {'iso3' : 'JOR', 'site' : ''}, - 'kz' : {'iso3' : 'KAZ', 'site' : ''}, - 'ke' : {'iso3' : 'KEN', 'site' : ''}, - 'ki' : {'iso3' : 'KIR', 'site' : ''}, - 'kp' : {'iso3' : 'PRK', 'site' : ''}, - 'kr' : {'iso3' : 'KOR', 'site' : ''}, - 'kw' : {'iso3' : 'KWT', 'site' : ''}, - 'kg' : {'iso3' : 'KGZ', 'site' : ''}, - 'la' : {'iso3' : 'LAO', 'site' : ''}, - 'lv' : {'iso3' : 'LVA', 'site' : ''}, - 'lb' : {'iso3' : 'LBN', 'site' : ''}, - 'ls' : {'iso3' : 'LSO', 'site' : ''}, - 'lr' : {'iso3' : 'LBR', 'site' : ''}, - 'ly' : {'iso3' : 'LBY', 'site' : ''}, - 'li' : {'iso3' : 'LIE', 'site' : ''}, - 'lt' : {'iso3' : 'LTU', 'site' : ''}, - 'lu' : {'iso3' : 'LUX', 'site' : ''}, - 'mo' : {'iso3' : 'MAC', 'site' : ''}, - 'mk' : {'iso3' : 'MKD', 'site' : ''}, - 'mg' : {'iso3' : 'MDG', 'site' : ''}, - 'mw' : {'iso3' : 'MWI', 'site' : ''}, - 'my' : {'iso3' : 'MYS', 'site' : ''}, - 'mv' : {'iso3' : 'MDV', 'site' : ''}, - 'ml' : {'iso3' : 'MLI', 'site' : ''}, - 'mt' : {'iso3' : 'MLT', 'site' : ''}, - 'mh' : {'iso3' : 'MHL', 'site' : ''}, - 'mq' : {'iso3' : 'MTQ', 'site' : ''}, - 'mr' : {'iso3' : 'MRT', 'site' : ''}, - 'mu' : {'iso3' : 'MUS', 'site' : ''}, - 'yt' : {'iso3' : 'MYT', 'site' : ''}, - 'mx' : {'iso3' : 'MEX', 'site' : 'https://www.morningstar.com.mx/mx/'}, - 'fm' : {'iso3' : 'FSM', 'site' : ''}, - 'md' : {'iso3' : 'MDA', 'site' : ''}, - 'mc' : {'iso3' : 'MCO', 'site' : ''}, - 'mn' : {'iso3' : 'MNG', 'site' : ''}, - 'me' : {'iso3' : 'MNE', 'site' : ''}, - 'ms' : {'iso3' : 'MSR', 'site' : ''}, - 'ma' : {'iso3' : 'MAR', 'site' : ''}, - 'mz' : {'iso3' : 'MOZ', 'site' : ''}, - 'mm' : {'iso3' : 'MMR', 'site' : ''}, - 'na' : {'iso3' : 'NAM', 'site' : ''}, - 'nr' : {'iso3' : 'NRU', 'site' : ''}, - 'np' : {'iso3' : 'NPL', 'site' : ''}, - 'nl' : {'iso3' : 'NLD', 'site' : 'https://www.morningstar.nl/nl/'}, - 'nc' : {'iso3' : 'NCL', 'site' : ''}, - 'nz' : {'iso3' : 'NZL', 'site' : ''}, - 'ni' : {'iso3' : 'NIC', 'site' : ''}, - 'ne' : {'iso3' : 'NER', 'site' : ''}, - 'ng' : {'iso3' : 'NGA', 'site' : ''}, - 'nu' : {'iso3' : 'NIU', 'site' : ''}, - 'nf' : {'iso3' : 'NFK', 'site' : ''}, - 'mp' : {'iso3' : 'MNP', 'site' : ''}, - 'no' : {'iso3' : 'NOR', 'site' : 'https://www.morningstar.no/no/'}, - 'om' : {'iso3' : 'OMN', 'site' : ''}, - 'pk' : {'iso3' : 'PAK', 'site' : ''}, - 'pw' : {'iso3' : 'PLW', 'site' : ''}, - 'ps' : {'iso3' : 'PSE', 'site' : ''}, - 'pa' : {'iso3' : 'PAN', 'site' : ''}, - 'pg' : {'iso3' : 'PNG', 'site' : ''}, - 'py' : {'iso3' : 'PRY', 'site' : ''}, - 'pe' : {'iso3' : 'PER', 'site' : ''}, - 'ph' : {'iso3' : 'PHL', 'site' : ''}, - 'pn' : {'iso3' : 'PCN', 'site' : ''}, - 'pl' : {'iso3' : 'POL', 'site' : ''}, - 'pt' : {'iso3' : 'PRT', 'site' : 'https://www.morningstar.pt/pt/'}, - 'pr' : {'iso3' : 'PRI', 'site' : ''}, - 'qa' : {'iso3' : 'QAT', 'site' : ''}, - 're' : {'iso3' : 'REU', 'site' : ''}, - 'ro' : {'iso3' : 'ROU', 'site' : ''}, - 'ru' : {'iso3' : 'RUS', 'site' : ''}, - 'rw' : {'iso3' : 'RWA', 'site' : ''}, - 'bl' : {'iso3' : 'BLM', 'site' : ''}, - 'sh' : {'iso3' : 'SHN', 'site' : ''}, - 'kn' : {'iso3' : 'KNA', 'site' : ''}, - 'lc' : {'iso3' : 'LCA', 'site' : ''}, - 'mf' : {'iso3' : 'MAF', 'site' : ''}, - 'pm' : {'iso3' : 'SPM', 'site' : ''}, - 'vc' : {'iso3' : 'VCT', 'site' : ''}, - 'ws' : {'iso3' : 'WSM', 'site' : ''}, - 'sm' : {'iso3' : 'SMR', 'site' : ''}, - 'st' : {'iso3' : 'STP', 'site' : ''}, - 'sa' : {'iso3' : 'SAU', 'site' : ''}, - 'sn' : {'iso3' : 'SEN', 'site' : ''}, - 'rs' : {'iso3' : 'SRB', 'site' : ''}, - 'sc' : {'iso3' : 'SYC', 'site' : ''}, - 'sl' : {'iso3' : 'SLE', 'site' : ''}, - 'sg' : {'iso3' : 'SGP', 'site' : 'https://sg.morningstar.com/sg/'}, - 'sx' : {'iso3' : 'SXM', 'site' : ''}, - 'sk' : {'iso3' : 'SVK', 'site' : ''}, - 'si' : {'iso3' : 'SVN', 'site' : ''}, - 'sb' : {'iso3' : 'SLB', 'site' : ''}, - 'so' : {'iso3' : 'SOM', 'site' : ''}, - 'za' : {'iso3' : 'ZAF', 'site' : ''}, - 'gs' : {'iso3' : 'SGS', 'site' : ''}, - 'ss' : {'iso3' : 'SSD', 'site' : ''}, - 'es' : {'iso3' : 'ESP', 'site' : 'https://www.morningstar.es/es/'}, - 'lk' : {'iso3' : 'LKA', 'site' : ''}, - 'sd' : {'iso3' : 'SDN', 'site' : ''}, - 'sr' : {'iso3' : 'SUR', 'site' : ''}, - 'sj' : {'iso3' : 'SJM', 'site' : ''}, - 'se' : {'iso3' : 'SWE', 'site' : 'https://www.morningstar.se/se/'}, - 'ch' : {'iso3' : 'CHE', 'site' : 'https://www.morningstar.ch/ch/'}, - 'sy' : {'iso3' : 'SYR', 'site' : ''}, - 'tw' : {'iso3' : 'TWN', 'site' : ''}, - 'tj' : {'iso3' : 'TJK', 'site' : ''}, - 'tz' : {'iso3' : 'TZA', 'site' : ''}, - 'th' : {'iso3' : 'THA', 'site' : ''}, - 'tl' : {'iso3' : 'TLS', 'site' : ''}, - 'tg' : {'iso3' : 'TGO', 'site' : ''}, - 'tk' : {'iso3' : 'TKL', 'site' : ''}, - 'to' : {'iso3' : 'TON', 'site' : ''}, - 'tt' : {'iso3' : 'TTO', 'site' : ''}, - 'tn' : {'iso3' : 'TUN', 'site' : ''}, - 'tr' : {'iso3' : 'TUR', 'site' : ''}, - 'tm' : {'iso3' : 'TKM', 'site' : ''}, - 'tc' : {'iso3' : 'TCA', 'site' : ''}, - 'tv' : {'iso3' : 'TUV', 'site' : ''}, - 'ug' : {'iso3' : 'UGA', 'site' : ''}, - 'ua' : {'iso3' : 'UKR', 'site' : ''}, - 'ae' : {'iso3' : 'ARE', 'site' : ''}, - 'gb' : {'iso3' : 'GBR', 'site' : 'https://www.morningstar.co.uk/uk/'}, - 'um' : {'iso3' : 'UMI', 'site' : ''}, - 'us' : {'iso3' : 'USA', 'site' : 'https://www.morningstar.com/'}, - 'uy' : {'iso3' : 'URY', 'site' : ''}, - 'uz' : {'iso3' : 'UZB', 'site' : ''}, - 'vu' : {'iso3' : 'VUT', 'site' : ''}, - 've' : {'iso3' : 'VEN', 'site' : ''}, - 'vn' : {'iso3' : 'VNM', 'site' : ''}, - 'vg' : {'iso3' : 'VGB', 'site' : ''}, - 'vi' : {'iso3' : 'VIR', 'site' : ''}, - 'wf' : {'iso3' : 'WLF', 'site' : ''}, - 'eh' : {'iso3' : 'ESH', 'site' : ''}, - 'ye' : {'iso3' : 'YEM', 'site' : ''}, - 'zm' : {'iso3' : 'ZMB', 'site' : ''}, - 'zw' : {'iso3' : 'ZWE', 'site' : ''}, - - } + "af": {"iso3": "AFG", "site": ""}, + "ax": {"iso3": "ALA", "site": ""}, + "al": {"iso3": "ALB", "site": ""}, + "dz": {"iso3": "DZA", "site": ""}, + "as": {"iso3": "ASM", "site": ""}, + "ad": {"iso3": "AND", "site": ""}, + "ao": {"iso3": "AGO", "site": ""}, + "ai": {"iso3": "AIA", "site": ""}, + "aq": {"iso3": "ATA", "site": ""}, + "ag": {"iso3": "ATG", "site": ""}, + "ar": {"iso3": "ARG", "site": ""}, + "am": {"iso3": "ARM", "site": ""}, + "aw": {"iso3": "ABW", "site": ""}, + "au": {"iso3": "AUS", "site": ""}, + "at": {"iso3": "AUT", "site": ""}, + "az": {"iso3": "AZE", "site": ""}, + "bs": {"iso3": "BHS", "site": ""}, + "bh": {"iso3": "BHR", "site": ""}, + "bd": {"iso3": "BGD", "site": ""}, + "bb": {"iso3": "BRB", "site": ""}, + "by": {"iso3": "BLR", "site": ""}, + "be": {"iso3": "BEL", "site": "https://www.morningstar.be/be/"}, + "bz": {"iso3": "BLZ", "site": ""}, + "bj": {"iso3": "BEN", "site": ""}, + "bm": {"iso3": "BMU", "site": ""}, + "bt": {"iso3": "BTN", "site": ""}, + "bo": {"iso3": "BOL", "site": ""}, + "bq": {"iso3": "BES", "site": ""}, + "ba": {"iso3": "BIH", "site": ""}, + "bw": {"iso3": "BWA", "site": ""}, + "bv": {"iso3": "BVT", "site": ""}, + "br": {"iso3": "BRA", "site": "https://www.morningstarbr.com/br/"}, + "io": {"iso3": "IOT", "site": ""}, + "bn": {"iso3": "BRN", "site": ""}, + "bg": {"iso3": "BGR", "site": ""}, + "bf": {"iso3": "BFA", "site": ""}, + "bi": {"iso3": "BDI", "site": ""}, + "cv": {"iso3": "CPV", "site": ""}, + "kh": {"iso3": "KHM", "site": ""}, + "cm": {"iso3": "CMR", "site": ""}, + "ca": {"iso3": "CAN", "site": "https://www.morningstar.ca/ca/"}, + "ky": {"iso3": "CYM", "site": ""}, + "cf": {"iso3": "CAF", "site": ""}, + "td": {"iso3": "TCD", "site": ""}, + "cl": {"iso3": "CHL", "site": "https://www.morningstar.cl/cl/"}, + "cn": {"iso3": "CHN", "site": ""}, + "cx": {"iso3": "CXR", "site": ""}, + "cc": {"iso3": "CCK", "site": ""}, + "co": {"iso3": "COL", "site": ""}, + "km": {"iso3": "COM", "site": ""}, + "cd": {"iso3": "COD", "site": ""}, + "cg": {"iso3": "COG", "site": ""}, + "ck": {"iso3": "COK", "site": ""}, + "cr": {"iso3": "CRI", "site": ""}, + "ci": {"iso3": "CIV", "site": ""}, + "hr": {"iso3": "HRV", "site": ""}, + "cu": {"iso3": "CUB", "site": ""}, + "cw": {"iso3": "CUW", "site": ""}, + "cy": {"iso3": "CYP", "site": ""}, + "cz": {"iso3": "CZE", "site": ""}, + "dk": {"iso3": "DNK", "site": "https://www.morningstar.dk/dk/"}, + "dj": {"iso3": "DJI", "site": ""}, + "dm": {"iso3": "DMA", "site": ""}, + "do": {"iso3": "DOM", "site": ""}, + "ec": {"iso3": "ECU", "site": ""}, + "eg": {"iso3": "EGY", "site": ""}, + "sv": {"iso3": "SLV", "site": ""}, + "gq": {"iso3": "GNQ", "site": ""}, + "er": {"iso3": "ERI", "site": ""}, + "ee": {"iso3": "EST", "site": ""}, + "sz": {"iso3": "SWZ", "site": ""}, + "et": {"iso3": "ETH", "site": ""}, + "fk": {"iso3": "FLK", "site": ""}, + "fo": {"iso3": "FRO", "site": ""}, + "fj": {"iso3": "FJI", "site": ""}, + "fi": {"iso3": "FIN", "site": "https://www.morningstar.fi/fi/"}, + "fr": {"iso3": "FRA", "site": "https://www.morningstar.fr/fr/"}, + "gf": {"iso3": "GUF", "site": ""}, + "pf": {"iso3": "PYF", "site": ""}, + "tf": {"iso3": "ATF", "site": ""}, + "ga": {"iso3": "GAB", "site": ""}, + "gm": {"iso3": "GMB", "site": ""}, + "ge": {"iso3": "GEO", "site": ""}, + "de": {"iso3": "DEU", "site": "https://www.morningstar.de/de/"}, + "gh": {"iso3": "GHA", "site": ""}, + "gi": {"iso3": "GIB", "site": ""}, + "gr": {"iso3": "GRC", "site": ""}, + "gl": {"iso3": "GRL", "site": ""}, + "gd": {"iso3": "GRD", "site": ""}, + "gp": {"iso3": "GLP", "site": ""}, + "gu": {"iso3": "GUM", "site": ""}, + "gt": {"iso3": "GTM", "site": ""}, + "gg": {"iso3": "GGY", "site": ""}, + "gn": {"iso3": "GIN", "site": ""}, + "gw": {"iso3": "GNB", "site": ""}, + "gy": {"iso3": "GUY", "site": ""}, + "ht": {"iso3": "HTI", "site": ""}, + "hm": {"iso3": "HMD", "site": ""}, + "va": {"iso3": "VAT", "site": ""}, + "hn": {"iso3": "HND", "site": ""}, + "hk": {"iso3": "HKG", "site": "https://www.morningstar.hk/hk/"}, + "hu": {"iso3": "HUN", "site": ""}, + "is": {"iso3": "ISL", "site": ""}, + "in": {"iso3": "IND", "site": ""}, + "id": {"iso3": "IDN", "site": ""}, + "ir": {"iso3": "IRN", "site": ""}, + "iq": {"iso3": "IRQ", "site": ""}, + "ie": {"iso3": "IRL", "site": "https://www.morningstarfunds.ie/ie/"}, + "im": {"iso3": "IMN", "site": ""}, + "il": {"iso3": "ISR", "site": ""}, + "it": {"iso3": "ITA", "site": "https://www.morningstar.it/it/"}, + "jm": {"iso3": "JAM", "site": ""}, + "jp": {"iso3": "JPN", "site": ""}, + "je": {"iso3": "JEY", "site": ""}, + "jo": {"iso3": "JOR", "site": ""}, + "kz": {"iso3": "KAZ", "site": ""}, + "ke": {"iso3": "KEN", "site": ""}, + "ki": {"iso3": "KIR", "site": ""}, + "kp": {"iso3": "PRK", "site": ""}, + "kr": {"iso3": "KOR", "site": ""}, + "kw": {"iso3": "KWT", "site": ""}, + "kg": {"iso3": "KGZ", "site": ""}, + "la": {"iso3": "LAO", "site": ""}, + "lv": {"iso3": "LVA", "site": ""}, + "lb": {"iso3": "LBN", "site": ""}, + "ls": {"iso3": "LSO", "site": ""}, + "lr": {"iso3": "LBR", "site": ""}, + "ly": {"iso3": "LBY", "site": ""}, + "li": {"iso3": "LIE", "site": ""}, + "lt": {"iso3": "LTU", "site": ""}, + "lu": {"iso3": "LUX", "site": ""}, + "mo": {"iso3": "MAC", "site": ""}, + "mk": {"iso3": "MKD", "site": ""}, + "mg": {"iso3": "MDG", "site": ""}, + "mw": {"iso3": "MWI", "site": ""}, + "my": {"iso3": "MYS", "site": ""}, + "mv": {"iso3": "MDV", "site": ""}, + "ml": {"iso3": "MLI", "site": ""}, + "mt": {"iso3": "MLT", "site": ""}, + "mh": {"iso3": "MHL", "site": ""}, + "mq": {"iso3": "MTQ", "site": ""}, + "mr": {"iso3": "MRT", "site": ""}, + "mu": {"iso3": "MUS", "site": ""}, + "yt": {"iso3": "MYT", "site": ""}, + "mx": {"iso3": "MEX", "site": "https://www.morningstar.com.mx/mx/"}, + "fm": {"iso3": "FSM", "site": ""}, + "md": {"iso3": "MDA", "site": ""}, + "mc": {"iso3": "MCO", "site": ""}, + "mn": {"iso3": "MNG", "site": ""}, + "me": {"iso3": "MNE", "site": ""}, + "ms": {"iso3": "MSR", "site": ""}, + "ma": {"iso3": "MAR", "site": ""}, + "mz": {"iso3": "MOZ", "site": ""}, + "mm": {"iso3": "MMR", "site": ""}, + "na": {"iso3": "NAM", "site": ""}, + "nr": {"iso3": "NRU", "site": ""}, + "np": {"iso3": "NPL", "site": ""}, + "nl": {"iso3": "NLD", "site": "https://www.morningstar.nl/nl/"}, + "nc": {"iso3": "NCL", "site": ""}, + "nz": {"iso3": "NZL", "site": ""}, + "ni": {"iso3": "NIC", "site": ""}, + "ne": {"iso3": "NER", "site": ""}, + "ng": {"iso3": "NGA", "site": ""}, + "nu": {"iso3": "NIU", "site": ""}, + "nf": {"iso3": "NFK", "site": ""}, + "mp": {"iso3": "MNP", "site": ""}, + "no": {"iso3": "NOR", "site": "https://www.morningstar.no/no/"}, + "om": {"iso3": "OMN", "site": ""}, + "pk": {"iso3": "PAK", "site": ""}, + "pw": {"iso3": "PLW", "site": ""}, + "ps": {"iso3": "PSE", "site": ""}, + "pa": {"iso3": "PAN", "site": ""}, + "pg": {"iso3": "PNG", "site": ""}, + "py": {"iso3": "PRY", "site": ""}, + "pe": {"iso3": "PER", "site": ""}, + "ph": {"iso3": "PHL", "site": ""}, + "pn": {"iso3": "PCN", "site": ""}, + "pl": {"iso3": "POL", "site": ""}, + "pt": {"iso3": "PRT", "site": "https://www.morningstar.pt/pt/"}, + "pr": {"iso3": "PRI", "site": ""}, + "qa": {"iso3": "QAT", "site": ""}, + "re": {"iso3": "REU", "site": ""}, + "ro": {"iso3": "ROU", "site": ""}, + "ru": {"iso3": "RUS", "site": ""}, + "rw": {"iso3": "RWA", "site": ""}, + "bl": {"iso3": "BLM", "site": ""}, + "sh": {"iso3": "SHN", "site": ""}, + "kn": {"iso3": "KNA", "site": ""}, + "lc": {"iso3": "LCA", "site": ""}, + "mf": {"iso3": "MAF", "site": ""}, + "pm": {"iso3": "SPM", "site": ""}, + "vc": {"iso3": "VCT", "site": ""}, + "ws": {"iso3": "WSM", "site": ""}, + "sm": {"iso3": "SMR", "site": ""}, + "st": {"iso3": "STP", "site": ""}, + "sa": {"iso3": "SAU", "site": ""}, + "sn": {"iso3": "SEN", "site": ""}, + "rs": {"iso3": "SRB", "site": ""}, + "sc": {"iso3": "SYC", "site": ""}, + "sl": {"iso3": "SLE", "site": ""}, + "sg": {"iso3": "SGP", "site": "https://sg.morningstar.com/sg/"}, + "sx": {"iso3": "SXM", "site": ""}, + "sk": {"iso3": "SVK", "site": ""}, + "si": {"iso3": "SVN", "site": ""}, + "sb": {"iso3": "SLB", "site": ""}, + "so": {"iso3": "SOM", "site": ""}, + "za": {"iso3": "ZAF", "site": ""}, + "gs": {"iso3": "SGS", "site": ""}, + "ss": {"iso3": "SSD", "site": ""}, + "es": {"iso3": "ESP", "site": "https://www.morningstar.es/es/"}, + "lk": {"iso3": "LKA", "site": ""}, + "sd": {"iso3": "SDN", "site": ""}, + "sr": {"iso3": "SUR", "site": ""}, + "sj": {"iso3": "SJM", "site": ""}, + "se": {"iso3": "SWE", "site": "https://www.morningstar.se/se/"}, + "ch": {"iso3": "CHE", "site": "https://www.morningstar.ch/ch/"}, + "sy": {"iso3": "SYR", "site": ""}, + "tw": {"iso3": "TWN", "site": ""}, + "tj": {"iso3": "TJK", "site": ""}, + "tz": {"iso3": "TZA", "site": ""}, + "th": {"iso3": "THA", "site": ""}, + "tl": {"iso3": "TLS", "site": ""}, + "tg": {"iso3": "TGO", "site": ""}, + "tk": {"iso3": "TKL", "site": ""}, + "to": {"iso3": "TON", "site": ""}, + "tt": {"iso3": "TTO", "site": ""}, + "tn": {"iso3": "TUN", "site": ""}, + "tr": {"iso3": "TUR", "site": ""}, + "tm": {"iso3": "TKM", "site": ""}, + "tc": {"iso3": "TCA", "site": ""}, + "tv": {"iso3": "TUV", "site": ""}, + "ug": {"iso3": "UGA", "site": ""}, + "ua": {"iso3": "UKR", "site": ""}, + "ae": {"iso3": "ARE", "site": ""}, + "gb": {"iso3": "GBR", "site": "https://www.morningstar.co.uk/uk/"}, + "um": {"iso3": "UMI", "site": ""}, + "us": {"iso3": "USA", "site": "https://www.morningstar.com/"}, + "uy": {"iso3": "URY", "site": ""}, + "uz": {"iso3": "UZB", "site": ""}, + "vu": {"iso3": "VUT", "site": ""}, + "ve": {"iso3": "VEN", "site": ""}, + "vn": {"iso3": "VNM", "site": ""}, + "vg": {"iso3": "VGB", "site": ""}, + "vi": {"iso3": "VIR", "site": ""}, + "wf": {"iso3": "WLF", "site": ""}, + "eh": {"iso3": "ESH", "site": ""}, + "ye": {"iso3": "YEM", "site": ""}, + "zm": {"iso3": "ZMB", "site": ""}, + "zw": {"iso3": "ZWE", "site": ""}, +} USER_AGENTS = [ @@ -682,31 +560,24 @@ "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.38 Safari/537.36", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36", "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36", - "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36" + "Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.62 Safari/537.36", ] - - def random_user_agent(): """ This function selects a random User-Agent from the User-Agent list, . User-Agents are used in order to avoid the limitations of the requests to morningstar.com. The User-Agent is specified on the headers of the requests and is different for every request. - + Returns: :obj:`str` - user_agent: - The returned :obj:`str` is the name of a random User-Agent, which will be passed on the - headers of a request so to avoid restrictions due to the use of multiple requests from the + The returned :obj:`str` is the name of a random User-Agent, which will be passed on the + headers of a request so to avoid restrictions due to the use of multiple requests from the same User-Agent. - + """ return random.choice(USER_AGENTS) - - - - -