Ejemplo n.º 1
0
 def place_order(self, is_buy, trading_pair, amount, order_type, price,
                 nonce, post_resp, ws_resp):
     global EXCHANGE_ORDER_ID
     order_id, exch_order_id = None, None
     if API_MOCK_ENABLED:
         exch_order_id = f"BITTREX_{EXCHANGE_ORDER_ID}"
         EXCHANGE_ORDER_ID += 1
         self._t_nonce_mock.return_value = nonce
         resp = post_resp.copy()
         resp["id"] = exch_order_id
         side = 'buy' if is_buy else 'sell'
         resp["direction"] = side.upper()
         resp["type"] = order_type.name.upper()
         if order_type == OrderType.LIMIT:
             del resp["limit"]
         self.web_app.update_response("post", API_BASE_URL, "/v3/orders",
                                      resp)
     if is_buy:
         order_id = self.market.buy(trading_pair, amount, order_type, price)
     else:
         order_id = self.market.sell(trading_pair, amount, order_type,
                                     price)
     if API_MOCK_ENABLED:
         resp = ws_resp.copy()
         resp["content"]["o"]["OU"] = exch_order_id
         MockWebSocketServerFactory.send_json_threadsafe(WS_BASE_URL,
                                                         resp,
                                                         delay=1.0)
     return order_id, exch_order_id
Ejemplo n.º 2
0
    def test_cancel_order(self):
        self.assertGreater(self.market.get_balance("USDC"), 16000)
        trading_pair = "WETH-USDC"
        bid_price: Decimal = self.market.get_price(trading_pair, True)
        amount: Decimal = Decimal("40.0")

        # Intentionally setting price far away from best ask
        client_order_id = self.place_order(True, "WETH-USDC", amount,
                                           OrderType.LIMIT_MAKER,
                                           bid_price * Decimal('0.5'), 10001,
                                           FixtureDydx.BUY_LIMIT_ORDER,
                                           FixtureDydx.WS_AFTER_BUY_1)
        self.run_parallel(asyncio.sleep(1.0))
        self.market.cancel(trading_pair, client_order_id)
        if API_MOCK_ENABLED:
            MockWebSocketServerFactory.send_json_threadsafe(
                self._ws_user_url, FixtureDydx.WS_AFTER_CANCEL_BUY, delay=0.1)
        [order_cancelled_event] = self.run_parallel(
            self.market_logger.wait_for(OrderCancelledEvent))

        order_cancelled_event: OrderCancelledEvent = order_cancelled_event

        self.run_parallel(asyncio.sleep(6.0))
        self.assertEqual(0, len(self.market.limit_orders))
        self.assertEqual(client_order_id, order_cancelled_event.order_id)
Ejemplo n.º 3
0
    def test_limit_makers_unfilled(self):
        if API_MOCK_ENABLED:
            return
        trading_pair = "ETH-USDC"
        bid_price: Decimal = self.market.get_price(trading_pair,
                                                   True) * Decimal("0.5")
        ask_price: Decimal = self.market.get_price(trading_pair, False) * 2
        amount: Decimal = 10 / bid_price
        quantized_amount: Decimal = self.market.quantize_order_amount(
            trading_pair, amount)

        # Intentionally setting invalid price to prevent getting filled
        quantize_bid_price: Decimal = self.market.quantize_order_price(
            trading_pair, bid_price * Decimal("0.7"))
        quantize_ask_price: Decimal = self.market.quantize_order_price(
            trading_pair, ask_price * Decimal("1.5"))

        order_id, exch_order_id = self.place_order(
            True, trading_pair, quantized_amount, OrderType.LIMIT_MAKER,
            quantize_bid_price, 10001, FixtureCoinbasePro.OPEN_BUY_LIMIT_ORDER,
            FixtureCoinbasePro.WS_ORDER_OPEN)
        [order_created_event] = self.run_parallel(
            self.market_logger.wait_for(BuyOrderCreatedEvent))
        order_created_event: BuyOrderCreatedEvent = order_created_event
        self.assertEqual(order_id, order_created_event.order_id)

        order_id_2, exch_order_id_2 = self.place_order(
            False, trading_pair, quantized_amount, OrderType.LIMIT_MAKER,
            quantize_ask_price, 10002,
            FixtureCoinbasePro.OPEN_SELL_LIMIT_ORDER,
            FixtureCoinbasePro.WS_ORDER_OPEN)
        [order_created_event] = self.run_parallel(
            self.market_logger.wait_for(SellOrderCreatedEvent))
        order_created_event: BuyOrderCreatedEvent = order_created_event
        self.assertEqual(order_id_2, order_created_event.order_id)

        self.run_parallel(asyncio.sleep(1))

        if API_MOCK_ENABLED:
            self.web_app.update_response("delete", API_BASE_URL,
                                         f"/orders/{exch_order_id}",
                                         exch_order_id)
            self.web_app.update_response("delete", API_BASE_URL,
                                         f"/orders/{exch_order_id_2}",
                                         exch_order_id_2)
        [cancellation_results] = self.run_parallel(self.market.cancel_all(5))
        if API_MOCK_ENABLED:
            resp = FixtureCoinbasePro.WS_ORDER_CANCELLED.copy()
            resp["order_id"] = exch_order_id
            MockWebSocketServerFactory.send_json_threadsafe(WS_BASE_URL,
                                                            resp,
                                                            delay=0.1)
            resp = FixtureCoinbasePro.WS_ORDER_CANCELLED.copy()
            resp["order_id"] = exch_order_id_2
            MockWebSocketServerFactory.send_json_threadsafe(WS_BASE_URL,
                                                            resp,
                                                            delay=0.11)
        for cr in cancellation_results:
            self.assertEqual(cr.success, True)
