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 HummingWsServerFactory.send_json_threadsafe(WS_BASE_URL, resp, delay=1.0) return order_id, exch_order_id
def test_cancel_all(self): 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")) _, 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) _, 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) 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 HummingWsServerFactory.send_json_threadsafe(WS_BASE_URL, resp, delay=0.1) resp = FixtureCoinbasePro.WS_ORDER_CANCELLED.copy() resp["order_id"] = exch_order_id_2 HummingWsServerFactory.send_json_threadsafe(WS_BASE_URL, resp, delay=0.11) for cr in cancellation_results: self.assertEqual(cr.success, True)
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: HummingWsServerFactory.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)
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: data = self.fixture(fixture_ws_1, c=order_id) HummingWsServerFactory.send_json_threadsafe(self._ws_user_url, data, delay=0.1) data = self.fixture(fixture_ws_2, c=order_id) HummingWsServerFactory.send_json_threadsafe(self._ws_user_url, data, delay=0.11) return order_id
def setUpClass(cls): global MAINNET_RPC_URL cls.ev_loop = asyncio.get_event_loop() if API_MOCK_ENABLED: cls.web_app = HummingWebApp.get_instance() cls.web_app.add_host_to_mock(BASE_API_URL, []) cls.web_app.start() cls.ev_loop.run_until_complete(cls.web_app.wait_til_started()) cls._patcher = mock.patch("aiohttp.client.URL") cls._url_mock = cls._patcher.start() cls._url_mock.side_effect = cls.web_app.reroute_local cls.web_app.update_response("get", BASE_API_URL, "/v2/public/get-ticker", fixture.TICKERS) cls.web_app.update_response("get", BASE_API_URL, "/v2/public/get-instruments", fixture.INSTRUMENTS) cls.web_app.update_response("get", BASE_API_URL, "/v2/public/get-book", fixture.GET_BOOK) cls.web_app.update_response("post", BASE_API_URL, "/v2/private/get-account-summary", fixture.BALANCES) cls.web_app.update_response("post", BASE_API_URL, "/v2/private/cancel-order", fixture.CANCEL) HummingWsServerFactory.start_new_server(WSS_PRIVATE_URL) HummingWsServerFactory.start_new_server(WSS_PUBLIC_URL) cls._ws_patcher = unittest.mock.patch("websockets.connect", autospec=True) cls._ws_mock = cls._ws_patcher.start() cls._ws_mock.side_effect = HummingWsServerFactory.reroute_ws_connect cls.clock: Clock = Clock(ClockMode.REALTIME) cls.connector: CryptoComExchange = CryptoComExchange( crypto_com_api_key=API_KEY, crypto_com_api_secret=API_SECRET, trading_pairs=[cls.trading_pair], trading_required=True) print( "Initializing CryptoCom market... this will take about a minute.") cls.clock.add_iterator(cls.connector) cls.stack: contextlib.ExitStack = contextlib.ExitStack() cls._clock = cls.stack.enter_context(cls.clock) if API_MOCK_ENABLED: HummingWsServerFactory.send_json_threadsafe(WSS_PRIVATE_URL, fixture.WS_INITIATED, delay=0.5) HummingWsServerFactory.send_json_threadsafe(WSS_PRIVATE_URL, fixture.WS_SUBSCRIBE, delay=0.51) HummingWsServerFactory.send_json_threadsafe(WSS_PRIVATE_URL, fixture.WS_HEARTBEAT, delay=0.52) cls.ev_loop.run_until_complete(cls.wait_til_ready()) print("Ready.")
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 HummingWsServerFactory.send_json_threadsafe(WS_BASE_URL, resp, delay=0.1)
def _cancel_order(self, cl_order_id): self.connector.cancel(self.trading_pair, cl_order_id) if API_MOCK_ENABLED: data = fixture.WS_ORDER_CANCELLED.copy() data["result"]["data"][0]["client_oid"] = cl_order_id HummingWsServerFactory.send_json_threadsafe(WSS_PRIVATE_URL, data, delay=0.1)
def _mock_ws_bal_update(self, token, available): if API_MOCK_ENABLED: available = float(available) data = fixture.WS_BALANCE.copy() data["result"]["data"][0]["currency"] = token data["result"]["data"][0]["available"] = available HummingWsServerFactory.send_json_threadsafe(WSS_PRIVATE_URL, fixture.WS_BALANCE, delay=0.1)
def setUpClass(cls): global MAINNET_RPC_URL cls.ev_loop = asyncio.get_event_loop() if API_MOCK_ENABLED: cls.web_app = HummingWebApp.get_instance() cls.web_app.add_host_to_mock(cls.base_api_url, ["/api/v1/ping", "/api/v1/time", "/api/v1/ticker/24hr"]) cls.web_app.start() cls.ev_loop.run_until_complete(cls.web_app.wait_til_started()) cls._patcher = mock.patch("aiohttp.client.URL") cls._url_mock = cls._patcher.start() cls._url_mock.side_effect = cls.web_app.reroute_local cls._req_patcher = unittest.mock.patch.object(requests.Session, "request", autospec=True) cls._req_url_mock = cls._req_patcher.start() cls._req_url_mock.side_effect = HummingWebApp.reroute_request cls.web_app.update_response("get", cls.base_api_url, "/api/v3/account", FixtureBinance.BALANCES) cls.web_app.update_response("get", cls.base_api_url, "/api/v1/exchangeInfo", FixtureBinance.MARKETS) cls.web_app.update_response("get", cls.base_api_url, "/wapi/v3/tradeFee.html", FixtureBinance.TRADE_FEES) cls.web_app.update_response("post", cls.base_api_url, "/api/v1/userDataStream", FixtureBinance.LISTEN_KEY) cls.web_app.update_response("put", cls.base_api_url, "/api/v1/userDataStream", FixtureBinance.LISTEN_KEY) cls.web_app.update_response("get", cls.base_api_url, "/api/v1/depth", FixtureBinance.LINKETH_SNAP, params={'symbol': 'LINKETH'}) cls.web_app.update_response("get", cls.base_api_url, "/api/v1/depth", FixtureBinance.ZRXETH_SNAP, params={'symbol': 'ZRXETH'}) cls.web_app.update_response("get", cls.base_api_url, "/api/v3/myTrades", {}, params={'symbol': 'ZRXETH'}) cls.web_app.update_response("get", cls.base_api_url, "/api/v3/myTrades", {}, params={'symbol': 'LINKETH'}) ws_base_url = "wss://stream.binance.com:9443/ws" cls._ws_user_url = f"{ws_base_url}/{FixtureBinance.LISTEN_KEY['listenKey']}" HummingWsServerFactory.start_new_server(cls._ws_user_url) HummingWsServerFactory.start_new_server(f"{ws_base_url}/linketh@depth/zrxeth@depth") cls._ws_patcher = unittest.mock.patch("websockets.connect", autospec=True) cls._ws_mock = cls._ws_patcher.start() cls._ws_mock.side_effect = HummingWsServerFactory.reroute_ws_connect cls._t_nonce_patcher = unittest.mock.patch( "hummingbot.connector.exchange.binance.binance_exchange.get_tracking_nonce") cls._t_nonce_mock = cls._t_nonce_patcher.start() cls.current_nonce = 1000000000000000 cls.clock: Clock = Clock(ClockMode.REALTIME) cls.market: BinanceExchange = BinanceExchange(API_KEY, API_SECRET, ["LINK-ETH", "ZRX-ETH"], True) print("Initializing Binance market... this will take about a minute.") cls.ev_loop: asyncio.BaseEventLoop = asyncio.get_event_loop() cls.clock.add_iterator(cls.market) cls.stack: contextlib.ExitStack = contextlib.ExitStack() cls._clock = cls.stack.enter_context(cls.clock) cls.ev_loop.run_until_complete(cls.wait_til_ready()) print("Ready.")
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) HummingWsServerFactory.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)
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: HummingWsServerFactory.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))
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 HummingWsServerFactory.send_json_threadsafe(WS_BASE_URL, resp, delay=0.1) return order_id, exch_order_id
def _place_order(self, is_buy, amount, order_type, price, ex_order_id, get_order_fixture=None, ws_trade_fixture=None, ws_order_fixture=None) -> str: if API_MOCK_ENABLED: data = fixture.PLACE_ORDER.copy() data["result"]["order_id"] = str(ex_order_id) self.web_app.update_response("post", BASE_API_URL, "/v2/private/create-order", data) if is_buy: cl_order_id = self.connector.buy(self.trading_pair, amount, order_type, price) else: cl_order_id = self.connector.sell(self.trading_pair, amount, order_type, price) if API_MOCK_ENABLED: if get_order_fixture is not None: data = get_order_fixture.copy() data["result"]["order_info"]["client_oid"] = cl_order_id data["result"]["order_info"]["order_id"] = ex_order_id self.web_app.update_response("post", BASE_API_URL, "/v2/private/get-order-detail", data) if ws_trade_fixture is not None: data = ws_trade_fixture.copy() data["result"]["data"][0]["order_id"] = str(ex_order_id) HummingWsServerFactory.send_json_threadsafe(WSS_PRIVATE_URL, data, delay=0.1) if ws_order_fixture is not None: data = ws_order_fixture.copy() data["result"]["data"][0]["order_id"] = str(ex_order_id) data["result"]["data"][0]["client_oid"] = cl_order_id HummingWsServerFactory.send_json_threadsafe(WSS_PRIVATE_URL, data, delay=0.12) return cl_order_id
def setUpClass(cls): cls.ev_loop = asyncio.get_event_loop() trading_pair = "ETH-USDC" if API_MOCK_ENABLED: cls.web_app = HummingWebApp.get_instance() cls.web_app.add_host_to_mock( API_BASE_URL, ["/time", "/products", f"/products/{trading_pair}/book"]) cls.web_app.start() cls.ev_loop.run_until_complete(cls.web_app.wait_til_started()) cls._patcher = mock.patch("aiohttp.client.URL") cls._url_mock = cls._patcher.start() cls._url_mock.side_effect = cls.web_app.reroute_local cls.web_app.update_response("get", API_BASE_URL, "/accounts", FixtureCoinbasePro.ACCOUNTS_GET) cls.web_app.update_response("get", API_BASE_URL, "/fees", FixtureCoinbasePro.FEES_GET) cls.web_app.update_response("get", API_BASE_URL, "/orders", FixtureCoinbasePro.ORDERS_STATUS) HummingWsServerFactory.start_new_server(WS_BASE_URL) cls._ws_patcher = unittest.mock.patch("websockets.connect", autospec=True) cls._ws_mock = cls._ws_patcher.start() cls._ws_mock.side_effect = HummingWsServerFactory.reroute_ws_connect cls._t_nonce_patcher = unittest.mock.patch( "hummingbot.market.coinbase_pro.coinbase_pro_market.get_tracking_nonce" ) cls._t_nonce_mock = cls._t_nonce_patcher.start() cls.clock: Clock = Clock(ClockMode.REALTIME) cls.market: CoinbaseProMarket = CoinbaseProMarket( API_KEY, API_SECRET, API_PASSPHRASE, trading_pairs=[trading_pair]) print( "Initializing Coinbase Pro market... this will take about a minute." ) cls.clock.add_iterator(cls.market) cls.stack = contextlib.ExitStack() cls._clock = cls.stack.enter_context(cls.clock) cls.ev_loop.run_until_complete(cls.wait_til_ready()) print("Ready.")
def place_order(self, is_buy, trading_pair, amount, order_type, price, ws_resps=[]): global EXCHANGE_ORDER_ID order_id, exch_order_id = None, None 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: for delay, ws_resp in ws_resps: HummingWsServerFactory.send_str_threadsafe( BeaxyConstants.TradingApi.WS_BASE_URL, ws_resp, delay=delay) return order_id, exch_order_id
def test_cancel_order(self): if API_MOCK_ENABLED: self.web_app.update_response("post", PRIVET_API_BASE_URL, "/api/v2/orders", FixtureBeaxy.TEST_CANCEL_BUY_ORDER) self.web_app.update_response( "delete", PRIVET_API_BASE_URL, "/api/v2/orders/open/435118B0-A7F7-40D2-A409-820E8FC342A2", '') amount: Decimal = Decimal("0.01") self.assertGreater(self.market.get_balance("BTC"), 0.00005) trading_pair = "DASH-BTC" # make worst price so order wont be executed price: Decimal = self.market.get_price(trading_pair, True) * Decimal('0.5') quantized_amount: Decimal = self.market.quantize_order_amount( trading_pair, amount) order_id, exch_order_id = self.place_order( True, trading_pair, quantized_amount, OrderType.LIMIT, price, [(3, FixtureBeaxy.TEST_CANCEL_BUY_WS_ORDER_COMPLETED)]) [order_completed_event] = self.run_parallel( self.market_logger.wait_for(BuyOrderCreatedEvent)) if API_MOCK_ENABLED: HummingWsServerFactory.send_str_threadsafe( BeaxyConstants.TradingApi.WS_BASE_URL, FixtureBeaxy.TEST_CANCEL_BUY_WS_ORDER_CANCELED, delay=3) self.cancel_order(trading_pair, order_id, exch_order_id) [order_cancelled_event] = self.run_parallel( self.market_logger.wait_for(OrderCancelledEvent)) order_cancelled_event: OrderCancelledEvent = order_cancelled_event self.assertEqual(order_cancelled_event.order_id, order_id)
def test_cancel_all(self): bid_price = self.connector.get_price(self.trading_pair, True) ask_price = self.connector.get_price(self.trading_pair, False) bid_price = self.connector.quantize_order_price( self.trading_pair, bid_price * Decimal("0.7")) ask_price = self.connector.quantize_order_price( self.trading_pair, ask_price * Decimal("1.5")) amount = self.connector.quantize_order_amount(self.trading_pair, Decimal("0.0001")) buy_id = self._place_order(True, amount, OrderType.LIMIT, bid_price, 1) sell_id = self._place_order(False, amount, OrderType.LIMIT, ask_price, 2) self.ev_loop.run_until_complete(asyncio.sleep(1)) asyncio.ensure_future(self.connector.cancel_all(3)) if API_MOCK_ENABLED: data = fixture.WS_ORDER_CANCELLED.copy() data["result"]["data"][0]["client_oid"] = buy_id data["result"]["data"][0]["order_id"] = 1 HummingWsServerFactory.send_json_threadsafe(WSS_PRIVATE_URL, data, delay=0.1) self.ev_loop.run_until_complete(asyncio.sleep(1)) data = fixture.WS_ORDER_CANCELLED.copy() data["result"]["data"][0]["client_oid"] = sell_id data["result"]["data"][0]["order_id"] = 2 HummingWsServerFactory.send_json_threadsafe(WSS_PRIVATE_URL, data, delay=0.11) self.ev_loop.run_until_complete(asyncio.sleep(3)) cancel_events = [ t for t in self.event_logger.event_log if isinstance(t, OrderCancelledEvent) ] self.assertEqual({buy_id, sell_id}, {o.order_id for o in cancel_events})
def setUpClass(cls): cls.ev_loop: asyncio.BaseEventLoop = asyncio.get_event_loop() if API_MOCK_ENABLED: cls.web_app = HummingWebApp.get_instance() cls.web_app.add_host_to_mock(API_BASE_URL, []) cls.web_app.start() cls.ev_loop.run_until_complete(cls.web_app.wait_til_started()) cls._patcher = mock.patch("aiohttp.client.URL") cls._url_mock = cls._patcher.start() cls._url_mock.side_effect = cls.web_app.reroute_local cls.web_app.update_response("get", API_BASE_URL, "/v3/ping", FixtureBittrex.PING) cls.web_app.update_response("get", API_BASE_URL, "/v3/markets", FixtureBittrex.MARKETS) cls.web_app.update_response("get", API_BASE_URL, "/v3/markets/tickers", FixtureBittrex.MARKETS_TICKERS) cls.web_app.update_response("get", API_BASE_URL, "/v3/balances", FixtureBittrex.BALANCES) cls.web_app.update_response("get", API_BASE_URL, "/v3/orders/open", FixtureBittrex.ORDERS_OPEN) cls._t_nonce_patcher = unittest.mock.patch("hummingbot.market.bittrex.bittrex_market.get_tracking_nonce") cls._t_nonce_mock = cls._t_nonce_patcher.start() cls._us_patcher = unittest.mock.patch("hummingbot.market.bittrex.bittrex_api_user_stream_data_source." "BittrexAPIUserStreamDataSource._transform_raw_message", autospec=True) cls._us_mock = cls._us_patcher.start() cls._us_mock.side_effect = _transform_raw_message_patch cls._ob_patcher = unittest.mock.patch("hummingbot.market.bittrex.bittrex_api_order_book_data_source." "BittrexAPIOrderBookDataSource._transform_raw_message", autospec=True) cls._ob_mock = cls._ob_patcher.start() cls._ob_mock.side_effect = _transform_raw_message_patch HummingWsServerFactory.url_host_only = True ws_server = HummingWsServerFactory.start_new_server(WS_BASE_URL) cls._ws_patcher = unittest.mock.patch("websockets.connect", autospec=True) cls._ws_mock = cls._ws_patcher.start() cls._ws_mock.side_effect = HummingWsServerFactory.reroute_ws_connect ws_server.add_stock_response("queryExchangeState", FixtureBittrex.WS_ORDER_BOOK_SNAPSHOT.copy()) cls.clock: Clock = Clock(ClockMode.REALTIME) cls.market: BittrexMarket = BittrexMarket( bittrex_api_key=API_KEY, bittrex_secret_key=API_SECRET, trading_pairs=["ETH-USDT"] ) print("Initializing Bittrex market... this will take about a minute. ") cls.clock.add_iterator(cls.market) cls.stack = contextlib.ExitStack() cls._clock = cls.stack.enter_context(cls.clock) cls.ev_loop.run_until_complete(cls.wait_til_ready()) print("Ready.")
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) HummingWsServerFactory.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) HummingWsServerFactory.send_json_threadsafe(self._ws_user_url, data, delay=0.11) if fixture_ws_3 is not None: HummingWsServerFactory.send_json_threadsafe(self._ws_user_url, fixture_ws_3, delay=0.1) return order_id
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: HummingWsServerFactory.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: HummingWsServerFactory.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()
def setUpClass(cls): cls.clock: Clock = Clock(ClockMode.REALTIME) cls.ev_loop: asyncio.BaseEventLoop = asyncio.get_event_loop() if API_MOCK_ENABLED: cls.web_app = HummingWebApp.get_instance() cls.web_app.add_host_to_mock(cls.base_api_url, []) cls.web_app.start() cls.ev_loop.run_until_complete(cls.web_app.wait_til_started()) cls._req_patcher = mock.patch.object(requests.Session, "request", autospec=True) cls._req_url_mock = cls._req_patcher.start() cls._req_url_mock.side_effect = HummingWebApp.reroute_request cls.web_app.update_response("get", cls.base_api_url, f"/v1/accounts/{WALLET_ADDRESS}", FixtureDydx.BALANCES, params={'number': f'{ACCOUNT_NUMBER}'}) cls.web_app.update_response("get", cls.base_api_url, "/v2/markets", FixtureDydx.MARKETS) cls.web_app.update_response("get", cls.base_api_url, "/v1/orderbook/WETH-USDC", FixtureDydx.WETHUSDC_SNAP) cls._buy_order_exchange_id = "0xb0751a113c759779ff5fd6a53b37b26211a9\ f8845d443323b9f877f32d9aafd9" cls._sell_order_exchange_id = "0x03dfd18edc2f26fc9298edcd28ca6cad4971\ bd1f44d40253d5154b0d1f217680" cls.web_app.update_response( "delete", cls.base_api_url, f"/v2/orders/{cls._buy_order_exchange_id}", FixtureDydx.CANCEL_ORDER_BUY) cls.web_app.update_response( "delete", cls.base_api_url, f"/v2/orders/{cls._sell_order_exchange_id}", FixtureDydx.CANCEL_ORDER_SELL) ws_base_url = "wss://api.dydx.exchange/v1/ws" cls._ws_user_url = f"{ws_base_url}" HummingWsServerFactory.start_new_server(cls._ws_user_url) HummingWsServerFactory.start_new_server(f"{ws_base_url}") cls._ws_patcher = unittest.mock.patch("websockets.connect", autospec=True) cls._ws_mock = cls._ws_patcher.start() cls._ws_mock.side_effect = HummingWsServerFactory.reroute_ws_connect cls._t_nonce_patcher = unittest.mock.patch( "hummingbot.connector.exchange.dydx.\ dydx_exchange.get_tracking_nonce") cls._t_nonce_mock = cls._t_nonce_patcher.start() cls.market: DydxExchange = DydxExchange( dydx_eth_private_key=PRIVATE_KEY, dydx_node_address=NODE_ADDRESS, poll_interval=10.0, trading_pairs=['WETH-USDC'], trading_required=True) print("Initializing Dydx market... ") cls.clock.add_iterator(cls.market) cls.stack = contextlib.ExitStack() cls._clock = cls.stack.enter_context(cls.clock) cls.ev_loop.run_until_complete(cls.wait_til_ready()) print("Ready.")
def test_limit_makers_unfilled(self): if API_MOCK_ENABLED: self.web_app.update_response("post", PRIVET_API_BASE_URL, "/api/v2/orders", FixtureBeaxy.TEST_UNFILLED_ORDER1) self.assertGreater(self.market.get_balance("BTC"), 0.00005) trading_pair = "DASH-BTC" current_bid_price: Decimal = self.market.get_price( trading_pair, True) * Decimal('0.8') quantize_bid_price: Decimal = self.market.quantize_order_price( trading_pair, current_bid_price) bid_amount: Decimal = Decimal('0.01') quantized_bid_amount: Decimal = self.market.quantize_order_amount( trading_pair, bid_amount) current_ask_price: Decimal = self.market.get_price(trading_pair, False) quantize_ask_price: Decimal = self.market.quantize_order_price( trading_pair, current_ask_price) ask_amount: Decimal = Decimal('0.01') quantized_ask_amount: Decimal = self.market.quantize_order_amount( trading_pair, ask_amount) order_id1, exch_order_id_1 = self.place_order( True, trading_pair, quantized_bid_amount, OrderType.LIMIT, quantize_bid_price, [(3, FixtureBeaxy.TEST_UNFILLED_ORDER1_WS_ORDER_CREATED)]) [order_created_event] = self.run_parallel( self.market_logger.wait_for(BuyOrderCreatedEvent)) order_created_event: BuyOrderCreatedEvent = order_created_event self.assertEqual(order_id1, order_created_event.order_id) if API_MOCK_ENABLED: self.web_app.update_response("post", PRIVET_API_BASE_URL, "/api/v2/orders", FixtureBeaxy.TEST_UNFILLED_ORDER2) order_id2, exch_order_id_2 = self.place_order( False, trading_pair, quantized_ask_amount, OrderType.LIMIT, quantize_ask_price, [(3, FixtureBeaxy.TEST_UNFILLED_ORDER2_WS_ORDER_CREATED)]) [order_created_event] = self.run_parallel( self.market_logger.wait_for(SellOrderCreatedEvent)) order_created_event: BuyOrderCreatedEvent = order_created_event self.assertEqual(order_id2, order_created_event.order_id) if API_MOCK_ENABLED: HummingWsServerFactory.send_str_threadsafe( BeaxyConstants.TradingApi.WS_BASE_URL, FixtureBeaxy.TEST_UNFILLED_ORDER1_WS_ORDER_CANCELED, delay=3) HummingWsServerFactory.send_str_threadsafe( BeaxyConstants.TradingApi.WS_BASE_URL, FixtureBeaxy.TEST_UNFILLED_ORDER2_WS_ORDER_CANCELED, delay=3) self.web_app.update_response("delete", PRIVET_API_BASE_URL, "/api/v1/orders", "") self.run_parallel(asyncio.sleep(1)) [cancellation_results] = self.run_parallel(self.market.cancel_all(5)) for cr in cancellation_results: self.assertEqual(cr.success, True)
def test_cancel_all(self): if API_MOCK_ENABLED: self.web_app.update_response( "delete", PRIVET_API_BASE_URL, "/api/v2/orders/open/435118B0-A7F7-40D2-A409-820E8FC342A2", '') self.assertGreater(self.market.get_balance("BTC"), 0.00005) self.assertGreater(self.market.get_balance("DASH"), 0.01) trading_pair = "DASH-BTC" # make worst price so order wont be executed current_bid_price: Decimal = self.market.get_price( trading_pair, True) * Decimal('0.5') quantize_bid_price: Decimal = self.market.quantize_order_price( trading_pair, current_bid_price) bid_amount: Decimal = Decimal('0.01') quantized_bid_amount: Decimal = self.market.quantize_order_amount( trading_pair, bid_amount) # make worst price so order wont be executed current_ask_price: Decimal = self.market.get_price( trading_pair, False) * Decimal('2') quantize_ask_price: Decimal = self.market.quantize_order_price( trading_pair, current_ask_price) ask_amount: Decimal = Decimal('0.01') quantized_ask_amount: Decimal = self.market.quantize_order_amount( trading_pair, ask_amount) if API_MOCK_ENABLED: self.web_app.update_response("post", PRIVET_API_BASE_URL, "/api/v2/orders", FixtureBeaxy.TEST_CANCEL_ALL_ORDER1) _, exch_order_id_1 = self.place_order(True, trading_pair, quantized_bid_amount, OrderType.LIMIT_MAKER, quantize_bid_price) if API_MOCK_ENABLED: self.web_app.update_response("post", PRIVET_API_BASE_URL, "/api/v2/orders", FixtureBeaxy.TEST_CANCEL_ALL_ORDER2) _, exch_order_id_2 = self.place_order(False, trading_pair, quantized_ask_amount, OrderType.LIMIT_MAKER, quantize_ask_price) self.run_parallel(asyncio.sleep(1)) [cancellation_results] = self.run_parallel(self.market.cancel_all(5)) if API_MOCK_ENABLED: HummingWsServerFactory.send_str_threadsafe( BeaxyConstants.TradingApi.WS_BASE_URL, FixtureBeaxy.TEST_CANCEL_BUY_WS_ORDER1_CANCELED, delay=3) HummingWsServerFactory.send_str_threadsafe( BeaxyConstants.TradingApi.WS_BASE_URL, FixtureBeaxy.TEST_CANCEL_BUY_WS_ORDER2_CANCELED, delay=3) for cr in cancellation_results: self.assertEqual(cr.success, True)
def setUpClass(cls): cls.ev_loop: asyncio.BaseEventLoop = asyncio.get_event_loop() if API_MOCK_ENABLED: cls.web_app = HummingWebApp.get_instance() cls.web_app.add_host_to_mock(PRIVET_API_BASE_URL, []) cls.web_app.add_host_to_mock(PUBLIC_API_BASE_URL, []) cls.web_app.start() cls.ev_loop.run_until_complete(cls.web_app.wait_til_started()) cls._patcher = mock.patch("aiohttp.client.URL") cls._url_mock = cls._patcher.start() cls._url_mock.side_effect = cls.web_app.reroute_local cls.web_app.update_response("get", PUBLIC_API_BASE_URL, "/api/v2/symbols", FixtureBeaxy.SYMBOLS) cls.web_app.update_response("get", PUBLIC_API_BASE_URL, "/api/v2/symbols/DASHBTC/book", FixtureBeaxy.TRADE_BOOK) cls.web_app.update_response("get", PUBLIC_API_BASE_URL, "/api/v2/symbols/DASHBTC/rate", FixtureBeaxy.EXCHANGE_RATE) cls.web_app.update_response("get", PRIVET_API_BASE_URL, "/api/v2/health", FixtureBeaxy.HEALTH) cls.web_app.update_response("get", PRIVET_API_BASE_URL, "/api/v2/wallets", FixtureBeaxy.BALANCES) cls.web_app.update_response("get", PRIVET_API_BASE_URL, "/api/v2/tradingsettings", FixtureBeaxy.TRADE_SETTINGS) cls.web_app.update_response("get", PRIVET_API_BASE_URL, "/api/v2/orders/open", FixtureBeaxy.ORDERS_OPEN_EMPTY) cls.web_app.update_response("get", PRIVET_API_BASE_URL, "/api/v2/orders/closed", FixtureBeaxy.ORDERS_CLOSED_EMPTY) cls._t_nonce_patcher = unittest.mock.patch( "hummingbot.connector.exchange.beaxy.beaxy_exchange.get_tracking_nonce" ) cls._t_nonce_mock = cls._t_nonce_patcher.start() HummingWsServerFactory.url_host_only = True HummingWsServerFactory.start_new_server( BeaxyConstants.TradingApi.WS_BASE_URL) HummingWsServerFactory.start_new_server( BeaxyConstants.PublicApi.WS_BASE_URL) cls._ws_patcher = unittest.mock.patch("websockets.connect", autospec=True) cls._ws_mock = cls._ws_patcher.start() cls._ws_mock.side_effect = HummingWsServerFactory.reroute_ws_connect cls._auth_confirm_patcher = unittest.mock.patch( "hummingbot.connector.exchange.beaxy.beaxy_auth.BeaxyAuth._BeaxyAuth__login_confirm" ) cls._auth_confirm_mock = cls._auth_confirm_patcher.start() cls._auth_session_patcher = unittest.mock.patch( "hummingbot.connector.exchange.beaxy.beaxy_auth.BeaxyAuth._BeaxyAuth__get_session_data" ) cls._auth_session_mock = cls._auth_session_patcher.start() cls._auth_session_mock.return_value = { "sign_key": 123, "session_id": '123' } cls._auth_headers_patcher = unittest.mock.patch( "hummingbot.connector.exchange.beaxy.beaxy_auth.BeaxyAuth.get_token" ) cls._auth_headers_mock = cls._auth_headers_patcher.start() cls._auth_headers_mock.return_value = '123' cls._auth_poll_patcher = unittest.mock.patch( "hummingbot.connector.exchange.beaxy.beaxy_auth.BeaxyAuth._auth_token_polling_loop" ) cls._auth_poll_mock = cls._auth_poll_patcher.start() cls.clock: Clock = Clock(ClockMode.REALTIME) cls.market: BeaxyExchange = BeaxyExchange(API_KEY, API_SECRET, trading_pairs=["DASH-BTC"]) if API_MOCK_ENABLED: async def mock_status_polling_task(): pass # disable status polling as it will make orders update inconsistent from mock view cls.market._status_polling_task = asyncio.ensure_future( mock_status_polling_task()) cls.ev_loop.run_until_complete(cls.market._update_balances()) print("Initializing Beaxy market... this will take about a minute.") cls.clock.add_iterator(cls.market) cls.stack: contextlib.ExitStack = contextlib.ExitStack() cls._clock = cls.stack.enter_context(cls.clock) cls.ev_loop.run_until_complete(cls.wait_til_ready()) print("Ready.")