From 9dff170bb71a0cadb97c3fe6bdbff9fb1c6a8254 Mon Sep 17 00:00:00 2001 From: BlueFox Date: Wed, 17 Jan 2024 22:30:05 +0100 Subject: [PATCH] Got things to work finally. Still things to do (one bug discovered, see TODOs) --- SimpleStockData.py | 80 +++++++++++++++++++++++++++++++--------------- 1 file changed, 55 insertions(+), 25 deletions(-) diff --git a/SimpleStockData.py b/SimpleStockData.py index 416bf10..41a087c 100755 --- a/SimpleStockData.py +++ b/SimpleStockData.py @@ -3,7 +3,7 @@ import pandas as pd class SimpleStockData: - def __init__(self, ticker_list: list, period_start: str, period_end: str, to_currency: str = ""): + def __init__(self, ticker_list: list, period_start: str, period_end: str, to_currency: str, ohcl: str = "Close"): """ :param period_start: start date (format YYYY-MM-DD) @@ -11,14 +11,16 @@ class SimpleStockData: end date (format YYYY-MM-DD) :param ticker_list: list containing all stocks/exchange rates (yfinance considers both as "Tickers") + Example: [] :param to_currency: - currency to convert rates to + currency to convert rates to (e.g. EUR) """ self.ticker_list = ticker_list - self.to_currency = to_currency + self.to_currency = to_currency.upper() # make it uppercase self._period_start = period_start self._period_end = period_end + self._ohcl = ohcl.capitalize() self._exchange_df = None # Mapping: time mapped to conversion factor, to get the right converted value per date self._create_exchange_dataframe() # initialize self.exchange_df attribute @@ -43,7 +45,7 @@ class SimpleStockData: The mapping is recreated by this function following the information in the _from_currency_list. return: - boolean + boolean - success or not """ # check if a to_currency is even given @@ -54,26 +56,29 @@ class SimpleStockData: _from_currency_list = [] for i in range(len(self.ticker_list)): # to get all indexes; this adds an entry for each currency add_currency = f"{self.get_info(i, 'currency')}{self.to_currency}=X" # Format: "fffttt=X" f=from, t=to + add_currency = add_currency.upper() # make everything uppercase # for the case that FROM and TO are equal, just don't download the data (as conversion factor's 1) if add_currency == f"{self.to_currency}{self.to_currency}=X": pass elif add_currency not in _from_currency_list: # add a new item if not already there _from_currency_list.append(add_currency) - print(_from_currency_list) - # now the real process begins tickers = yf.Tickers(" ".join(_from_currency_list)) # create a new Ticker instance with all wanted currencies - exchange_rates = [] - for i in tickers.tickers: # get all the history of each currency conversion factors - # for simplicity: using the conversion factor of closing (.Close at the end) - exchange_rates.append(tickers.tickers[i].history(start=self._period_start, end=self._period_end).Close) - self._exchange_df = pd.DataFrame( - exchange_rates).T # transpose the dataframe (imagine just switching rows and columns) - self._exchange_df.columns = _from_currency_list # set the right names for the columns in the dataframe - self._exchange_df[ - f"{self.to_currency}{self.to_currency}=X"] = 1.0 # for FROM and TO being equal: set factor to 1 + exchange_rates = [] # temporary variable where all exchange rates are stored in (as objects of pd.Series) + for er_name in tickers.tickers: # get all the history of each currency conversion factors + # for simplicity: using the conversion factor of closing (.Close at the end) + exchange_rates.append((tickers.tickers[er_name].history(start=self._period_start, end=self._period_end)[self._ohcl], er_name)) + # now exchange_rates contains tuples of the form (ticker, er_name) where er_name is the name of the + # currency ticker, used only internal in this method (_from_currency_list variable). The index is now taken + # to set the right names for every row in the dataframe + + # now, the rates are taken from the exchange_rates list and are all wrapped up in a beautiful DataFrame + self._exchange_df = pd.DataFrame() + for exchange_rate, er_name in exchange_rates: + self._exchange_df[er_name] = exchange_rate + self._exchange_df[f"{self.to_currency}{self.to_currency}=X"] = 1.0 # for FROM and TO being equal: set factor to 1 return True @@ -102,20 +107,45 @@ class SimpleStockData: :param convert: decides if the resulting values should be converted to the specified to_convert currency (given at object creation) - :return: pandas.DataFrame + :return: pandas.DataFrame (with extra columns for the converted value if wanted) """ - # TODO: Not working. Something is wrong with the access to the _exchange_df via []. - - result = self._get_history(idx, interval) + result = self._get_history(idx, interval)[self._ohcl].to_frame() result["Ticker Index"] = idx - ticker_currency = self.get_info(idx, "currency") + ticker_currency = self.get_info(idx, "currency").upper() # upper it as sometimes it doesn't fit if convert: - value_list = ["Open", "High", "Low", "Close"] - for value in value_list: - result["rating_name"] = f"{ticker_currency}{self.to_currency}=X" - result["rating"] = self._exchange_df[f"{ticker_currency}{self.to_currency}=X"] - result[f"{value}.conv"] = result[value] / result["rating"] + exrate_name = f"{ticker_currency}{self.to_currency}=X" + result["ex_rate_name"] = exrate_name + ex_rate_series = self._exchange_df[exrate_name] + + # now there's a result dataframe with ticker, currency, rate name etc. as column names + # to add only matching ex rates per day (sometimes there are more days with exchange rates recorded than + # share prices), the result df has to be transposed so that the following function df.append can select + # by columns. TODO: implement the bug fix when not the exact same timestamps and amount of data are given in both the series and the df + """ + result = result.T + + ex_rate_df = ex_rate_df.to_list() + print(ex_rate_df) + result.append(ex_rate_df[result.columns], ignore_index=True) # magic - see above :) + result = result.T # transpose back + result[f"{self._ohcl} in {self.to_currency}"] = result[self._ohcl] / result["ex_rate"] + """ + + result["ex_rate"] = ex_rate_series.to_list() + result[f"{self._ohcl} in {self.to_currency}"] = result[self._ohcl] / result["ex_rate"] + return result + + +def test(): + ssd = SimpleStockData(["RHM.DE", "BAS.DE", "AZN.L"], "2024-01-02", "2024-01-18", "EUR") + print(ssd.get_info(0)) + print(ssd.get_info(1)) + print(ssd.get_info(2)) + + print(ssd.get_history(0)) + print(ssd.get_history(1)) + print(ssd.get_history(2)) \ No newline at end of file