Ejemplo n.º 4
0
    def test_prevent_duplicated_orders(self):
        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        sql: SQLConnectionManager = SQLConnectionManager(SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        buy_id: Optional[str] = None
        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market], config_path, strategy_name)
        recorder.start()

        try:
            # Perform the same order twice which should produce the same exchange_order_id
            # Try to buy 1 LINK from the exchange, and watch for completion event.
            bid_price: Decimal = self.market.get_price("LINK-ETH", True)
            amount: Decimal = 1
            buy_id = self.place_order(True, "LINK-ETH", amount, OrderType.LIMIT, bid_price, self.get_current_nonce(),
                                      FixtureBinance.BUY_LIMIT_ORDER, FixtureBinance.WS_AFTER_BUY_1,
                                      FixtureBinance.WS_AFTER_BUY_2)
            [buy_order_completed_event] = self.run_parallel(self.market_logger.wait_for(BuyOrderCompletedEvent))

            self.market_logger.clear()

            # Simulate that order is still in in_flight_orders
            order_json = {"client_order_id": buy_id,
                          "exchange_order_id": str(FixtureBinance.WS_AFTER_BUY_2['t']),
                          "trading_pair": "LINK-ETH",
                          "order_type": "MARKET",
                          "trade_type": "BUY",
                          "price": bid_price,
                          "amount": amount,
                          "last_state": "NEW",
                          "executed_amount_base": "0",
                          "executed_amount_quote": "0",
                          "fee_asset": "LINK",
                          "fee_paid": "0.0"}
            self.market.restore_tracking_states({buy_id: order_json})
            self.market.in_flight_orders.get(buy_id).trade_id_set.add(str(FixtureBinance.WS_AFTER_BUY_2['t']))
            # Simulate incoming responses as if buy_id is executed again
            data = self.fixture(FixtureBinance.WS_AFTER_BUY_2, c=buy_id)
            MockWebSocketServerFactory.send_json_threadsafe(self._ws_user_url, data, delay=0.11)
            # Will wait, but no order filled event should be triggered because order is ignored
            self.run_parallel(asyncio.sleep(1))
            # Query the persisted trade logs
            trade_fills: List[TradeFill] = recorder.get_trades_for_config(config_path)
            buy_fills: List[TradeFill] = [t for t in trade_fills if t.trade_type == "BUY"]
            exchange_trade_id = FixtureBinance.WS_AFTER_BUY_2['t']
            self.assertEqual(len([bf for bf in buy_fills if int(bf.exchange_trade_id) == exchange_trade_id]), 1)

            buy_id = None

        finally:
            if buy_id is not None:
                self.market.cancel("LINK-ETH", buy_id)
                self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()
            os.unlink(self.db_path)
Ejemplo n.º 5
0
 def cancel_order(self, trading_pair, order_id, exchange_order_id,
                  fixture_ws):
     if API_MOCK_ENABLED:
         self.web_app.update_response("delete", API_BASE_URL,
                                      f"/orders/{exchange_order_id}",
                                      exchange_order_id)
     self.market.cancel(trading_pair, order_id)
     if API_MOCK_ENABLED:
         resp = fixture_ws.copy()
         resp["order_id"] = exchange_order_id
         MockWebSocketServerFactory.send_json_threadsafe(WS_BASE_URL,
                                                         resp,
                                                         delay=0.1)
