def test_metrics_for_trades_without_fee(): single_route_backtest('TestMetrics1') trades = store.completed_trades.trades assert len(trades) == 1 stats = metrics.trades(store.completed_trades.trades, store.app.daily_balance) assert stats['total'] == 1 assert stats['starting_balance'] == 10000 assert stats['finishing_balance'] == 10050 assert stats['win_rate'] == 1 assert stats['ratio_avg_win_loss'] is np.nan assert stats['longs_count'] == 1 assert stats['shorts_count'] == 0 assert stats['longs_percentage'] == 100 assert stats['short_percentage'] == 0 assert stats['fee'] == 0 assert stats['net_profit'] == 50 assert stats['net_profit_percentage'] == 0.5 assert stats['average_win'] == 50 assert stats['average_loss'] is np.nan assert stats['expectancy'] == 50 assert stats['expectancy_percentage'] == 0.5 assert stats['expected_net_profit_every_100_trades'] == 50 assert stats['average_holding_period'] == 300 assert stats['average_losing_holding_period'] is np.nan assert stats['average_winning_holding_period'] == 300 assert stats['gross_loss'] == 0 assert stats['gross_profit'] == 50 assert stats['open_pl'] == 0 assert stats['largest_losing_trade'] == 0 assert stats['largest_winning_trade'] == 50
def metrics(self) -> dict: """ Returns all the metrics of the strategy. """ if self.trades_count not in self._cached_metrics: self._cached_metrics[self.trades_count] = metrics.trades( store.completed_trades.trades, store.app.daily_balance) return self._cached_metrics[self.trades_count]
def portfolio_metrics() -> List[ Union[Union[List[Union[str, Any]], List[str], List[Union[Union[str, float], Any]]], Any]]: data = stats.trades(store.completed_trades.trades, store.app.daily_balance) metrics = [ ['Total Closed Trades', data['total']], ['Total Net Profit', '{} ({})'.format(jh.format_currency(round(data['net_profit'], 4)), str(round(data['net_profit_percentage'], 2)) + '%')], ['Starting => Finishing Balance', '{} => {}'.format(jh.format_currency(round(data['starting_balance'], 2)), jh.format_currency(round(data['finishing_balance'], 2)))], ['Total Open Trades', data['total_open_trades']], ['Open PL', jh.format_currency(round(data['open_pl'], 2))], ['Total Paid Fees', jh.format_currency(round(data['fee'], 2))], ['Max Drawdown', '{}%'.format(round(data['max_drawdown'], 2))], ['Annual Return', '{}%'.format(round(data['annual_return'], 2))], ['Expectancy', '{} ({})'.format(jh.format_currency(round(data['expectancy'], 2)), str(round(data['expectancy_percentage'], 2)) + '%')], ['Avg Win | Avg Loss', '{} | {}'.format(jh.format_currency(round(data['average_win'], 2)), jh.format_currency(round(data['average_loss'], 2)))], ['Ratio Avg Win / Avg Loss', round(data['ratio_avg_win_loss'], 2)], ['Percent Profitable', str(round(data['win_rate'] * 100)) + '%'], ['Longs | Shorts', '{}% | {}%'.format(round(data['longs_percentage']), round(data['short_percentage']))], ['Avg Holding Time', jh.readable_duration(data['average_holding_period'], 3)], ['Winning Trades Avg Holding Time', np.nan if np.isnan(data['average_winning_holding_period']) else jh.readable_duration( data['average_winning_holding_period'], 3)], ['Losing Trades Avg Holding Time', np.nan if np.isnan(data['average_losing_holding_period']) else jh.readable_duration( data['average_losing_holding_period'], 3)] ] if jh.get_config('env.metrics.sharpe_ratio', True): metrics.append(['Sharpe Ratio', round(data['sharpe_ratio'], 2)]) if jh.get_config('env.metrics.calmar_ratio', False): metrics.append(['Calmar Ratio', round(data['calmar_ratio'], 2)]) if jh.get_config('env.metrics.sortino_ratio', False): metrics.append(['Sortino Ratio', round(data['sortino_ratio'], 2)]) if jh.get_config('env.metrics.omega_ratio', False): metrics.append(['Omega Ratio', round(data['omega_ratio'], 2)]) if jh.get_config('env.metrics.winning_streak', False): metrics.append(['Winning Streak', data['winning_streak']]) if jh.get_config('env.metrics.losing_streak', False): metrics.append(['Losing Streak', data['losing_streak']]) if jh.get_config('env.metrics.largest_winning_trade', False): metrics.append(['Largest Winning Trade', jh.format_currency(round(data['largest_winning_trade'], 2))]) if jh.get_config('env.metrics.largest_losing_trade', False): metrics.append(['Largest Losing Trade', jh.format_currency(round(data['largest_losing_trade'], 2))]) if jh.get_config('env.metrics.total_winning_trades', False): metrics.append(['Total Winning Trades', data['total_winning_trades']]) if jh.get_config('env.metrics.total_losing_trades', False): metrics.append(['Total Losing Trades', data['total_losing_trades']]) return metrics
def metrics(self) -> dict: """ Returns all the metrics of the strategy. """ if self.index in self._cached_metrics: return self._cached_metrics[self.index] else: self._cached_metrics[self.index] = metrics.trades( store.completed_trades.trades, store.app.daily_balance) return self._cached_metrics[self.index]
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 fitness(self, dna: str) -> tuple: hp = jh.dna_to_hp(self.strategy_hp, dna) # init candle store store.candles.init_storage(5000) # inject required TRAINING candles to the candle store for num, c in enumerate(config['app']['considering_candles']): required_candles.inject_required_candles_to_store( self.training_initial_candles[num], c[0], c[1] ) # run backtest simulation simulator(self.training_candles, hp) training_data = {'win_rate': None, 'total': None, 'net_profit_percentage': None} testing_data = {'win_rate': None, 'total': None, 'net_profit_percentage': None} # TODO: some of these have to be dynamic based on how many days it's trading for like for example "total" # I'm guessing we should accept "optimal" total from command line if store.completed_trades.count > 5: training_data = stats.trades(store.completed_trades.trades, store.app.daily_balance) total_effect_rate = log10(training_data['total']) / log10(self.optimal_total) total_effect_rate = min(total_effect_rate, 1) ratio_config = jh.get_config('env.optimization.ratio', 'sharpe') if ratio_config == 'sharpe': ratio = training_data['sharpe_ratio'] ratio_normalized = jh.normalize(ratio, -.5, 5) elif ratio_config == 'calmar': ratio = training_data['calmar_ratio'] ratio_normalized = jh.normalize(ratio, -.5, 30) elif ratio_config == 'sortino': ratio = training_data['sortino_ratio'] ratio_normalized = jh.normalize(ratio, -.5, 15) elif ratio_config == 'omega': ratio = training_data['omega_ratio'] ratio_normalized = jh.normalize(ratio, -.5, 5) elif ratio_config == 'serenity': ratio = training_data['serenity_index'] ratio_normalized = jh.normalize(ratio, -.5, 15) elif ratio_config == 'smart sharpe': ratio = training_data['smart_sharpe'] ratio_normalized = jh.normalize(ratio, -.5, 5) elif ratio_config == 'smart sortino': ratio = training_data['smart_sortino'] ratio_normalized = jh.normalize(ratio, -.5, 15) else: raise ValueError( f'The entered ratio configuration `{ratio_config}` for the optimization is unknown. Choose between sharpe, calmar, sortino, serenity, smart shapre, smart sortino and omega.') if ratio < 0: score = 0.0001 # reset store store.reset() return score, training_data, testing_data score = total_effect_rate * ratio_normalized # perform backtest with testing data. this is using data # model hasn't trained for. if it works well, there is # high change it will do good with future data too. store.reset() store.candles.init_storage(5000) # inject required TESTING candles to the candle store for num, c in enumerate(config['app']['considering_candles']): required_candles.inject_required_candles_to_store( self.testing_initial_candles[num], c[0], c[1] ) # run backtest simulation simulator(self.testing_candles, hp) # log for debugging/monitoring if store.completed_trades.count > 0: testing_data = stats.trades(store.completed_trades.trades, store.app.daily_balance) else: score = 0.0001 # reset store store.reset() return score, training_data, testing_data
def metrics(self) -> dict: """ Returns all the metrics of the strategy. """ return metrics.trades(store.completed_trades.trades, store.app.daily_balance)
def portfolio_metrics() -> dict: return stats.trades(store.completed_trades.trades, store.app.daily_balance)
def _isolated_backtest(config: dict, routes: List[Dict[str, str]], extra_routes: List[Dict[str, str]], candles: dict, run_silently: bool = True, hyperparameters: dict = None) -> dict: from jesse.services.validators import validate_routes from jesse.modes.backtest_mode import simulator from jesse.config import config as jesse_config, reset_config from jesse.routes import router from jesse.store import store from jesse.config import set_config from jesse.services import metrics from jesse.services import required_candles import jesse.helpers as jh jesse_config['app']['trading_mode'] = 'backtest' # inject (formatted) configuration values set_config(_format_config(config)) # set routes router.initiate(routes, extra_routes) # register_custom_exception_handler() validate_routes(router) # TODO: further validate routes and allow only one exchange # TODO: validate the name of the exchange in the config and the route? or maybe to make sure it's a supported exchange # initiate candle store store.candles.init_storage(5000) # divide candles into warm_up_candles and trading_candles and then inject warm_up_candles max_timeframe = jh.max_timeframe( jesse_config['app']['considering_timeframes']) warm_up_num = config['warm_up_candles'] * jh.timeframe_to_one_minutes( max_timeframe) trading_candles = candles if warm_up_num != 0: for c in jesse_config['app']['considering_candles']: key = jh.key(c[0], c[1]) # update trading_candles trading_candles[key]['candles'] = candles[key]['candles'][ warm_up_num:] # inject warm-up candles required_candles.inject_required_candles_to_store( candles[key]['candles'][:warm_up_num], c[0], c[1]) # run backtest simulation simulator(trading_candles, run_silently, hyperparameters) result = { 'metrics': { 'total': 0, 'win_rate': 0, 'net_profit_percentage': 0 }, 'charts': None, 'logs': None, } if store.completed_trades.count > 0: # add metrics result['metrics'] = metrics.trades(store.completed_trades.trades, store.app.daily_balance) # add charts result['charts'] = charts.portfolio_vs_asset_returns() # add logs result['logs'] = store.logs.info # reset store and config so rerunning would be flawlessly possible reset_config() store.reset() return result