def test_normalize(): assert jh.normalize(10, 0, 20) == 0.5 assert jh.normalize(20, 0, 20) == 1 assert jh.normalize(0, 0, 20) == 0
def fitness(self, dna) -> 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) log = '' # 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) if total_effect_rate > 1: total_effect_rate = 1 win_rate = training_data['win_rate'] ratio_config = jh.get_config('env.optimization.ratio', 'sharpe') if ratio_config == 'sharpe': ratio = training_data['sharpe_ratio'] elif ratio_config == 'calmar': ratio = training_data['calmar_ratio'] elif ratio_config == 'sortiono': ratio = training_data['sortino_ratio'] elif ratio_config == 'omega': ratio = training_data['omega_ratio'] else: raise ValueError( 'The entered ratio configuration `{}` for the optimization is unknown. Choose between sharpe, calmar, sortino and omega.' .format(ratio_config)) if ratio < 0: score = 0.0001 # reset store store.reset() return score, log ratio_normalized = jh.normalize(ratio, -.5, 4) # log for debugging/monitoring log = 'win-rate: {}%, total: {}, PNL: {}%'.format( int(win_rate * 100), training_data['total'], round(training_data['net_profit_percentage'], 2), ) 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) testing_data = stats.trades(store.completed_trades.trades, store.app.daily_balance) # log for debugging/monitoring log += ' || ' if store.completed_trades.count > 0: log += 'win-rate: {}%, total: {}, PNL: {}%'.format( int(testing_data['win_rate'] * 100), testing_data['total'], round(testing_data['net_profit_percentage'], 2), ) if testing_data['net_profit_percentage'] > 0 and training_data[ 'net_profit_percentage'] > 0: log = jh.style(log, 'bold') else: log += 'win-rate: -, total: -, PNL%: -' else: score = 0.0001 # reset store store.reset() return score, log
def fitness(self, dna) -> 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 required_candles.inject_required_candles_to_store( self.required_initial_training_candles, self.exchange, self.symbol) # run backtest simulation simulator(self.training_candles, hp) log = '' # 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) optimal_expected_total = 100 total = jh.normalize(training_data['total'], 0, 200) total_effect_rate = log10( training_data['total']) / log10(optimal_expected_total) win_rate = training_data['win_rate'] # log for debugging/monitoring log = 'win_rate:[{}-{}], total:[{}-{}], PNL%:[{}], TER:[{}]'.format( round(win_rate, 2), round(training_data['win_rate'], 2), round(total, 2), training_data['total'], round(training_data['pnl_percentage'], 2), round(total_effect_rate, 3)) # the fitness score score = win_rate * total_effect_rate # 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 required_candles.inject_required_candles_to_store( self.required_initial_testing_candles, self.exchange, self.symbol) # run backtest simulation simulator(self.testing_candles, hp) testing_data = stats.trades(store.completed_trades.trades) # log for debugging/monitoring log += ' | ' log += 'win_rate:[{}], total:[{}], PNL%:[{}]'.format( round(testing_data['win_rate'], 2), testing_data['total'], round(testing_data['pnl_percentage'], 2), ) if testing_data['pnl_percentage'] > 0 and training_data[ 'pnl_percentage'] > 0: log = jh.style(log, 'bold') else: score = 0.0001 # reset store store.reset() return score, log
def get_fitness(optimization_config: dict, routes: list, extra_routes: list, strategy_hp, dna: str, training_candles, testing_candles, optimal_total) -> tuple: """ Notice that this function is likely to be executed inside workers, hence its inputs must have everything required for it to run. So it cannot access store, config, etc """ hp = jh.dna_to_hp(strategy_hp, dna) # run backtest simulation try: training_data_metrics = isolated_backtest( _formatted_inputs_for_isolated_backtest(optimization_config, routes), routes, extra_routes, training_candles, hyperparameters=hp)['metrics'] except Exception as e: # get the main title of the exception log_text = e log_text = f"Exception in strategy execution:\n {log_text}" logger.log_optimize_mode(log_text) raise e training_log = {'win-rate': None, 'total': None, 'PNL': None} testing_log = {'win-rate': None, 'total': None, 'PNL': None} # TODO: some of these have to be dynamic based on how many days it's trading for like for example "total" if training_data_metrics['total'] > 5: total_effect_rate = log10( training_data_metrics['total']) / log10(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_metrics['sharpe_ratio'] ratio_normalized = jh.normalize(ratio, -.5, 5) elif ratio_config == 'calmar': ratio = training_data_metrics['calmar_ratio'] ratio_normalized = jh.normalize(ratio, -.5, 30) elif ratio_config == 'sortino': ratio = training_data_metrics['sortino_ratio'] ratio_normalized = jh.normalize(ratio, -.5, 15) elif ratio_config == 'omega': ratio = training_data_metrics['omega_ratio'] ratio_normalized = jh.normalize(ratio, -.5, 5) elif ratio_config == 'serenity': ratio = training_data_metrics['serenity_index'] ratio_normalized = jh.normalize(ratio, -.5, 15) elif ratio_config == 'smart sharpe': ratio = training_data_metrics['smart_sharpe'] ratio_normalized = jh.normalize(ratio, -.5, 5) elif ratio_config == 'smart sortino': ratio = training_data_metrics['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 logger.log_optimize_mode( f"NEGATIVE RATIO: DNA is not usable => {ratio_config}: {ratio}, total: {training_data_metrics['total']}" ) return score, training_log, testing_log # log for debugging/monitoring training_log = { 'win-rate': int(training_data_metrics['win_rate'] * 100), 'total': training_data_metrics['total'], 'PNL': round(training_data_metrics['net_profit_percentage'], 2) } score = total_effect_rate * ratio_normalized # if score is numpy nan, replace it with 0.0001 if np.isnan(score): logger.log_optimize_mode(f'Score is nan. DNA is invalid') score = 0.0001 # elif jh.is_debugging(): else: logger.log_optimize_mode( f"DNA is usable => {ratio_config}: {round(ratio, 2)}, total: {training_data_metrics['total']}, PNL%: {round(training_data_metrics['net_profit_percentage'], 2)}%, win-rate: {round(training_data_metrics['win_rate']*100, 2)}%" ) # run backtest simulation testing_data_metrics = isolated_backtest( _formatted_inputs_for_isolated_backtest(optimization_config, routes), routes, extra_routes, testing_candles, hyperparameters=hp)['metrics'] # log for debugging/monitoring if testing_data_metrics['total'] > 0: testing_log = { 'win-rate': int(testing_data_metrics['win_rate'] * 100), 'total': testing_data_metrics['total'], 'PNL': round(testing_data_metrics['net_profit_percentage'], 2) } else: logger.log_optimize_mode( f'Less than 5 trades in the training data. DNA is invalid') score = 0.0001 return score, training_log, testing_log
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