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()
Exemple #3
0
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()
Exemple #7
0
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
        }