From caefca49122b108d824ae2e62cce859187f1da9e Mon Sep 17 00:00:00 2001 From: alvarob96 Date: Wed, 16 Oct 2019 19:44:28 +0200 Subject: [PATCH 1/8] fix dev/ branch since it was not removed #52 --- investpy/bonds.py | 186 ---------------------- investpy/data/bonds_data.py | 218 ++++++++++++++++++++++++++ investpy/retrieval/bonds_retrieval.py | 185 ++++++++++++++++++++++ tests/test_investpy_errors.py | 34 ++-- 4 files changed, 420 insertions(+), 203 deletions(-) diff --git a/investpy/bonds.py b/investpy/bonds.py index 0f84915d..b093d851 100644 --- a/investpy/bonds.py +++ b/investpy/bonds.py @@ -2,189 +2,3 @@ # Copyright 2018-2019 Alvaro Bartolome @ alvarob96 in GitHub # See LICENSE for details. - -import pandas as pd -import pkg_resources -import requests -from lxml.html import fromstring - -from investpy.utils import user_agent - - -def retrieve_bonds(test_mode=False): - """ - This function retrieves all the available `government bonds` indexed on Investing.com, so to retrieve data - from them which will be used later in inner functions for data retrieval. Additionally, when indices are - retrieved all the information is both returned as a :obj:`pandas.DataFrame` and stored on a CSV file on a - package folder containing all the available resources. - - Args: - test_mode (:obj:`bool`): - variable to avoid time waste on travis-ci since it just needs to test the basics in order to improve code - coverage. - - Returns: - :obj:`pandas.DataFrame` - indices: - The resulting :obj:`pandas.DataFrame` contains all the bonds information if found, if not, an - empty :obj:`pandas.DataFrame` will be returned and no CSV file will be stored. - - In the case that the retrieval process of indices was successfully completed, the resulting - :obj:`pandas.DataFrame` will look like:: - - country | name | full_name | tag | id - --------|------|-----------|-----|---- - xxxxxxx | xxxx | xxxxxxxxx | xxx | xx - - Raises: - ValueError: raised if any of the introduced arguments is not valid. - FileNotFoundError: - raised if `bond_countries.csv` files do not exist or are empty. - ConnectionError: raised if GET requests did not return 200 status code. - IndexError: raised if bonds information was unavailable or not found. - - """ - - if not isinstance(test_mode, bool): - raise ValueError('ERR#0041: test_mode can just be either True or False') - - results = list() - - resource_package = 'investpy' - resource_path = '/'.join(('resources', 'bonds', 'bond_countries.csv')) - if pkg_resources.resource_exists(resource_package, resource_path): - countries = pd.read_csv(pkg_resources.resource_filename(resource_package, resource_path)) - else: - raise IOError("ERR#0062: bonds country list not found or unable to retrieve.") - - for country in countries['tag'].tolist(): - head = { - "User-Agent": user_agent.get_random(), - "X-Requested-With": "XMLHttpRequest", - "Accept": "text/html", - "Accept-Encoding": "gzip, deflate, br", - "Connection": "keep-alive", - } - - url = "https://www.investing.com/rates-bonds/" + country + "-government-bonds" - - req = requests.get(url, headers=head) - - if req.status_code != 200: - raise ConnectionError("ERR#0015: error " + str(req.status_code) + ", try again later.") - - root_ = fromstring(req.text) - path_ = root_.xpath(".//table[@id='cr1']/tbody/tr") - - if path_: - for elements_ in path_: - id_ = elements_.get('id').replace('pair_', '') - - for element_ in elements_.xpath('.//a'): - tag_ = element_.get('href') - - if str(tag_).__contains__('/rates-bonds/'): - tag_ = tag_.replace('/rates-bonds/', '') - full_name_ = element_.get('title').strip() - name = element_.text.strip() - - data = { - 'country': 'united kingdom' if country == 'uk' else 'united states' if country == 'usa' else country, - 'name': name, - 'full_name': full_name_, - 'tag': tag_, - 'id': id_, - } - - results.append(data) - - if test_mode is True: - break - - resource_package = 'investpy' - resource_path = '/'.join(('resources', 'bonds', 'bonds.csv')) - file_ = pkg_resources.resource_filename(resource_package, resource_path) - - df = pd.DataFrame(results) - - df = df.where((pd.notnull(df)), None) - df.drop_duplicates(subset="tag", keep='first', inplace=True) - df.sort_values('country', ascending=True, inplace=True) - df.reset_index(drop=True, inplace=True) - - if test_mode is False: - df.to_csv(file_, index=False) - - return df - - -def retrieve_bond_countries(test_mode=False): - """ - This function retrieves all the country names indexed in Investing.com with available government bonds to retrieve data - from. This process is made in order to dispose of a listing with all the countries from where bond information - can be retrieved from Investing.com. So on, the retrieved country listing will be used whenever the bonds are - retrieved, while looping over it. - - Args: - test_mode (:obj:`bool`): - variable to avoid time waste on travis-ci since it just needs to test the basics in order to improve code - coverage. - - Returns: - :obj:`pandas.DataFrame` - bond_countries: - The resulting :obj:`pandas.DataFrame` contains all the available countries which have available government - bonds as indexed in Investing.com, from which bond data is going to be retrieved. - - Raises: - ValueError: raised whenever any of the introduced arguments is not valid. - ConnectionError: raised if connection to Investing.com could not be established. - RuntimeError: raised if no countries were found in the Investing.com government bonds listing. - - """ - - if not isinstance(test_mode, bool): - raise ValueError('ERR#0041: test_mode can just be either True or False') - - headers = { - "User-Agent": user_agent.get_random(), - "X-Requested-With": "XMLHttpRequest", - "Accept": "text/html", - "Accept-Encoding": "gzip, deflate, br", - "Connection": "keep-alive", - } - - url = 'https://www.investing.com/rates-bonds/' - - req = requests.get(url, headers=headers) - - if req.status_code != 200: - raise ConnectionError("ERR#0015: error " + str(req.status_code) + ", try again later.") - - root = fromstring(req.text) - path = root.xpath("//select[@name='country']/option") - - countries = list() - - for element in path: - if element.get('exchangeid') != "": - obj = { - 'exchange_id': int(element.get('exchangeid')), - 'tag': element.get('value').replace('/rates-bonds/', '').replace('-government-bonds', ''), - 'country_id': int(element.get('data-country-id')), - 'country': element.text_content().lower(), - } - - countries.append(obj) - - if len(countries) == 0: - raise RuntimeError('ERR#0035: no countries could be retrieved!') - - resource_package = 'investpy' - resource_path = '/'.join(('resources', 'bonds', 'bond_countries.csv')) - file_ = pkg_resources.resource_filename(resource_package, resource_path) - - df = pd.DataFrame(countries) - - if test_mode is False: - df.to_csv(file_, index=False) - - return df diff --git a/investpy/data/bonds_data.py b/investpy/data/bonds_data.py index b093d851..f0b6495c 100644 --- a/investpy/data/bonds_data.py +++ b/investpy/data/bonds_data.py @@ -2,3 +2,221 @@ # Copyright 2018-2019 Alvaro Bartolome @ alvarob96 in GitHub # See LICENSE for details. + +import unidecode +import json + +import pandas as pd +import pkg_resources + +from investpy.retrieval.bonds_retrieval import retrieve_bonds, retrieve_bond_countries + + +def bond_countries_as_list(): + """ + This function returns a listing with all the available countries from where stocks can be retrieved, so to + let the user know which of them are available, since the parameter country is mandatory in every stock retrieval + function. Also, not just the available countries, but the required name is provided since Investing.com has a + certain country name standard and countries should be specified the same way they are in Investing.com. + + Returns: + :obj:`list` - countries: + The resulting :obj:`list` contains all the available countries with government bonds as indexed in Investing.com + + Raises: + IOError: raised when `bond_countries.csv` file is missing or empty. + + """ + + resource_package = 'investpy' + resource_path = '/'.join(('resources', 'bonds', 'bond_countries.csv')) + if pkg_resources.resource_exists(resource_package, resource_path): + countries = pd.read_csv(pkg_resources.resource_filename(resource_package, resource_path)) + else: + countries = retrieve_bond_countries(test_mode=False) + + if countries is None: + raise IOError("ERR#0062: bonds country list not found or unable to retrieve.") + else: + return countries['country'].tolist() + + +def bonds_as_df(country=None): + """ + This function retrieves all the stock data stored in `stocks.csv` file, which previously was + retrieved from Investing.com. Since the resulting object is a matrix of data, the stock data is properly + structured in rows and columns, where columns are the stock data attribute names. Additionally, country + filtering can be specified, which will make this function return not all the stored stock data, but just + the stock data of the stocks from the introduced country. + + Args: + country (:obj:`str`, optional): name of the country to retrieve all its available stocks from. + + Returns: + :obj:`pandas.DataFrame` - stocks_df: + The resulting :obj:`pandas.DataFrame` contains all the stock data from the introduced country if specified, + or from every country if None was specified, as indexed in Investing.com from the information previously + retrieved by investpy and stored on a csv file. + + So on, the resulting :obj:`pandas.DataFrame` will look like:: + + country | name | full name | isin | currency | symbol + --------|------|-----------|------|----------|-------- + xxxxxxx | xxxx | xxxxxxxxx | xxxx | xxxxxxxx | xxxxxx + + Raises: + ValueError: raised whenever any of the introduced arguments is not valid. + IOError: raised when `stocks.csv` file is missing or empty. + + """ + + if country is not None and not isinstance(country, str): + raise ValueError("ERR#0025: specified country value not valid.") + + resource_package = 'investpy' + resource_path = '/'.join(('resources', 'bonds', 'bonds.csv')) + if pkg_resources.resource_exists(resource_package, resource_path): + bonds = pd.read_csv(pkg_resources.resource_filename(resource_package, resource_path)) + else: + bonds = retrieve_bonds(test_mode=False) + + if bonds is None: + raise IOError("ERR#0062: bonds country list not found or unable to retrieve.") + + bonds.drop(columns=['tag', 'id'], inplace=True) + + if country is None: + bonds.reset_index(drop=True, inplace=True) + return bonds + elif unidecode.unidecode(country.lower()) in bond_countries_as_list(): + bonds = bonds[bonds['country'] == unidecode.unidecode(country.lower())] + bonds.reset_index(drop=True, inplace=True) + return bonds + + +def bonds_as_list(country=None): + """ + This function retrieves all the stock symbols stored in `stocks.csv` file, which contains all the + data from the stocks as previously retrieved from Investing.com. So on, this function will just return + the stock symbols which will be one of the input parameters when it comes to stock data retrieval functions + from investpy. Additionally, note that the country filtering can be applied, which is really useful since + this function just returns the symbols and in stock data retrieval functions both the symbol and the country + must be specified and they must match. + + Args: + country (:obj:`str`, optional): name of the country to retrieve all its available stocks from. + + Returns: + :obj:`list` - stocks_list: + The resulting :obj:`list` contains the all the stock symbols from the introduced country if specified, + or from every country if None was specified, as indexed in Investing.com from the information previously + retrieved by investpy and stored on a csv file. + + In case the information was successfully retrieved, the :obj:`list` of stock symbols will look like:: + + stocks_list = ['TS', 'APBR', 'GGAL', 'TXAR', 'PAMP', ...] + + Raises: + ValueError: raised whenever any of the introduced arguments is not valid. + IOError: raised when `stocks.csv` file is missing or empty. + """ + + if country is not None and not isinstance(country, str): + raise ValueError("ERR#0025: specified country value not valid.") + + resource_package = 'investpy' + resource_path = '/'.join(('resources', 'bonds', 'bonds.csv')) + if pkg_resources.resource_exists(resource_package, resource_path): + bonds = pd.read_csv(pkg_resources.resource_filename(resource_package, resource_path)) + else: + bonds = retrieve_bonds(test_mode=False) + + if bonds is None: + raise IOError("ERR#0062: bonds country list not found or unable to retrieve.") + + bonds.drop(columns=['tag', 'id'], inplace=True) + + if country is None: + return bonds['symbol'].tolist() + elif unidecode.unidecode(country.lower()) in bond_countries_as_list(): + return bonds[bonds['country'] == unidecode.unidecode(country.lower())]['symbol'].tolist() + + +def bonds_as_dict(country=None, columns=None, as_json=False): + """ + This function retrieves all the stock information stored in the `stocks.csv` file and formats it as a + Python dictionary which contains the same information as the file, but every row is a :obj:`dict` and + all of them are contained in a :obj:`list`. Note that the dictionary structure is the same one as the + JSON structure. Some optional paramaters can be specified such as the country, columns or as_json, which + are a filtering by country so not to return all the stocks but just the ones from the introduced country, + the column names that want to be retrieved in case of needing just some columns to avoid unnecessary information + load, and whether the information wants to be returned as a JSON object or as a dictionary; respectively. + + Args: + country (:obj:`str`, optional): name of the country to retrieve all its available stocks from. + columns (:obj:`list`, optional):column names of the stock data to retrieve, can be: + as_json (:obj:`bool`, optional): if True the returned data will be a :obj:`json` object, if False, a :obj:`list` of :obj:`dict`. + + Returns: + :obj:`list` of :obj:`dict` OR :obj:`json` - equities_dict: + The resulting :obj:`list` of :obj:`dict` contains the retrieved data from every stock as indexed in Investing.com from + the information previously retrieved by investpy and stored on a csv file. + + In case the information was successfully retrieved, the :obj:`list` of :obj:`dict` will look like:: + + { + 'country': country, + 'name': name, + 'full_name': full_name, + 'tag': tag, + 'isin': isin, + 'id': id, + 'currency': currency, + 'symbol': symbol, + } + + Raises: + ValueError: raised whenever any of the introduced arguments is not valid. + IOError: raised when `stocks.csv` file is missing or empty. + + """ + + if country is not None and not isinstance(country, str): + raise ValueError("ERR#0025: specified country value not valid.") + + if not isinstance(as_json, bool): + raise ValueError("ERR#0002: as_json argument can just be True or False, bool type.") + + resource_package = 'investpy' + resource_path = '/'.join(('resources', 'bonds', 'bonds.csv')) + if pkg_resources.resource_exists(resource_package, resource_path): + bonds = pd.read_csv(pkg_resources.resource_filename(resource_package, resource_path)) + else: + bonds = retrieve_bonds(test_mode=False) + + if bonds is None: + raise IOError("ERR#0062: bonds country list not found or unable to retrieve.") + + bonds.drop(columns=['tag', 'id'], inplace=True) + + if columns is None: + columns = bonds.columns.tolist() + else: + if not isinstance(columns, list): + raise ValueError("ERR#0020: specified columns argument is not a list, it can just be list type.") + + if not all(column in bonds.columns.tolist() for column in columns): + raise ValueError("ERR#0063: specified columns does not exist, available columns are " + "") + + if country is None: + if as_json: + return json.dumps(bonds[columns].to_dict(orient='records')) + else: + return bonds[columns].to_dict(orient='records') + elif country in bond_countries_as_list(): + if as_json: + return json.dumps( + bonds[bonds['country'] == unidecode.unidecode(country.lower())][columns].to_dict(orient='records')) + else: + return bonds[bonds['country'] == unidecode.unidecode(country.lower())][columns].to_dict(orient='records') diff --git a/investpy/retrieval/bonds_retrieval.py b/investpy/retrieval/bonds_retrieval.py index 81b93fa8..0f84915d 100644 --- a/investpy/retrieval/bonds_retrieval.py +++ b/investpy/retrieval/bonds_retrieval.py @@ -3,3 +3,188 @@ # Copyright 2018-2019 Alvaro Bartolome @ alvarob96 in GitHub # See LICENSE for details. +import pandas as pd +import pkg_resources +import requests +from lxml.html import fromstring + +from investpy.utils import user_agent + + +def retrieve_bonds(test_mode=False): + """ + This function retrieves all the available `government bonds` indexed on Investing.com, so to retrieve data + from them which will be used later in inner functions for data retrieval. Additionally, when indices are + retrieved all the information is both returned as a :obj:`pandas.DataFrame` and stored on a CSV file on a + package folder containing all the available resources. + + Args: + test_mode (:obj:`bool`): + variable to avoid time waste on travis-ci since it just needs to test the basics in order to improve code + coverage. + + Returns: + :obj:`pandas.DataFrame` - indices: + The resulting :obj:`pandas.DataFrame` contains all the bonds information if found, if not, an + empty :obj:`pandas.DataFrame` will be returned and no CSV file will be stored. + + In the case that the retrieval process of indices was successfully completed, the resulting + :obj:`pandas.DataFrame` will look like:: + + country | name | full_name | tag | id + --------|------|-----------|-----|---- + xxxxxxx | xxxx | xxxxxxxxx | xxx | xx + + Raises: + ValueError: raised if any of the introduced arguments is not valid. + FileNotFoundError: + raised if `bond_countries.csv` files do not exist or are empty. + ConnectionError: raised if GET requests did not return 200 status code. + IndexError: raised if bonds information was unavailable or not found. + + """ + + if not isinstance(test_mode, bool): + raise ValueError('ERR#0041: test_mode can just be either True or False') + + results = list() + + resource_package = 'investpy' + resource_path = '/'.join(('resources', 'bonds', 'bond_countries.csv')) + if pkg_resources.resource_exists(resource_package, resource_path): + countries = pd.read_csv(pkg_resources.resource_filename(resource_package, resource_path)) + else: + raise IOError("ERR#0062: bonds country list not found or unable to retrieve.") + + for country in countries['tag'].tolist(): + head = { + "User-Agent": user_agent.get_random(), + "X-Requested-With": "XMLHttpRequest", + "Accept": "text/html", + "Accept-Encoding": "gzip, deflate, br", + "Connection": "keep-alive", + } + + url = "https://www.investing.com/rates-bonds/" + country + "-government-bonds" + + req = requests.get(url, headers=head) + + if req.status_code != 200: + raise ConnectionError("ERR#0015: error " + str(req.status_code) + ", try again later.") + + root_ = fromstring(req.text) + path_ = root_.xpath(".//table[@id='cr1']/tbody/tr") + + if path_: + for elements_ in path_: + id_ = elements_.get('id').replace('pair_', '') + + for element_ in elements_.xpath('.//a'): + tag_ = element_.get('href') + + if str(tag_).__contains__('/rates-bonds/'): + tag_ = tag_.replace('/rates-bonds/', '') + full_name_ = element_.get('title').strip() + name = element_.text.strip() + + data = { + 'country': 'united kingdom' if country == 'uk' else 'united states' if country == 'usa' else country, + 'name': name, + 'full_name': full_name_, + 'tag': tag_, + 'id': id_, + } + + results.append(data) + + if test_mode is True: + break + + resource_package = 'investpy' + resource_path = '/'.join(('resources', 'bonds', 'bonds.csv')) + file_ = pkg_resources.resource_filename(resource_package, resource_path) + + df = pd.DataFrame(results) + + df = df.where((pd.notnull(df)), None) + df.drop_duplicates(subset="tag", keep='first', inplace=True) + df.sort_values('country', ascending=True, inplace=True) + df.reset_index(drop=True, inplace=True) + + if test_mode is False: + df.to_csv(file_, index=False) + + return df + + +def retrieve_bond_countries(test_mode=False): + """ + This function retrieves all the country names indexed in Investing.com with available government bonds to retrieve data + from. This process is made in order to dispose of a listing with all the countries from where bond information + can be retrieved from Investing.com. So on, the retrieved country listing will be used whenever the bonds are + retrieved, while looping over it. + + Args: + test_mode (:obj:`bool`): + variable to avoid time waste on travis-ci since it just needs to test the basics in order to improve code + coverage. + + Returns: + :obj:`pandas.DataFrame` - bond_countries: + The resulting :obj:`pandas.DataFrame` contains all the available countries which have available government + bonds as indexed in Investing.com, from which bond data is going to be retrieved. + + Raises: + ValueError: raised whenever any of the introduced arguments is not valid. + ConnectionError: raised if connection to Investing.com could not be established. + RuntimeError: raised if no countries were found in the Investing.com government bonds listing. + + """ + + if not isinstance(test_mode, bool): + raise ValueError('ERR#0041: test_mode can just be either True or False') + + headers = { + "User-Agent": user_agent.get_random(), + "X-Requested-With": "XMLHttpRequest", + "Accept": "text/html", + "Accept-Encoding": "gzip, deflate, br", + "Connection": "keep-alive", + } + + url = 'https://www.investing.com/rates-bonds/' + + req = requests.get(url, headers=headers) + + if req.status_code != 200: + raise ConnectionError("ERR#0015: error " + str(req.status_code) + ", try again later.") + + root = fromstring(req.text) + path = root.xpath("//select[@name='country']/option") + + countries = list() + + for element in path: + if element.get('exchangeid') != "": + obj = { + 'exchange_id': int(element.get('exchangeid')), + 'tag': element.get('value').replace('/rates-bonds/', '').replace('-government-bonds', ''), + 'country_id': int(element.get('data-country-id')), + 'country': element.text_content().lower(), + } + + countries.append(obj) + + if len(countries) == 0: + raise RuntimeError('ERR#0035: no countries could be retrieved!') + + resource_package = 'investpy' + resource_path = '/'.join(('resources', 'bonds', 'bond_countries.csv')) + file_ = pkg_resources.resource_filename(resource_package, resource_path) + + df = pd.DataFrame(countries) + + if test_mode is False: + df.to_csv(file_, index=False) + + return df diff --git a/tests/test_investpy_errors.py b/tests/test_investpy_errors.py index 5c788d56..76359867 100644 --- a/tests/test_investpy_errors.py +++ b/tests/test_investpy_errors.py @@ -1827,22 +1827,22 @@ def test_currency_crosses_errors(): pass -# def test_user_agent_errors(): -# """ -# This function raises errors on user_agent functions -# """ -# -# clear_file() -# try: -# get_random() -# except: -# pass -# -# delete_file() -# try: -# get_random() -# except: -# pass +def test_user_agent_errors(): + """ + This function raises errors on user_agent functions + """ + + clear_file() + try: + get_random() + except: + pass + + delete_file() + try: + get_random() + except: + pass if __name__ == '__main__': @@ -1851,4 +1851,4 @@ def test_currency_crosses_errors(): test_etfs_errors() test_indices_errors() test_currency_crosses_errors() - # test_user_agent_errors() + test_user_agent_errors() From b06774d44babb811a3371a6eab0f4202f125231f Mon Sep 17 00:00:00 2001 From: alvarob96 Date: Wed, 16 Oct 2019 20:35:05 +0200 Subject: [PATCH 2/8] bonds recent & historical data created #52 - bond data retrieval functions created for both recent and historical data created - function to search bonds created - Data class methods to parse bond data included - docstrings need to be updated since the ones used in bonds.py are the ones from stocks.py - tests/ are missing and docs/ need to be checked and updated --- investpy/bonds.py | 676 +++++++++++++++++++++++++++++++++++++++++ investpy/utils/Data.py | 18 ++ 2 files changed, 694 insertions(+) diff --git a/investpy/bonds.py b/investpy/bonds.py index b093d851..4eabab9f 100644 --- a/investpy/bonds.py +++ b/investpy/bonds.py @@ -2,3 +2,679 @@ # Copyright 2018-2019 Alvaro Bartolome @ alvarob96 in GitHub # See LICENSE for details. + +from datetime import datetime, date +import json +from random import randint +import logging + +import pandas as pd +import pkg_resources +import requests +import unidecode +from lxml.html import fromstring + +from investpy.utils import user_agent +from investpy.utils.Data import Data + +from investpy.data.bonds_data import bonds_as_df, bonds_as_list, bonds_as_dict +from investpy.data.bonds_data import bond_countries_as_list + + +def get_bonds(country=None): + """ + This function retrieves all the stock data stored in `stocks.csv` file, which previously was + retrieved from Investing.com. Since the resulting object is a matrix of data, the stock data is properly + structured in rows and columns, where columns are the stock data attribute names. Additionally, country + filtering can be specified, which will make this function return not all the stored stock data, but just + the stock data of the stocks from the introduced country. + + Args: + country (:obj:`str`, optional): name of the country to retrieve all its available stocks from. + + Returns: + :obj:`pandas.DataFrame` - stocks_df: + The resulting :obj:`pandas.DataFrame` contains all the stock data from the introduced country if specified, + or from every country if None was specified, as indexed in Investing.com from the information previously + retrieved by investpy and stored on a csv file. + + So on, the resulting :obj:`pandas.DataFrame` will look like:: + + country | name | full name | isin | currency | symbol + --------|------|-----------|------|----------|-------- + xxxxxxx | xxxx | xxxxxxxxx | xxxx | xxxxxxxx | xxxxxx + + Raises: + ValueError: raised whenever any of the introduced arguments is not valid. + IOError: raised when `stocks.csv` file is missing or empty. + + """ + + return bonds_as_df(country) + + +def get_bonds_list(country=None): + """ + This function retrieves all the stock symbols stored in `stocks.csv` file, which contains all the + data from the stocks as previously retrieved from Investing.com. So on, this function will just return + the stock symbols which will be one of the input parameters when it comes to stock data retrieval functions + from investpy. Additionally, note that the country filtering can be applied, which is really useful since + this function just returns the symbols and in stock data retrieval functions both the symbol and the country + must be specified and they must match. + + Args: + country (:obj:`str`, optional): name of the country to retrieve all its available stocks from. + + Returns: + :obj:`list` - stocks_list: + The resulting :obj:`list` contains the all the stock symbols from the introduced country if specified, + or from every country if None was specified, as indexed in Investing.com from the information previously + retrieved by investpy and stored on a csv file. + + In case the information was successfully retrieved, the :obj:`list` of stock symbols will look like:: + + stocks_list = ['TS', 'APBR', 'GGAL', 'TXAR', 'PAMP', ...] + + Raises: + ValueError: raised whenever any of the introduced arguments is not valid. + IOError: raised when `stocks.csv` file is missing or empty. + """ + + return bonds_as_list(country) + + +def get_bonds_dict(country=None, columns=None, as_json=False): + """ + This function retrieves all the stock information stored in the `stocks.csv` file and formats it as a + Python dictionary which contains the same information as the file, but every row is a :obj:`dict` and + all of them are contained in a :obj:`list`. Note that the dictionary structure is the same one as the + JSON structure. Some optional paramaters can be specified such as the country, columns or as_json, which + are a filtering by country so not to return all the stocks but just the ones from the introduced country, + the column names that want to be retrieved in case of needing just some columns to avoid unnecessary information + load, and whether the information wants to be returned as a JSON object or as a dictionary; respectively. + + Args: + country (:obj:`str`, optional): name of the country to retrieve all its available stocks from. + columns (:obj:`list`, optional):column names of the stock data to retrieve, can be: + as_json (:obj:`bool`, optional): if True the returned data will be a :obj:`json` object, if False, a :obj:`list` of :obj:`dict`. + + Returns: + :obj:`list` of :obj:`dict` OR :obj:`json` - equities_dict: + The resulting :obj:`list` of :obj:`dict` contains the retrieved data from every stock as indexed in Investing.com from + the information previously retrieved by investpy and stored on a csv file. + + In case the information was successfully retrieved, the :obj:`list` of :obj:`dict` will look like:: + + { + 'country': country, + 'name': name, + 'full_name': full_name, + 'tag': tag, + 'id': id, + } + + Raises: + ValueError: raised whenever any of the introduced arguments is not valid. + IOError: raised when `bonds.csv` file is missing or empty. + + """ + + return bonds_as_dict(country=country, columns=columns, as_json=as_json) + + +def get_bond_countries(): + """ + This function returns a listing with all the available countries from where stocks can be retrieved, so to + let the user know which of them are available, since the parameter country is mandatory in every stock retrieval + function. Also, not just the available countries, but the required name is provided since Investing.com has a + certain country name standard and countries should be specified the same way they are in Investing.com. + + Returns: + :obj:`list` - countries: + The resulting :obj:`list` contains all the available countries with stocks as indexed in Investing.com + + Raises: + IOError: raised when `stock_countries.csv` file is missing or empty. + + """ + + return bond_countries_as_list() + + +def get_bond_recent_data(bond, country, as_json=False, order='ascending', debug=False): + """ + This function retrieves recent historical data from the introduced stock from Investing.com. So on, the recent data + of the introduced stock from the specified country will be retrieved and returned as a :obj:`pandas.DataFrame` if + the parameters are valid and the request to Investing.com succeeds. Note that additionally some optional parameters + can be specified: as_json, order and debug, which let the user decide if the data is going to be returned as a + :obj:`json` or not, if the historical data is going to be ordered ascending or descending (where the index is the date) + and whether debug messages are going to be printed or not, respectively. + + Args: + stock (:obj:`str`): symbol of the stock to retrieve recent historical data from. + country (:obj:`str`): name of the country from where the stock is. + as_json (:obj:`bool`, optional): + to determine the format of the output data, either a :obj:`pandas.DataFrame` if False and a :obj:`json` if True. + order (:obj:`str`, optional): to define the order of the retrieved data which can either be ascending or descending. + debug (:obj:`bool`, optional): + optional argument to either show or hide debug messages on log, either True or False, respectively. + + Returns: + :obj:`pandas.DataFrame` or :obj:`json`: + The function can return either a :obj:`pandas.DataFrame` or a :obj:`json` object, containing the retrieved + recent data of the specified stock from the specified country. So on, the resulting dataframe contains the + open, high, low, close and volume values for the selected stock on market days and the currency in which those + values are presented. + + The resulting recent data, in case that the default parameters were applied, will look like:: + + date || open | high | low | close | volume | currency + -----||----------------------------------------------- + xxxx || xxxx | xxxx | xxx | xxxxx | xxxxxx | xxxxxxxx + + but in case that as_json parameter was defined as True, then the output will be:: + + { + name: name, + recent: [ + dd/mm/yyyy: { + open: x, + high: x, + low: x, + close: x, + }, + ... + ] + } + + Raises: + ValueError: raised whenever any of the introduced arguments is not valid or errored. + IOError: raised if stocks object/file was not found or unable to retrieve. + RuntimeError: raised if the introduced stock/country was not found or did not match any of the existing ones. + ConnectionError: raised if connection to Investing.com could not be established. + IndexError: raised if stock recent data was unavailable or not found in Investing.com. + + Examples: + >>> investpy.get_bond_recent_data(bond='bbva', country='spain') + Open High Low Close + Date + 2019-08-13 4.263 4.395 4.230 4.353 + 2019-08-14 4.322 4.325 4.215 4.244 + 2019-08-15 4.281 4.298 4.187 4.234 + 2019-08-16 4.234 4.375 4.208 4.365 + 2019-08-19 4.396 4.425 4.269 4.269 + + """ + + if not bond: + raise ValueError("ERR#0066: bond parameter is mandatory and must be a valid bond name.") + + if not isinstance(bond, str): + raise ValueError("ERR#0067: bond argument needs to be a str.") + + if country is None: + raise ValueError("ERR#0039: country can not be None, it should be a str.") + + if country is not None and not isinstance(country, str): + raise ValueError("ERR#0025: specified country value not valid.") + + if not isinstance(as_json, bool): + raise ValueError("ERR#0002: as_json argument can just be True or False, bool type.") + + if order not in ['ascending', 'asc', 'descending', 'desc']: + raise ValueError("ERR#0003: order argument can just be ascending (asc) or descending (desc), str type.") + + if not isinstance(debug, bool): + raise ValueError("ERR#0033: debug argument can just be a boolean value, either True or False.") + + resource_package = 'investpy' + resource_path = '/'.join(('resources', 'bonds', 'bonds.csv')) + if pkg_resources.resource_exists(resource_package, resource_path): + bonds = pd.read_csv(pkg_resources.resource_filename(resource_package, resource_path)) + else: + raise FileNotFoundError("ERR#0064: bonds file not found or errored.") + + if bonds is None: + raise IOError("ERR#0065: bonds object not found or unable to retrieve.") + + if unidecode.unidecode(country.lower()) not in get_bond_countries(): + raise RuntimeError("ERR#0034: country " + country.lower() + " not found, check if it is correct.") + + bonds = bonds[bonds['country'] == unidecode.unidecode(country.lower())] + + bond = bond.strip() + bond = bond.lower() + + if unidecode.unidecode(bond) not in [unidecode.unidecode(value.lower()) for value in bonds['name'].tolist()]: + raise RuntimeError("ERR#0068: bond " + bond + " not found, check if it is correct.") + + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger('investpy') + + if debug is False: + logger.disabled = True + else: + logger.disabled = False + + logger.info('Searching introduced bond on Investing.com') + + id_ = bonds.loc[(bonds['name'].str.lower() == bond).idxmax(), 'id'] + name = bonds.loc[(bonds['name'].str.lower() == bond).idxmax(), 'name'] + full_name = bonds.loc[(bonds['name'].str.lower() == bond).idxmax(), 'full_name'] + + logger.info(str(bond) + ' found on Investing.com') + + header = full_name + " Bond Yield Historical Data" + + params = { + "curr_id": id_, + "smlID": str(randint(1000000, 99999999)), + "header": header, + "interval_sec": "Daily", + "sort_col": "date", + "sort_ord": "DESC", + "action": "historical_data" + } + + head = { + "User-Agent": user_agent.get_random(), + "X-Requested-With": "XMLHttpRequest", + "Accept": "text/html", + "Accept-Encoding": "gzip, deflate, br", + "Connection": "keep-alive", + } + + url = "https://www.investing.com/instruments/HistoricalDataAjax" + + logger.info('Request sent to Investing.com!') + + req = requests.post(url, headers=head, data=params) + + if req.status_code != 200: + raise ConnectionError("ERR#0015: error " + str(req.status_code) + ", try again later.") + + logger.info('Request to Investing.com data succeeded with code ' + str(req.status_code) + '!') + + root_ = fromstring(req.text) + path_ = root_.xpath(".//table[@id='curr_table']/tbody/tr") + result = list() + + if path_: + logger.info('Data parsing process starting...') + + for elements_ in path_: + info = [] + for nested_ in elements_.xpath(".//td"): + info.append(nested_.get('data-real-value')) + + bond_date = datetime.fromtimestamp(int(info[0])) + bond_date = date(bond_date.year, bond_date.month, bond_date.day) + bond_close = float(info[1]) + bond_open = float(info[2]) + bond_high = float(info[3]) + bond_low = float(info[4]) + + result.insert(len(result), + Data(bond_date, bond_open, bond_high, bond_low, + bond_close, None, None)) + + if order in ['ascending', 'asc']: + result = result[::-1] + elif order in ['descending', 'desc']: + result = result + + logger.info('Data parsing process finished...') + + if as_json is True: + json_ = {'name': name, + 'recent': + [value.bond_as_json() for value in result] + } + + return json.dumps(json_, sort_keys=False) + elif as_json is False: + df = pd.DataFrame.from_records([value.bond_to_dict() for value in result]) + df.set_index('Date', inplace=True) + + return df + else: + raise RuntimeError("ERR#0004: data retrieval error while scraping.") + + +def get_bond_historical_data(bond, country, from_date, to_date, as_json=False, order='ascending', debug=False): + """ + This function retrieves historical data from the introduced stock from Investing.com. So on, the historical data + of the introduced stock from the specified country in the specified data range will be retrieved and returned as + a :obj:`pandas.DataFrame` if the parameters are valid and the request to Investing.com succeeds. Note that additionally + some optional parameters can be specified: as_json, order and debug, which let the user decide if the data is going to + be returned as a :obj:`json` or not, if the historical data is going to be ordered ascending or descending (where the + index is the date) and whether debug messages are going to be printed or not, respectively. + + Args: + bond (:obj:`str`): symbol of the stock to retrieve historical data from. + country (:obj:`str`): name of the country from where the stock is. + from_date (:obj:`str`): date formatted as `dd/mm/yyyy`, since when data is going to be retrieved. + to_date (:obj:`str`): date formatted as `dd/mm/yyyy`, until when data is going to be retrieved. + as_json (:obj:`bool`, optional): + to determine the format of the output data, either a :obj:`pandas.DataFrame` if False and a :obj:`json` if True. + order (:obj:`str`, optional): to define the order of the retrieved data which can either be ascending or descending. + debug (:obj:`bool`, optional): + optional argument to either show or hide debug messages on log, either True or False, respectively. + + Returns: + :obj:`pandas.DataFrame` or :obj:`json`: + The function returns a either a :obj:`pandas.DataFrame` or a :obj:`json` file containing the retrieved + recent data from the specified stock via argument. The dataset contains the open, high, low, close and + volume values for the selected stock on market days. + + The returned data is case we use default arguments will look like:: + + date || open | high | low | close | volume | currency + -----||----------------------------------------------- + xxxx || xxxx | xxxx | xxx | xxxxx | xxxxxx | xxxxxxxx + + but if we define `as_json=True`, then the output will be:: + + { + name: name, + historical: [ + dd/mm/yyyy: { + open: x, + high: x, + low: x, + close: x, + volume: x, + currency: x + }, + ... + ] + } + + Raises: + ValueError: raised whenever any of the introduced arguments is not valid or errored. + IOError: raised if stocks object/file was not found or unable to retrieve. + RuntimeError: raised if the introduced stock/country was not found or did not match any of the existing ones. + ConnectionError: raised if connection to Investing.com could not be established. + IndexError: raised if stock historical data was unavailable or not found in Investing.com. + + Examples: + >>> investpy.get_bond_historical_data(bond='bbva', country='spain', from_date='01/01/2010', to_date='01/01/2019') + Open High Low Close Volume Currency + Date + 2010-01-04 12.73 12.96 12.73 12.96 0 EUR + 2010-01-05 13.00 13.11 12.97 13.09 0 EUR + 2010-01-06 13.03 13.17 13.02 13.12 0 EUR + 2010-01-07 13.02 13.11 12.93 13.05 0 EUR + 2010-01-08 13.12 13.22 13.04 13.18 0 EUR + + """ + + if not bond: + raise ValueError("ERR#0066: bond parameter is mandatory and must be a valid bond name.") + + if not isinstance(bond, str): + raise ValueError("ERR#0067: bond argument needs to be a str.") + + if country is None: + raise ValueError("ERR#0039: country can not be None, it should be a str.") + + if country is not None and not isinstance(country, str): + raise ValueError("ERR#0025: specified country value not valid.") + + if not isinstance(as_json, bool): + raise ValueError("ERR#0002: as_json argument can just be True or False, bool type.") + + if order not in ['ascending', 'asc', 'descending', 'desc']: + raise ValueError("ERR#0003: order argument can just be ascending (asc) or descending (desc), str type.") + + if not isinstance(debug, bool): + raise ValueError("ERR#0033: debug argument can just be a boolean value, either True or False.") + + try: + datetime.strptime(from_date, '%d/%m/%Y') + except ValueError: + raise ValueError("ERR#0011: incorrect from_date date format, it should be 'dd/mm/yyyy'.") + + try: + datetime.strptime(to_date, '%d/%m/%Y') + except ValueError: + raise ValueError("ERR#0012: incorrect to_date format, it should be 'dd/mm/yyyy'.") + + start_date = datetime.strptime(from_date, '%d/%m/%Y') + end_date = datetime.strptime(to_date, '%d/%m/%Y') + + if start_date >= end_date: + raise ValueError("ERR#0032: to_date should be greater than from_date, both formatted as 'dd/mm/yyyy'.") + + date_interval = { + 'intervals': [], + } + + flag = True + + while flag is True: + diff = end_date.year - start_date.year + + if diff > 20: + obj = { + 'start': start_date.strftime('%d/%m/%Y'), + 'end': start_date.replace(year=start_date.year + 20).strftime('%d/%m/%Y'), + } + + date_interval['intervals'].append(obj) + + start_date = start_date.replace(year=start_date.year + 20) + else: + obj = { + 'start': start_date.strftime('%d/%m/%Y'), + 'end': end_date.strftime('%d/%m/%Y'), + } + + date_interval['intervals'].append(obj) + + flag = False + + interval_limit = len(date_interval['intervals']) + interval_counter = 0 + + data_flag = False + + resource_package = 'investpy' + resource_path = '/'.join(('resources', 'bonds', 'bonds.csv')) + if pkg_resources.resource_exists(resource_package, resource_path): + bonds = pd.read_csv(pkg_resources.resource_filename(resource_package, resource_path)) + else: + raise FileNotFoundError("ERR#0064: bonds file not found or errored.") + + if bonds is None: + raise IOError("ERR#0065: bonds object not found or unable to retrieve.") + + if unidecode.unidecode(country.lower()) not in get_bond_countries(): + raise RuntimeError("ERR#0034: country " + country.lower() + " not found, check if it is correct.") + + bonds = bonds[bonds['country'] == unidecode.unidecode(country.lower())] + + bond = bond.strip() + bond = bond.lower() + + if unidecode.unidecode(bond) not in [unidecode.unidecode(value.lower()) for value in bonds['name'].tolist()]: + raise RuntimeError("ERR#0068: bond " + bond + " not found, check if it is correct.") + + logging.basicConfig(level=logging.INFO) + logger = logging.getLogger('investpy') + + if debug is False: + logger.disabled = True + else: + logger.disabled = False + + logger.info('Searching introduced bond on Investing.com') + + id_ = bonds.loc[(bonds['name'].str.lower() == bond).idxmax(), 'id'] + name = bonds.loc[(bonds['name'].str.lower() == bond).idxmax(), 'name'] + full_name = bonds.loc[(bonds['name'].str.lower() == bond).idxmax(), 'full_name'] + + logger.info(str(bond) + ' found on Investing.com') + + final = list() + + logger.info('Data parsing process starting...') + + header = full_name + " Bond Yield Historical Data" + + for index in range(len(date_interval['intervals'])): + interval_counter += 1 + + params = { + "curr_id": id_, + "smlID": str(randint(1000000, 99999999)), + "header": header, + "st_date": date_interval['intervals'][index]['start'], + "end_date": date_interval['intervals'][index]['end'], + "interval_sec": "Daily", + "sort_col": "date", + "sort_ord": "DESC", + "action": "historical_data" + } + + head = { + "User-Agent": user_agent.get_random(), + "X-Requested-With": "XMLHttpRequest", + "Accept": "text/html", + "Accept-Encoding": "gzip, deflate, br", + "Connection": "keep-alive", + } + + url = "https://www.investing.com/instruments/HistoricalDataAjax" + + req = requests.post(url, headers=head, data=params) + + if req.status_code != 200: + raise ConnectionError("ERR#0015: error " + str(req.status_code) + ", try again later.") + + if not req.text: + continue + + root_ = fromstring(req.text) + path_ = root_.xpath(".//table[@id='curr_table']/tbody/tr") + + result = list() + + if path_: + for elements_ in path_: + if elements_.xpath(".//td")[0].text_content() == 'No results found': + if interval_counter < interval_limit: + data_flag = False + else: + raise IndexError("ERR#0069: bond information unavailable or not found.") + else: + data_flag = True + + if data_flag is True: + info = [] + for nested_ in elements_.xpath(".//td"): + info.append(nested_.get('data-real-value')) + + bond_date = datetime.fromtimestamp(int(info[0])) + bond_date = date(bond_date.year, bond_date.month, bond_date.day) + bond_close = float(info[1]) + bond_open = float(info[2]) + bond_high = float(info[3]) + bond_low = float(info[4]) + + result.insert(len(result), + Data(bond_date, bond_open, bond_high, bond_low, + bond_close, None, None)) + + if data_flag is True: + if order in ['ascending', 'asc']: + result = result[::-1] + elif order in ['descending', 'desc']: + result = result + + if as_json is True: + json_ = {'name': name, + 'historical': + [value.bond_as_json() for value in result] + } + + final.append(json_) + elif as_json is False: + df = pd.DataFrame.from_records([value.bond_to_dict() for value in result]) + df.set_index('Date', inplace=True) + + final.append(df) + else: + raise RuntimeError("ERR#0004: data retrieval error while scraping.") + + logger.info('Data parsing process finished...') + + if as_json is True: + return json.dumps(final[0], sort_keys=False) + elif as_json is False: + return pd.concat(final) + + +def search_bonds(by, value): + """ + This function searches stocks by the introduced value for the specified field. This means that this function + is going to search if there is a value that matches the introduced one for the specified field which is the + `stocks.csv` column name to search in. Available fields to search stocks are 'name', 'full_name' and 'isin'. + + Args: + by (:obj:`str`): name of the field to search for, which is the column name which can be: 'name', 'full_name' or 'isin'. + value (:obj:`str`): value of the field to search for, which is the value that is going to be searched. + + Returns: + :obj:`pandas.DataFrame` - search_result: + The resulting :obj:`pandas.DataFrame` contains the search results from the given query, which is + any match of the specified value in the specified field. If there are no results for the given query, + an error will be raised, but otherwise the resulting :obj:`pandas.DataFrame` will contain all the + available stocks that match the introduced query. + + Raises: + ValueError: raised if any of the introduced parameters is not valid or errored. + IOError: raised if data could not be retrieved due to file error. + RuntimeError: raised if no results were found for the introduced value in the introduced field. + + """ + + available_search_fields = ['name', 'full_name'] + + if not by: + raise ValueError('ERR#0006: the introduced field to search is mandatory and should be a str.') + + if not isinstance(by, str): + raise ValueError('ERR#0006: the introduced field to search is mandatory and should be a str.') + + if isinstance(by, str) and by not in available_search_fields: + raise ValueError('ERR#0026: the introduced field to search can either just be ' + + ' or '.join(available_search_fields)) + + if not value: + raise ValueError('ERR#0017: the introduced value to search is mandatory and should be a str.') + + if not isinstance(value, str): + raise ValueError('ERR#0017: the introduced value to search is mandatory and should be a str.') + + resource_package = 'investpy' + resource_path = '/'.join(('resources', 'bonds', 'bonds.csv')) + if pkg_resources.resource_exists(resource_package, resource_path): + bonds = pd.read_csv(pkg_resources.resource_filename(resource_package, resource_path)) + else: + raise FileNotFoundError("ERR#0064: bonds file not found or errored.") + + if bonds is None: + raise IOError("ERR#0065: bonds object not found or unable to retrieve.") + + bonds['matches'] = bonds[by].str.contains(value, case=False) + + search_result = bonds.loc[bonds['matches'] == True].copy() + + if len(search_result) == 0: + raise RuntimeError('ERR#0043: no results were found for the introduced ' + str(by) + '.') + + search_result.drop(columns=['tag', 'id', 'matches'], inplace=True) + search_result.reset_index(drop=True, inplace=True) + + return search_result diff --git a/investpy/utils/Data.py b/investpy/utils/Data.py index 97a18721..16d4c595 100644 --- a/investpy/utils/Data.py +++ b/investpy/utils/Data.py @@ -142,3 +142,21 @@ def currency_cross_to_dict(self): 'Volume': self.volume, 'Currency': self.currency, } + + def bond_to_dict(self): + return { + 'Date': self.date, + 'Open': self.open, + 'High': self.high, + 'Low': self.low, + 'Close': self.close, + } + + def bond_as_json(self): + return { + 'date': self.date.strftime('%d/%m/%Y'), + 'open': self.open, + 'high': self.high, + 'low': self.low, + 'close': self.close, + } From e7ae305b19814a08214cca2b5eef0373bf2fcaf6 Mon Sep 17 00:00:00 2001 From: alvarob96 Date: Wed, 23 Oct 2019 12:54:23 +0200 Subject: [PATCH 3/8] added tests/ on success for bond data retrieval #52 - added tests to improve codecov and test that the main bond data retrieval functions work properly - tests on exception raise need to be added so to improve codecov and test that all the exceptions are raising as expected - updated README with minor changes - bond data retrieval functions added to the __init__.py file of investpy --- README.md | 12 ++--- investpy/__init__.py | 2 + investpy/data/bonds_data.py | 4 +- tests/test_investpy.py | 102 +++++++++++++++++++++++++++++++++++- 4 files changed, 110 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 158254a1..6a852e02 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ pip on the terminal by typing: ``$ pip install investpy==0.9.7`` -Every package used is listed in [requirements.txt](https://github.com/alvarob96/investpy/blob/master/requirements.txt) +Every package used is listed in [requirements.txt](https://github.com/alvarob96/investpy/blob/master/requirements.txt) file, which can also be installed via pip: ``$ pip install -r requirements.txt`` @@ -119,12 +119,12 @@ as indexed in Investing.com. All the functions definitions and usage can be found in the [Documentation](https://investpy.readthedocs.io/)! -## Utilites +## Utilities -Since investpy is just intended to retrieva data from different financial products as indexed in Investing.com, -the development of support modules is presented, which implement an additional functionallity based on investpy data. -Note that anyone can contribute to this section by creating any package, module or utility which uses this package. So on, the -ones already created are going to be presented, since they are intended to be used combined with investpy: +Since investpy is intended to retrieve data from different financial products as indexed in Investing.com, the development +of some support modules, which implement an additional functionallity based on investpy data, is presented. Note that anyone +can contribute to this section by creating any package, module or utility which uses this package. So on, the ones already +created are going to be presented, since they are intended to be used combined with investpy: - [investpy_portfolio](https://github.com/alvarob96/investpy_portfolio/): is a Python package to generate stock portfolios. - [trendet](https://github.com/alvarob96/trendet/): is a Python package for trend detection on stock time series data. diff --git a/investpy/__init__.py b/investpy/__init__.py index 241da23c..9965d872 100644 --- a/investpy/__init__.py +++ b/investpy/__init__.py @@ -17,3 +17,5 @@ from investpy.currency_crosses import get_currency_crosses, get_currency_crosses_list, get_currency_crosses_dict, \ get_available_currencies, get_currency_cross_recent_data, get_currency_cross_historical_data, \ search_currency_crosses +from investpy.bonds import get_bonds, get_bonds_list, get_bonds_dict, get_bond_countries, get_bond_recent_data, \ + get_bond_historical_data, search_bonds diff --git a/investpy/data/bonds_data.py b/investpy/data/bonds_data.py index f0b6495c..a22a202b 100644 --- a/investpy/data/bonds_data.py +++ b/investpy/data/bonds_data.py @@ -137,9 +137,9 @@ def bonds_as_list(country=None): bonds.drop(columns=['tag', 'id'], inplace=True) if country is None: - return bonds['symbol'].tolist() + return bonds['name'].tolist() elif unidecode.unidecode(country.lower()) in bond_countries_as_list(): - return bonds[bonds['country'] == unidecode.unidecode(country.lower())]['symbol'].tolist() + return bonds[bonds['country'] == unidecode.unidecode(country.lower())]['name'].tolist() def bonds_as_dict(country=None, columns=None, as_json=False): diff --git a/tests/test_investpy.py b/tests/test_investpy.py index 62ee3be2..8fa51095 100644 --- a/tests/test_investpy.py +++ b/tests/test_investpy.py @@ -10,9 +10,9 @@ from investpy.retrieval.currency_crosses_retrieval import retrieve_currency_crosses, retrieve_currency_cross_continents from investpy.retrieval.etfs_retrieval import retrieve_etfs from investpy.retrieval.funds_retrieval import retrieve_funds, retrieve_fund_countries -from investpy.retrieval.indices_retrieval import retrieve_indices, retrieve_index_countries, \ - retrieve_global_indices_countries +from investpy.retrieval.indices_retrieval import retrieve_indices, retrieve_index_countries, retrieve_global_indices_countries from investpy.retrieval.stocks_retrieval import retrieve_stocks, retrieve_stock_countries +from investpy.retrieval.bonds_retrieval import retrieve_bonds, retrieve_bond_countries def test_investpy(): @@ -582,6 +582,103 @@ def test_investpy_currency_crosses(): retrieve_currency_cross_continents() +def test_investpy_bonds(): + """ + This function checks that bond data retrieval functions listed in investpy work properly. + """ + + params = [ + { + 'country': 'spain', + }, + { + 'country': None, + }, + ] + + for param in params: + investpy.get_bonds(country=param['country']) + investpy.get_bonds_list(country=param['country']) + + params = [ + { + 'country': None, + 'columns': ['full_name', 'name'], + 'as_json': True + }, + { + 'country': None, + 'columns': ['full_name', 'name'], + 'as_json': False + }, + { + 'country': 'spain', + 'columns': ['full_name', 'name'], + 'as_json': True + }, + { + 'country': 'spain', + 'columns': ['full_name', 'name'], + 'as_json': False + }, + { + 'country': 'spain', + 'columns': None, + 'as_json': False + }, + ] + + for param in params: + investpy.get_bonds_dict(country=param['country'], + columns=param['columns'], + as_json=param['as_json']) + + investpy.get_bond_countries() + + params = [ + { + 'as_json': True, + 'order': 'ascending', + 'debug': False + }, + { + 'as_json': False, + 'order': 'ascending', + 'debug': True + }, + { + 'as_json': True, + 'order': 'descending', + 'debug': False + }, + { + 'as_json': False, + 'order': 'descending', + 'debug': False + }, + ] + + for param in params: + investpy.get_bond_recent_data(bond='Spain 30Y', + country='spain', + as_json=param['as_json'], + order=param['order'], + debug=param['debug']) + + investpy.get_bond_historical_data(bond='Spain 30Y', + country='spain', + from_date='01/01/1990', + to_date='01/01/2019', + as_json=param['as_json'], + order=param['order'], + debug=param['debug']) + + investpy.search_bonds(by='name', value='Spain') + + retrieve_bonds(test_mode=True) + retrieve_bond_countries(test_mode=True) + + if __name__ == '__main__': test_investpy() test_investpy_stocks() @@ -589,3 +686,4 @@ def test_investpy_currency_crosses(): test_investpy_etfs() test_investpy_indices() test_investpy_currency_crosses() + test_investpy_bonds() From f4bc5eabca144200522331db7950e25cf3e0daaa Mon Sep 17 00:00:00 2001 From: alvarob96 Date: Wed, 23 Oct 2019 14:34:49 +0200 Subject: [PATCH 4/8] updated bond functions docstrings & docs/ - documented docstrings for bond data retrieval functions - updated docs/ with bonds documentation --- docs/_build/doctrees/api.doctree | Bin 2615 -> 2679 bytes docs/_build/doctrees/disclaimer.doctree | Bin 3860 -> 3860 bytes docs/_build/doctrees/environment.pickle | Bin 37312 -> 40674 bytes docs/_build/doctrees/etfs_api.doctree | Bin 101486 -> 58532 bytes docs/_build/doctrees/funds_api.doctree | Bin 99512 -> 62841 bytes docs/_build/doctrees/index.doctree | Bin 5275 -> 5275 bytes docs/_build/doctrees/information.doctree | Bin 8554 -> 8554 bytes docs/_build/doctrees/installation.doctree | Bin 13246 -> 13246 bytes docs/_build/doctrees/introduction.doctree | Bin 14838 -> 14838 bytes docs/_build/doctrees/main_api.doctree | Bin 2857 -> 266074 bytes docs/_build/doctrees/model.doctree | Bin 5498 -> 5498 bytes docs/_build/html/_sources/api.rst.txt | 5 +- docs/_build/html/api.html | 6 + docs/_build/html/etfs_api.html | 253 ++---- docs/_build/html/funds_api.html | 318 +++---- docs/_build/html/genindex.html | 100 ++- docs/_build/html/index.html | 3 + docs/_build/html/information.html | 4 +- docs/_build/html/main_api.html | 967 ++++++++++++++++++++++ docs/_build/html/py-modindex.html | 5 - docs/api.rst | 5 +- docs/bonds_api.rst | 7 + docs/currency_crosses_api.rst | 7 + docs/indices_api.rst | 7 + investpy/bonds.py | 164 ++-- investpy/data/bonds_data.py | 130 +-- investpy/data/stocks_data.py | 4 +- 27 files changed, 1374 insertions(+), 611 deletions(-) create mode 100644 docs/bonds_api.rst create mode 100644 docs/currency_crosses_api.rst create mode 100644 docs/indices_api.rst diff --git a/docs/_build/doctrees/api.doctree b/docs/_build/doctrees/api.doctree index 477239c88baaa85e70694baf5bf7dc9e7e873636..65180d8eba23cd2852ab643b88e96d4cfe92f2a5 100644 GIT binary patch delta 193 zcmdlk@?C_bfn{pWMiz5MUH#DF)S_bj#GJCkqWq-9qLLDQm(=3ylKcYw5I1-I%)GMH z;*x?&{gnLVV*SY(j0cSTdbl(5QZkcMi{lduGN-gn@#_&uE-flb%}cI~PcF(YE=H2! zOv=wonXJI1%$DJv;hEvR*_lb4S-hi%FSV#BzbGE4CqE@KFMUc6YiNku=0et1W|^dn i_~IVUyu{qplFEYA;wiOLGLrm?GvtAE{AM9eRYm|J#79&B delta 135 zcmew^vR#Cwfn}=BMiz5MU1zJ9(BjmhVj!8A6XTMfT$-DjS5h3~o>}5iniP|nSC(2_ zQcxL_lAl~0GdY9tz~m$*>CMed;>_YrJ$$J}MfpYXsd>ryDVcfcQ+ilSOVT!PWNl@Z liOGm8?%~W!%uOw+EJ!V$QadFh#;-U-9!N)S4&qd01OPTIGFSir diff --git a/docs/_build/doctrees/disclaimer.doctree b/docs/_build/doctrees/disclaimer.doctree index 654ae5cc5ea4cdbb4a54e3b0a3aa33f3b3dbf8ed..a15e493f10520f5d62f19e9df7f3c3d948893c54 100644 GIT binary patch delta 67 zcmbOtH$`rP8KbU#XmM&$v3_DsSz=LsQesg_iM~r}adt_5fqsaayMAU~S!!`fL8X35 XesZz?=48ef9L%91ZkzqMjNDEiNgjj7iB) VE{@rp%=m(XxwIs0vme(n764AQ8E*gp diff --git a/docs/_build/doctrees/environment.pickle b/docs/_build/doctrees/environment.pickle index 0eba038a2a550759f09d3f190af42350d5f589b1..07292f4fb70824f49b616e79572647143d8f7c93 100644 GIT binary patch literal 40674 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 J5mH=T{{N>t@ZA6a literal 37312 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 GrT+(`diUP| diff --git a/docs/_build/doctrees/etfs_api.doctree b/docs/_build/doctrees/etfs_api.doctree index 83a89d9488f7a6e180d43c319dfeb1a1a42846d4..c53e16e67dcb16c46a3aabbcff89ef22b76cc392 100644 GIT binary patch literal 58532 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 literal 101486 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 NW+}r6=Yy$<{|`Mw?n?jw diff --git a/docs/_build/doctrees/funds_api.doctree b/docs/_build/doctrees/funds_api.doctree index ed80a11740a836044415a30ed20cfcbe8a7ed02d..f384a7b5d76ec1208ddb226804951fc8cd4bad20 100644 GIT binary patch literal 62841 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 SiKlABm?aM*d0{X=`~Lxn#EBdL diff --git a/docs/_build/doctrees/index.doctree b/docs/_build/doctrees/index.doctree index a0bf01f655c10e1645aec86a39b54c93fc154047..ab557ffe1dc2bbc314d53721f8608fa8654d67e6 100644 GIT binary patch delta 67 zcmbQOIa_nXN+w|f=d0A X{N!T&&DWS7@i2#mxNY9e|9}MmWJejm delta 67 zcmbQOIa_nXN+w-rtC-N@)S_Y_nV1vflAm0fo0?Zr9OIr@;!&CulbKhRT3k|48IzKq WTpY9c8q*^l=F*b1&Aa&@umAviz8eJq diff --git a/docs/_build/doctrees/information.doctree b/docs/_build/doctrees/information.doctree index dc8fd62f9678ef4a8b540a93b7b0821309af7212..925514be5187858bc61d9c3ea62cd31ff3f044df 100644 GIT binary patch delta 67 zcmaFm^vY?2FQcx0XmM&$v3_DsSz=LsQesg_iM~r}adt_5fqsaayMAU~S!!`fL8X35 WesZz?<|@W{lFXqYZkwg0{n-F`k{Emd delta 67 zcmaFm^vY?2FQcxrRZM7cYEdzeOw5UK$xklLP0cGQj&aW{@hDA-$;>NDEiNgjj7iB) VE{@q;#W+urxwIs0v$V878vu|A8NmPm diff --git a/docs/_build/doctrees/installation.doctree b/docs/_build/doctrees/installation.doctree index fea22034a24f979e62cf1111679f15d25858d150..2ad33a8ddc4c2298ee5d8cb1a47b919688173295 100644 GIT binary patch delta 67 zcmdm&zAt@)2&1lkXmM&$v3_DsSz=LsQesg_iM~r}adt_5fqsaayMAU~S!!`fL8X35 XesZz?W=FNDEiNgjj7iB) VE{@sk$k?gLTw0R0`K;DQ4gh~-8leCH diff --git a/docs/_build/doctrees/introduction.doctree b/docs/_build/doctrees/introduction.doctree index 881946ea94c70c2b153aabd7e1a35cac7192e607..c9d758191ade19e47d25ce265c23b3ece126ec16 100644 GIT binary patch delta 67 zcmexX{H=I{C8Mr>XmM&$v3_DsSz=LsQesg_iM~r}adt_5fqsaayMAU~S!!`fL8X35 XesZz?<}}7T`plsrZkt^VH*x>~mi`&W delta 67 zcmexX{H=I{C8MshRZM7cYEdzeOw5UK$xklLP0cGQj&aW{@hDA-$;>NDEiNgjj7iB) VE{@rp#&}1cxwIs0v#a4o4gjuo8w&sc diff --git a/docs/_build/doctrees/main_api.doctree b/docs/_build/doctrees/main_api.doctree index ffc7aa50bb19203d95f5877317e42d2699375649..b9914baaf6b34f9a1b8ead2b066de0ae45ba394a 100644 GIT binary patch literal 266074 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ÐlK((G>0eSTih_}}vqU~tplZy=S*=eglA#U>s( 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
Ri%px+SE#($0!cZgZW@OAcFK6N7Re(sCIe7 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 a>TxCVd5!+eYGJ5CL!BHIsMmW_WB(ss97FQ} delta 239 zcmcaLPhh1OTLa5fIc|oHYzj=e&Q>v@#i>QbKr%5W#w9ye-g$z}oC}Re5hI>XL$ez-q?dE(e G%8USoid8`X diff --git a/docs/_build/doctrees/model.doctree b/docs/_build/doctrees/model.doctree index ca0af7830bc3b87f0ef1a7a38a852aa74c5fe47e..ec5f5e852058bf6a33689a80e8db541714c88446 100644 GIT binary patch delta 81 zcmeyR^-F7mHlwb7XmM&$v3_DsSz=LsQesg_iM~r}adt_5fqsaayMAU~S!!`fL8X35 lesZz?<_N|}e%{a!H(iSNDEiNgjj7iB) jE{@q8!5GQUTUwH)YcZuqCbg(2zi6_ffaK=u0*hGy4;mix diff --git a/docs/_build/html/_sources/api.rst.txt b/docs/_build/html/_sources/api.rst.txt index b03aed88..5a6e23cf 100644 --- a/docs/_build/html/_sources/api.rst.txt +++ b/docs/_build/html/_sources/api.rst.txt @@ -7,4 +7,7 @@ API Reference main_api.rst stocks_api.rst funds_api.rst - etfs_api.rst \ No newline at end of file + etfs_api.rst + indices_api.rst + currency_crosses_api.rst + bonds_api.rst diff --git a/docs/_build/html/api.html b/docs/_build/html/api.html index 36fd3749..bb3dfbf5 100644 --- a/docs/_build/html/api.html +++ b/docs/_build/html/api.html @@ -97,6 +97,9 @@
  • investpy.stocks
  • investpy.funds
  • investpy.etfs
  • +
  • investpy.indices
  • +
  • investpy.currency_crosses
  • +
  • investpy.bonds
  • Additional Information
  • @@ -173,6 +176,9 @@

    API Referenceinvestpy.stocks
  • investpy.funds
  • investpy.etfs
  • +
  • investpy.indices
  • +
  • investpy.currency_crosses
  • +
  • investpy.bonds
  • diff --git a/docs/_build/html/etfs_api.html b/docs/_build/html/etfs_api.html index 571e828d..0dbcdde1 100644 --- a/docs/_build/html/etfs_api.html +++ b/docs/_build/html/etfs_api.html @@ -35,7 +35,7 @@ - + @@ -97,6 +97,9 @@
  • investpy.stocks
  • investpy.funds
  • investpy.etfs
  • +
  • investpy.indices
  • +
  • investpy.currency_crosses
  • +
  • investpy.bonds
  • Additional Information
  • @@ -170,8 +173,8 @@

    investpy.etfs

    -
    -investpy.etfs.get_etf_countries()
    +
    +investpy.etfs.etf_countries_as_list()

    This function retrieves all the available countries to retrieve etfs from, as the listed countries are the ones indexed on Investing.com. The purpose of this function is to list the countries which have available etfs according to Investing.com data, so to ease the @@ -197,152 +200,8 @@

    -
    -investpy.etfs.get_etf_historical_data(etf, country, from_date, to_date, as_json=False, order='ascending', debug=False)
    -

    This function retrieves historical data from the introduced etf from Investing -via Web Scraping on the introduced date range. The resulting data can it either be -stored in a pandas.DataFrame or in a json object with ascending or descending order.

    -
    -
    Parameters
    -
      -
    • etf (str) – name of the etf to retrieve recent historical data from.

    • -
    • country (str) – name of the country from where the etf is.

    • -
    • from_date (str) – date as str formatted as dd/mm/yyyy, from where data is going to be retrieved.

    • -
    • to_date (str) – date as str formatted as dd/mm/yyyy, until where data is going to be retrieved.

    • -
    • as_json (bool, optional) – to determine the format of the output data (pandas.DataFrame or json).

    • -
    • order (str, optional) – optional argument to define the order of the retrieved data (ascending, asc or descending, desc).

    • -
    • debug (bool, optional) – optional argument to either show or hide debug messages on log, True or False, respectively.

    • -
    -
    -
    Returns
    -

    The function returns either a pandas.DataFrame or a json file containing the retrieved -recent data from the specified etf via argument. The dataset contains the open, high, low and close -values for the selected etf on market days.

    -

    The returned data is case we use default arguments will look like:

    -
    date || open | high | low | close | currency
    ------||--------------------------------------
    -xxxx || xxxx | xxxx | xxx | xxxxx | xxxxxxxx
    -
    -
    -

    but if we define as_json=True, then the output will be:

    -
    {
    -    name: name,
    -    historical: [
    -        {
    -            date: dd/mm/yyyy,
    -            open: x,
    -            high: x,
    -            low: x,
    -            close: x,
    -            currency: x
    -        },
    -        ...
    -    ]
    -}
    -
    -
    -

    -
    -
    Return type
    -

    pandas.DataFrame or json

    -
    -
    Raises
    -
      -
    • ValueError – argument error.

    • -
    • IOError – etfs object/file not found or unable to retrieve.

    • -
    • RuntimeError – introduced etf does not match any of the indexed ones.

    • -
    • ConnectionError – if GET requests does not return 200 status code.

    • -
    • IndexError – if etf information was unavailable or not found.

    • -
    -
    -
    -

    Examples

    -
    >>> investpy.get_etf_historical_data(etf='bbva accion dj eurostoxx 50', country='spain', from_date='01/01/2010', to_date='01/01/2019', as_json=False, order='ascending', debug=False)
    -                 Open   High    Low  Close Currency
    -    Date
    -    2011-12-07  23.70  23.70  23.70  23.62      EUR
    -    2011-12-08  23.53  23.60  23.15  23.04      EUR
    -    2011-12-09  23.36  23.60  23.36  23.62      EUR
    -    2011-12-12  23.15  23.26  23.00  22.88      EUR
    -    2011-12-13  22.88  22.88  22.88  22.80      EUR
    -
    -
    -
    - -
    -
    -investpy.etfs.get_etf_recent_data(etf, country, as_json=False, order='ascending', debug=False)
    -

    This function retrieves recent historical data from the introduced etf from Investing -via Web Scraping. The resulting data can it either be stored in a pandas.DataFrame or in a -json file, with ascending or descending order.

    -
    -
    Parameters
    -
      -
    • etf (str) – name of the etf to retrieve recent historical data from.

    • -
    • country (str) – name of the country from where the etf is.

    • -
    • as_json (bool, optional) – optional argument to determine the format of the output data (pandas.DataFrame or json).

    • -
    • order (str, optional) – optional argument to define the order of the retrieved data (ascending, asc or descending, desc).

    • -
    • debug (bool, optional) – optional argument to either show or hide debug messages on log, True or False, respectively.

    • -
    -
    -
    Returns
    -

    The function returns either a pandas.DataFrame or a json file containing the retrieved -recent data from the specified etf via argument. The dataset contains the open, high, low and close -values for the selected etf on market days.

    -

    The returned data is case we use default arguments will look like:

    -
    date || open | high | low | close | currency
    ------||--------------------------------------
    -xxxx || xxxx | xxxx | xxx | xxxxx | xxxxxxxx
    -
    -
    -

    but if we define as_json=True, then the output will be:

    -
    {
    -    name: name,
    -    recent: [
    -        {
    -            date: dd/mm/yyyy,
    -            open: x,
    -            high: x,
    -            low: x,
    -            close: x,
    -            currency: x
    -        },
    -        ...
    -    ]
    -}
    -
    -
    -

    -
    -
    Return type
    -

    pandas.DataFrame or json

    -
    -
    Raises
    -
      -
    • ValueError – argument error.

    • -
    • IOError – etfs object/file not found or unable to retrieve.

    • -
    • RuntimeError – introduced etf does not match any of the indexed ones.

    • -
    • ConnectionError – if GET requests does not return 200 status code.

    • -
    • IndexError – if etf information was unavailable or not found.

    • -
    -
    -
    -

    Examples

    -
    >>> investpy.get_etf_recent_data(etf='bbva accion dj eurostoxx 50', country='spain', as_json=False, order='ascending', debug=False)
    -                  Open    High     Low   Close Currency
    -    Date
    -    2019-08-13  33.115  33.780  32.985  33.585      EUR
    -    2019-08-14  33.335  33.335  32.880  32.905      EUR
    -    2019-08-15  32.790  32.925  32.455  32.845      EUR
    -    2019-08-16  33.115  33.200  33.115  33.305      EUR
    -    2019-08-19  33.605  33.735  33.490  33.685      EUR
    -
    -
    -
    - -
    -
    -investpy.etfs.get_etfs(country=None)
    +
    +investpy.etfs.etfs_as_df(country=None)

    This function retrieves all the available countries to retrieve etfs from, as the listed countries are the ones indexed on Investing.com. The purpose of this function is to list the countries which have available etfs according to Investing.com data, so to ease the @@ -358,9 +217,9 @@ are returned.

    In the case that the file reading of etfs.csv or the retrieval process from Investing.com was successfully completed, the resulting pandas.DataFrame will look like:

    -
    country | name | symbol | tag | id | currency
    ---------|------|--------|-----|----|----------
    -xxxxxxx | xxxx | xxxxxx | xxx | xx | xxxxxxxx
    +
    country | country_code | name | symbol | tag | id
    +--------|--------------|------|--------|-----|----
    +xxxxxxx | xxxxxxxxxxxx | xxxx | xxxxxx | xxx | xx
     

    @@ -378,19 +237,19 @@
    -
    -investpy.etfs.get_etfs_dict(country=None, columns=None, as_json=False)
    +
    +investpy.etfs.etfs_as_dict(country=None, columns=None, as_json=False)

    This function retrieves all the available etfs indexed on Investing.com, already stored on etfs.csv, which if does not exists, will be created by investpy.etfs.retrieve_etfs(). This function also allows the user to specify which country do they want to retrieve data from, or from every listed country; the columns which the user wants to be included on the resulting -dict; and the output of the function (dict or json).

    +dict; and the output of the function will either be a dict or a json.

    Parameters
    • country (str, optional) – name of the country to retrieve all its available etfs from.

    • columns (list, optional) – names of the columns of the etf data to retrieve <country, country_code, id, name, symbol, tag>

    • -
    • as_json (bool, optional) – value to determine the format of the output data (dict or json).

    • +
    • as_json (bool, optional) – value to determine the format of the output data which can either be a dict or a json.

    Returns
    @@ -399,11 +258,11 @@

    In case the information was successfully retrieved, the dict will look like:

    {
         'country': country,
    +    'country_code': country_code,
         'id': id,
         'tag': tag,
         'name': name,
    -    'symbol': symbol,
    -    'currency': currency
    +    'symbol': symbol
     }
     
    @@ -422,8 +281,8 @@
    -
    -investpy.etfs.get_etfs_list(country=None)
    +
    +investpy.etfs.etfs_as_list(country=None)

    This function retrieves all the available etfs indexed on Investing.com, already stored on etfs.csv, which if does not exists, will be created by investpy.etfs.retrieve_etfs(). This function also allows the users to specify which country do they want to retrieve data from or if they @@ -458,69 +317,65 @@

    -
    -investpy.etfs.get_etfs_overview(country, as_json=False)
    -

    This function retrieves an object containing all the real time data available for all the ETFs from a country, -such as the ETF names, symbols, current value, etc. as indexed in Investing.com. So on, the main usage of this -function is to get an overview on all the available ETFs from a country.

    +
    +investpy.etfs.retrieve_etf_info(tag)
    +

    This function retrieves additional information from the specified etf as indexed in Investing.com, in order to add +more information to etfs.csv which can later be useful. Currently just the currency value is retrieved, since it +is needed so to determine in which currency the historical data values are.

    Parameters
    -
      -
    • country (str) – name of the country to retrieve all its available etf data from.

    • -
    • as_json (bool, optional) – optional argument to determine the format of the output data (pandas.DataFrame or json).

    • -
    +

    tag (str) – is the tag of the etf to retrieve the information from as indexed by Investing.com.

    Returns
    -

    The resulting pandas.DataFrame contains all the data available in Investing.com from all the ETFs -from a country in order to get an overview of it.

    -

    If the retrieval process succeeded, the resulting pandas.DataFrame should look like:

    -
    name | symbol | last | change | turnover
    -----------------------------------------
    -xxxx | xxxxxx | xxxx | xxxxxx | xxxxxxxx
    -
    -
    -

    +

    The resulting dict contains the needed information for the etfs listing.

    Return type
    -

    pandas.DataFrame - etfs_overview

    +

    dict - info

    Raises
      -
    • ValueError – raised if there was any argument error.

    • -
    • FileNotFoundError – raised when etf_countries.csv file is missing.

    • -
    • RuntimeError – raised it the introduced country does not match any of the indexed ones.

    • ConnectionError – raised if GET requests does not return 200 status code.

    • +
    • IndexError – raised if the information from the etf was not found or unable to retrieve.

    -
    -investpy.etfs.search_etfs(by, value)
    -

    This function searches etfs by the introduced value for the specified field. This means that this function -is going to search if there is a value that matches the introduced value for the specified field which is the -etfs.csv column name to search in. Available fields to search etfs are ‘name’ and ‘symbol’.

    +
    +investpy.etfs.retrieve_etfs(test_mode=False)
    +

    This function retrieves all the available world etfs indexed on Investing.com, so to +retrieve data from them which will be used later for inner functions for data retrieval. +All the etfs available can be found at: https://es.investing.com/etfs/world-etfs. Additionally, +when etfs are retrieved all the meta-information is both returned as a pandas.DataFrame +and stored on a CSV file on a package folder containing all the available resources. +Note that maybe some of the information contained in the resulting pandas.DataFrame is useless as it is +just used for inner function purposes.

    Parameters
    -
      -
    • by (str) – name of the field to search for, which is the column name (‘name’ or ‘symbol’).

    • -
    • value (str) – value of the field to search for, which is the str that is going to be searched.

    • -
    +

    test_mode (bool) – variable to avoid time waste on travis-ci since it just needs to test the basics in order to improve code +coverage.

    Returns
    -

    The resulting pandas.DataFrame contains the search results from the given query (the specified value -in the specified field). If there are no results and error will be raised, but otherwise this -pandas.DataFrame will contain all the available field values that match the introduced query.

    +

    The resulting pandas.DataFrame contains all the world etfs meta-information if found, if not, an +empty pandas.DataFrame will be returned and no CSV file will be stored.

    +

    In the case that the retrieval process of world etfs was successfully completed, the resulting +pandas.DataFrame will look like:

    +
    country | country_code | name | symbol | tag | id
    +--------|--------------|------|--------|-----|----
    +xxxxxxx | xxxxxxxxxxxx | xxxx | xxxxxx | xxx | xx
    +
    +
    +

    Return type
    -

    pandas.DataFrame - search_result

    +

    pandas.DataFrame - etfs

    Raises
      -
    • ValueError – raised if any of the introduced params is not valid or errored.

    • -
    • IOError – raised if data could not be retrieved due to file error.

    • -
    • RuntimeError – raised if no results were found for the introduced value in the introduced field.

    • +
    • ValueError – if any of the introduced arguments is not valid.

    • +
    • FileNotFoundError – raised when etf_countries.csv file is missing.

    • +
    • ConnectionError – if GET requests does not return 200 status code.

    @@ -536,7 +391,7 @@