Skip to content

Commit

Permalink
CoinZoom: More work on order creation and status
Browse files Browse the repository at this point in the history
  • Loading branch information
TheHolyRoger committed Mar 29, 2021
1 parent 563d745 commit 6842f87
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 94 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,32 +71,24 @@ cdef class CoinzoomActiveOrderTracker:
dict order_dict
double timestamp = message.timestamp
double amount = 0
dict nps = {'bids': s_empty_diff, 'asks': s_empty_diff}

if "b" in content_keys:
bid_entries = content["b"]
if "s" in content_keys:
ask_entries = content["s"]

nps = {
'bids': s_empty_diff,
'asks': s_empty_diff,
}

for entries, diff_key, id_list in [
(content["b"], 'bids', self._active_bids_ids),
(content["s"], 'asks', self._active_asks_ids)
(bid_entries, 'bids', self._active_bids_ids),
(ask_entries, 'asks', self._active_asks_ids)
]:
if len(entries) > 0:
nps[diff_key] = np.array(
[[timestamp,
price,
amount,
message.update_id]
for price, amount in [self.get_rates_and_amts_with_ids(entry, id_list) for entry in entries] if price is not None],
dtype="float64",
ndmin=2
[[timestamp, price, amount, message.update_id]
for price, amount in [self.get_rates_and_amts_with_ids(entry, id_list) for entry in entries]
if price is not None],
dtype="float64", ndmin=2
)

return nps['bids'], nps['asks']

