def test_is_similar_with_prices_close_to_own_price_very_low_prices(): container = arbitrage_container_import.ArbitrageContainer( 0.00000621, 0.00000645, trading_enums.EvaluatorStates.LONG) # too different prices comparing to own_exchange_price for price in (0.0000060, 0.0000061, 0.0000065, 0.000007): assert not container.is_similar(price, trading_enums.EvaluatorStates.LONG) assert not container.is_similar(price, trading_enums.EvaluatorStates.LONG) # similar prices comparing to own_exchange_price for price in (0.000006196, 0.00000620, 0.00000646, 0.000006463): assert container.is_similar(price, trading_enums.EvaluatorStates.LONG) assert container.is_similar(price, trading_enums.EvaluatorStates.LONG) container = arbitrage_container_import.ArbitrageContainer( 0.00000062, 0.00000064, trading_enums.EvaluatorStates.LONG) # too different prices comparing to own_exchange_price for price in (0.00000060, 0.00000061, 0.00000065, 0.0000007): assert not container.is_similar(price, trading_enums.EvaluatorStates.LONG) assert not container.is_similar(price, trading_enums.EvaluatorStates.LONG) # similar prices comparing to own_exchange_price for price in (0.0000006199, 0.0000006401): assert container.is_similar(price, trading_enums.EvaluatorStates.LONG) assert container.is_similar(price, trading_enums.EvaluatorStates.LONG)
async def test_ensure_no_existing_arbitrage_on_this_price(): async with arbitrage_trading_mode_tests.exchange( "binance") as exchange_tuple: binance_producer, binance_consumer, _ = exchange_tuple arbitrage_1 = arbitrage_container_import.ArbitrageContainer( 10, 15, trading_enums.EvaluatorStates.LONG) arbitrage_2 = arbitrage_container_import.ArbitrageContainer( 20, 18, trading_enums.EvaluatorStates.SHORT) binance_consumer.open_arbitrages = [arbitrage_1, arbitrage_2] binance_producer.own_exchange_mark_price = 9 assert binance_producer._ensure_no_existing_arbitrage_on_this_price( trading_enums.EvaluatorStates.LONG) assert binance_producer._ensure_no_existing_arbitrage_on_this_price( trading_enums.EvaluatorStates.SHORT) for price in (9.99, 10, 11, 15): binance_producer.own_exchange_mark_price = price assert not binance_producer._ensure_no_existing_arbitrage_on_this_price( trading_enums.EvaluatorStates.LONG) assert binance_producer._ensure_no_existing_arbitrage_on_this_price( trading_enums.EvaluatorStates.SHORT) for price in (18, 17.99, 20, 20.001): binance_producer.own_exchange_mark_price = price assert binance_producer._ensure_no_existing_arbitrage_on_this_price( trading_enums.EvaluatorStates.LONG) assert not binance_producer._ensure_no_existing_arbitrage_on_this_price( trading_enums.EvaluatorStates.SHORT)
async def test_create_initial_arbitrage_order(): async with arbitrage_trading_mode_tests.exchange( "binance") as arbitrage_trading_mode_tests.exchange_tuple: _, binance_consumer, _ = arbitrage_trading_mode_tests.exchange_tuple price = 10 # long arbitrage = arbitrage_container_import.ArbitrageContainer( price, 15, trading_enums.EvaluatorStates.LONG) orders = await binance_consumer._create_initial_arbitrage_order( arbitrage) assert orders order = orders[0] assert order.exchange_order_type is trading_enums.TradeOrderType.LIMIT assert order.order_type is trading_enums.TraderOrderType.BUY_LIMIT assert order.side is trading_enums.TradeOrderSide.BUY assert order.symbol == binance_consumer.trading_mode.symbol assert order.order_id == arbitrage.initial_limit_order_id assert arbitrage in binance_consumer.open_arbitrages # short arbitrage = arbitrage_container_import.ArbitrageContainer( price, 15, trading_enums.EvaluatorStates.SHORT) orders = await binance_consumer._create_initial_arbitrage_order( arbitrage) assert orders order = orders[0] assert order.exchange_order_type is trading_enums.TradeOrderType.LIMIT assert order.order_type is trading_enums.TraderOrderType.SELL_LIMIT assert order.side is trading_enums.TradeOrderSide.SELL assert order.symbol == binance_consumer.trading_mode.symbol assert order.order_id == arbitrage.initial_limit_order_id assert arbitrage in binance_consumer.open_arbitrages
async def test_ensure_no_expired_opportunities(): async with arbitrage_trading_mode_tests.exchange( "binance") as exchange_tuple: binance_producer, binance_consumer, exchange_manager = exchange_tuple arbitrage_1 = arbitrage_container_import.ArbitrageContainer( 10, 15, trading_enums.EvaluatorStates.LONG) arbitrage_2 = arbitrage_container_import.ArbitrageContainer( 20, 17, trading_enums.EvaluatorStates.SHORT) binance_consumer.open_arbitrages = [arbitrage_1, arbitrage_2] with mock.patch.object(binance_producer, "_cancel_order", new=mock.AsyncMock()) as cancel_order_mock: # average price is 18 # long order is valid # short order is expired (price > 17) await binance_producer._ensure_no_expired_opportunities( 18, trading_enums.EvaluatorStates.LONG) assert arbitrage_2 not in binance_consumer.open_arbitrages cancel_order_mock.assert_called_once() cancel_order_mock.reset_mock() await binance_producer._ensure_no_expired_opportunities( 18, trading_enums.EvaluatorStates.SHORT) assert binance_consumer.open_arbitrages == [arbitrage_1] cancel_order_mock.assert_not_called()
async def test_trigger_arbitrage_secondary_order(): async with arbitrage_trading_mode_tests.exchange( "binance") as exchange_tuple: binance_producer, _, _ = exchange_tuple order_id = "1" price = 10 quantity = 3 fees = 0.1 fees_currency = "BTC" symbol = "BTC/USDT" order_dict = get_order_dict(order_id, symbol, price, quantity, trading_enums.OrderStatus.FILLED.value, trading_enums.TradeOrderType.LIMIT.value, fees, fees_currency) with mock.patch.object(binance_producer, "_create_arbitrage_secondary_order", new=mock.AsyncMock()) as order_mock: # long: already bought, is now selling arbitrage = arbitrage_container_import.ArbitrageContainer( price, 15, trading_enums.EvaluatorStates.LONG) await binance_producer._trigger_arbitrage_secondary_order( arbitrage, order_dict, 3) updated_arbitrage, secondary_quantity = order_mock.mock_calls[ 0].args assert updated_arbitrage is arbitrage assert arbitrage.passed_initial_order assert arbitrage.initial_before_fee_filled_quantity == 30 assert secondary_quantity == 2.9 order_mock.reset_mock() # short: already sold, is now buying: no fee on base side arbitrage_2 = arbitrage_container_import.ArbitrageContainer( price, 7, trading_enums.EvaluatorStates.SHORT) await binance_producer._trigger_arbitrage_secondary_order( arbitrage_2, order_dict, 3) updated_arbitrage, secondary_quantity = order_mock.mock_calls[ 0].args assert updated_arbitrage is arbitrage_2 assert arbitrage_2.passed_initial_order assert arbitrage_2.initial_before_fee_filled_quantity == 3 assert round(secondary_quantity, 5) == 4.14282 order_mock.reset_mock() # short: already sold, is now buying: fee on base side arbitrage_3 = arbitrage_container_import.ArbitrageContainer( price, 7, trading_enums.EvaluatorStates.SHORT) order_dict = get_order_dict( order_id, symbol, price, quantity, trading_enums.OrderStatus.FILLED.value, trading_enums.TradeOrderType.STOP_LOSS.value, fees, "USDT") await binance_producer._trigger_arbitrage_secondary_order( arbitrage_3, order_dict, 3) updated_arbitrage, secondary_quantity = order_mock.mock_calls[ 0].args assert updated_arbitrage is arbitrage_3 assert arbitrage_3.passed_initial_order assert arbitrage_3.initial_before_fee_filled_quantity == 3 assert round(secondary_quantity, 5) == 4.27139
def test_is_expired(): container = arbitrage_container_import.ArbitrageContainer( 90, 100, trading_enums.EvaluatorStates.LONG) assert not container.is_expired(99.99) assert container.is_expired(99) container = arbitrage_container_import.ArbitrageContainer( 100, 90, trading_enums.EvaluatorStates.SHORT) assert not container.is_expired(90.01) assert container.is_expired(91)
def test_is_expired_very_low_prices(): container = arbitrage_container_import.ArbitrageContainer( 0.00000621, 0.00000645, trading_enums.EvaluatorStates.LONG) assert not container.is_expired(0.00000644) assert container.is_expired(0.00000643) container = arbitrage_container_import.ArbitrageContainer( 0.00000062, 0.00000064, trading_enums.EvaluatorStates.LONG) assert not container.is_expired(0.000000639) assert container.is_expired(0.000000637)
async def test_create_secondary_arbitrage_order(): async with arbitrage_trading_mode_tests.exchange( "binance") as arbitrage_trading_mode_tests.exchange_tuple: _, binance_consumer, _ = arbitrage_trading_mode_tests.exchange_tuple price = 10 # long arbitrage = arbitrage_container_import.ArbitrageContainer( price, 15, trading_enums.EvaluatorStates.LONG) quantity = 5 orders = await binance_consumer._create_secondary_arbitrage_order( arbitrage, quantity) assert orders limit_order = orders[0] assert limit_order.exchange_order_type is trading_enums.TradeOrderType.LIMIT assert limit_order.order_type is trading_enums.TraderOrderType.SELL_LIMIT assert limit_order.side is trading_enums.TradeOrderSide.SELL assert limit_order.symbol == binance_consumer.trading_mode.symbol assert limit_order.order_id == arbitrage.secondary_limit_order_id assert limit_order.origin_quantity == quantity stop_order = limit_order.linked_orders[0] assert stop_order.exchange_order_type is trading_enums.TradeOrderType.STOP_LOSS assert stop_order.order_type is trading_enums.TraderOrderType.STOP_LOSS assert stop_order.side is trading_enums.TradeOrderSide.SELL assert stop_order.symbol == binance_consumer.trading_mode.symbol assert stop_order.order_id == arbitrage.secondary_stop_order_id assert stop_order.origin_quantity == quantity # short arbitrage = arbitrage_container_import.ArbitrageContainer( price, 15, trading_enums.EvaluatorStates.SHORT) quantity = 5 orders = await binance_consumer._create_secondary_arbitrage_order( arbitrage, quantity) assert orders limit_order = orders[0] assert limit_order.exchange_order_type is trading_enums.TradeOrderType.LIMIT assert limit_order.order_type is trading_enums.TraderOrderType.BUY_LIMIT assert limit_order.side is trading_enums.TradeOrderSide.BUY assert limit_order.symbol == binance_consumer.trading_mode.symbol assert limit_order.order_id == arbitrage.secondary_limit_order_id assert limit_order.origin_quantity == quantity stop_order = limit_order.linked_orders[0] assert stop_order.exchange_order_type is trading_enums.TradeOrderType.STOP_LOSS assert stop_order.order_type is trading_enums.TraderOrderType.STOP_LOSS assert stop_order.side is trading_enums.TradeOrderSide.BUY assert stop_order.symbol == binance_consumer.trading_mode.symbol assert stop_order.order_id == arbitrage.secondary_stop_order_id assert stop_order.origin_quantity == quantity
async def test_get_arbitrage(): async with arbitrage_trading_mode_tests.exchange( "binance") as exchange_tuple: binance_producer, binance_consumer, _ = exchange_tuple arbitrage_1 = arbitrage_container_import.ArbitrageContainer( 10, 15, trading_enums.EvaluatorStates.LONG) arbitrage_2 = arbitrage_container_import.ArbitrageContainer( 20, 18, trading_enums.EvaluatorStates.SHORT) binance_consumer.open_arbitrages = [arbitrage_1, arbitrage_2] arbitrage_1.initial_limit_order_id = "1" assert arbitrage_1 is binance_producer._get_arbitrage("1") assert None is binance_producer._get_arbitrage("2")
async def test_close_arbitrage(): async with arbitrage_trading_mode_tests.exchange( "binance") as exchange_tuple: binance_producer, binance_consumer, _ = exchange_tuple arbitrage_1 = arbitrage_container_import.ArbitrageContainer( 10, 15, trading_enums.EvaluatorStates.LONG) arbitrage_2 = arbitrage_container_import.ArbitrageContainer( 20, 17, trading_enums.EvaluatorStates.SHORT) binance_consumer.open_arbitrages = [arbitrage_1, arbitrage_2] binance_producer._close_arbitrage(arbitrage_1) assert arbitrage_1 not in binance_consumer.open_arbitrages assert binance_producer.state is trading_enums.EvaluatorStates.NEUTRAL assert binance_producer.final_eval == ""
def test_is_similar_with_prices_in_arbitrage_range(): container = arbitrage_container_import.ArbitrageContainer( 90, 100, trading_enums.EvaluatorStates.LONG) for price in range(container.own_exchange_price, container.target_price): assert container.is_similar(price, trading_enums.EvaluatorStates.LONG) assert container.is_similar(price, trading_enums.EvaluatorStates.LONG) container = arbitrage_container_import.ArbitrageContainer( 100, 90, trading_enums.EvaluatorStates.SHORT) for price in range(container.target_price, container.own_exchange_price): assert container.is_similar(price, trading_enums.EvaluatorStates.SHORT) assert container.is_similar(price, trading_enums.EvaluatorStates.SHORT)
async def test_order_cancelled_callback(): async with arbitrage_trading_mode_tests.exchange( "binance") as exchange_tuple: binance_producer, binance_consumer, _ = exchange_tuple order_id = "1" price = 10 quantity = 3 fees = 0.1 fees_currency = "BTC" symbol = "BTC/USD" order_dict = get_order_dict(order_id, symbol, price, quantity, trading_enums.OrderStatus.FILLED.value, trading_enums.TradeOrderType.LIMIT.value, fees, fees_currency) with mock.patch.object(binance_producer, "_close_arbitrage", new=mock.Mock()) as close_mock: # no open arbitrage await binance_producer.order_cancelled_callback(order_dict) close_mock.assert_not_called() # open arbitrage with different order id: nothing happens arbitrage = arbitrage_container_import.ArbitrageContainer( price, 15, trading_enums.EvaluatorStates.LONG) binance_consumer.open_arbitrages.append(arbitrage) await binance_producer.order_cancelled_callback(order_dict) close_mock.assert_not_called() # open arbitrage with this order id: arbitrage gets closed arbitrage.initial_limit_order_id = order_id await binance_producer.order_cancelled_callback(order_dict) close_mock.assert_called_once()
def test_should_be_discarded_after_order_cancel(): container = arbitrage_container_import.ArbitrageContainer( 90, 100, trading_enums.EvaluatorStates.LONG) assert not container.should_be_discarded_after_order_cancel("123") container.initial_limit_order_id = "123" assert container.should_be_discarded_after_order_cancel("123") assert not container.should_be_discarded_after_order_cancel("1234")
async def _trigger_arbitrage_opportunity(self, other_exchanges_average_price, state): # ensure no similar arbitrage is already in place if self._ensure_no_existing_arbitrage_on_this_price(state): self._log_arbitrage_opportunity_details(other_exchanges_average_price, state) arbitrage_container = arbitrage_container_import.ArbitrageContainer(self.own_exchange_mark_price, other_exchanges_average_price, state) await self._create_arbitrage_initial_order(arbitrage_container) self._register_state(state, other_exchanges_average_price - self.own_exchange_mark_price)
async def test_get_open_arbitrages(): binance = "binance" kraken = "kraken" async with arbitrage_trading_mode_tests.exchange(binance) as binance_tuple, \ arbitrage_trading_mode_tests.exchange(kraken, backtesting=binance_tuple[2].backtesting) as kraken_tuple: binance_producer, binance_consumer, _ = binance_tuple kraken_producer, kraken_consumer, _ = kraken_tuple arbitrage_1 = arbitrage_container_import.ArbitrageContainer( 10, 15, trading_enums.EvaluatorStates.LONG) arbitrage_2 = arbitrage_container_import.ArbitrageContainer( 20, 17, trading_enums.EvaluatorStates.SHORT) binance_consumer.open_arbitrages = [arbitrage_1, arbitrage_2] assert kraken_consumer.open_arbitrages == [] assert binance_producer._get_open_arbitrages( ) is binance_consumer.open_arbitrages assert kraken_producer._get_open_arbitrages( ) is kraken_consumer.open_arbitrages
def test_is_similar_with_prices_close_to_own_price(): container = arbitrage_container_import.ArbitrageContainer( 90, 100, trading_enums.EvaluatorStates.LONG) # same price and state assert container.is_similar(90, trading_enums.EvaluatorStates.LONG) # same price but different state assert not container.is_similar(90, trading_enums.EvaluatorStates.SHORT) # too different prices comparing to own_exchange_price for price in (110, 200, 80, 20, 0): assert not container.is_similar(price, trading_enums.EvaluatorStates.LONG) assert not container.is_similar(price, trading_enums.EvaluatorStates.LONG) # similar prices comparing to own_exchange_price for price in (89.97, 90.01): assert container.is_similar(price, trading_enums.EvaluatorStates.LONG) assert container.is_similar(price, trading_enums.EvaluatorStates.LONG)
def test_is_watching_this_order(): container = arbitrage_container_import.ArbitrageContainer( 90, 100, trading_enums.EvaluatorStates.LONG) assert not container.is_watching_this_order("init") assert not container.is_watching_this_order("sec") assert not container.is_watching_this_order("stop") container.initial_limit_order_id = "init" assert container.is_watching_this_order("init") assert not container.is_watching_this_order("sec") assert not container.is_watching_this_order("stop") container.secondary_limit_order_id = "sec" assert container.is_watching_this_order("init") assert container.is_watching_this_order("sec") assert not container.is_watching_this_order("stop") container.secondary_stop_order_id = "stop" assert container.is_watching_this_order("init") assert container.is_watching_this_order("sec") assert container.is_watching_this_order("stop") container.initial_limit_order_id = None assert not container.is_watching_this_order("init") assert container.is_watching_this_order("sec") assert container.is_watching_this_order("stop") container.secondary_limit_order_id = None assert not container.is_watching_this_order("init") assert not container.is_watching_this_order("sec") assert container.is_watching_this_order("stop") container.secondary_stop_order_id = None assert not container.is_watching_this_order("init") assert not container.is_watching_this_order("sec") assert not container.is_watching_this_order("stop")
async def test_order_filled_callback(): async with arbitrage_trading_mode_tests.exchange( "binance") as exchange_tuple: binance_producer, binance_consumer, _ = exchange_tuple order_id = "1" price = 10 quantity = 3 fees = 0.1 fees_currency = "BTC" symbol = "BTC/USD" order_dict = get_order_dict(order_id, symbol, price, quantity, trading_enums.OrderStatus.FILLED.value, trading_enums.TradeOrderType.LIMIT.value, fees, fees_currency) with mock.patch.object(binance_producer, "_close_arbitrage", new=mock.Mock()) as close_mock, \ mock.patch.object(binance_producer, "_trigger_arbitrage_secondary_order", new=mock.AsyncMock()) as trigger_mock, \ mock.patch.object(binance_producer, "_log_results", new=mock.Mock()) as result_mock: # nothing happens: order id not in open arbitrages await binance_producer.order_filled_callback(order_dict) close_mock.assert_not_called() trigger_mock.assert_not_called() # order id now in open arbitrages arbitrage = arbitrage_container_import.ArbitrageContainer( price, 15, trading_enums.EvaluatorStates.LONG) arbitrage.initial_limit_order_id = order_id binance_consumer.open_arbitrages.append(arbitrage) await binance_producer.order_filled_callback(order_dict) close_mock.assert_not_called() result_mock.assert_not_called() # call create secondary order trigger_mock.assert_called_once() trigger_mock.reset_mock() # last step case 1: close arbitrage: fill callback with secondary limit order limit_id = "2" arbitrage.passed_initial_order = True arbitrage.secondary_limit_order_id = limit_id arbitrage.initial_before_fee_filled_quantity = 29.9 sec_limit_order_dict = get_order_dict( limit_id, symbol, price, quantity, trading_enums.OrderStatus.FILLED.value, trading_enums.TradeOrderType.LIMIT.value, fees, fees_currency) await binance_producer.order_filled_callback(sec_limit_order_dict) # call close arbitrage close_mock.assert_called_once() trigger_mock.assert_not_called() result_mock.assert_called_once() _, arbitrage_success, filled_quantity = result_mock.mock_calls[ 0].args assert arbitrage_success assert filled_quantity == quantity * price close_mock.reset_mock() result_mock.reset_mock() # last step case 2: close arbitrage: fill callback with secondary stop order stop_id = "3" arbitrage.secondary_stop_order_id = stop_id sec_stop_order_dict = get_order_dict( stop_id, symbol, price, quantity, trading_enums.OrderStatus.FILLED.value, trading_enums.TradeOrderType.STOP_LOSS.value, fees, fees_currency) await binance_producer.order_filled_callback(sec_stop_order_dict) # call close arbitrage close_mock.assert_called_once() result_mock.assert_called_once() _, arbitrage_success, filled_quantity = result_mock.mock_calls[ 0].args assert not arbitrage_success assert filled_quantity == quantity * price trigger_mock.assert_not_called()