Ejemplo n.º 6
0
 def place_order(self, is_buy, trading_pair, amount, order_type, price, nonce, fixture_resp,
                 fixture_ws_1 = None, fixture_ws_2 = None):
     order_id = None
     if API_MOCK_ENABLED:
         resp = self.order_response(fixture_resp, nonce, 'buy' if is_buy else 'sell', trading_pair)
         self.web_app.update_response("post", self.base_api_url, "/api/v3/order", resp)
     if is_buy:
         order_id = self.market.buy(trading_pair, amount, order_type, price)
     else:
         order_id = self.market.sell(trading_pair, amount, order_type, price)
     if API_MOCK_ENABLED and fixture_ws_1 is not None and fixture_ws_2 is not None:
         exchange_order_id = str(resp['orderId'])
         data = self.fixture(fixture_ws_1, c=order_id, i=exchange_order_id)
         MockWebSocketServerFactory.send_json_threadsafe(self._ws_user_url, data, delay=0.1)
         data = self.fixture(fixture_ws_2, c=order_id, i=exchange_order_id)
         MockWebSocketServerFactory.send_json_threadsafe(self._ws_user_url, data, delay=0.11)
     return order_id
Ejemplo n.º 7
0
    def test_limit_makers_unfilled(self):
        self.assertGreater(self.market.get_balance("USDC"), 16000)
        trading_pair = "WETH-USDC"
        amount: Decimal = Decimal("40.0")
        bid_price: Decimal = self.market.get_price(trading_pair, True)
        buy_order_id: str = self.place_order(True, "WETH-USDC", amount,
                                             OrderType.LIMIT_MAKER,
                                             bid_price * Decimal('0.5'), 10001,
                                             FixtureDydx.BUY_LIMIT_MAKER_ORDER,
                                             FixtureDydx.WS_AFTER_BUY_1)
        self.run_parallel(asyncio.sleep(6.0))
        self.market.cancel(trading_pair, buy_order_id)

        if API_MOCK_ENABLED:
            MockWebSocketServerFactory.send_json_threadsafe(
                self._ws_user_url, FixtureDydx.WS_AFTER_CANCEL_BUY, delay=0.1)
        [order_cancelled_event] = self.run_parallel(
            self.market_logger.wait_for(OrderCancelledEvent))
Ejemplo n.º 8
0
 def place_order(self, is_buy, trading_pair, amount, order_type, price,
                 nonce, fixture_resp, fixture_ws):
     order_id, exch_order_id = None, None
     if API_MOCK_ENABLED:
         self._t_nonce_mock.return_value = nonce
         side = 'buy' if is_buy else 'sell'
         resp = fixture_resp.copy()
         exch_order_id = resp["id"]
         resp["side"] = side
         self.web_app.update_response("post", API_BASE_URL, "/orders", resp)
     if is_buy:
         order_id = self.market.buy(trading_pair, amount, order_type, price)
     else:
         order_id = self.market.sell(trading_pair, amount, order_type,
                                     price)
     if API_MOCK_ENABLED:
         resp = fixture_ws.copy()
         resp["order_id"] = exch_order_id
         resp["side"] = side
         MockWebSocketServerFactory.send_json_threadsafe(WS_BASE_URL,
                                                         resp,
                                                         delay=0.1)
     return order_id, exch_order_id
Ejemplo n.º 9
0
 def place_order(self,
                 is_buy,
                 trading_pair,
                 amount,
                 order_type,
                 price,
                 nonce,
                 fixture_resp,
                 fixture_ws_1=None,
                 fixture_ws_2=None,
                 fixture_ws_3=None):
     order_id = None
     if API_MOCK_ENABLED:
         resp = self.order_response(fixture_resp, nonce,
                                    'buy' if is_buy else 'sell',
                                    trading_pair)
         self.web_app.update_response("post", self.base_api_url,
                                      "/v2/orders", resp)
     if is_buy:
         order_id = self.market.buy(trading_pair, amount, order_type, price)
     else:
         order_id = self.market.sell(trading_pair, amount, order_type,
                                     price)
     if API_MOCK_ENABLED and fixture_ws_1 is not None:
         self.web_app.update_response("get",
                                      self.base_api_url,
                                      "/v2/fills",
                                      FixtureDydx.FILLS,
                                      params={
                                          "orderId": order_id,
                                          "limit": 100
                                      })
         data = self.fixture(fixture_ws_1, id=order_id)
         MockWebSocketServerFactory.send_json_threadsafe(self._ws_user_url,
                                                         data,
                                                         delay=0.1)
         if fixture_ws_2 is not None:
             data = self.fixture(fixture_ws_2, id=order_id)
             MockWebSocketServerFactory.send_json_threadsafe(
                 self._ws_user_url, data, delay=0.11)
         if fixture_ws_3 is not None:
             MockWebSocketServerFactory.send_json_threadsafe(
                 self._ws_user_url, fixture_ws_3, delay=0.1)
     return order_id