cdef tuple c_convert_snapshot_message_to_np_arrays(self, object message):
Expand Down Expand Up @@ -130,16 +122,10 @@ cdef class CoinzoomActiveOrderTracker:
# Return the sorted snapshot tables.
cdef:
np.ndarray[np.float64_t, ndim=2] bids = np.array(
[[message.timestamp,
float(price),
float(self._active_bids[price]),
message.update_id]
[[message.timestamp, float(price), float(self._active_bids[price]), message.update_id]
for price in sorted(self._active_bids.keys())], dtype='float64', ndmin=2)
np.ndarray[np.float64_t, ndim=2] asks = np.array(
[[message.timestamp,
float(price),
float(self._active_asks[price]),
message.update_id]
[[message.timestamp, float(price), float(self._active_asks[price]), message.update_id]
for price in sorted(self._active_asks.keys(), reverse=True)], dtype='float64', ndmin=2)

if bids.shape[1] != 4:
Expand All @@ -153,14 +139,10 @@ cdef class CoinzoomActiveOrderTracker:
cdef np.ndarray[np.float64_t, ndim=1] c_convert_trade_message_to_np_array(self, object message):
cdef:
double trade_type_value = 1.0 if message.content[4] == "BUY" else 2.0
list content = message.content

timestamp = message.timestamp
content = message.content

return np.array(
[timestamp, trade_type_value, float(content[1]), float(content[2])],
dtype="float64"
)
return np.array([message.timestamp, trade_type_value, float(content[1]), float(content[2])],
dtype="float64")

def convert_diff_message_to_order_book_row(self, message):
np_bids, np_asks = self.c_convert_diff_message_to_np_arrays(message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,11 +52,12 @@ async def _listen_to_orders_trades_balances(self) -> AsyncIterable[Any]:

await self._ws.connect()

await self._ws.subscribe({stream_key: {} for stream_key in Constants.WS_SUB["USER_ORDERS_TRADES"]})
await self._ws.subscribe({Constants.WS_SUB["USER_ORDERS_TRADES"]: {}})

event_methods = [
Constants.WS_METHODS["USER_ORDERS"],
Constants.WS_METHODS["USER_ORDERS_CANCEL"],
# We don't need to know about pending cancels
# Constants.WS_METHODS["USER_ORDERS_CANCEL"],
]

async for msg in self._ws.on_message():
Expand Down
6 changes: 3 additions & 3 deletions hummingbot/connector/exchange/coinzoom/coinzoom_constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ class Constants:
WS_PRIVATE_URL = "wss://api.stage.coinzoom.com/api/v1/public/market/data/stream"
WS_PUBLIC_URL = "wss://api.stage.coinzoom.com/api/v1/public/market/data/stream"

HBOT_BROKER_ID = "refzzz48"
HBOT_BROKER_ID = "CZ_API_HBOT"

ENDPOINT = {
# Public Endpoints
Expand All @@ -20,14 +20,14 @@ class Constants:
"ORDER_CREATE": "orders/new",
"ORDER_DELETE": "orders/cancel",
"ORDER_STATUS": "orders/list",
"USER_ORDERS": "order",
"USER_ORDERS": "orders/list",
"USER_BALANCES": "ledger/list",
}

WS_SUB = {
"TRADES": "TradeSummaryRequest",
"ORDERS": "OrderBookRequest",
"USER_ORDERS_TRADES": ["OrderUpdateRequest"],
"USER_ORDERS_TRADES": "OrderUpdateRequest",

}

Expand Down
109 changes: 63 additions & 46 deletions hummingbot/connector/exchange/coinzoom/coinzoom_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -436,6 +436,9 @@ async def _create_order(self,
"orderSide": trade_type.name.upper(),
"quantity": f"{amount:f}",
"price": f"{price:f}",
# Waiting for changes to CoinZoom API for this one.
# "originType": Constants.HBOT_BROKER_ID,
# CoinZoom doesn't support client order id yet
# "clientOrderId": order_id,
"payFeesWithZoomToken": "true",
}
Expand Down Expand Up @@ -530,10 +533,11 @@ async def _execute_cancel(self, trading_pair: str, order_id: str) -> str:
except CoinzoomAPIError as e:
err = e.error_payload.get('error', e.error_payload)
print(f"order cancel error: {err}")
self._order_not_found_records[order_id] = self._order_not_found_records.get(order_id, 0) + 1
if err.get('code') == 20002 and \
self._order_not_found_records[order_id] >= self.ORDER_NOT_EXIST_CANCEL_COUNT:
order_was_cancelled = True
# TODO: Still need to handle order cancel errors.
# self._order_not_found_records[order_id] = self._order_not_found_records.get(order_id, 0) + 1
# if err.get('code') == 20002 and \
# self._order_not_found_records[order_id] >= self.ORDER_NOT_EXIST_CANCEL_COUNT:
# order_was_cancelled = True
if order_was_cancelled:
self.logger().info(f"Successfully cancelled order {order_id} on {Constants.EXCHANGE_NAME}.")
self.stop_tracking_order(order_id)
Expand Down Expand Up @@ -602,29 +606,25 @@ async def _update_order_status(self):
Constants.ENDPOINT["ORDER_STATUS"],
api_params,
is_auth_required=True)
for response, tracked_order in zip(open_orders, tracked_orders):

open_orders_dict = {o['id']: o for o in open_orders}
found_ex_order_ids = list(open_orders_dict.keys())

for tracked_order in tracked_orders:
client_order_id = tracked_order.client_order_id
if isinstance(response, CoinzoomAPIError):
err = response.error_payload.get('error', response.error_payload)
print(f"order update err {err}")
if err.get('code') == 20002:
self._order_not_found_records[client_order_id] = \
self._order_not_found_records.get(client_order_id, 0) + 1
if self._order_not_found_records[client_order_id] < self.ORDER_NOT_EXIST_CONFIRMATION_COUNT:
# Wait until the order not found error have repeated a few times before actually treating
# it as failed. See: https://github.com/CoinAlpha/hummingbot/issues/601
continue
self.trigger_event(MarketEvent.OrderFailure,
MarketOrderFailureEvent(
self.current_timestamp, client_order_id, tracked_order.order_type))
self.stop_tracking_order(client_order_id)
else:
ex_order_id = tracked_order.exchange_order_id
if ex_order_id not in found_ex_order_ids:
self._order_not_found_records[client_order_id] = \
self._order_not_found_records.get(client_order_id, 0) + 1
if self._order_not_found_records[client_order_id] < self.ORDER_NOT_EXIST_CONFIRMATION_COUNT:
# Wait until the order is not found a few times before actually treating it as failed.
continue
elif "id" not in response:
self.logger().info(f"_update_order_status id not in resp: {response}")
continue
self.trigger_event(MarketEvent.OrderFailure,
MarketOrderFailureEvent(
self.current_timestamp, client_order_id, tracked_order.order_type))
self.stop_tracking_order(client_order_id)
else:
self._process_order_message(response)
self._process_order_message(open_orders_dict[ex_order_id])

def _process_order_message(self, order_msg: Dict[str, Any]):
"""
Expand Down Expand Up @@ -661,7 +661,6 @@ def _process_order_message(self, order_msg: Dict[str, Any]):
}
WS request
{
'id': '4eb3f26c-91bd-4bd2-bacb-15b2f432c452',
'orderId': '962a2a54-fbcf-4d89-8f37-a8854020a823',
'symbol': 'BTC/USD', 'orderType': 'LIMIT',
'orderSide': 'BUY',
Expand All @@ -673,6 +672,13 @@ def _process_order_message(self, order_msg: Dict[str, Any]):
'leavesQuantity': 0,
'cumulativeQuantity': 0,
'transactTime': '2021-03-23T19:06:51.155520Z'
... Optional fields
'id': '4eb3f26c-91bd-4bd2-bacb-15b2f432c452',
"orderType": "LIMIT",
"lastPrice": 56518.7,
"averagePrice": 56518.7,
}
"""
if order_msg.get('clientOrderId') is not None:
Expand All @@ -690,11 +696,6 @@ def _process_order_message(self, order_msg: Dict[str, Any]):
if not track_order:
return
tracked_order = track_order[0]
# Update order execution status
tracked_order.last_state = order_msg["orderStatus"]
# update order
tracked_order.executed_amount_base = Decimal(order_msg["cumulativeQuantity"])
tracked_order.executed_amount_quote = Decimal(order_msg["price"]) * Decimal(order_msg["cumulativeQuantity"])

# Estimate fee
order_msg["trade_fee"] = self.estimate_fee_pct(tracked_order.order_type is OrderType.LIMIT_MAKER)
Expand All @@ -703,17 +704,17 @@ def _process_order_message(self, order_msg: Dict[str, Any]):
if updated:
safe_ensure_future(self._trigger_order_fill(tracked_order, order_msg))
elif tracked_order.is_cancelled:
self.logger().info(f"Successfully cancelled order {client_order_id}.")
self.stop_tracking_order(client_order_id)
self.logger().info(f"Successfully cancelled order {tracked_order.client_order_id}.")
self.stop_tracking_order(tracked_order.client_order_id)
self.trigger_event(MarketEvent.OrderCancelled,
OrderCancelledEvent(self.current_timestamp, client_order_id))
OrderCancelledEvent(self.current_timestamp, tracked_order.client_order_id))
tracked_order.cancelled_event.set()
elif tracked_order.is_failure:
self.logger().info(f"The market order {client_order_id} has failed according to order status API. ")
self.logger().info(f"The order {tracked_order.client_order_id} has failed according to order status API. ")
self.trigger_event(MarketEvent.OrderFailure,
MarketOrderFailureEvent(
self.current_timestamp, client_order_id, tracked_order.order_type))
self.stop_tracking_order(client_order_id)
self.current_timestamp, tracked_order.client_order_id, tracked_order.order_type))
self.stop_tracking_order(tracked_order.client_order_id)

