def initiate(self, routes: list, extra_routes: list = None): if extra_routes is None: extra_routes = [] self.set_routes(routes) self.set_extra_candles(extra_routes) from jesse.store import store store.reset(force_install_routes=jh.is_unit_testing())
def set_up(): reset_config() config['app']['trading_exchanges'] = [ exchanges.SANDBOX, exchanges.BITFINEX ] config['app']['trading_symbols'] = ['BTCUSD', 'ETHUSD'] store.reset()
def test_backtesting_three_routes(): reset_config() router.set_routes([ (exchanges.SANDBOX, 'BTCUSD', timeframes.MINUTE_5, 'Test19'), (exchanges.SANDBOX, 'ETHUSD', timeframes.MINUTE_5, 'Test19'), (exchanges.SANDBOX, 'XRPUSD', timeframes.MINUTE_15, 'Test19'), ]) store.reset(True) candles = {} routes = router.routes for r in routes: key = jh.key(r.exchange, r.symbol) candles[key] = { 'exchange': r.exchange, 'symbol': r.symbol, 'candles': fake_range_candle(5 * 3 * 20) } # assert that strategy hasn't been initiated before running backtest_mode() assert r.strategy is None # run backtest (dates are fake just to pass) backtest_mode.run('2019-04-01', '2019-04-02', candles) # there must be three positions present with the updated current_price assert len(store.positions.storage) == 3 for r in routes: # r3's '15m' timeframe makes r1 and r2 to support # '15' timeframe as well. r1 and r2 also make r3 # to support '5m' timeframe also. r_one_min = store.candles.get_candles(r.exchange, r.symbol, '1m') r_five_min = store.candles.get_candles(r.exchange, r.symbol, '5m') r_fifteen_min = store.candles.get_candles(r.exchange, r.symbol, '15m') # assert the count of present candles assert len(r_one_min) == (5 * 3) * 20 assert len(r_five_min) == 20 * 3 assert len(r_fifteen_min) == 20 r_first_1 = r_one_min[0] r_last_1 = r_one_min[-1] r_first_5 = r_five_min[0] r_last_5 = r_five_min[-1] r_first_15 = r_fifteen_min[0] r_last_15 = r_fifteen_min[-1] # assert timestamps assert r_first_1[0] == r_first_5[0] assert r_last_1[0] == (r_last_5[0] + 60000 * 4) assert r_last_5[0] == (r_last_15[0] + 60000 * 10) # assert positions p = selectors.get_position(r.exchange, r.symbol) assert p.is_close is True last_candle = store.candles.get_candles(r.exchange, r.symbol, '1m')[-1] assert p.current_price == last_candle[2] # assert that the strategy has been initiated assert r.strategy is not None
def set_up(count=2): reset_config() config['app']['considering_timeframes'] = ['1m', '5m'] config['app']['considering_symbols'] = ['BTCUSD'] config['app']['considering_exchanges'] = ['Sandbox'] store.reset() store.candles.init_storage(count)
def set_up_with_fee(is_futures_trading=False): reset_config() config['env']['exchanges'][exchanges.SANDBOX]['fee'] = 0.002 config['env']['exchanges'][exchanges.SANDBOX]['assets'] = [ { 'asset': 'USDT', 'balance': 1000 }, { 'asset': 'BTC', 'balance': 0 }, ] if is_futures_trading: # used only in futures trading config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'futures' else: config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'spot' config['env']['exchanges'][ exchanges.SANDBOX]['settlement_currency'] = 'USDT' config['app']['trading_mode'] = 'backtest' config['app']['considering_exchanges'] = ['Sandbox'] router.set_routes([(exchanges.SANDBOX, 'BTC-USDT', '5m', 'Test19')]) store.reset(True) global position global exchange global broker position = selectors.get_position(exchanges.SANDBOX, 'BTC-USDT') position.current_price = 50 exchange = selectors.get_exchange(exchanges.SANDBOX) broker = Broker(position, exchanges.SANDBOX, 'BTC-USDT', timeframes.MINUTE_5)
def set_up(): reset_config() config['app']['considering_exchanges'] = [exchanges.SANDBOX] config['app']['trading_exchanges'] = [exchanges.SANDBOX] config['app']['trading_symbols'] = ['BTC-USD'] config['app']['trading_timeframes'] = ['5m'] store.reset()
def test_forming_candles(): reset_config() router.set_routes([(exchanges.SANDBOX, 'BTC-USDT', timeframes.MINUTE_5, 'Test19')]) router.set_extra_candles([(exchanges.SANDBOX, 'BTC-USDT', timeframes.MINUTE_15)]) store.reset(True) candles = {} key = jh.key(exchanges.SANDBOX, 'BTC-USDT') candles[key] = { 'exchange': exchanges.SANDBOX, 'symbol': 'BTC-USDT', 'candles': test_candles_0 } backtest_mode.run('2019-04-01', '2019-04-02', candles) # use math.ceil because it must include forming candle too assert len( store.candles.get_candles(exchanges.SANDBOX, 'BTC-USDT', timeframes.MINUTE_5)) == math.ceil(1382 / 5) assert len( store.candles.get_candles(exchanges.SANDBOX, 'BTC-USDT', timeframes.MINUTE_15)) == math.ceil(1382 / 15)
def set_up_without_fee(is_margin_trading=False): reset_config() config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'margin' config['env']['exchanges'][exchanges.SANDBOX]['fee'] = 0 config['env']['exchanges'][exchanges.SANDBOX]['assets'] = [ { 'asset': 'USDT', 'balance': 1000 }, { 'asset': 'BTC', 'balance': 0 }, ] if is_margin_trading: # used only in margin trading config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'margin' config['env']['exchanges'][ exchanges.SANDBOX]['settlement_currency'] = 'USDT' config['app']['trading_mode'] = 'backtest' config['app']['considering_exchanges'] = ['Sandbox'] router.set_routes([(exchanges.SANDBOX, 'BTC-USDT', '5m', 'Test19')]) store.reset(True) global position global exchange position = selectors.get_position(exchanges.SANDBOX, 'BTC-USDT') position.current_price = 50 exchange = selectors.get_exchange(exchanges.SANDBOX)
def set_up(routes, fee=0): reset_config() config['env']['exchanges'][exchanges.SANDBOX]['starting_balance'] = 10000 config['env']['exchanges'][exchanges.SANDBOX]['fee'] = fee router.set_routes(routes) router.set_extra_candles([]) store.reset(True)
def set_up(routes=None, is_futures_trading=True, leverage=1, leverage_mode='cross'): reset_config() config['env']['exchanges'][exchanges.SANDBOX]['assets'] = [ { 'asset': 'USDT', 'balance': 10_000 }, { 'asset': 'BTC', 'balance': 0 }, { 'asset': 'ETH', 'balance': 0 }, ] if is_futures_trading: # used only in futures trading config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'futures' config['env']['exchanges'][ exchanges.SANDBOX]['futures_leverage_mode'] = leverage_mode config['env']['exchanges'][ exchanges.SANDBOX]['futures_leverage'] = leverage else: config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'spot' if routes: router.set_routes(routes) store.reset(True)
def test_routes(): # re-define routes router.set_routes([ (exchanges.BITFINEX, 'ETHUSD', timeframes.HOUR_3, 'Test19'), (exchanges.SANDBOX, 'BTCUSD', timeframes.MINUTE_15, 'Test19'), ]) router.set_extra_candles([ (exchanges.BITFINEX, 'EOSUSD', timeframes.HOUR_3), (exchanges.BITFINEX, 'EOSUSD', timeframes.HOUR_1), ]) # reset store for new routes to take affect store.reset(True) # now assert it's working as expected assert set(config['app']['trading_exchanges']) == set( [exchanges.SANDBOX, exchanges.BITFINEX]) assert set(config['app']['trading_symbols']) == set(['BTCUSD', 'ETHUSD']) assert set(config['app']['trading_timeframes']) == set( [timeframes.HOUR_3, timeframes.MINUTE_15]) assert set(config['app']['considering_exchanges']) == set( [exchanges.SANDBOX, exchanges.BITFINEX]) assert set(config['app']['considering_symbols']) == set( ['BTCUSD', 'ETHUSD', 'EOSUSD']) assert set(config['app']['considering_timeframes']) == set([ timeframes.MINUTE_1, timeframes.HOUR_3, timeframes.MINUTE_15, timeframes.HOUR_1 ])
def set_up(): """ """ reset_config() config['app']['considering_candles'] = [('Sandbox', 'BTCUSD')] store.reset() store.trades.init_storage()
def set_up(routes): """ :param routes: """ reset_config() router.set_routes(routes) store.reset(True)
def set_up(): reset_config() config['app']['considering_exchanges'] = [exchanges.SANDBOX] config['env']['exchanges'][exchanges.SANDBOX]['fee'] = 0 config['env']['exchanges'][exchanges.SANDBOX]['starting_balance'] = 1000 config['app']['trading_exchanges'] = [exchanges.SANDBOX] config['app']['trading_symbols'] = ['BTCUSD'] config['app']['trading_timeframes'] = ['5m'] store.reset()
def set_up(routes, is_margin_trading=True): reset_config() if is_margin_trading: # used only in margin trading config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'margin' else: config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'spot' router.set_routes(routes) store.reset(True)
def test_backtesting_one_route(): reset_config() router.set_routes([ (exchanges.SANDBOX, 'BTC-USDT', timeframes.MINUTE_5, 'Test19') ]) config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'margin' store.reset(True) candles = {} key = jh.key(exchanges.SANDBOX, 'BTC-USDT') candles[key] = { 'exchange': exchanges.SANDBOX, 'symbol': 'BTC-USDT', 'candles': fake_range_candle(5 * 20) } routes = router.routes # assert that strategy hasn't been initiated before running backtest_mode() assert routes[0].strategy is None # run backtest (dates are fake just to pass) backtest_mode.run('2019-04-01', '2019-04-02', candles) one_min = store.candles.get_candles(exchanges.SANDBOX, 'BTC-USDT', '1m') five_min = store.candles.get_candles(exchanges.SANDBOX, 'BTC-USDT', '5m') # assert the count of present candles assert len(five_min) == 20 assert len(one_min) == 20 * 5 first_1 = one_min[0] last_1 = one_min[-1] first_5 = five_min[0] last_5 = five_min[-1] # assert time in store assert store.app.time == last_1[0] + 60000 # assert timestamps assert first_1[0] == first_5[0] assert last_1[0] == (last_5[0] + 60000 * 4) # there must be only one positions present assert len(store.positions.storage) == 1 p = selectors.get_position(exchanges.SANDBOX, 'BTC-USDT') assert p.is_close assert p.current_price == last_1[2] assert p.current_price == last_5[2] # assert routes assert len(routes) == 1 assert routes[0].exchange == exchanges.SANDBOX assert routes[0].symbol == 'BTC-USDT' assert routes[0].timeframe == '5m' assert routes[0].strategy_name == 'Test19' # assert that the strategy has been initiated assert routes[0].strategy is not None
def set_up(): reset_config() config['app']['considering_exchanges'] = [exchanges.SANDBOX] config['app']['trading_exchanges'] = [exchanges.SANDBOX] config['env']['exchanges'][exchanges.SANDBOX]['assets'] = [{ 'asset': 'USDT', 'balance': 2000 }] store.reset()
def set_up(): """ """ reset_config() config['app']['considering_exchanges'] = [exchanges.SANDBOX] config['app']['trading_exchanges'] = [exchanges.SANDBOX] config['env']['exchanges'][exchanges.SANDBOX]['starting_balance'] = 2000 store.reset()
def test_must_not_be_able_to_set_two_similar_routes(): reset_config() router.set_routes([ (exchanges.SANDBOX, 'ETH-USDT', timeframes.MINUTE_5, 'Test01'), (exchanges.SANDBOX, 'ETH-USDT', timeframes.MINUTE_30, 'Test02'), ]) with pytest.raises(Exception) as err: store.reset(True) assert str(err.value).startswith( 'each exchange-symbol pair can be traded only once')
def set_up(routes, fee=0): reset_config() config['env']['exchanges'][exchanges.SANDBOX]['assets'] = [ {'asset': 'USDT', 'balance': 1000}, {'asset': 'BTC', 'balance': 0}, ] config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'margin' config['env']['exchanges'][exchanges.SANDBOX]['fee'] = fee router.set_routes(routes) router.set_extra_candles([]) store.reset(True)
def set_up(routes, is_margin_trading=True): reset_config() config['env']['exchanges'][exchanges.SANDBOX]['assets'] = [ {'asset': 'USDT', 'balance': 10000}, {'asset': 'BTC', 'balance': 0}, {'asset': 'ETH', 'balance': 0}, ] if is_margin_trading: # used only in margin trading config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'margin' else: config['env']['exchanges'][exchanges.SANDBOX]['type'] = 'spot' router.set_routes(routes) store.reset(True)
def set_up_with_fee(): reset_config() config['env']['exchanges'][exchanges.SANDBOX]['fee'] = 0.002 config['env']['exchanges'][exchanges.SANDBOX]['starting_balance'] = 1000 config['app']['trading_mode'] = 'backtest' config['app']['considering_exchanges'] = ['Sandbox'] router.set_routes([(exchanges.SANDBOX, 'BTCUSD', '5m', 'Test19')]) store.reset(True) global position global exchange global broker position = selectors.get_position(exchanges.SANDBOX, 'BTCUSD') position.current_price = 50 exchange = selectors.get_exchange(exchanges.SANDBOX) broker = Broker(position, exchanges.SANDBOX, 'BTCUSD', timeframes.MINUTE_5)
def test_routes(): # re-define routes router.set_routes([ { 'exchange': exchanges.BITFINEX, 'symbol': 'ETH-USD', 'timeframe': timeframes.HOUR_3, 'strategy': 'Test19' }, { 'exchange': exchanges.SANDBOX, 'symbol': 'BTC-USD', 'timeframe': timeframes.MINUTE_15, 'strategy': 'Test19' }, ]) router.set_extra_candles([ { 'exchange': exchanges.BITFINEX, 'symbol': 'EOS-USD', 'timeframe': timeframes.HOUR_3 }, { 'exchange': exchanges.BITFINEX, 'symbol': 'EOS-USD', 'timeframe': timeframes.HOUR_1 }, ]) # reset store for new routes to take affect store.reset(True) # now assert it's working as expected assert set(config['app']['trading_exchanges']) == set( [exchanges.SANDBOX, exchanges.BITFINEX]) assert set(config['app']['trading_symbols']) == set(['BTC-USD', 'ETH-USD']) assert set(config['app']['trading_timeframes']) == set( [timeframes.HOUR_3, timeframes.MINUTE_15]) assert set(config['app']['considering_exchanges']) == set( [exchanges.SANDBOX, exchanges.BITFINEX]) assert set(config['app']['considering_symbols']) == set( ['BTC-USD', 'ETH-USD', 'EOS-USD']) assert set(config['app']['considering_timeframes']) == set([ timeframes.MINUTE_1, timeframes.HOUR_3, timeframes.MINUTE_15, timeframes.HOUR_1 ])
def set_up(): reset_config() from jesse.routes import router router.set_routes([{ 'exchange': 'Sandbox', 'symbol': 'BTC-USD', 'timeframe': '1m', 'strategy': 'Test01' }]) router.set_extra_candles([{ 'exchange': 'Sandbox', 'symbol': 'BTC-USD', 'timeframe': '5m' }]) config['app']['considering_timeframes'] = ['1m', '5m'] config['app']['considering_symbols'] = ['BTC-USD'] config['app']['considering_exchanges'] = ['Sandbox'] store.reset(True) store.candles.init_storage()
def test_can_add_trade_to_store(): set_up() assert store.completed_trades.trades == [] trade = CompletedTrade({ 'type': 'long', 'exchange': 'Sandbox', 'entry_price': 10, 'exit_price': 20, 'take_profit_at': 20, 'stop_loss_at': 5, 'qty': 1, 'orders': [], 'symbol': 'BTC-USD', 'opened_at': 1552309186171, 'closed_at': 1552309186171 + 60000 }) store.completed_trades.add_trade(trade) assert store.completed_trades.trades == [trade] store.reset() assert store.completed_trades.trades == []
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 set_up(routes): reset_config() router.set_routes(routes) store.reset(True)
def set_up(): reset_config() config['app']['considering_candles'] = [('Sandbox', 'BTC-USD')] store.reset() store.tickers.init_storage()
def set_up(): """ """ reset_config() store.reset(True)