def backtest(self, prices, ref_prices, params): m = params["m"] x = params["x"] interval = params[Ct.interval_key()] mon_ret_df = Financials.pct_change(prices) p_return = IntradayResistanceBreakout.calculate(mon_ret_df, m, x) cagr_params = {Ct.interval_key(): interval} cagr = CAGR.get_cagr(p_return, cagr_params) sharpe_params = {"rf": 0.025, Ct.interval_key(): interval} sharpe = Sharpe.get_sharpe(p_return, sharpe_params) max_dd = MaxDrawdown.get_max_drawdown(p_return) # calculating overall strategy's KPIs mon_ret_df = Financials.pct_change(ref_prices) cagr_ref = CAGR.get_cagr(mon_ret_df, cagr_params) sharpe_params = {"rf": 0.025, Ct.interval_key(): interval} sharpe_ref = Sharpe.get_sharpe(mon_ret_df, sharpe_params) max_dd_ref = MaxDrawdown.get_max_drawdown(mon_ret_df) print("CAGR - Portfolio: {}".format(cagr[Ct.cagr_key()]["mon_ret"])) print("Sharpe - Portfolio: {}".format( sharpe[Ct.sharpe_key()]["mon_ret"])) print("MAX-Drawdown - Portfolio: {}".format( max_dd[Ct.max_drawdown_key()]["mon_ret"])) print("CAGR - Ref: {}".format(cagr_ref[Ct.cagr_key()]["^DJI"])) print("Sharpe - Ref: {}".format(sharpe_ref[Ct.sharpe_key()]["^DJI"])) print("MAX-Drawdown - Ref: {}".format( max_dd_ref[Ct.max_drawdown_key()]["^DJI"])) IntradayResistanceBreakout.plot(p_return, mon_ret_df)
def get_sortino(input_df, params=None): if params is None: params = {} if "rf" not in params.keys(): # USA: risk free rate params = {"rf": 0.0144} rf = params["rf"] "function to calculate Sortino ratio ; rf is the risk free rate" cagr = CAGR.get_cagr(input_df, params) vol_params = params vol_params[Ct.neg_volatility_key()] = True neg_vol = Volatility.get_volatility(input_df, vol_params) result_df = pd.DataFrame() result_df[Sortino.kpi_name] = (cagr.loc[:, Ct.cagr_key()] - rf) / neg_vol.loc[:, Ct.volatility_key()] result_df.rename(index={0: Sortino.kpi_name}, inplace=True) return result_df
def get_volatility(input_df, params=None): """function to calculate annualized volatility of a trading strategy""" if params is None: raise ValueError("Please set the corresponding Interval parameter" "{interval:Ct.INTERVAL.MONTH|Ct.INTERVAL.DAY}") if Ct.neg_volatility_key() not in params.keys(): params[Ct.neg_volatility_key()] = False reference_days = KPI.get_reference_days(params) negative = params[Ct.neg_volatility_key()] df = input_df.copy() df.columns = df.columns.droplevel(1) result_df = pd.DataFrame() # Whole volatility was calculated if negative is False: result_df[Volatility.kpi_name] = (df.std() * np.sqrt(reference_days)) else: df_neg = df.where(df < 0, 0) result_df[Volatility.kpi_name] = (df_neg.std() * np.sqrt(reference_days)) return result_df
def calculate(self): """function to calculate On Balance Volume""" super().calculate() daily_ret_key = Constants.get_key("daily_ret") direction_key = Constants.get_key("direction") volume_adjusted_key = Constants.get_key("vol_adj") df_result = [] for ticker in self.tickers: df_data = self.df[ticker].copy() df_data[daily_ret_key] = df_data[self.prices_key].pct_change() df_data[direction_key] = np.where(df_data[daily_ret_key] >= 0, 1, -1) df_data.iloc[0].at[direction_key] = 0 df_data[volume_adjusted_key] = df_data[ self.volume_key] * df_data[direction_key] df_data[self.indicator_key] = df_data[volume_adjusted_key].cumsum() df_result.append(df_data.loc[:, [self.indicator_key]]) self.df = pd.concat(df_result, axis=1, keys=self.tickers) return self.df
def set_input_data(self, df): super().set_input_data(df) # Set dataFrame keys self.low_key = Constants.get_low_key() self.high_key = Constants.get_high_key() self.indicator_key = Constants.get_key("ADX") prices_temp = pd.DataFrame() df_list = [] for ticker in self.tickers: if ticker in df: df_list.append( pd.concat([ df[ticker]. loc[:, [self.low_key, self.high_key, self.prices_key]], prices_temp ], axis=1, keys=[ticker])) df_indicator = pd.concat(df_list, axis=1) self.df = df_indicator.copy()
def get_calmar(input_df, params=None): """function to calculate Calmar""" if params is None: params = {} cagr = CAGR.get_cagr(input_df, params) max_dd = MaxDrawdown.get_max_drawdown(input_df) result_df = pd.DataFrame() result_df[Calmar.kpi_name] = (cagr[Ct.cagr_key()] / max_dd[Ct.max_drawdown_key()]) return result_df
def set_input_data(self, df): super().set_input_data(df) self.low_key = Constants.get_low_key() self.high_key = Constants.get_high_key() self.open_key = Constants.get_open_key() # Set dataframe keys prices_temp = pd.DataFrame() df_list = [] for ticker in self.tickers: if ticker in df: df_temp = df[ticker].loc[:, [ self.high_key, self.low_key, self.open_key, self.prices_key ]] df_list.append( pd.concat([df_temp, prices_temp], axis=1, keys=[ticker])) df_indicator = pd.concat(df_list, axis=1) self.df = df_indicator.copy() prices_temp = pd.DataFrame() df_list = [] for ticker in self.tickers: df_temp = df[ticker] df_temp.reset_index(level=0, inplace=True) df_temp = df_temp.loc[:, [ self.date_key, self.high_key, self.low_key, self.open_key, self.prices_key ]] # TODO: Is this rebundant??? df_temp.rename(columns={ "Date": "date", self.high_key: "high", self.low_key: "low", self.open_key: "open", self.prices_key: "close" }, inplace=True) df_list.append( pd.concat([df_temp, prices_temp], axis=1, keys=[ticker])) df_indicator = pd.concat(df_list, axis=1) self.df_renko_input = df_indicator.copy()
class CAGR(KPI): kpi_name = Ct.cagr_key() def __init__(self, params=None): super().__init__(params) if not params: self.params = {} def calculate(self, df, params=None): """"function to calculate the Cumulative Annual Growth Rate of a trading strategy""" super().calculate(df, params) self.result = CAGR.get_cagr(df, self.params) return self.result # Params: {period:Ct.INTERVAL.MONTH|Ct.INTERVAL.DAY} # DF should be a percentage change @staticmethod def get_cagr(input_df, params=None): """"function to calculate the Cumulative Annual Growth Rate of a trading strategy""" reference_days = KPI.get_reference_days(params) df = input_df.copy() df.columns = df.columns.droplevel(1) cagr_data = (1 + df).cumprod() n = len(cagr_data) / reference_days result_df = pd.DataFrame() result_df[CAGR.kpi_name] = cagr_data.iloc[-1]**(1 / n) - 1 return result_df
def calculate(self): """function to convert ohlc data into renko bricks""" super().calculate() df_result = [] for ticker in self.tickers: df_data_atr = self.df[[ticker]].copy() atr = ATR(df_data_atr, 120) df_atr = atr.calculate() df_data = self.df_renko_input[ticker].copy() self.df_renko = Renko(df_data) atr_key = Constants.get_key("ATR") self.df_renko.brick_size = round(df_atr[ticker][atr_key][-1], 0) self.brick_size = self.df_renko.brick_size # renko_df = df2.get_bricks() #if get_bricks() does not work try using get_ohlc_data() instead self.renko_data = self.df_renko.get_ohlc_data() df_result.append(self.renko_data) self.df = pd.concat(df_result, axis=1, keys=self.tickers) return self.df
class Calmar(KPI): kpi_name = Ct.calmar_key() def __init__(self, params=None): super().__init__(params) if not params: self.params = {} def calculate(self, df, params=None): super().calculate(df, params) self.result = Calmar.get_calmar(df, self.params) return self.result @staticmethod def get_calmar(input_df, params=None): """function to calculate Calmar""" if params is None: params = {} cagr = CAGR.get_cagr(input_df, params) max_dd = MaxDrawdown.get_max_drawdown(input_df) result_df = pd.DataFrame() result_df[Calmar.kpi_name] = (cagr[Ct.cagr_key()] / max_dd[Ct.max_drawdown_key()]) return result_df
class MaxDrawdown(KPI): kpi_name = Constants.max_drawdown_key() def __init__(self, params=None): super().__init__(params) if not params: self.params = {} def calculate(self, df, params=None): super().calculate(df) self.result = MaxDrawdown.get_max_drawdown(df) return self.result @staticmethod def get_max_drawdown(input_df): """ function to calculate max drawdown" """ df = input_df.copy() df.columns = df.columns.droplevel(1) cumprod_df = (1 + df).cumprod() cum_roll_max_df = cumprod_df.cummax() drawdown_df = cum_roll_max_df - cumprod_df drawdown_pct_df = drawdown_df / cum_roll_max_df result_df = pd.DataFrame() result_df[MaxDrawdown.kpi_name] = drawdown_pct_df.max() return result_df
def get_reference_days(params): if Ct.interval_key() in params.keys(): period = params[Ct.interval_key()] else: raise ValueError( "Please set the corresponding Interval parameter" "{Ct.interval_key(): interval:Ct.INTERVAL.MONTH|Ct.INTERVAL.DAY}" ) if period == Ct.INTERVAL.DAY: # 252 trading days reference_days = 252 else: # 12 months reference_days = 12 return reference_days
def get_standard_input_data(df): if df is None: raise ValueError("Error: Dataframe has not been provided, there is no data to calculate the requested KPI") input_data = {} # Set dataFrame keys adj_close_key = Constants.get_adj_close_key() close_key = Constants.get_close_key() if adj_close_key in df.columns is True: prices_key = adj_close_key else: prices_key = close_key prices_temp = pd.DataFrame() #TODO: Create a utilities class df.columns = pd.MultiIndex.from_tuples(df.columns.values) tickers = df.columns.levels[0] df_list = [] for ticker in tickers: df_list.append( pd.concat( [df[ticker].loc[:, [prices_key]], prices_temp], axis=1, keys=[ticker] ) ) input_df =\ pd.concat( df_list, axis=1 ) input_data[Constants.get_prices_key()] = prices_key input_data[Constants.get_tickers_key()] = tickers input_data[Constants.get_input_df_key()] = input_df return input_data
def test_login(self, setup): self.logger.info( "******************** Test_003_AddCustomer ********************") self.driver = setup self.driver.get(self.baseURL) self.driver.maximize_window() self.loginPage = LoginPage(self.driver) self.loginPage.setUserName(self.username) self.loginPage.setPassword(self.password) self.loginPage.clickLogin() time.sleep(5) print("********** Login Successful **********") self.addCustomer = AddCustomer(self.driver) self.addCustomer.clickCustomers() self.addCustomer.clickSubMenuCustomers() self.addCustomer.clickAddNewCustomer() time.sleep(5) self.logger.info( "******************** Providing Customer Information ********************" ) print("********** Providing Customer Information **********") self.email = Constants.random_email_generator() self.addCustomer.inputEmail(self.email) self.addCustomer.inputPassword("test123") self.addCustomer.inputFirstName("Bright") self.addCustomer.inputLastName("Brightest") self.addCustomer.selectGender(Constants.MALE) self.addCustomer.inputDateOfBirth("7/05/1985") self.addCustomer.inputCompanyName("NTTData GmbH") self.addCustomer.inputCustomerRoles(Constants.GUESTS) self.addCustomer.selectManagerVendor(Constants.VENDORS_TWO) self.addCustomer.inputAdminContent("This is for testing purposes only") self.driver.save_screenshot(".\\Screenshots\\" + "first_run.png") self.addCustomer.clickSaveButton() self.msg = self.driver.find_element_by_tag_name("body").text # print(self.msg) if 'customer has been added successfully.' in self.msg: assert True self.logger.info("********* Add customer Test Passed *********") else: self.driver.save_screenshot( ".\\Screenshots\\" + "test_addCustomer_scr.png") # Screenshot self.logger.error( "********* Add customer Test Failed ************") assert False self.driver.close() self.logger.info("******* Ending Add customer test **********")
def get_sharpe(input_df, params): """ function to calculate sharpe """ if params is None: params = {} if "rf" not in params.keys(): # USA: risk free rate params["rf"] = 0.0144 rf = params["rf"] "function to calculate sharpe ratio ; rf is the risk free rate" cagr = CAGR.get_cagr(input_df, params) volatility = Volatility.get_volatility(input_df, params) result_df = pd.DataFrame() result_df[Sharpe.kpi_name] = (cagr.loc[:, Ct.cagr_key()] - rf) / volatility.loc[:, Ct.volatility_key()] return result_df
def set_input_data(self, df): super().set_input_data(df) # Set dataFrame keys self.bb_up_key = Constants.get_key("BB_up") self.bb_down_key = Constants.get_key("BB_down") self.bb_width_key = Constants.get_key("BB_width") prices_temp = pd.DataFrame() df_list = [] for ticker in self.tickers: if ticker in df: df_list.append( pd.concat( [df[ticker].loc[:, [self.prices_key]], prices_temp], axis=1, keys=[ticker])) df_indicator = pd.concat(df_list, axis=1) self.df = df_indicator.copy()
def calculate(self): """function to calculate RSI typical values n=14""" super().calculate() delta_key = Constants.get_key("delta") gain_key = Constants.get_key("gain") loss_key = Constants.get_key("loss") avg_gain_key = Constants.get_key("avg_gain") avg_loss_key = Constants.get_key("avg_loss") rs_key = Constants.get_key("RS") df_result = [] for ticker in self.tickers: df_data = self.df[ticker].copy() df_data[delta_key] = df_data[self.prices_key] - df_data[ self.prices_key].shift(1) df_data[gain_key] = np.where(df_data[delta_key] >= 0, df_data[delta_key], 0) df_data[loss_key] = np.where(df_data[delta_key] < 0, abs(df_data[delta_key]), 0) avg_gain = [] avg_loss = [] gain = df_data[gain_key].tolist() loss = df_data[loss_key].tolist() for i in range(len(df_data)): if i < self.n: avg_gain.append(np.NaN) avg_loss.append(np.NaN) elif i == self.n: avg_gain.append(df_data[gain_key].rolling( self.n).mean().tolist()[self.n]) avg_loss.append(df_data[loss_key].rolling( self.n).mean().tolist()[self.n]) elif i > self.n: avg_gain.append( ((self.n - 1) * avg_gain[i - 1] + gain[i]) / self.n) avg_loss.append( ((self.n - 1) * avg_loss[i - 1] + loss[i]) / self.n) df_data[avg_gain_key] = np.array(avg_gain) df_data[avg_loss_key] = np.array(avg_loss) df_data[rs_key] = df_data[avg_gain_key] / df_data[avg_loss_key] df_data[self.indicator_key] = 100 - (100 / (1 + df_data[rs_key])) df_result.append(df_data.loc[:, [self.indicator_key]]) self.df = pd.concat(df_result, axis=1, keys=self.tickers) return self.df
class Sortino(KPI): kpi_name = Ct.sortino_key() # RF is risk free rate def __init__(self, params=None): super().__init__(params) if not params: self.params = {} def calculate(self, df, params=None): super().calculate(df, params) self.result = Sortino.get_sortino(df, self.params) return self.result @staticmethod def get_sortino(input_df, params=None): if params is None: params = {} if "rf" not in params.keys(): # USA: risk free rate params = {"rf": 0.0144} rf = params["rf"] "function to calculate Sortino ratio ; rf is the risk free rate" cagr = CAGR.get_cagr(input_df, params) vol_params = params vol_params[Ct.neg_volatility_key()] = True neg_vol = Volatility.get_volatility(input_df, vol_params) result_df = pd.DataFrame() result_df[Sortino.kpi_name] = (cagr.loc[:, Ct.cagr_key()] - rf) / neg_vol.loc[:, Ct.volatility_key()] result_df.rename(index={0: Sortino.kpi_name}, inplace=True) return result_df
def get_prices_data(self, keys=None): if keys is None or len(keys) == 0: print("No keys has been specified. All keys were selected. ") keys = { Ct.high_key(): True, Ct.low_key(): True, Ct.open_key(): True, Ct.close_key(): True, Ct.adj_close_key(): True, Ct.volume_key(): True } method_tag = "get_prices_data" if self.data_source_historical is not None: if self.data_source_historical.prices is None or self.data_source_historical.prices.empty is True: raise ValueError( "No historical data available, call method self.get_historical_data() first" ) key_titles = self.get_key_titles(keys) if len(key_titles) > 0: prices = self.data_source_historical.get_prices( self.tickers, key_titles) else: print("{} - There are no prices information, for ticker:{}". format(method_tag, self.tickers)) raise ValueError else: print("There has been an error in {}".format(method_tag)) raise ValueError # Validate Price dataframe if prices.empty == True: print("There has been an error in {}".format(method_tag)) raise ValueError self.price_info = prices return prices
class Sharpe(KPI): kpi_name = Ct.sharpe_key() # rf = Risk free rate def __init__(self, params=None): super().__init__(params) if not params: self.params = {} def calculate(self, df, params=None): super().calculate(df, params) self.result = Sharpe.get_sharpe(df, self.params) return self.result @staticmethod def get_sharpe(input_df, params): """ function to calculate sharpe """ if params is None: params = {} if "rf" not in params.keys(): # USA: risk free rate params["rf"] = 0.0144 rf = params["rf"] "function to calculate sharpe ratio ; rf is the risk free rate" cagr = CAGR.get_cagr(input_df, params) volatility = Volatility.get_volatility(input_df, params) result_df = pd.DataFrame() result_df[Sharpe.kpi_name] = (cagr.loc[:, Ct.cagr_key()] - rf) / volatility.loc[:, Ct.volatility_key()] return result_df
def calculate(self): """function to calculate Bollinger Bands""" super().calculate() # Set temp dataframe keys ma_key = Constants.get_key("MA") df_data = pd.DataFrame() df_result = [] for ticker in self.tickers: df_data = self.df[ticker].copy() #df_data.rename(columns={self.ticker: self.prices_key}, inplace=True) #TODO: what happens with this df_data[ma_key] = \ df_data[self.prices_key].rolling(self.n).mean() # ddof=0 is required since we want to take the standard deviation of the population and not sample df_data[self.bb_up_key] = \ df_data[ma_key] + 2 * df_data[self.prices_key].rolling(self.n).std(ddof=0) # ddof=0 is required since we want to take the standard deviation of the population and not sample df_data[self.bb_down_key] = \ df_data[ma_key] - 2 * df_data[self.prices_key].rolling(self.n).std(ddof=0) df_data[self.bb_width_key] = df_data[self.bb_up_key] - df_data[ self.bb_down_key] df_data.dropna(inplace=True) df_result.append(df_data.loc[:, [self.bb_up_key, self.bb_down_key]]) self.df = pd.concat(df_result, axis=1, keys=self.tickers) return df_data
def calculate(self): """"function to calculate ADX""" super().calculate() tr_key = Constants.get_key("TR") trn_key = Constants.get_key("TRn") dm_plus_key = Constants.get_key("DMplus") dm_plus_n_key = Constants.get_key("DMplusN") dm_minus_key = Constants.get_key("DMminus") dm_minus_n_key = Constants.get_key("DMminusN") di_plus_n_key = Constants.get_key("DIplusN") di_minus_n_key = Constants.get_key("DIminusN") di_diff_key = Constants.get_key("DIdiff") di_sum_key = Constants.get_key("DIsum") dx_key = Constants.get_key("DX") df_result = [] for ticker in self.tickers: df_data_atr = self.df[[ticker]].copy() atr_ind = ATR(df=df_data_atr, n=14) df_data = self.df[ticker].copy() # the period parameter of ATR function does not matter because period does not influence TR calculation df_data[tr_key] = atr_ind.calculate()[ticker][[tr_key]] df_data[dm_plus_key] = \ np.where( (df_data[self.high_key] - df_data[self.high_key].shift(1)) > (df_data[self.low_key].shift(1) - df_data[self.low_key]), df_data[self.high_key] - df_data[self.high_key].shift(1), 0) df_data[dm_plus_key] = \ np.where( df_data[dm_plus_key] < 0, 0, df_data[dm_plus_key]) df_data[dm_minus_key] = \ np.where((df_data[self.low_key].shift(1) - df_data[self.low_key]) > (df_data[self.high_key] - df_data[self.high_key].shift(1)), df_data[self.low_key].shift(1) - df_data[self.low_key], 0) df_data[dm_minus_key] = np.where(df_data[dm_minus_key] < 0, 0, df_data[dm_minus_key]) TRn = [] DMplusN = [] DMminusN = [] TR = df_data[tr_key].tolist() DMplus = df_data[dm_plus_key].tolist() DMminus = df_data[dm_minus_key].tolist() for i in range(len(df_data)): if i < self.n: TRn.append(np.NaN) DMplusN.append(np.NaN) DMminusN.append(np.NaN) elif i == self.n: TRn.append(df_data[tr_key].rolling( self.n).sum().tolist()[self.n]) DMplusN.append(df_data[dm_plus_key].rolling( self.n).sum().tolist()[self.n]) DMminusN.append(df_data[dm_minus_key].rolling( self.n).sum().tolist()[self.n]) elif i > self.n: TRn.append(TRn[i - 1] - (TRn[i - 1] / 14) + TR[i]) DMplusN.append(DMplusN[i - 1] - (DMplusN[i - 1] / 14) + DMplus[i]) DMminusN.append(DMminusN[i - 1] - (DMminusN[i - 1] / 14) + DMminus[i]) df_data[trn_key] = np.array(TRn) df_data[dm_plus_n_key] = np.array(DMplusN) df_data[dm_minus_n_key] = np.array(DMminusN) df_data[di_plus_n_key] = 100 * (df_data[dm_plus_n_key] / df_data[trn_key]) df_data[di_minus_n_key] = 100 * (df_data[dm_minus_n_key] / df_data[trn_key]) df_data[di_diff_key] = abs(df_data[di_plus_n_key] - df_data[di_minus_n_key]) df_data[ di_sum_key] = df_data[di_plus_n_key] + df_data[di_minus_n_key] df_data[dx_key] = 100 * (df_data[di_diff_key] / df_data[di_sum_key]) ADX = [] DX = df_data[dx_key].tolist() for j in range(len(df_data)): if j < 2 * self.n - 1: ADX.append(np.NaN) elif j == 2 * self.n - 1: ADX.append(df_data[dx_key][j - self.n + 1:j + 1].mean()) elif j > 2 * self.n - 1: ADX.append(((self.n - 1) * ADX[j - 1] + DX[j]) / self.n) df_data[self.indicator_key] = np.array(ADX) df_result.append(df_data.loc[:, [self.indicator_key]]) self.df = pd.concat(df_result, axis=1, keys=self.tickers) return self.df
def get_atr(input_df, params=None): n = params["n"] # Set temp dataframe keys h_l_key = Ct.get_key("H-L") h_pc_key = Ct.get_key("H-PC") l_pc_key = Ct.get_key("L-PC") tr_key = Ct.get_key("TR") df_data = input_df.copy() df_data.columns = df_data.columns.droplevel(1) df_data[h_l_key] = abs(df_data[Ct.high_key()] - df_data[Ct.low_key()]) df_data[h_pc_key] = abs(df_data[Ct.high_key()] - df_data[Ct.prices_key()].shift(1)) df_data[l_pc_key] = abs(df_data[Ct.low_key()] - df_data[Ct.prices_key()].shift(1)) df_data[tr_key] = df_data[[h_l_key, h_pc_key, l_pc_key]].max(axis=1, skipna=False) df_data[Ct.atr_key()] = df_data[tr_key].rolling(n).mean() # df[Ct.atr_key()] = df[tr_key].ewm(span=n,adjust=False,min_periods=n).mean() # df_data.dropna(inplace=True, axis=0) df_data.drop([h_l_key, h_pc_key, l_pc_key], axis=1, inplace=True) result_df = pd.DataFrame() result_df.append(df_data.loc[:, [Ct.atr_key(), tr_key]]) return result_df
def test_intra_day(self): # DJI constituent stocks tickers = ["MMM", "AXP", "T", "BA", "CAT", "CSCO", "KO", "XOM", "GE", "GS", "HD", "IBM", "INTC", "JNJ", "JPM", "MCD", "MRK", "MSFT", "NKE", "PFE", "PG", "TRV", "UNH", "VZ", "V", "WMT", "DIS"] ref_tickers = ["^DJI"] end_date = dt.datetime.now() # dt.datetime(2021, 3, 7) interval = Ct.INTERVAL.MONTH conf = { Ct.tickers_key(): tickers, Ct.historical_type_key(): DATASOURCETYPE.YFINANCE, Ct.fundamentals_type_key(): None, Ct.fundamentals_options_key(): [], Ct.force_fundamentals_key(): False, Ct.indicators_key(): [], Ct.start_date_key(): end_date - dt.timedelta(3650), Ct.end_date_key(): end_date, Ct.interval_key(): interval, Ct.period_key(): None, Ct.bulk_key(): True } stocks = \ StocksFactory.create_stocks( conf=conf ) conf[Ct.tickers_key()] = ref_tickers stocks_ref = \ StocksFactory.create_stocks( conf=conf ) prices_df = stocks[0].get_prices_data(keys={Ct.adj_close_key(): True}) ref_prices_df = stocks_ref[0].get_prices_data(keys={Ct.adj_close_key(): True}) pr = PortfolioRebalance() params = {"m": 6, "x": 3, Ct.interval_key(): conf[Ct.interval_key()]} pr.backtest(prices_df, ref_prices_df, params)
def create_stocks(conf): tickers = conf[Ct.tickers_key()] data_source_historical = None data_source_fundamentals = None data_sources = \ DataCollector.get_data_sources( conf[Ct.historical_type_key()], conf[Ct.fundamentals_type_key()] ) if DataCollector.HISTORICAL_KEY in data_sources.keys(): data_source_historical = data_sources[DataCollector.HISTORICAL_KEY] if data_source_historical is not None: data_source_historical.extract_historical_data( tickers=tickers, start_date=conf[Ct.start_date_key()], end_date=conf[Ct.end_date_key()], period=conf[Ct.period_key()], interval=conf[Ct.interval_key()]) if DataCollector.FUNDAMENTALS_KEY in data_sources.keys(): data_source_fundamentals = data_sources[ DataCollector.FUNDAMENTALS_KEY] if data_source_fundamentals is not None: data_source_fundamentals.extract_fundamentals( tickers=tickers, date=conf[Ct.start_date_key], required_elements=conf[Ct.fundamentals_options_key()], force_server_data=conf[Ct.force_fundamentals_key()]) stocks = StocksFactory.load_stocks( tickers=tickers, data_source_historical=data_source_historical, data_source_fundamentals=data_source_fundamentals, bulk=conf[Ct.bulk_key()], indicators=conf[Ct.indicators_key()]) return stocks
def plot_stock(self, stock, tickers=None, collapse_indicators=False): Plotter.legend_id = 0 Plotter.current_color_indicator = 0 if stock is None: print("There is no ticker Information, nothing to be plot") return if tickers is None: tickers = stock.tickers elif isinstance(tickers, list) is True: tickers = tickers else: tickers = [tickers] if self.fig is None or self.axes_main is None or self.axes_indicators is None: if stock.price_info is None or stock.price_info[tickers].empty: raise ValueError( "There is no price information for this stock") adj_close_key = Constants.get_adj_close_key() volume_key = Constants.get_volume_key() self.price_series = {} self.volume_series = {} self.x_series = {} for ticker in tickers: if (adj_close_key in stock.price_info[ticker]) == False: adj_close_key = Constants.get_close_key() self.x_series[ticker] = stock.price_info[ticker].iloc[ -self.period:, :].index self.price_series[ticker] = stock.price_info[ticker].iloc[ -self.period:, :][adj_close_key] self.volume_series[ticker] = stock.price_info[ticker].iloc[ -self.period:, :][volume_key] self.axes_main = dict() self.axes_indicators = dict() if len(stock.indicators) == 0: subplots = 1 elif len(stock.indicators) > 0 and collapse_indicators is True: extra = len( list( filter( lambda x: x.collapse is False or x.in_main_plot is False, stock.indicators))) no_collapse = len( list( filter( lambda x: x.collapse is False and x. in_main_plot is False, stock.indicators))) if no_collapse > 0: no_collapse = no_collapse - 1 fixed = 2 if extra == 0: fixed = 1 subplots = fixed + no_collapse else: subplots = len( list( filter(lambda x: x.in_main_plot is False, stock.indicators))) + 1 heights_list = [2 for i in range(subplots - 1)] if subplots == 1: heights_list.insert(0, 2) else: heights_list.insert(0, 3) self.fig = plt.figure(figsize=(8, 6), dpi=80) gridspec = self.fig.add_gridspec(ncols=1, nrows=subplots, height_ratios=heights_list) # gridspec_kw = {'height_ratios': heights_list} self.axes_main[Constants.volume_axis] = self.fig.add_subplot( gridspec[0, 0]) self.set_volume(ticker=ticker) self.set_stock_price(ticker=ticker, color=self.stock_color) i = 1 # Indicator axis begins in 2 indicator_axis = None Plotter.legend_id = 0 for indicator in stock.indicators: if indicator.in_main_plot is False: if collapse_indicators == True: if i > 1: if indicator.collapse is False: self.axes_indicators = self.fig.add_subplot( gridspec[i, 0]) indicator_axis = self.axes_indicators else: indicator_axis = indicator_axis.twinx() else: # Executed first self.axes_indicators = \ self.fig.add_subplot( gridspec[i, 0], sharex=self.axes_main[Constants.prices_axis]) indicator_axis = self.axes_indicators else: Plotter.legend_id = 0 sharex = None if indicator.collapse is True: sharex = self.axes_main[Constants.prices_axis] self.axes_indicators = \ self.fig.add_subplot( gridspec[i, 0], sharex=sharex) indicator_axis = self.axes_indicators if indicator_axis is None: indicator_axis = self.axes_indicators i += 1 else: indicator_axis = self.axes_main[Constants.prices_axis] self.set_plot_indicator(indicator=indicator, ticker=ticker, axis=indicator_axis) plt.tight_layout()
def test_KPI_calmar(self): tickers = ["TSLA", "SPY"] interval = Ct.INTERVAL.DAY conf = { Ct.tickers_key(): tickers, Ct.historical_type_key(): DATASOURCETYPE.YFINANCE, Ct.fundamentals_type_key(): None, Ct.fundamentals_options_key(): [], Ct.force_fundamentals_key(): False, Ct.indicators_key(): [], Ct.start_date_key(): dt.datetime(2021, 3, 7) - dt.timedelta(1825), Ct.end_date_key(): dt.datetime(2021, 3, 7), Ct.interval_key(): interval, Ct.period_key(): None, Ct.bulk_key(): True } stocks = \ StocksFactory.create_stocks( conf=conf ) prices_df = stocks[0].get_prices_data(keys={Ct.adj_close_key(): True}) df = Financials.pct_change(prices_df) params = {Ct.interval_key(): interval} calmar = Calmar() result = calmar.calculate(df, params) self.assertEqual(1.1700790532917946, result[Ct.calmar_key()]['TSLA'])
def test_KPI_max_drawdown(self): tickers = ["TSLA", "SPY"] interval = Ct.INTERVAL.DAY conf = { Ct.tickers_key(): tickers, Ct.historical_type_key(): DATASOURCETYPE.YFINANCE, Ct.fundamentals_type_key(): None, Ct.fundamentals_options_key(): [], Ct.force_fundamentals_key(): False, Ct.indicators_key(): [], Ct.start_date_key(): dt.datetime(2021, 3, 7) - dt.timedelta(1825), Ct.end_date_key(): dt.datetime(2021, 3, 7), Ct.interval_key(): interval, Ct.period_key(): None, Ct.bulk_key(): True } stocks = \ StocksFactory.create_stocks( conf=conf ) prices_df = stocks[0].get_prices_data(keys={Ct.adj_close_key(): True}) df = Financials.pct_change(prices_df) md = MaxDrawdown() result = md.calculate(df) self.assertEqual(0.6062653645917145, result[Ct.max_drawdown_key()]['TSLA']) self.assertEqual(0.3371725544013077, result[Ct.max_drawdown_key()]['SPY'])