def run(start_date: str, finish_date: str, candles=None, chart=False, tradingview=False): # clear the screen if not jh.should_execute_silently(): click.clear() # validate routes validate_routes(router) # initiate candle store store.candles.init_storage(5000) # load historical candles if candles is None: print('loading candles...') candles = _load_candles(start_date, finish_date) click.clear() if not jh.should_execute_silently(): # print candles table key = '{}-{}'.format(config['app']['trading_exchanges'][0], config['app']['trading_symbols'][0]) table.key_value(stats.candles(candles[key]['candles']), 'candles', alignments=('left', 'right')) print('\n') # print routes table table.multi_value(stats.routes(router.routes)) print('\n') # print guidance for debugging candles if jh.is_debuggable('trading_candles') or jh.is_debuggable( 'shorter_period_candles'): print( ' Symbol | timestamp | open | close | high | low | volume' ) # run backtest simulation simulator(candles) if not jh.should_execute_silently(): # print trades statistics if store.completed_trades.count > 0: print('\n') table.key_value(report.portfolio_metrics(), 'Metrics', alignments=('left', 'right')) print('\n') # save logs store_logs(tradingview) if chart: charts.portfolio_vs_asset_returns() else: print(jh.color('No trades were made.', 'yellow'))
def _terminate(self) -> None: """ Optional for executing code after completion of a backTest. This block will not execute in live use as a live Jesse is never ending. """ if not jh.should_execute_silently() or jh.is_debugging(): logger.info("Terminating strategy...") self.terminate() self._detect_and_handle_entry_and_exit_modifications() # fake execution of market orders in backtest simulation if not jh.is_live(): store.orders.execute_pending_market_orders() if jh.is_live(): return if self.position.is_open: store.app.total_open_trades += 1 store.app.total_open_pl += self.position.pnl logger.info( f"Closed open {self.exchange}-{self.symbol} position at {self.position.current_price} with PNL: {round(self.position.pnl, 4)}({round(self.position.pnl_percentage, 2)}%) because we reached the end of the backtest session." ) # fake a closing (market) order so that the calculations would be correct self.broker.reduce_position_at(self.position.qty, self.position.current_price, order_roles.CLOSE_POSITION) return if self._open_position_orders: self._execute_cancel() logger.info('Canceled open-position orders because we reached the end of the backtest session.')
def print_candle(candle, is_partial, symbol): """ :param candle: :param is_partial: :param symbol: :return: """ if jh.should_execute_silently(): return if is_bullish(candle) and is_partial is True: candle_form = click.style(' ==', fg='green') elif is_bullish(candle) and is_partial is False: candle_form = click.style('====', bg='green') elif is_bearish(candle) and is_partial is True: candle_form = click.style(' ==', fg='red') else: candle_form = click.style('====', bg='red') if is_bullish(candle): candle_info = click.style(' {} | {} | {} | {} | {} | {} | {}'.format( symbol, str(arrow.get(candle[0] / 1000))[:-9], candle[1], candle[2], candle[3], candle[4], round(candle[5], 2)), fg='green') else: candle_info = click.style(' {} | {} | {} | {} | {} | {} | {}'.format( symbol, str(arrow.get(candle[0] / 1000))[:-9], candle[1], candle[2], candle[3], candle[4], round(candle[5], 2)), fg='red') print(candle_form + candle_info)
def _on_stop_loss(self, order: Order) -> None: if not jh.should_execute_silently() or jh.is_debugging(): logger.info('Stop-loss has been executed.') self._broadcast('route-stop-loss') self._execute_cancel() self.on_stop_loss(order) self._detect_and_handle_entry_and_exit_modifications()
def _on_take_profit(self, order: Order) -> None: if not jh.should_execute_silently() or jh.is_debugging(): logger.info("Take-profit order has been executed.") self._broadcast('route-take-profit') self._execute_cancel() self.on_take_profit(order) self._detect_and_handle_entry_and_exit_modifications()
def _on_increased_position(self): if not jh.should_execute_silently() or jh.is_debugging(): logger.info("Position size increased.") self._broadcast('route-increased-position') self.on_increased_position() self._detect_and_handle_entry_and_exit_modifications()
def _on_close_position(self, order: Order): if not jh.should_execute_silently() or jh.is_debugging(): logger.info("A closing order has been executed") self._broadcast('route-close-position') self._execute_cancel() self.on_close_position(order) self._detect_and_handle_entry_and_exit_modifications()
def _on_reduced_position(self): """ prepares for on_reduced_position() is implemented by user """ if not jh.should_execute_silently() or jh.is_debugging(): logger.info("Position size reduced.") self._broadcast('route-reduced-position') self.on_reduced_position() self._detect_and_handle_entry_and_exit_modifications()
def _on_take_profit(self, order: Order) -> None: if not jh.should_execute_silently() or jh.is_debugging(): logger.info("Take-profit order has been executed.") self._broadcast('route-take-profit') self._execute_cancel() # set metrics only after a trade happens and they actually change self.metrics = metrics.trades(store.completed_trades.trades, store.app.daily_balance) self.on_take_profit(order) self._detect_and_handle_entry_and_exit_modifications()
def print_candle(candle: np.ndarray, is_partial: bool, symbol: str) -> None: """ Ever since the new GUI dashboard, this function should log instead of actually printing :param candle: np.ndarray :param is_partial: bool :param symbol: str """ if jh.should_execute_silently(): return candle_form = ' ==' if is_partial else '====' candle_info = f' {symbol} | {str(arrow.get(candle[0] / 1000))[:-9]} | {candle[1]} | {candle[2]} | {candle[3]} | {candle[4]} | {round(candle[5], 2)}' msg = candle_form + candle_info # store it in the log file logger.info(msg)
def run(debug_mode, user_config: dict, routes: List[Dict[str, str]], extra_routes: List[Dict[str, str]], start_date: str, finish_date: str, optimal_total: int, csv: bool, json: bool) -> None: from jesse.config import config, set_config config['app']['trading_mode'] = 'optimize' # debug flag config['app']['debug_mode'] = debug_mode cpu_cores = int(user_config['cpu_cores']) # inject config set_config(user_config) # set routes router.initiate(routes, extra_routes) store.app.set_session_id() register_custom_exception_handler() # clear the screen if not jh.should_execute_silently(): click.clear() # validate routes validate_routes(router) print('loading candles...') # load historical candles and divide them into training # and testing periods (15% for test, 85% for training) training_candles, testing_candles = _get_training_and_testing_candles( start_date, finish_date) # clear the screen click.clear() optimizer = Optimizer(training_candles, testing_candles, optimal_total, cpu_cores, csv, json, start_date, finish_date) # start the process optimizer.run()
def print_candle(candle: np.ndarray, is_partial: bool, symbol: str) -> None: if jh.should_execute_silently(): return if is_bullish(candle) and is_partial: candle_form = click.style(' ==', fg='green') elif is_bullish(candle) and not is_partial: candle_form = click.style('====', bg='green') elif is_bearish(candle) and is_partial: candle_form = click.style(' ==', fg='red') else: candle_form = click.style('====', bg='red') if is_bullish(candle): candle_info = click.style( f' {symbol} | {str(arrow.get(candle[0] / 1000))[:-9]} | {candle[1]} | {candle[2]} | {candle[3]} | {candle[4]} | {round(candle[5], 2)}', fg='green') else: candle_info = click.style( f' {symbol} | {str(arrow.get(candle[0] / 1000))[:-9]} | {candle[1]} | {candle[2]} | {candle[3]} | {candle[4]} | {round(candle[5], 2)}', fg='red') print(candle_form + candle_info)
def _terminate(self): """ Optional for executing code after completion of a backTest. This block will not execute in live use as a live Jesse is never ending. """ if not jh.should_execute_silently() or jh.is_debugging(): logger.info("Terminating strategy...") self.terminate() self._detect_and_handle_entry_and_exit_modifications() # fake execution of market orders in backtest simulation if not jh.is_live(): store.orders.execute_pending_market_orders() if jh.is_live(): return if self.position.is_open: store.app.total_open_trades += 1 store.app.total_open_pl += self.position.pnl logger.info( "Closed open {}-{} position at {} with PNL: {}({}%) because we reached the end of the backtest session." .format(self.exchange, self.symbol, self.position.current_price, self.position.pnl, self.position.pnl_percentage)) self.position._close(self.position.current_price) self._execute_cancel() return if self._open_position_orders: self._execute_cancel() logger.info( 'Canceled open-position orders because we reached the end of the backtest session.' )
def run( debug_mode, user_config: dict, routes: List[Dict[str, str]], extra_routes: List[Dict[str, str]], start_date: str, finish_date: str, candles: dict = None, chart: bool = False, tradingview: bool = False, full_reports: bool = False, csv: bool = False, json: bool = False ) -> None: if not jh.is_unit_testing(): # at every second, we check to see if it's time to execute stuff status_checker = Timeloop() @status_checker.job(interval=timedelta(seconds=1)) def handle_time(): if process_status() != 'started': raise exceptions.Termination status_checker.start() from jesse.config import config, set_config config['app']['trading_mode'] = 'backtest' # debug flag config['app']['debug_mode'] = debug_mode # inject config if not jh.is_unit_testing(): set_config(user_config) # set routes router.initiate(routes, extra_routes) store.app.set_session_id() register_custom_exception_handler() # clear the screen if not jh.should_execute_silently(): click.clear() # validate routes validate_routes(router) # initiate candle store store.candles.init_storage(5000) # load historical candles if candles is None: candles = load_candles(start_date, finish_date) click.clear() if not jh.should_execute_silently(): sync_publish('general_info', { 'session_id': jh.get_session_id(), 'debug_mode': str(config['app']['debug_mode']), }) # candles info key = f"{config['app']['considering_candles'][0][0]}-{config['app']['considering_candles'][0][1]}" sync_publish('candles_info', stats.candles_info(candles[key]['candles'])) # routes info sync_publish('routes_info', stats.routes(router.routes)) # run backtest simulation simulator(candles, run_silently=jh.should_execute_silently()) # hyperparameters (if any) if not jh.should_execute_silently(): sync_publish('hyperparameters', stats.hyperparameters(router.routes)) if not jh.should_execute_silently(): if store.completed_trades.count > 0: sync_publish('metrics', report.portfolio_metrics()) routes_count = len(router.routes) more = f"-and-{routes_count - 1}-more" if routes_count > 1 else "" study_name = f"{router.routes[0].strategy_name}-{router.routes[0].exchange}-{router.routes[0].symbol}-{router.routes[0].timeframe}{more}-{start_date}-{finish_date}" store_logs(study_name, json, tradingview, csv) if chart: charts.portfolio_vs_asset_returns(study_name) sync_publish('equity_curve', charts.equity_curve()) # QuantStats' report if full_reports: price_data = [] # load close candles for Buy and hold and calculate pct_change for index, c in enumerate(config['app']['considering_candles']): exchange, symbol = c[0], c[1] if exchange in config['app']['trading_exchanges'] and symbol in config['app']['trading_symbols']: # fetch from database candles_tuple = Candle.select( Candle.timestamp, Candle.close ).where( Candle.timestamp.between(jh.date_to_timestamp(start_date), jh.date_to_timestamp(finish_date) - 60000), Candle.exchange == exchange, Candle.symbol == symbol ).order_by(Candle.timestamp.asc()).tuples() candles = np.array(candles_tuple) timestamps = candles[:, 0] price_data.append(candles[:, 1]) price_data = np.transpose(price_data) price_df = pd.DataFrame(price_data, index=pd.to_datetime(timestamps, unit="ms"), dtype=float).resample( 'D').mean() price_pct_change = price_df.pct_change(1).fillna(0) bh_daily_returns_all_routes = price_pct_change.mean(1) quantstats.quantstats_tearsheet(bh_daily_returns_all_routes, study_name) else: sync_publish('equity_curve', None) sync_publish('metrics', None) # close database connection from jesse.services.db import database database.close_connection()
def test_should_execute_silently(): assert jh.should_execute_silently() is True
def generate_signal(start_date: str, finish_date: str, candles=None, chart=False, tradingview=False, csv=False, json=False): # clear the screen if not jh.should_execute_silently(): click.clear() # validate routes validate_routes(router) # initiate candle store store.candles.init_storage(5000) # load historical candles if candles is None: print('loading candles...') candles = load_candles(start_date, finish_date) click.clear() if not jh.should_execute_silently(): # print candles table key = '{}-{}'.format(config['app']['considering_candles'][0][0], config['app']['considering_candles'][0][1]) table.key_value(stats.candles(candles[key]['candles']), 'candles', alignments=('left', 'right')) print('\n') # print routes table table.multi_value(stats.routes(router.routes)) print('\n') # print guidance for debugging candles if jh.is_debuggable('trading_candles') or jh.is_debuggable( 'shorter_period_candles'): print( ' Symbol | timestamp | open | close | high | low | volume' ) # run backtest simulation signal_simulator(candles) if not jh.should_execute_silently(): # print trades statistics if store.completed_trades.count > 0: print('\n') table.key_value(report.portfolio_metrics(), 'Metrics', alignments=('left', 'right')) print('\n') # save logs store_logs(json, tradingview, csv) if chart: charts.portfolio_vs_asset_returns() # # clone the trades so that the original is not mutated # completed_trades = copy.deepcopy(store.completed_trades.trades) # # for trade in completed_trades: # print(trade.to_dict()) # # # filter trades which were generated for current day # completed_trades = filter(lambda x: x.opened_at == jh.get_current_time_in_epoch(), completed_trades) # # # TODO: add a slack notification here instead of print to notify the user # print("Trades to execute today: ") # for trade in completed_trades: # print(trade.to_dict()) else: print(jh.color('No trades were completed/closed.', 'yellow')) open_positions = store.positions.get_open_positions() open_orders = store.orders.get_all_active_orders() current_report = jh.generate_signals_report(open_positions, open_orders) return current_report
def simulator(candles: Dict[str, Dict[str, Union[str, np.ndarray]]], hyperparameters=None) -> None: begin_time_track = time.time() key = '{}-{}'.format(config['app']['considering_candles'][0][0], config['app']['considering_candles'][0][1]) first_candles_set = candles[key]['candles'] length = len(first_candles_set) # to preset the array size for performance store.app.starting_time = first_candles_set[0][0] store.app.time = first_candles_set[0][0] # initiate strategies for r in router.routes: StrategyClass = jh.get_strategy_class(r.strategy_name) try: r.strategy = StrategyClass() except TypeError: raise exceptions.InvalidStrategy( "Looks like the structure of your strategy directory is incorrect. Make sure to include the strategy INSIDE the __init__.py file." "\nIf you need working examples, check out: https://github.com/jesse-ai/example-strategies" ) except: raise r.strategy.name = r.strategy_name r.strategy.exchange = r.exchange r.strategy.symbol = r.symbol r.strategy.timeframe = r.timeframe # inject hyper parameters (used for optimize_mode) # convert DNS string into hyperparameters if r.dna and hyperparameters is None: hyperparameters = jh.dna_to_hp(r.strategy.hyperparameters(), r.dna) # inject hyperparameters sent within the optimize mode if hyperparameters is not None: r.strategy.hp = hyperparameters # init few objects that couldn't be initiated in Strategy __init__ # it also injects hyperparameters into self.hp in case the route does not uses any DNAs r.strategy._init_objects() selectors.get_position(r.exchange, r.symbol).strategy = r.strategy # add initial balance save_daily_portfolio_balance() with click.progressbar(length=length, label='Executing simulation...') as progressbar: for i in range(length): # update time store.app.time = first_candles_set[i][0] + 60_000 # add candles for j in candles: short_candle = candles[j]['candles'][i] if i != 0: previous_short_candle = candles[j]['candles'][i - 1] short_candle = _get_fixed_jumped_candle( previous_short_candle, short_candle) exchange = candles[j]['exchange'] symbol = candles[j]['symbol'] store.candles.add_candle(short_candle, exchange, symbol, '1m', with_execution=False, with_generation=False) # print short candle if jh.is_debuggable('shorter_period_candles'): print_candle(short_candle, True, symbol) _simulate_price_change_effect(short_candle, exchange, symbol) # generate and add candles for bigger timeframes for timeframe in config['app']['considering_timeframes']: # for 1m, no work is needed if timeframe == '1m': continue count = jh.timeframe_to_one_minutes(timeframe) until = count - ((i + 1) % count) if (i + 1) % count == 0: generated_candle = generate_candle_from_one_minutes( timeframe, candles[j]['candles'][(i - (count - 1)):(i + 1)]) store.candles.add_candle(generated_candle, exchange, symbol, timeframe, with_execution=False, with_generation=False) # update progressbar if not jh.is_debugging() and not jh.should_execute_silently( ) and i % 60 == 0: progressbar.update(60) # now that all new generated candles are ready, execute for r in router.routes: count = jh.timeframe_to_one_minutes(r.timeframe) # 1m timeframe if r.timeframe == timeframes.MINUTE_1: r.strategy._execute() elif (i + 1) % count == 0: # print candle if jh.is_debuggable('trading_candles'): print_candle( store.candles.get_current_candle( r.exchange, r.symbol, r.timeframe), False, r.symbol) r.strategy._execute() # now check to see if there's any MARKET orders waiting to be executed store.orders.execute_pending_market_orders() if i != 0 and i % 1440 == 0: save_daily_portfolio_balance() if not jh.should_execute_silently(): if jh.is_debuggable('trading_candles') or jh.is_debuggable( 'shorter_period_candles'): print('\n') # print executed time for the backtest session finish_time_track = time.time() print( 'Executed backtest simulation in: ', '{} seconds'.format(round(finish_time_track - begin_time_track, 2))) for r in router.routes: r.strategy._terminate() store.orders.execute_pending_market_orders() # now that backtest is finished, add finishing balance save_daily_portfolio_balance()
def simulator(candles, hyper_parameters=None): begin_time_track = time.time() key = '{}-{}'.format(config['app']['trading_exchanges'][0], config['app']['trading_symbols'][0]) first_candles_set = candles[key]['candles'] length = len(first_candles_set) # to preset the array size for performance store.app.starting_time = first_candles_set[0][0] # initiate strategies for r in router.routes: StrategyClass = jh.get_strategy_class(r.strategy_name) # convert DNS string into hyper_parameters if r.dna and hyper_parameters is None: hyper_parameters = jh.dna_to_hp(StrategyClass.hyper_parameters(), r.dna) r.strategy = StrategyClass() r.strategy.name = r.strategy_name r.strategy.exchange = r.exchange r.strategy.symbol = r.symbol r.strategy.timeframe = r.timeframe # init few objects that couldn't be initiated in Strategy __init__ r.strategy._init_objects() # inject hyper parameters (used for optimize_mode) if hyper_parameters is not None: r.strategy.hp = hyper_parameters selectors.get_position(r.exchange, r.symbol).strategy = r.strategy # add initial balance _save_daily_portfolio_balance() with click.progressbar(length=length, label='Executing simulation...') as progressbar: for i in range(length): # update time store.app.time = first_candles_set[i][0] + 60_000 # add candles for j in candles: short_candle = candles[j]['candles'][i] exchange = candles[j]['exchange'] symbol = candles[j]['symbol'] store.candles.add_candle(short_candle, exchange, symbol, '1m', with_execution=False, with_generation=False) # print short candle if jh.is_debuggable('shorter_period_candles'): print_candle(short_candle, True, symbol) _simulate_price_change_effect(short_candle, exchange, symbol) # generate and add candles for bigger timeframes for timeframe in config['app']['considering_timeframes']: # for 1m, no work is needed if timeframe == '1m': continue count = jh.timeframe_to_one_minutes(timeframe) until = count - ((i + 1) % count) if (i + 1) % count == 0: generated_candle = generate_candle_from_one_minutes( timeframe, candles[j]['candles'][(i - (count - 1)):(i + 1)]) store.candles.add_candle(generated_candle, exchange, symbol, timeframe, with_execution=False, with_generation=False) # update progressbar if not jh.is_debugging() and not jh.should_execute_silently( ) and i % 60 == 0: progressbar.update(60) # now that all new generated candles are ready, execute for r in router.routes: count = jh.timeframe_to_one_minutes(r.timeframe) # 1m timeframe if r.timeframe == timeframes.MINUTE_1: r.strategy._execute() elif (i + 1) % count == 0: # print candle if jh.is_debuggable('trading_candles'): print_candle( store.candles.get_current_candle( r.exchange, r.symbol, r.timeframe), False, r.symbol) r.strategy._execute() # now check to see if there's any MARKET orders waiting to be executed store.orders.execute_pending_market_orders() if i != 0 and i % 1440 == 0: _save_daily_portfolio_balance() if not jh.should_execute_silently(): if jh.is_debuggable('trading_candles') or jh.is_debuggable( 'shorter_period_candles'): print('\n') # print executed time for the backtest session finish_time_track = time.time() print( 'Executed backtest simulation in: ', '{} seconds'.format(round(finish_time_track - begin_time_track, 2))) for r in router.routes: r.strategy._terminate() # now that backtest is finished, add finishing balance _save_daily_portfolio_balance()
def run(start_date: str, finish_date: str, candles: Dict[str, Dict[str, Union[str, np.ndarray]]] = None, chart: bool = False, tradingview: bool = False, full_reports: bool = False, csv: bool = False, json: bool = False) -> None: # clear the screen if not jh.should_execute_silently(): click.clear() # validate routes validate_routes(router) # initiate candle store store.candles.init_storage(5000) # load historical candles if candles is None: print('loading candles...') candles = load_candles(start_date, finish_date) click.clear() if not jh.should_execute_silently(): # print candles table key = '{}-{}'.format(config['app']['considering_candles'][0][0], config['app']['considering_candles'][0][1]) table.key_value(stats.candles(candles[key]['candles']), 'candles', alignments=('left', 'right')) print('\n') # print routes table table.multi_value(stats.routes(router.routes)) print('\n') # print guidance for debugging candles if jh.is_debuggable('trading_candles') or jh.is_debuggable( 'shorter_period_candles'): print( ' Symbol | timestamp | open | close | high | low | volume' ) # run backtest simulation simulator(candles) if not jh.should_execute_silently(): # print trades metrics if store.completed_trades.count > 0: change = [] # calcualte market change for e in router.routes: if e.strategy is None: return first = Candle.select(Candle.close).where( Candle.timestamp == jh.date_to_timestamp(start_date), Candle.exchange == e.exchange, Candle.symbol == e.symbol).first() last = Candle.select(Candle.close).where( Candle.timestamp == jh.date_to_timestamp(finish_date) - 60000, Candle.exchange == e.exchange, Candle.symbol == e.symbol).first() change.append( ((last.close - first.close) / first.close) * 100.0) data = report.portfolio_metrics() data.append( ['Market Change', str(round(np.average(change), 2)) + "%"]) print('\n') table.key_value(data, 'Metrics', alignments=('left', 'right')) print('\n') # save logs store_logs(json, tradingview, csv) if chart: charts.portfolio_vs_asset_returns() # QuantStats' report if full_reports: quantstats.quantstats_tearsheet() else: print(jh.color('No trades were made.', 'yellow'))
def run(start_date: str, finish_date: str, candles: Dict[str, Dict[str, Union[str, np.ndarray]]] = None, chart: bool = False, tradingview: bool = False, full_reports: bool = False, csv: bool = False, json: bool = False) -> None: # clear the screen if not jh.should_execute_silently(): click.clear() # validate routes validate_routes(router) # initiate candle store store.candles.init_storage(5000) # load historical candles if candles is None: print('loading candles...') candles = load_candles(start_date, finish_date) click.clear() if not jh.should_execute_silently(): # print candles table key = f"{config['app']['considering_candles'][0][0]}-{config['app']['considering_candles'][0][1]}" table.key_value(stats.candles(candles[key]['candles']), 'candles', alignments=('left', 'right')) print('\n') # print routes table table.multi_value(stats.routes(router.routes)) print('\n') # print guidance for debugging candles if jh.is_debuggable('trading_candles') or jh.is_debuggable( 'shorter_period_candles'): print( ' Symbol | timestamp | open | close | high | low | volume' ) # run backtest simulation simulator(candles) if not jh.should_execute_silently(): # print trades metrics if store.completed_trades.count > 0: change = [] # calcualte market change for e in router.routes: if e.strategy is None: return first = Candle.select(Candle.close).where( Candle.timestamp == jh.date_to_timestamp(start_date), Candle.exchange == e.exchange, Candle.symbol == e.symbol).first() last = Candle.select(Candle.close).where( Candle.timestamp == jh.date_to_timestamp(finish_date) - 60000, Candle.exchange == e.exchange, Candle.symbol == e.symbol).first() change.append( ((last.close - first.close) / first.close) * 100.0) data = report.portfolio_metrics() data.append( ['Market Change', f"{str(round(np.average(change), 2))}%"]) print('\n') table.key_value(data, 'Metrics', alignments=('left', 'right')) print('\n') # save logs more = "" routes_count = len(router.routes) if routes_count > 1: more = f"-and-{routes_count-1}-more" study_name = f"{router.routes[0].strategy_name}-{router.routes[0].exchange}-{router.routes[0].symbol}-{router.routes[0].timeframe}{more}-{start_date}-{finish_date}" store_logs(study_name, json, tradingview, csv) if chart: charts.portfolio_vs_asset_returns(study_name) # QuantStats' report if full_reports: price_data = [] # load close candles for Buy and hold and calculate pct_change for index, c in enumerate( config['app']['considering_candles']): exchange, symbol = c[0], c[1] if exchange in config['app'][ 'trading_exchanges'] and symbol in config['app'][ 'trading_symbols']: # fetch from database candles_tuple = Candle.select( Candle.timestamp, Candle.close).where( Candle.timestamp.between( jh.date_to_timestamp(start_date), jh.date_to_timestamp(finish_date) - 60000), Candle.exchange == exchange, Candle.symbol == symbol).order_by( Candle.timestamp.asc()).tuples() candles = np.array(candles_tuple) timestamps = candles[:, 0] price_data.append(candles[:, 1]) price_data = np.transpose(price_data) price_df = pd.DataFrame(price_data, index=pd.to_datetime(timestamps, unit="ms"), dtype=float).resample('D').mean() price_pct_change = price_df.pct_change(1).fillna(0) bh_daily_returns_all_routes = price_pct_change.mean(1) quantstats.quantstats_tearsheet(bh_daily_returns_all_routes, study_name) else: print(jh.color('No trades were made.', 'yellow'))