class TestStrategy(unittest.TestCase): def __init__(self, method_name): unittest.TestCase.__init__(self, method_name) self.strategy_bull_up = BullUpStrategy() self.indicator_strategy = IndicatorStrategy() self.offline_stock_action = OfflineStockAction() # def test_bull_up(self): # # log.info("===============================================") # result = self.strategy_bull_up.list_bull_up_stock(count=4, up_thread_hold=0.03)[0] # log.info("===============================================") # result = self.strategy_bull_up.list_bull_up_stock(7, 0.03, result)[0] # log.info("===============================================") # result = self.strategy_bull_up.list_bull_up_stock(8, 0.03, result)[0] # log.info("===============================================") # def test_stock_concept(self): # self # def test_dmi_indicator(self): # 找出放量突破布林线中轴 # 高于昨日最高价3%以上 # 当日K线实体部分至少占80%的 # 认为有企稳信号 def test_bollinger_band(self): stocks = self.offline_stock_action.query_all_stock() for index, stock in stocks.iterrows(): all_prices = self.offline_stock_action.query_all_history_prices( stock['stock_code']) avg_vol = all_prices.tail(5)['volume'].mean() #5日均量 newest_price = all_prices.tail(1) close_price = newest_price['close'] newest_vol = newest_price['volume'] newest_high = newest_price['high'] newest_low = newest_price['low'] newest_open = newest_price['open'] last_high_price = all_prices.tail(2).head(1)['high'] last_close_price = all_prices.tail(2).head(1)['close'] if newest_high.item() > last_high_price.item()*1.03\ and (close_price.item() - newest_open.item()) > 0.8*(newest_high.item() - newest_low.item())\ and (newest_vol.item() > avg_vol.item()): upperband, middleband, lowerband = self.indicator_strategy.calculate_boll( all_prices['close']) if close_price.item() > middleband[len(middleband)-1] \ and last_close_price.item() < middleband[len(middleband)-2]: log.info("stock_info: %s(%s) close_price:%.2f, +%.2f%%", stock['display_name'], stock['stock_code'], close_price.item(), (close_price.item() - last_close_price.item()) / last_close_price.item() * 100) log.info("upper :%.2f; middle: %.2f; lower: %.2f", upperband[len(upperband) - 1], middleband[len(middleband) - 1], lowerband[len(lowerband) - 1]) log.info( "======================================================" ) pass
def __init__(self): BaseBackTesing.__init__(self) self.stock_code = None self.offline_stock_action = OfflineStockAction() self.indicator_strategy = IndicatorStrategy() self.cci = None self.stock = None
def __init__(self): super().__init__() self.stock_code = None self.offline_stock_action = OfflineStockAction() self.indicator_strategy = IndicatorStrategy() self.boll = None self.stock = None self.buy_date = None
def __init__(self): BaseBackTesing.__init__(self) self.stock_code = None self.offline_stock_action = OfflineStockAction() self.bull_up_strategy = BullUpStrategy() self.indicator_strategy = IndicatorStrategy() self.total_dmi = None self.stock = None self.pressure_level = self.MAX_VALUE self.stop_loss_level = 0 pass
class SimpleBackTesting(BaseBackTesing): def __init__(self): BaseBackTesing.__init__(self) self.stock_code = None self.offline_stock_action = OfflineStockAction() self.bull_up_strategy = BullUpStrategy() self.indicator_strategy = IndicatorStrategy() self.total_dmi = None self.stock = None self.pressure_level = self.MAX_VALUE self.stop_loss_level = 0 pass def set_stock_code(self, stock_code): self.stock_code = stock_code def is_buy_position(self, history_prices, current_price): history_prices = history_prices.append(current_price) # 判断是否是反弹时间点 bull_up_list = self.bull_up_strategy\ .list_bull_up_stock(count=4, up_thread_hold=0.03, stock_list=self.stock, end_day=current_price['trade_day'], prices=history_prices[history_prices['trade_day'] <= current_price['trade_day']], safe_flag=True) if bull_up_list[0].empty is False: self.stock = bull_up_list[0] self.pressure_level = self.stock['pressure_price'] self.stop_loss_level = self.stock['stop_loss_price'] current_dmi = self.total_dmi[self.total_dmi['trade_day'] == current_price['trade_day']] if float(current_dmi['pdi'].tail(1)) > float(current_dmi['mdi'].tail(1)) \ and float(current_dmi['adx'].tail(1)) > 30: return True return False def buy_action(self, history_prices, current_price): self.buy_position_percent_value(current_price=current_price, percent=0.8) def is_sell_position(self, history_prices, current_price): return super().is_sell_position(history_prices, current_price) def sell_action(self, history_prices, current_price): super().sell_action(history_prices, current_price) def is_add_position(self, history_prices, current_price): if float(current_price['close']) > float(self.pressure_level) \ and current_price['volume'] > history_prices.tail(5)['volume'].mean(): return True return super().is_add_position(history_prices, current_price) #放量突破压力位,加仓20% def add_position_action(self, history_prices, current_price): self.stop_loss_level = self.pressure_level self.pressure_level = self.pressure_level * 1.05 self.buy_position_percent_value(current_price=current_price, percent=0.6) def is_reduce_position(self, history_prices, current_price): if int(self.position) > 0 and float(current_price['close']) < float(self.pressure_level)\ < float(current_price['high']): return True return super().is_reduce_position(history_prices, current_price) #遇到压力位如果当天最高价超过,但是收盘没有超过则减仓50% def reduce_position_action(self, history_prices, current_price): self.sell_position_percent_value(current_price=current_price, percent=0.5) def is_clear_position(self, history_prices, current_price): if int(self.position) > 0 and float(current_price['close']) < float(self.stop_loss_level): return True return super().is_clear_position(history_prices, current_price) # 到达止损位,清仓出局 def clear_position_action(self, history_prices, current_price): self.sell_position_percent_value(current_price=current_price, percent=1) def buy_fee(self): return 5 #TODO 暂时固定按5元 def sell_fee(self): return 5 #TODO 暂时固定按5元 def end_backtesting(self, history_prices, current_price): if self.position > 0: self.sell_position_percent_value(current_price=current_price, percent=1) def run(self): if self.stock_code is None: raise Exception("stock code can not be None") self.stock = pd.DataFrame(self.offline_stock_action.query_by_stock_code(self.stock_code)) total_history_prices = self.offline_stock_action.query_all_history_prices( self.stock_code) mdi, pdi, adx = self.indicator_strategy.calculate_dmi(total_history_prices['high'], total_history_prices['low'], total_history_prices['close']) dmis = { 'mdi': mdi, 'pdi': pdi, 'adx': adx, 'trade_day': total_history_prices['trade_day'] } self.total_dmi = pd.DataFrame(dmis) super().run_backtesting(total_history_prices=total_history_prices, start_date=datetime.datetime(2019, 1, 1)) return float(round((self.balance - self.INIT_BALANCE)/self.INIT_BALANCE*100, 2))
def __init__(self): self.stock_action = OfflineStockAction() self.indicator_strategy = IndicatorStrategy() BaseStrategy.__init__(self)
class BullUpStrategy(BaseStrategy): def __init__(self): self.stock_action = OfflineStockAction() self.indicator_strategy = IndicatorStrategy() BaseStrategy.__init__(self) def list_bull_up_stock(self, count, up_thread_hold, stock_list=None, end_day=get_today(), prices=None, safe_flag=False): log.debug("当前条件为, %s日下跌之后进行放量反弹,并且超过前日最高价%s%%", str(count - 1), str(up_thread_hold * 100)) if stock_list is None: stock_list = self.stock_action.query_all_stock() # stock_code_list = stocks['stock_code'] result = pd.DataFrame() result_price = pd.DataFrame() i = 1 for index, stock in stock_list.iterrows(): i = i + 1 if prices is None: prices = self.stock_action.query_prices_by_stock_code_time( stock['stock_code'], count=count, end_date=end_day) else: pass if prices is None: raise StrategyError(str(count) + "日价格为空") ratio = self.is_bull_up(prices, up_thread_hold, count) if ratio is not None: is_clash_the_top = False str_f = "NOTICE ==> %s\n[%s 当前收盘 %s(最高价 %s) 涨幅 %.2f%%(最高涨幅 %.2f%%) " \ "建议止损位 %s 压力位 %s" if ratio[3] * 100 > 9.6 and ratio[1] * 100 < ratio[3] * 100: is_clash_the_top = True if safe_flag is False: # 忽略掉开板股票 log.info(str_f + " 警惕开板风险]", str(stock['stock_code']), str(stock['display_name']), str(ratio[0]), str(ratio[2]), ratio[1] * 100, ratio[3] * 100, ratio[4], ratio[5]) elif ratio[0] == ratio[2]: log.info(str_f + " 强势光头阳,值得关注]", str(stock['stock_code']), str(stock['display_name']), str(ratio[0]), str(ratio[2]), ratio[1] * 100, ratio[3] * 100, ratio[4], ratio[5]) else: log.info(str_f + "]", str(stock['stock_code']), str(stock['display_name']), str(ratio[0]), str(ratio[2]), ratio[1] * 100, ratio[3] * 100, ratio[4], ratio[5]) stock['stop_loss_price'] = ratio[4] stock['pressure_price'] = ratio[5] if safe_flag is False or is_clash_the_top is False: result = result.append(stock) result_price = result_price.append(prices) return [result, result_price] def is_bull_up(self, prices, thread_hold, count): if len(prices) < 4: log.warning("%s时间太短", str(prices['stock_code'][0])) return None prices.sort_values(by="trade_day", ascending=False, inplace=True) prices = prices.reset_index(drop=True) close_price = prices['close'][0] high_price = prices['high'][0] last_high = prices['high'][1] last_close = prices['close'][1] take_profit_price = 0 i = 1 while i < len(prices) - 1: i = i + 1 if last_high < prices['high'][i]: last_high = prices['high'][i] else: take_profit_price = last_high break if i > (count - 1) and prices['volume'][0] > self.calculate_avg_vol(prices) \ and close_price > prices['high'][1] * (1+thread_hold): # TODO 找出来近期高位 return [ close_price, close_price / prices['close'][1] - 1, high_price, high_price / prices['close'][1] - 1, last_close, take_profit_price ] return None @staticmethod def calculate_avg_vol(prices): avg_volume = prices['volume'].mean() return avg_volume
def __init__(self, user_name=config.get_user_name(), pwd=config.get_pwd()): OfflineStockAction.__init__(self) BaseJQData.__init__(self, user_name, pwd)
def __init__(self, method_name): unittest.TestCase.__init__(self, method_name) self.indicator_strategy = IndicatorStrategy() self.offline_stock_action = OfflineStockAction() self.bull_up_strategy = BullUpStrategy()
class TestMACDStrategy(unittest.TestCase): def __init__(self, method_name): unittest.TestCase.__init__(self, method_name) self.indicator_strategy = IndicatorStrategy() self.offline_stock_action = OfflineStockAction() self.bull_up_strategy = BullUpStrategy() # def test_dmi(self): # stocks = self.offline_stock_action.get_all_stock() # i = 1 # for index, stock in stocks.iterrows(): # price = self.offline_stock_action.query_all_history_prices(stock['stock_code']) # m_di, p_di, adx = self.indicator_strategy.calculate_dmi(price['high'], price['low'], price['close']) # if float(p_di.tail(1)) > float(m_di.tail(2).head(1)) \ # and float(adx.tail(1)) > 30: # log.info("NOTICE => %s", stock['stock_code']) # view_bar(i, len(stocks.index)) # i += 1 # def test_list_macd_gold(self): # stocks = self.offline_stock_action.get_all_stock() # log.info("======================================") # for index, stock in stocks.iterrows(): # price = self.offline_stock_action.query_all_history_prices(stock['stock_code']) # real = self.indicator_strategy.calculate_rsi(price['close']) # cci = self.indicator_strategy.calculate_cci(price['high'], price['low'], price['close']) # if float(price['paused'].tail(1)) <= 0: # if int(cci.tail(1)) > 100 > int(cci.tail(2).head(1)): # log.info("NOTICE ==> %s CCI今日超过100", stock['stock_code']) # macd, macdsignal, macdhist = self.indicator_strategy.calculate_macd(price['close']) # if float(macdhist.tail(1)) > 0 > float(macdhist.tail(2).head(1)) and float(macd.tail(1)) > 0: # log.info("\t\tNOTICE ==> %s MACD金叉行情",stock['stock_code']) # if float(macd.tail(1)) > 0 > float(macd.tail(2).head(1)): # log.info("\t\tNOTICE ==> %s DIF>0行情", stock['stock_code']) # def test_valuations(self): # print(self.stock_action.refresh_valuations()) # def test_concept(self): stock_action = StockAction() print(stock_action.refresh_concepts()) def test_refresh_concept_stock(self): stock_action = StockAction() stock_action.refresh_concept_stocks() # def test_concept_stocks(self): # # print(self.stock_action.refresh_concept_stocks()) # print(self.offline_stock_action.query_stock_concept('000027.XSHE')) def test_all_stock_history_price(self): stocks = self.offline_stock_action.query_all_stock() stocks_0 = stocks[0:600] stocks_1 = stocks[600:1200] stocks_2 = stocks[1200:1800] stocks_3 = stocks[1800:2400] stocks_4 = stocks[2400:3000] stocks_5 = stocks[3000:len(stocks)] thread_0 = threading.Thread(target=self.append_stock_price, args=(stocks_0, )) thread_1 = threading.Thread(target=self.append_stock_price, args=(stocks_1, )) thread_2 = threading.Thread(target=self.append_stock_price, args=(stocks_2, )) thread_3 = threading.Thread(target=self.append_stock_price1, args=(stocks_3, )) thread_4 = threading.Thread(target=self.append_stock_price1, args=(stocks_4, )) thread_5 = threading.Thread(target=self.append_stock_price1, args=(stocks_5, )) thread_0.setDaemon(True) thread_1.setDaemon(True) thread_2.setDaemon(True) thread_3.setDaemon(True) thread_4.setDaemon(True) thread_5.setDaemon(True) thread_0.start() thread_1.start() thread_2.start() thread_3.start() thread_4.start() thread_5.start() thread_0.join() thread_1.join() thread_2.join() thread_3.join() thread_4.join() thread_5.join() def append_stock_price(self, stocks): log.info("%s running", threading.get_ident()) stock_codes = list(stocks['stock_code']) for i in range(len(stock_codes)): #view_bar(i+1, len(stock_codes), '(' + str(threading.get_ident())+')') stock_action = StockAction() stock_action.append_stock_price(stock_codes[i]) log.info("%s done", threading.get_ident()) def append_stock_price1(self, stocks): log.info("%s running", threading.get_ident()) stock_codes = list(stocks['stock_code']) for i in range(len(stock_codes)): #view_bar(i+1, len(stock_codes), '(' + str(threading.get_ident())+')') stock_action = StockAction(user_name='****', pwd='****') stock_action.append_stock_price(stock_codes[i]) log.info("%s done", threading.get_ident())
class BollBacktesting(BaseBackTesing): def __init__(self): super().__init__() self.stock_code = None self.offline_stock_action = OfflineStockAction() self.indicator_strategy = IndicatorStrategy() self.boll = None self.stock = None self.buy_date = None def set_stock_code(self, stock_code): self.stock_code = stock_code def is_buy_position(self, history_prices, current_price): all_prices = history_prices avg_vol = all_prices.tail(5)['volume'].mean() # 5日均量 newest_price = all_prices.tail(1) close_price = newest_price['close'] newest_vol = newest_price['volume'] newest_high = newest_price['high'] newest_low = newest_price['low'] newest_open = newest_price['open'] last_high_price = all_prices.tail(2).head(1)['high'] last_close_price = all_prices.tail(2).head(1)['close'] try: if newest_high.item() > last_high_price.item() * 1.03 \ and (close_price.item() - newest_open.item()) > 0.8 * (newest_high.item() - newest_low.item()) \ and (newest_vol.item() > avg_vol.item()): upperband = self.boll['upperband'] middleband = self.boll['middleband'] lowerband = self.boll['lowerband'] if close_price.item() > middleband[len(middleband) - 1] \ and last_close_price.item() < middleband[len(middleband) - 2]: log.info("stock_info: %s(%s) close_price:%.2f, +%.2f%%", self.stock['display_name'], self.stock['stock_code'], close_price.item(), (close_price.item() - last_close_price.item()) / last_close_price.item() * 100) log.info("upper :%.2f; middle: %.2f; lower: %.2f", upperband[len(upperband) - 1], middleband[len(middleband) - 1], lowerband[len(lowerband) - 1]) log.info( "======================================================" ) self.buy_date = datetime.datetime.strptime( str(current_price['trade_day']).split(" ")[0], '%Y-%m-%d') return True except ValueError: print(newest_high) return False return False def buy_action(self, history_prices, current_price): self.buy_position_percent_value(current_price, 1) def is_sell_position(self, history_prices, current_price): if self.buy_date is not None \ and self.position > 0: cur_date = datetime.datetime.strptime( str(current_price['trade_day']).split(" ")[0], '%Y-%m-%d') print((cur_date - self.buy_date).days) if (cur_date - self.buy_date).days > 3: self.buy_date = None log.info("持有超过2天,卖出") return True return False def sell_action(self, history_prices, current_price): self.sell_position_percent_value(current_price, 1) def is_add_position(self, history_prices, current_price): super().is_add_position(history_prices, current_price) def add_position_action(self, history_prices, current_price): super().add_position_action(history_prices, current_price) def is_reduce_position(self, history_prices, current_price): super().is_reduce_position(history_prices, current_price) def reduce_position_action(self, history_prices, current_price): super().reduce_position_action(history_prices, current_price) def is_clear_position(self, history_prices, current_price): if self.position > 0: profit = self.position * current_price['open'] - self.INIT_BALANCE print(profit) if profit < 0 and (-profit) / self.INIT_BALANCE > 0.08: log.info("触及止损,清仓") return True return False def clear_position_action(self, history_prices, current_price): super().sell_position_percent_value(current_price=current_price, percent=1) def buy_fee(self): return super().buy_fee() def sell_fee(self): return super().sell_fee() def end_backtesting(self, history_prices, current_price): if self.position > 0: self.sell_position_percent_value(current_price, 1) def buy_position_percent_value(self, current_price, percent): # 早盘就卖 current_price['close'] = current_price['open'] return super().buy_position_percent_value(current_price, percent) def sell_position_percent_value(self, current_price, percent): return super().sell_position_percent_value(current_price, percent) def run(self): if self.stock_code is None: raise Exception("stock code can not be None") self.stock = pd.DataFrame( self.offline_stock_action.query_by_stock_code(self.stock_code)) total_history_prices = self.offline_stock_action.query_all_history_prices( self.stock_code) upperband, middleband, lowerband = self.indicator_strategy.calculate_boll( total_history_prices['close']) bollinger = { 'upperband': upperband, 'middleband': middleband, 'lowerband': lowerband, 'trade_day': total_history_prices['trade_day'] } self.boll = pd.DataFrame(bollinger) super().run_backtesting(total_history_prices=total_history_prices, start_date=datetime.datetime(2018, 1, 1)) return float( round((self.balance - self.INIT_BALANCE) / self.INIT_BALANCE * 100, 2))
def __init__(self, method_name): unittest.TestCase.__init__(self, method_name) self.offline_stock_action = OfflineStockAction()
class TestBacktesing(unittest.TestCase): def __init__(self, method_name): unittest.TestCase.__init__(self, method_name) self.offline_stock_action = OfflineStockAction() # def test_simple_backtesting(self): # stocks = self.offline_stock_action.get_all_stock() # sample_stocks = stocks.sample(100) # stock_codes = [] # profits = [] # for index, stock in sample_stocks.iterrows(): # simple_backtesting = SimpleBackTesting() # simple_backtesting.set_stock_code(stock['stock_code']) # profit = simple_backtesting.run() # stock_codes.append(stock['stock_code']) # profits.append(profit) # sample = { # 'stock_code': stock_codes, # 'profit': profits # } # sample_result = pd.DataFrame(sample) # sample_result.to_excel('./result/simple_backtesting-' + # str(datetime.datetime.strftime( # datetime.datetime.now(), '%Y%m%d%H%M%S' # )) +'.xlsx') # def test_cci_backtesting(self): # stocks = self.offline_stock_action.query_all_stock() # sample_stocks = stocks.sample(20) # stock_codes = [] # profits = [] # for index, stock in sample_stocks.iterrows(): # cci_backtesting = CCIBacktesing() # cci_backtesting.set_stock_code(stock['stock_code']) # profit = cci_backtesting.run() # stock_codes.append(stock['stock_code']) # profits.append(profit) # sample = { # 'stock_code': stock_codes, # 'profit': profits # } # sample_result = pd.DataFrame(sample) # sample_result.to_excel('./result/cci_backtesting-' + # str(datetime.datetime.strftime( # datetime.datetime.now(), '%Y%m%d%H%M%S' # )) +'.xlsx') def test_boll_backtesting(self): stocks = self.offline_stock_action.query_all_stock() sample_stocks = stocks.sample(100) stock_codes = [] profits = [] for index, stock in sample_stocks.iterrows(): boll_backtesting = BollBacktesting() stock_code = stock['stock_code'] boll_backtesting.set_stock_code(stock_code) profit = boll_backtesting.run() stock_codes.append(stock['stock_code']) profits.append(profit) sample = {'stock_code': stock_codes, 'profit': profits} sample_result = pd.DataFrame(sample) sample_result.to_excel('./result/boll_backtesting-' + str( datetime.datetime.strftime(datetime.datetime.now(), '%Y%m%d%H%M%S')) + '.xlsx')
def __init__(self, method_name): unittest.TestCase.__init__(self, method_name) self.index_strategy = IndexCalculatorStategy() self.offline_stock_action = OfflineStockAction()
class CCIBacktesing(BaseBackTesing): def __init__(self): BaseBackTesing.__init__(self) self.stock_code = None self.offline_stock_action = OfflineStockAction() self.indicator_strategy = IndicatorStrategy() self.cci = None self.stock = None def set_stock_code(self, stock_code): self.stock_code = stock_code def is_buy_position(self, history_prices, current_price): pre_price = history_prices.tail(1) pre_price = pre_price.reset_index(drop=True) try: pre_cci = self.cci[self.cci['trade_day'] == pre_price['trade_day'] [0]] except IndexError: print(pre_price['trade_day'][0]) raise Exception("ERROR") cur_cci = self.cci[self.cci['trade_day'] == current_price['trade_day']] if float(pre_cci['cci']) < 100 < float(cur_cci['cci']): return True return False def buy_action(self, history_prices, current_price): cur_cci = self.cci[self.cci['trade_day'] == current_price['trade_day']] self.buy_position_percent_value( current_price=current_price, percent=round(float(cur_cci['cci']) / 100, 1) - 0.5) def is_sell_position(self, history_prices, current_price): pre_price = history_prices.tail(1) pre_price = pre_price.reset_index(drop=True) pre_cci = self.cci[self.cci['trade_day'] == pre_price['trade_day'][0]] cur_cci = self.cci[self.cci['trade_day'] == current_price['trade_day']] if float(pre_cci['cci']) > 100 > float(cur_cci['cci']): return True return False def sell_action(self, history_prices, current_price): if self.position > 0: self.sell_position_percent_value(current_price=current_price, percent=1) def is_add_position(self, history_prices, current_price): super().is_add_position(history_prices=history_prices, current_price=current_price) def add_position_action(self, history_prices, current_price): super().add_position_action(history_prices=history_prices, current_price=current_price) def is_reduce_position(self, history_prices, current_price): super().is_reduce_position(history_prices=history_prices, current_price=current_price) def reduce_position_action(self, history_prices, current_price): super().reduce_position_action(history_prices=history_prices, current_price=current_price) def is_clear_position(self, history_prices, current_price): if self.position > 0: profit = self.balance - self.INIT_BALANCE if profit < 0 and (-profit) / self.INIT_BALANCE > 0.08: return True return False def clear_position_action(self, history_prices, current_price): super().sell_position_percent_value(current_price=current_price, percent=1) def buy_fee(self): return 5 def sell_fee(self): return 5 def end_backtesting(self, history_prices, current_price): if self.position > 0: self.sell_position_percent_value(current_price=current_price, percent=1) def buy_position_percent_value(self, current_price, percent): super().buy_position_percent_value(current_price, percent) def sell_position_percent_value(self, current_price, percent): super().sell_position_percent_value(current_price, percent) def run(self): if self.stock_code is None: raise Exception("stock code can not be None") self.stock = pd.DataFrame( self.offline_stock_action.query_by_stock_code(self.stock_code)) total_history_prices = self.offline_stock_action.query_all_history_prices( self.stock_code) ccis = self.indicator_strategy.calculate_cci( total_history_prices['high'], total_history_prices['low'], total_history_prices['close']) cci = {'cci': ccis, 'trade_day': total_history_prices['trade_day']} self.cci = pd.DataFrame(cci) super().run_backtesting(total_history_prices=total_history_prices, start_date=datetime.datetime(2018, 1, 1)) return float( round((self.balance - self.INIT_BALANCE) / self.INIT_BALANCE * 100, 2))