Ejemplo n.º 10
0
    def test_order_saving_and_restoration(self):
        config_path: str = "test_config"
        strategy_name: str = "test_strategy"
        sql: SQLConnectionManager = SQLConnectionManager(
            SQLConnectionType.TRADE_FILLS, db_path=self.db_path)
        order_id: Optional[str] = None
        trading_pair: str = "WETH-USDC"

        recorder: MarketsRecorder = MarketsRecorder(sql, [self.market],
                                                    config_path, strategy_name)
        recorder.start()
        try:
            self.assertEqual(0, len(self.market.tracking_states))
            self.assertGreater(self.market.get_balance("USDC"), 16000)
            amount: Decimal = Decimal("40.0")
            current_bid_price: Decimal = self.market.get_price(
                trading_pair, True)
            bid_price: Decimal = Decimal("0.5") * current_bid_price
            quantize_bid_price: Decimal = self.market.quantize_order_price(
                trading_pair, bid_price)
            order_id = self.place_order(True, trading_pair, amount,
                                        OrderType.LIMIT, quantize_bid_price,
                                        10001, FixtureDydx.BUY_LIMIT_ORDER,
                                        FixtureDydx.WS_AFTER_BUY_1)

            [order_created_event] = self.run_parallel(
                self.market_logger.wait_for(BuyOrderCreatedEvent))
            order_created_event: BuyOrderCreatedEvent = order_created_event

            self.assertEqual(order_id, order_created_event.order_id)

            # Verify tracking states
            self.assertEqual(1, len(self.market.tracking_states))
            self.assertEqual(order_id,
                             list(self.market.tracking_states.keys())[0])

            # Verify orders from recorder
            recorded_orders: List[
                Order] = recorder.get_orders_for_config_and_market(
                    config_path, self.market)
            self.assertEqual(1, len(recorded_orders))
            self.assertEqual(order_id, recorded_orders[0].id)

            # Verify saved market states
            saved_market_states: MarketState = recorder.get_market_states(
                config_path, self.market)
            self.assertIsNotNone(saved_market_states)
            self.assertIsInstance(saved_market_states.saved_state, dict)
            self.assertGreater(len(saved_market_states.saved_state), 0)

            # Close out the current market and start another market.
            self.clock.remove_iterator(self.market)

            for event_tag in self.market_events:
                self.market.remove_listener(event_tag, self.market_logger)

            self.market: DydxExchange = DydxExchange(
                dydx_eth_private_key=PRIVATE_KEY,
                dydx_node_address=NODE_ADDRESS,
                poll_interval=10.0,
                trading_pairs=[trading_pair],
                trading_required=True)
            for event_tag in self.market_events:
                self.market.add_listener(event_tag, self.market_logger)
            recorder.stop()
            recorder = MarketsRecorder(sql, [self.market], config_path,
                                       strategy_name)
            recorder.start()
            saved_market_states = recorder.get_market_states(
                config_path, self.market)
            self.clock.add_iterator(self.market)
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            self.market.restore_tracking_states(
                saved_market_states.saved_state)
            self.assertEqual(1, len(self.market.limit_orders))
            self.assertEqual(1, len(self.market.tracking_states))

            # Cancel the order and verify that the change is saved.
            self.run_parallel(asyncio.sleep(5.0))
            self.market.cancel(trading_pair, order_id)
            if API_MOCK_ENABLED:
                MockWebSocketServerFactory.send_json_threadsafe(
                    self._ws_user_url,
                    FixtureDydx.WS_AFTER_CANCEL_BUY,
                    delay=0.1)
            self.run_parallel(self.market_logger.wait_for(OrderCancelledEvent))
            order_id = None
            self.assertEqual(0, len(self.market.limit_orders))
            self.assertEqual(0, len(self.market.tracking_states))
            saved_market_states = recorder.get_market_states(
                config_path, self.market)
            self.assertEqual(0, len(saved_market_states.saved_state))
        finally:
            if order_id is not None:
                self.market.cancel(trading_pair, order_id)
                if API_MOCK_ENABLED:
                    MockWebSocketServerFactory.send_json_threadsafe(
                        self._ws_user_url,
                        FixtureDydx.WS_AFTER_CANCEL_BUY,
                        delay=0.1)
                self.run_parallel(
                    self.market_logger.wait_for(OrderCancelledEvent))

            recorder.stop()