async def test_sell_limit_order_trigger(sell_limit_order): order_price = decimal_random_price(min_value=2) sell_limit_order.update( price=order_price, quantity=decimal_random_quantity(max_value=DEFAULT_SYMBOL_QUANTITY), symbol=DEFAULT_ORDER_SYMBOL, order_type=TraderOrderType.SELL_LIMIT, ) sell_limit_order.exchange_manager.is_backtesting = True # force update_order_status await sell_limit_order.initialize() sell_limit_order.exchange_manager.exchange_personal_data.orders_manager.upsert_order_instance( sell_limit_order) price_events_manager = sell_limit_order.exchange_manager.exchange_symbols_data.get_exchange_symbol_data( DEFAULT_ORDER_SYMBOL).price_events_manager price_events_manager.handle_recent_trades([ decimal_random_recent_trade(price=decimal_random_price( max_value=order_price - trading_constants.ONE), timestamp=sell_limit_order.timestamp) ]) await wait_asyncio_next_cycle() assert not sell_limit_order.is_filled() price_events_manager.handle_recent_trades([ decimal_random_recent_trade(price=order_price, timestamp=sell_limit_order.timestamp - 1) ]) await wait_asyncio_next_cycle() assert not sell_limit_order.is_filled() price_events_manager.handle_recent_trades([ decimal_random_recent_trade(price=order_price, timestamp=sell_limit_order.timestamp) ]) await wait_asyncio_next_cycle() assert sell_limit_order.is_filled()
async def test_handle_price(price_events_manager): random_price_1 = decimal_random_price() random_timestamp_1 = random_timestamp(min_value=2, max_value=1000) price_event_1 = price_events_manager.add_event( decimal.Decimal(str(random_price_1)), random_timestamp_1, True) with patch.object(price_event_1, 'set', new=Mock()) as price_event_1_set: price_events_manager.handle_price(trading_constants.ZERO, random_timestamp()) with pytest.raises(AssertionError): price_event_1_set.assert_called_once() price_events_manager.handle_price(price=decimal_random_price( max_value=random_price_1 - trading_constants.ONE), timestamp=random_timestamp()) with pytest.raises(AssertionError): price_event_1_set.assert_called_once() price_events_manager.handle_price(price=decimal_random_price( max_value=random_price_1 - trading_constants.ONE), timestamp=random_timestamp()) price_events_manager.handle_price( price=decimal_random_price(min_value=random_price_1), timestamp=random_timestamp_1 - 1) with pytest.raises(AssertionError): price_event_1_set.assert_called_once() price_events_manager.handle_price( price=decimal_random_price(min_value=random_price_1), timestamp=random_timestamp_1 + 1) price_event_1_set.assert_called_once()
async def test_refresh_simulated_trader_portfolio_from_order(backtesting_trader): config, exchange_manager, trader = backtesting_trader portfolio_manager = exchange_manager.exchange_personal_data.portfolio_manager if os.getenv('CYTHON_IGNORE'): return order = BuyLimitOrder(trader) await order.initialize() with patch.object(portfolio_manager.portfolio, 'update_portfolio_available', new=Mock()) as update_portfolio_available_mock: update_portfolio_available_mock.assert_not_called() portfolio_manager._refresh_simulated_trader_portfolio_from_order(order) update_portfolio_available_mock.assert_called_once() price = decimal_random_price() order.update( price=decimal_random_price(), quantity=decimal_random_quantity(max_value=DEFAULT_MARKET_QUANTITY / price), symbol="BTC/USDT" ) await order.on_fill(force_fill=True) assert order.is_filled() with patch.object(portfolio_manager.portfolio, 'update_portfolio_from_filled_order', new=Mock()) as update_portfolio_from_filled_order_mock: update_portfolio_from_filled_order_mock.assert_not_called() portfolio_manager._refresh_simulated_trader_portfolio_from_order(order) update_portfolio_from_filled_order_mock.assert_called_once()
async def test_add_event(price_events_manager): price_events_manager.add_event(decimal_random_price(), random_timestamp(), True) price_events_manager.add_event(decimal_random_price(), random_timestamp(), False) if not os.getenv('CYTHON_IGNORE'): assert price_events_manager.events assert len(price_events_manager.events) == 2
async def test_remove_event(price_events_manager): event_1 = price_events_manager.add_event(decimal_random_price(), random_timestamp(), True) event_2 = price_events_manager.add_event(decimal_random_price(), random_timestamp(), False) if not os.getenv('CYTHON_IGNORE'): assert price_events_manager.events price_events_manager.remove_event(event_1) assert event_1 not in price_events_manager.events assert len(price_events_manager.events) == 1 price_events_manager.remove_event(Event()) assert len(price_events_manager.events) == 1 price_events_manager.remove_event(event_2) assert event_2 not in price_events_manager.events assert len(price_events_manager.events) == 0
async def test_sell_market_order_trigger(sell_market_order): order_price = decimal_random_price() sell_market_order.update( price=order_price, quantity=decimal_random_quantity(max_value=DEFAULT_SYMBOL_QUANTITY), symbol=DEFAULT_ORDER_SYMBOL, order_type=TraderOrderType.SELL_MARKET, ) sell_market_order.exchange_manager.is_backtesting = True # force update_order_status await sell_market_order.initialize() assert sell_market_order.is_filled()
async def test_take_profit_limit_order_trigger(take_profit_limit_order): order_price = decimal_random_price(min_value=2) take_profit_limit_order.limit_price = order_price + decimal.Decimal(10) take_profit_limit_order.update( price=order_price, quantity=decimal_random_quantity( max_value=decimal.Decimal(DEFAULT_SYMBOL_QUANTITY / 10)), symbol=DEFAULT_ORDER_SYMBOL, order_type=TraderOrderType.TAKE_PROFIT_LIMIT, ) take_profit_limit_order.exchange_manager.is_backtesting = True # force update_order_status await take_profit_limit_order.initialize() take_profit_limit_order.exchange_manager.exchange_personal_data.orders_manager.upsert_order_instance( take_profit_limit_order) price_events_manager = take_profit_limit_order.exchange_manager.exchange_symbols_data.get_exchange_symbol_data( DEFAULT_ORDER_SYMBOL).price_events_manager price_events_manager.handle_recent_trades([ decimal_random_recent_trade( price=decimal_random_price(max_value=order_price - trading_constants.ONE), timestamp=take_profit_limit_order.timestamp) ]) await wait_asyncio_next_cycle() assert not take_profit_limit_order.is_filled() price_events_manager.handle_recent_trades([ decimal_random_recent_trade( price=order_price, timestamp=take_profit_limit_order.timestamp - 1) ]) await wait_asyncio_next_cycle() assert not take_profit_limit_order.is_filled() price_events_manager.handle_recent_trades([ decimal_random_recent_trade( price=order_price, timestamp=take_profit_limit_order.timestamp) ]) # wait for 2 cycles as secondary orders are created await wait_asyncio_next_cycle() await wait_asyncio_next_cycle() assert take_profit_limit_order.is_filled()
async def test_limit_and_stop_loss(stop_loss_sell_order, sell_limit_order): # fill both orders: limit first limit_order_price = decimal_random_price() quantity = decimal_random_quantity(max_value=DEFAULT_SYMBOL_QUANTITY) sell_limit_order.update( price=limit_order_price, quantity=quantity, symbol=DEFAULT_ORDER_SYMBOL, order_type=TraderOrderType.SELL_LIMIT, ) stop_order_price = decimal_random_price(max_value=limit_order_price - 1) stop_loss_sell_order.update(price=stop_order_price, quantity=quantity, symbol=DEFAULT_ORDER_SYMBOL, order_type=TraderOrderType.STOP_LOSS, linked_to=sell_limit_order) stop_loss_sell_order.linked_orders.append(sell_limit_order) sell_limit_order.linked_orders.append(stop_loss_sell_order) stop_loss_sell_order.exchange_manager.is_backtesting = True # force update_order_status # initialize limit order first await sell_limit_order.initialize() await stop_loss_sell_order.initialize() price_events_manager = stop_loss_sell_order.exchange_manager.exchange_symbols_data.get_exchange_symbol_data( DEFAULT_ORDER_SYMBOL).price_events_manager # stop loss sell order triggers when price is bellow or equal to its trigger price # sell limit order triggers when price is above or equal to its trigger price # here trigger both: limit is triggered first (initialized first): sell stop loss order should be # cancelled and not filled even though its price has been hit price_events_manager.handle_recent_trades([ random_recent_trade( price=random_price(max_value=float(stop_order_price - 1)), timestamp=sell_limit_order.timestamp), random_recent_trade( price=random_price(min_value=float(limit_order_price + 1)), timestamp=stop_loss_sell_order.timestamp) ]) await wait_asyncio_next_cycle() assert stop_loss_sell_order.is_cancelled() assert sell_limit_order.is_filled()
async def test_get_mark_price(prices_manager): # without a set price with pytest.raises(asyncio.TimeoutError): await prices_manager.get_mark_price(0.01) assert not prices_manager.valid_price_received_event.is_set() # set price prices_manager.set_mark_price(decimal.Decimal(10), MarkPriceSources.EXCHANGE_MARK_PRICE.value) assert await prices_manager.get_mark_price(0.01) == decimal.Decimal(10) assert prices_manager.valid_price_received_event.is_set() # expired price if not os.getenv('CYTHON_IGNORE'): prices_manager.exchange_manager.backtesting.time_manager.current_timestamp = 66666666 with pytest.raises(asyncio.TimeoutError): await prices_manager.get_mark_price(0.01) assert not prices_manager.valid_price_received_event.is_set() # reset price with this time prices_manager.set_mark_price(decimal.Decimal(10), MarkPriceSources.EXCHANGE_MARK_PRICE.value) assert await prices_manager.get_mark_price(0.01) == decimal.Decimal(10) assert prices_manager.valid_price_received_event.is_set() # current time move within allowed range if not os.getenv('CYTHON_IGNORE'): prices_manager.exchange_manager.backtesting.time_manager.current_timestamp = 1 assert await prices_manager.get_mark_price(0.01) == decimal.Decimal(10) assert prices_manager.valid_price_received_event.is_set() # new value prices_manager.set_mark_price(decimal.Decimal(42.0000172), MarkPriceSources.EXCHANGE_MARK_PRICE.value) assert await prices_manager.get_mark_price(0.01) == decimal.Decimal( 42.0000172) assert prices_manager.valid_price_received_event.is_set() # random value random_mark_price = decimal_random_price() prices_manager.set_mark_price(random_mark_price, MarkPriceSources.EXCHANGE_MARK_PRICE.value) assert await prices_manager.get_mark_price(0.01) == random_mark_price assert prices_manager.valid_price_received_event.is_set()
async def test_handle_recent_trades_multiple_events(price_events_manager): random_price_1 = decimal_random_price(min_value=decimal.Decimal(2)) random_price_2 = decimal_random_price(min_value=random_price_1) random_timestamp_1 = random_timestamp(min_value=2, max_value=1000) random_timestamp_2 = random_timestamp(min_value=random_timestamp_1 + 2, max_value=5000) price_event_1 = price_events_manager.add_event(random_price_1, random_timestamp_1, True) price_event_2 = price_events_manager.add_event(random_price_2, random_timestamp_2, True) with patch.object(price_event_1, 'set', new=Mock()) as price_event_1_set, \ patch.object(price_event_2, 'set', new=Mock()) as price_event_2_set: price_events_manager.handle_recent_trades([ decimal_random_recent_trade(price=decimal_random_price( max_value=random_price_1 - trading_constants.ONE)), decimal_random_recent_trade(price=decimal_random_price( max_value=random_price_1 - trading_constants.ONE)), decimal_random_recent_trade(price=decimal_random_price( max_value=random_price_1 - trading_constants.ONE)) ]) with pytest.raises(AssertionError): price_event_1_set.assert_called_once() with pytest.raises(AssertionError): price_event_2_set.assert_called_once() price_events_manager.handle_recent_trades([ decimal_random_recent_trade( price=decimal_random_price(max_value=random_price_1 - trading_constants.ONE), timestamp=random_timestamp(max_value=random_timestamp_1 - 1)), decimal_random_recent_trade( price=random_price_2 - trading_constants.ONE, timestamp=random_timestamp(min_value=random_timestamp_1, max_value=random_timestamp_2)) ]) price_event_1_set.assert_called_once() with pytest.raises(AssertionError): price_event_2_set.assert_called_once() price_events_manager.handle_recent_trades([ decimal_random_recent_trade(price=decimal_random_price( max_value=random_price_1 - trading_constants.ONE)), decimal_random_recent_trade(price=random_price_2, timestamp=random_timestamp_2) ]) price_event_2_set.assert_called_once() price_event_1 = price_events_manager.add_event(random_price_1, random_timestamp_1, True) price_event_2 = price_events_manager.add_event(random_price_2, random_timestamp_2, True) with patch.object(price_event_1, 'set', new=Mock()) as price_event_1_set, \ patch.object(price_event_2, 'set', new=Mock()) as price_event_2_set: price_events_manager.handle_recent_trades([ decimal_random_recent_trade( price=decimal_random_price(max_value=random_price_1 - trading_constants.ONE), timestamp=random_timestamp(max_value=random_timestamp_1 - 1)), decimal_random_recent_trade( price=random_price_2 + decimal.Decimal(10), timestamp=random_timestamp(min_value=random_timestamp_2 + 1)) ]) price_event_1_set.assert_called_once() price_event_2_set.assert_called_once() price_event_1 = price_events_manager.add_event(random_price_1, random_timestamp_1, True) price_event_2 = price_events_manager.add_event(random_price_2, random_timestamp_2, True) with patch.object(price_event_1, 'set', new=Mock()) as price_event_1_set, \ patch.object(price_event_2, 'set', new=Mock()) as price_event_2_set: price_events_manager.handle_recent_trades([ decimal_random_recent_trade( price=decimal_random_price(min_value=random_price_1, max_value=random_price_2 - trading_constants.ONE), timestamp=random_timestamp(min_value=random_timestamp_1 - 1)), decimal_random_recent_trade( price=random_price_2, timestamp=random_timestamp(max_value=random_timestamp_2 - 1)) ]) price_event_1_set.assert_called_once() with pytest.raises(AssertionError): price_event_2_set.assert_called_once()
async def test_get_current_crypto_currencies_values(backtesting_trader): config, exchange_manager, trader = backtesting_trader portfolio_manager = exchange_manager.exchange_personal_data.portfolio_manager portfolio_value_holder = portfolio_manager.portfolio_value_holder assert portfolio_value_holder.get_current_crypto_currencies_values() == { 'BTC': constants.ONE, 'USDT': constants.ZERO } portfolio_manager.portfolio.update_portfolio_from_balance( { 'BTC': { 'available': decimal_random_quantity(), 'total': decimal_random_quantity() }, 'ETH': { 'available': decimal_random_quantity(), 'total': decimal_random_quantity() }, 'XRP': { 'available': decimal_random_quantity(), 'total': decimal_random_quantity() }, 'NANO': { 'available': decimal_random_quantity(), 'total': decimal_random_quantity() }, 'XLM': { 'available': decimal_random_quantity(), 'total': decimal_random_quantity() }, 'USDT': { 'available': decimal_random_quantity(), 'total': decimal_random_quantity() } }, exchange_manager) portfolio_manager.handle_balance_updated() assert portfolio_value_holder.get_current_crypto_currencies_values() == { 'BTC': constants.ONE, 'ETH': constants.ZERO, 'XRP': constants.ZERO, 'NANO': constants.ZERO, 'XLM': constants.ZERO, 'USDT': constants.ZERO } exchange_manager.client_symbols.append("XLM/BTC") exchange_manager.client_symbols.append("XRP/BTC") if not os.getenv('CYTHON_IGNORE'): portfolio_value_holder.missing_currency_data_in_exchange.remove("XRP") portfolio_manager.handle_mark_price_update("XRP/BTC", decimal.Decimal("0.005")) exchange_manager.client_symbols.append("NANO/BTC") portfolio_value_holder.missing_currency_data_in_exchange.remove("NANO") portfolio_manager.handle_mark_price_update("NANO/BTC", decimal.Decimal("0.05")) exchange_manager.client_symbols.append("BTC/USDT") assert portfolio_value_holder.get_current_crypto_currencies_values( ) == { 'BTC': constants.ONE, 'ETH': constants.ZERO, 'XRP': decimal.Decimal("0.005"), 'NANO': decimal.Decimal("0.05"), 'XLM': constants.ZERO, 'USDT': constants.ZERO } xlm_btc_price = decimal_random_price(max_value=decimal.Decimal(0.05)) portfolio_value_holder.missing_currency_data_in_exchange.remove("XLM") portfolio_manager.handle_mark_price_update("XLM/BTC", xlm_btc_price) assert portfolio_value_holder.get_current_crypto_currencies_values( ) == { 'BTC': constants.ONE, 'ETH': constants.ZERO, 'XRP': decimal.Decimal("0.005"), 'NANO': decimal.Decimal("0.05"), 'XLM': xlm_btc_price, 'USDT': constants.ZERO } usdt_btc_price = decimal_random_price( max_value=decimal.Decimal('0.01')) portfolio_value_holder.missing_currency_data_in_exchange.remove("USDT") portfolio_manager.handle_mark_price_update("BTC/USDT", usdt_btc_price) assert portfolio_value_holder.get_current_crypto_currencies_values( ) == { 'BTC': constants.ONE, 'ETH': constants.ZERO, 'XRP': decimal.Decimal("0.005"), 'NANO': decimal.Decimal("0.05"), 'XLM': xlm_btc_price, 'USDT': constants.ONE / usdt_btc_price } eth_btc_price = decimal_random_price(max_value=constants.ONE) exchange_manager.client_symbols.append("ETH/BTC") portfolio_value_holder.missing_currency_data_in_exchange.remove("ETH") portfolio_manager.handle_mark_price_update("ETH/BTC", eth_btc_price) assert portfolio_value_holder.get_current_crypto_currencies_values( ) == { 'BTC': constants.ONE, 'ETH': decimal.Decimal(str(eth_btc_price)), 'XRP': decimal.Decimal("0.005"), 'NANO': decimal.Decimal("0.05"), 'XLM': xlm_btc_price, 'USDT': constants.ONE / usdt_btc_price }