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 test_get_forming_candle(): set_up() candles_to_add = fake_range_candle(13) store.candles.batch_add_candle(candles_to_add[0:4], 'Sandbox', 'BTCUSD', '1m') forming_candle = store.candles.get_current_candle('Sandbox', 'BTCUSD', '5m') assert forming_candle[0] == candles_to_add[0][0] assert forming_candle[1] == candles_to_add[0][1] assert forming_candle[2] == candles_to_add[3][2] # add the rest of 1m candles store.candles.batch_add_candle(candles_to_add[4:], 'Sandbox', 'BTCUSD', '1m') # add 5m candles store.candles.batch_add_candle(candles_to_add[0:5], 'Sandbox', 'BTCUSD', '5m') store.candles.batch_add_candle(candles_to_add[5:10], 'Sandbox', 'BTCUSD', '5m') forming_candle = store.candles.get_current_candle('Sandbox', 'BTCUSD', '5m') assert forming_candle[0] == candles_to_add[10][0] assert forming_candle[1] == candles_to_add[10][1] assert forming_candle[2] == candles_to_add[12][2]
def test_get_candles_including_forming(): set_up() candles_to_add = fake_range_candle(14) store.candles.batch_add_candle(candles_to_add, 'Sandbox', 'BTCUSD', '1m') store.candles.add_candle( generate_candle_from_one_minutes('5m', candles_to_add[0:5], False), 'Sandbox', 'BTCUSD', '5m') store.candles.add_candle( generate_candle_from_one_minutes('5m', candles_to_add[5:10], False), 'Sandbox', 'BTCUSD', '5m') assert len(store.candles.get_candles('Sandbox', 'BTCUSD', '5m')) == 3 assert len(store.candles.get_candles('Sandbox', 'BTCUSD', '1m')) == 14 candles = store.candles.get_candles('Sandbox', 'BTCUSD', '5m') assert candles[0][0] == candles_to_add[0][0] assert candles[-1][2] == candles_to_add[13][2] assert candles[-1][0] == candles_to_add[10][0] # add third one while still a forming candle. Now since # we already have forming, get_candles() must not # append another forming candle to the end. store.candles.add_candle( generate_candle_from_one_minutes('5m', candles_to_add[10:14], True), 'Sandbox', 'BTCUSD', '5m') assert len(store.candles.get_candles('Sandbox', 'BTCUSD', '5m')) == 3 assert candles[-1][2] == candles_to_add[13][2] assert candles[-1][0] == candles_to_add[10][0]
def test_strategy_properties(): set_up([ (exchanges.SANDBOX, 'ETHUSDT', timeframes.MINUTE_5, 'Test19'), (exchanges.SANDBOX, 'BTCUSDT', timeframes.MINUTE_5, 'Test19'), ]) 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) } # run backtest (dates are fake just to pass) backtest_mode.run('2019-04-01', '2019-04-02', candles) for r in routes: s: Strategy = r.strategy assert s.name == r.strategy_name assert s.symbol == r.symbol assert s.exchange == r.exchange assert s.timeframe == r.timeframe assert s.trade is None assert s._is_executing is False assert s._is_initiated is True np.testing.assert_equal(s.current_candle, store.candles.get_current_candle(r.exchange, r.symbol, r.timeframe)) np.testing.assert_equal(s.candles, store.candles.get_candles(r.exchange, r.symbol, r.timeframe)) assert s.position == selectors.get_position(r.exchange, r.symbol) assert s.orders == store.orders.get_orders(r.exchange, r.symbol)
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 test_generate_candle_from_one_minutes(): candles = fake_range_candle(5) five_minutes_candle = generate_candle_from_one_minutes('5m', candles) assert five_minutes_candle[0] == candles[0][0] assert five_minutes_candle[1] == candles[0][1] assert five_minutes_candle[2] == candles[-1][2] assert five_minutes_candle[3] == candles[:, 3].max() assert five_minutes_candle[4] == candles[:, 4].min() assert five_minutes_candle[5] == candles[:, 5].sum()
def test_batch_add_candles(): set_up() assert len(store.candles.get_candles('Sandbox', 'BTCUSD', '1m')) == 0 # create 100 candles candles_to_add = fake_range_candle(100) assert len(candles_to_add) == 100 store.candles.batch_add_candle(candles_to_add, 'Sandbox', 'BTCUSD', '1m') np.testing.assert_equal(store.candles.get_candles('Sandbox', 'BTCUSD', '1m'), candles_to_add)
def test_should_buy_and_execute_buy(): set_up([ (exchanges.SANDBOX, 'ETHUSD', timeframes.MINUTE_5, 'Test01'), ]) 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) } # run backtest (dates are fake just to pass) backtest_mode.run('2019-04-01', '2019-04-02', candles) ss1 = store for r in routes: s: Strategy = r.strategy p = s.position assert p.is_close is True assert len(s.orders) == 3 o: Order = s.orders[0] short_candles = store.candles.get_candles(r.exchange, r.symbol, '1m') assert o.price == short_candles[4][2] assert o.price == s.candles[0][2] assert o.created_at == short_candles[4][0] + 60_000 assert o.is_executed is True assert s.orders[1].role == order_roles.CLOSE_POSITION assert s.orders[2].role == order_roles.CLOSE_POSITION assert s.orders[0].role == order_roles.OPEN_POSITION assert s.trade is None trade: CompletedTrade = store.completed_trades.trades[0] assert trade.type == 'long' # must include executed orders, in this case it's entry and take_profit assert len(trade.orders) == 2 assert trade.orders[0].side == 'buy' assert trade.orders[0].type == 'MARKET' assert trade.orders[1].side == 'sell' assert trade.orders[1].type == 'LIMIT' assert len(store.completed_trades.trades) == 1
def test_can_perform_backtest_with_multiple_routes(): set_up([ (exchanges.SANDBOX, 'ETH-USDT', timeframes.MINUTE_5, 'Test01'), (exchanges.SANDBOX, 'BTC-USDT', timeframes.MINUTE_5, 'Test02'), ]) 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) } # run backtest (dates are fake just to pass) backtest_mode.run('2019-04-01', '2019-04-02', candles) for r in routes: s: Strategy = r.strategy p = s.position assert p.is_close is True assert len(s.orders) == 3 o: Order = s.orders[0] short_candles = store.candles.get_candles(r.exchange, r.symbol, '1m') assert o.price == short_candles[4][2] assert o.price == s.candles[0][2] assert o.created_at == short_candles[4][0] + 60_000 assert o.is_executed is True assert s.orders[0].role == order_roles.OPEN_POSITION assert s.orders[0].type == order_types.MARKET assert s.orders[2].role == order_roles.CLOSE_POSITION assert s.orders[2].type == order_types.STOP assert s.orders[1].role == order_roles.CLOSE_POSITION assert s.orders[1].type == order_types.LIMIT assert s.trade is None assert len(store.completed_trades.trades) == 2 # assert one is long and the other is a short trade assert (store.completed_trades.trades[0].type == 'long' and store.completed_trades.trades[1].type == 'short') or ( store.completed_trades.trades[0].type == 'short' and store.completed_trades.trades[1].type == 'long')
def test_multiple_routes_can_communicate_with_each_other(): set_up([ (exchanges.SANDBOX, 'ETH-USDT', timeframes.MINUTE_5, 'Test03'), (exchanges.SANDBOX, 'BTC-USDT', timeframes.MINUTE_5, 'Test03'), ]) 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) } # run backtest (dates are fake just to pass) backtest_mode.run('2019-04-01', '2019-04-02', candles) assert len(store.completed_trades.trades) == 1 for r in routes: s: Strategy = r.strategy p = s.position assert p.is_close is True o: Order = s.orders[0] short_candles = store.candles.get_candles(r.exchange, r.symbol, '1m') assert o.created_at == short_candles[4][0] + 60_000 if r.strategy.trades_count == 1: assert len(s.orders) == 3 assert o.is_executed is True assert s.orders[0].role == order_roles.OPEN_POSITION assert s.orders[0].type == order_types.LIMIT assert s.orders[2].role == order_roles.CLOSE_POSITION assert s.orders[2].type == order_types.STOP assert s.orders[1].role == order_roles.CLOSE_POSITION assert s.orders[1].type == order_types.LIMIT elif r.strategy.trades_count == 0: assert len(s.orders) == 1 # assert that the order got canceled assert o.is_canceled is True assert s.orders[0].role == order_roles.OPEN_POSITION assert s.orders[0].type == order_types.LIMIT
def test_get_past_candle(): set_up() candles_to_add = fake_range_candle(14) store.candles.batch_add_candle(candles_to_add, 'Sandbox', 'BTCUSD', '1m') store.candles.add_candle( generate_candle_from_one_minutes('5m', candles_to_add[0:5]), 'Sandbox', 'BTCUSD', '5m') store.candles.add_candle( generate_candle_from_one_minutes('5m', candles_to_add[5:10]), 'Sandbox', 'BTCUSD', '5m') assert len(store.candles.get_candles('Sandbox', 'BTCUSD', '1m')) == 14 # get_past_candle() should act as if there are 3 candles (last one being the forming) np.testing.assert_equal( store.candles.get_past_candle('Sandbox', 'BTCUSD', '5m', 1), store.candles.storage[jh.key('Sandbox', 'BTCUSD', '5m')][1]) np.testing.assert_equal( store.candles.get_past_candle('Sandbox', 'BTCUSD', '5m', 0), generate_candle_from_one_minutes('5m', candles_to_add[10:], True))
def test_should_sell_and_execute_sell(): set_up([ (exchanges.SANDBOX, 'ETH-USDT', timeframes.MINUTE_5, 'Test02'), ]) 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) } # run backtest (dates are fake just to pass) backtest_mode.run('2019-04-01', '2019-04-02', candles) for r in routes: s: Strategy = r.strategy p = s.position assert p.is_close is True assert len(s.orders) == 3 o: Order = s.orders[0] short_candles = store.candles.get_candles(r.exchange, r.symbol, '1m') assert o.price == short_candles[4][2] assert o.price == s.candles[0][2] assert o.created_at == short_candles[4][0] + 60_000 assert o.is_executed is True assert s.orders[1].role == order_roles.CLOSE_POSITION assert s.orders[2].role == order_roles.CLOSE_POSITION assert s.orders[0].role == order_roles.OPEN_POSITION assert s.trade is None assert len(store.completed_trades.trades) == 1 assert store.completed_trades.trades[0].type == 'short'