async def _trigger_order_fill(self,
tracked_order: CoinzoomInFlightOrder,
Expand All @@ -726,7 +727,7 @@ async def _trigger_order_fill(self,
tracked_order.trading_pair,
tracked_order.trade_type,
tracked_order.order_type,
Decimal(str(update_msg.get("price", "0"))),
Decimal(str(update_msg.get("averagePrice", update_msg.get("price", "0")))),
tracked_order.executed_amount_base,
TradeFee(percent=update_msg["trade_fee"]),
)
Expand Down Expand Up @@ -777,8 +778,8 @@ async def cancel_all(self, timeout_seconds: float) -> List[CancellationResult]:
:param timeout_seconds: The timeout at which the operation will be canceled.
:returns List of CancellationResult which indicates whether each order is successfully cancelled.
"""
if self._trading_pairs is None:
raise Exception("cancel_all can only be used when trading_pairs are specified.")
# if self._trading_pairs is None:
# raise Exception("cancel_all can only be used when trading_pairs are specified.")
open_orders = [o for o in self._in_flight_orders.values() if not o.is_done]
if len(open_orders) == 0:
return []
Expand Down Expand Up @@ -872,25 +873,41 @@ async def _user_stream_event_listener(self):

# This is currently unused, but looks like a future addition.
async def get_open_orders(self) -> List[OpenOrder]:
result = await self._api_request("GET", Constants.ENDPOINT["USER_ORDERS"], is_auth_required=True)
tracked_orders = list(self._in_flight_orders.values())
api_params = {
'symbol': None,
'orderSide': None,
'orderStatuses': ["NEW", "PARTIALLY_FILLED"],
'size': 500,
'bookmarkOrderId': None
}
result = await self._api_request("POST", Constants.ENDPOINT["USER_ORDERS"], api_params, is_auth_required=True)
ret_val = []
for order in result:
if Constants.HBOT_BROKER_ID not in order["clientOrderId"]:
exchange_order_id = str(order["id"])
# CoinZoom doesn't support client order ids yet so we must find it from the tracked orders.
track_order = [o for o in tracked_orders if exchange_order_id == o.exchange_order_id]
if not track_order or len(track_order) < 1:
# Skip untracked orders
continue
if order["type"] != OrderType.LIMIT.name.lower():
client_order_id = track_order[0].client_order_id
# if Constants.HBOT_BROKER_ID not in order["clientOrderId"]:
# continue
if order["orderType"] != OrderType.LIMIT.name.upper():
self.logger().info(f"Unsupported order type found: {order['type']}")
# Skip and report non-limit orders
continue
ret_val.append(
OpenOrder(
client_order_id=order["clientOrderId"],
client_order_id=client_order_id,
trading_pair=convert_from_exchange_trading_pair(order["symbol"]),
price=Decimal(str(order["price"])),
amount=Decimal(str(order["quantity"])),
executed_amount=Decimal(str(order["cumQuantity"])),
status=order["status"],
status=order["orderStatus"],
order_type=OrderType.LIMIT,
is_buy=True if order["side"].lower() == TradeType.BUY.name.lower() else False,
time=str_date_to_ts(order["createdAt"]),
is_buy=True if order["orderSide"].lower() == TradeType.BUY.name.lower() else False,
time=str_date_to_ts(order["timestamp"]),
exchange_order_id=order["id"]
)
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,9 @@ def update_with_order_update(self, order_update: Dict[str, Any]) -> bool:
'transactTime': '2021-03-23T19:06:51.155520Z'
}
"""
# Update order execution status
self.last_state = order_update["orderStatus"]

if 'cumulativeQuantity' not in order_update and 'executions' not in order_update:
return False

Expand All @@ -147,7 +150,7 @@ def update_with_order_update(self, order_update: Dict[str, Any]) -> bool:
return False
self.trade_id_set.add(trade_id)
# Set executed amounts
executed_price = Decimal(str(order_update.get("price", "0")))
executed_price = Decimal(str(order_update.get("averagePrice", order_update.get("price", "0"))))
self.executed_amount_base = Decimal(str(order_update["cumulativeQuantity"]))
self.executed_amount_quote = executed_price * self.executed_amount_base
if self.executed_amount_base <= s_decimal_0:
Expand Down
2 changes: 1 addition & 1 deletion hummingbot/connector/exchange/coinzoom/coinzoom_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def get_new_client_order_id(is_buy: bool, trading_pair: str) -> str:
quote = symbols[1].upper()
base_str = f"{base[0]}{base[-1]}"
quote_str = f"{quote[0]}{quote[-1]}"
return f"{Constants.HBOT_BROKER_ID}-{side}-{base_str}{quote_str}-{get_tracking_nonce()}"
return f"{Constants.HBOT_BROKER_ID}{side}{base_str}{quote_str}{get_tracking_nonce()}"


def retry_sleep_time(try_count: int) -> float:
Expand Down
Loading

0 comments on commit 6842f87

Please sign in to comment.