Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,14 @@ async def _request_order_book_snapshot(self, trading_pair: str) -> Dict[str, Any

:return: the response from the exchange (JSON dictionary)
"""
symbol = await self._connector.exchange_symbol_associated_to_pair(trading_pair=trading_pair)
params = {
"symbol": await self._connector.exchange_symbol_associated_to_pair(trading_pair=trading_pair),
"limit": "1000"
"market-id": symbol
}

rest_assistant = await self._api_factory.get_rest_assistant()
data = await rest_assistant.execute_request(
url=web_utils.public_rest_url(path_url=CONSTANTS.SNAPSHOT_PATH_URL, domain=self._domain),
url=web_utils.public_rest_url(path_url=CONSTANTS.SNAPSHOT_PATH_URL.format(market_id=symbol), domain=self._domain),
params=params,
method=RESTMethod.GET,
throttler_limit_id=CONSTANTS.SNAPSHOT_PATH_URL,
Expand All @@ -69,14 +69,14 @@ async def _order_book_snapshot(self, trading_pair: str) -> OrderBookMessage:

async def _parse_trade_message(self, raw_message: Dict[str, Any], message_queue: asyncio.Queue):
if "result" not in raw_message:
trading_pair = await self._connector.trading_pair_associated_to_exchange_symbol(symbol=raw_message["s"])
trading_pair = await self._connector.trading_pair_associated_to_exchange_symbol(symbol=raw_message["market_id"])

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to verify wheter we can map symbol to market_id

trade_message = GeniusYieldOrderBook.trade_message_from_exchange(
raw_message, {"trading_pair": trading_pair})
message_queue.put_nowait(trade_message)

async def _parse_order_book_diff_message(self, raw_message: Dict[str, Any], message_queue: asyncio.Queue):
if "result" not in raw_message:
trading_pair = await self._connector.trading_pair_associated_to_exchange_symbol(symbol=raw_message["s"])
trading_pair = await self._connector.trading_pair_associated_to_exchange_symbol(symbol=raw_message["market_id"])
order_book_message: OrderBookMessage = GeniusYieldOrderBook.diff_message_from_exchange(
raw_message, time.time(), {"trading_pair": trading_pair})
message_queue.put_nowait(order_book_message)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ async def _listen_for_user_stream(self):
while True:
try:
data = await self._request_user_stream()
# Process the user stream data
except asyncio.CancelledError:
raise
except Exception as e:
Expand Down
11 changes: 4 additions & 7 deletions hummingbot/connector/exchange/genius_yield/genius_yield_auth.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import hashlib
import hmac
import json
from collections import OrderedDict
from typing import Any, Dict
from urllib.parse import urlencode

Expand All @@ -23,13 +22,11 @@ async def rest_authenticate(self, request: RESTRequest) -> RESTRequest:
:param request: the request to be configured for authenticated interaction
"""
if request.method == RESTMethod.POST:
request.data = self.add_auth_to_params(params=json.loads(request.data))
request.data = json.dumps(self.add_auth_to_params(params=json.loads(request.data)))
else:
request.params = self.add_auth_to_params(params=request.params)

headers = {}
if request.headers is not None:
headers.update(request.headers)
headers = request.headers or {}
headers.update(self.header_for_authentication())
request.headers = headers

Expand All @@ -38,7 +35,7 @@ async def rest_authenticate(self, request: RESTRequest) -> RESTRequest:
def add_auth_to_params(self, params: Dict[str, Any]) -> Dict[str, Any]:
timestamp = int(self.time_provider.time() * 1e3)

request_params = OrderedDict(params or {})
request_params = params or {}
request_params["timestamp"] = timestamp

signature = self._generate_signature(params=request_params)
Expand All @@ -47,7 +44,7 @@ def add_auth_to_params(self, params: Dict[str, Any]) -> Dict[str, Any]:
return request_params

def header_for_authentication(self) -> Dict[str, str]:
return {"X-MBX-APIKEY": self.api_key}
return {"api-key": self.api_key}

def _generate_signature(self, params: Dict[str, Any]) -> str:
encoded_params_str = urlencode(params)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
MAX_ORDER_ID_LEN = 32

# Base URL
REST_URL = "https://api.genius_yield.{}/api/"
REST_URL = "https://api.genius_yield.{}/v0/"
WSS_URL = "wss://stream.genius_yield.{}:9443/ws"

PUBLIC_API_VERSION = "v0"
Expand All @@ -25,6 +25,7 @@
ACCOUNTS_PATH_URL = "/balances/{address}"
MY_TRADES_PATH_URL = "/orders/details/{nft-token}"
ORDER_PATH_URL = "/orders"
CANCEL_ORDER_PATH_URL = "/orders"
GENIUS_YIELD_USER_STREAM_PATH_URL = "/userDataStream"

WS_HEARTBEAT_TIME_INTERVAL = 30
Expand Down
130 changes: 44 additions & 86 deletions hummingbot/connector/exchange/genius_yield/genius_yield_exchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,14 @@ def supported_order_types(self):
return [OrderType.LIMIT, OrderType.LIMIT_MAKER, OrderType.MARKET]

async def get_all_pairs_prices(self) -> List[Dict[str, str]]:
pairs_prices = await self._api_get(path_url=CONSTANTS.TICKER_BOOK_PATH_URL)
response = await self._api_get(path_url=CONSTANTS.ALL_MARKETS_PATH_URL)
pairs_prices = [
{
"symbol": market["market_id"],
"price": market["base_close"]
}
for market in response
]
return pairs_prices

def _is_request_exception_related_to_time_synchronizer(self, request_exception: Exception):
Expand Down Expand Up @@ -167,24 +174,20 @@ async def _place_order(self,
type_str = GeniusYieldExchange.genius_yield_order_type(order_type)
side_str = CONSTANTS.SIDE_BUY if trade_type is TradeType.BUY else CONSTANTS.SIDE_SELL
symbol = await self.exchange_symbol_associated_to_pair(trading_pair=trading_pair)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Needs to be double checked wheter trading_pair is appropriate here. Might be correct to use asset_ticker instead

api_params = {"symbol": symbol,
"side": side_str,
"quantity": amount_str,
"type": type_str,
"newClientOrderId": order_id}
if order_type is OrderType.LIMIT or order_type is OrderType.LIMIT_MAKER:
price_str = f"{price:f}"
api_params["price"] = price_str
if order_type == OrderType.LIMIT:
api_params["timeInForce"] = CONSTANTS.TIME_IN_FORCE_GTC
api_params = {
"offer_token": symbol.split('_')[0],
"offer_amount": str(amount),
"price_token": symbol.split('_')[1],
"price_amount": str(price)
}

try:
order_result = await self._api_post(
path_url=CONSTANTS.ORDER_PATH_URL,
data=api_params,
is_auth_required=True)
o_id = str(order_result["orderId"])
transact_time = order_result["transactTime"] * 1e-3
o_id = str(order_result["transaction_id"])
transact_time = self._time_synchronizer.time()
Comment on lines -186 to +190

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to check wheter we can use tx id as means of an orders id. Contact api does not have a seperate order id field it seems.

except IOError as e:
error_description = str(e)
is_server_overloaded = ("status is 503" in error_description
Expand All @@ -197,97 +200,52 @@ async def _place_order(self,
return o_id, transact_time

async def _place_cancel(self, order_id: str, tracked_order: InFlightOrder):
symbol = await self.exchange_symbol_associated_to_pair(trading_pair=tracked_order.trading_pair)
api_params = {
"symbol": symbol,
"origClientOrderId": order_id,
"order_references": [order_id]
}
cancel_result = await self._api_delete(
path_url=CONSTANTS.ORDER_PATH_URL,
params=api_params,
path_url=CONSTANTS.CANCEL_ORDER_PATH_URL,
data=api_params,
is_auth_required=True)
if cancel_result.get("status") == "CANCELED":
if cancel_result.get("transaction_id"):
return True
return False

async def _format_trading_rules(self, exchange_info_dict: Dict[str, Any]) -> List[TradingRule]:
"""
Example:
{
"symbol": "ETHBTC",
"baseAssetPrecision": 8,
"quotePrecision": 8,
"orderTypes": ["LIMIT", "MARKET"],
"filters": [
{
"filterType": "PRICE_FILTER",
"minPrice": "0.00000100",
"maxPrice": "100000.00000000",
"tickSize": "0.00000100"
}, {
"filterType": "LOT_SIZE",
"minQty": "0.00100000",
"maxQty": "100000.00000000",
"stepSize": "0.00100000"
}, {
"filterType": "MIN_NOTIONAL",
"minNotional": "0.00100000"
}
]
}
"""
trading_pair_rules = exchange_info_dict.get("symbols", [])
trading_pair_rules = exchange_info_dict
retval = []
for rule in filter(genius_yield_utils.is_exchange_information_valid, trading_pair_rules):
for rule in trading_pair_rules:
try:
trading_pair = await self.trading_pair_associated_to_exchange_symbol(symbol=rule.get("symbol"))
filters = rule.get("filters")
price_filter = [f for f in filters if f.get("filterType") == "PRICE_FILTER"][0]
lot_size_filter = [f for f in filters if f.get("filterType") == "LOT_SIZE"][0]
min_notional_filter = [f for f in filters if f.get("filterType") in ["MIN_NOTIONAL", "NOTIONAL"]][0]

min_order_size = Decimal(lot_size_filter.get("minQty"))
tick_size = price_filter.get("tickSize")
step_size = Decimal(lot_size_filter.get("stepSize"))
min_notional = Decimal(min_notional_filter.get("minNotional"))
trading_pair = combine_to_hb_trading_pair(base=rule["base_asset"], quote=rule["target_asset"])
min_order_size = Decimal(rule["filters"]["minQty"])
tick_size = Decimal(rule["filters"]["tickSize"])
step_size = Decimal(rule["filters"]["stepSize"])
min_notional = Decimal(rule["filters"]["minNotional"])

retval.append(
TradingRule(trading_pair,
min_order_size=min_order_size,
min_price_increment=Decimal(tick_size),
min_base_amount_increment=Decimal(step_size),
min_notional_size=Decimal(min_notional)))
min_price_increment=tick_size,
min_base_amount_increment=step_size,
min_notional_size=min_notional))

except Exception:
self.logger().exception(f"Error parsing the trading pair rule {rule}. Skipping.")
return retval

async def _status_polling_loop_fetch_updates(self):
await self._update_order_fills_from_trades()
await super()._status_polling_loop_fetch_updates()

async def _update_trading_fees(self):
"""
Update fees information from the exchange
"""
pass

async def _request_order_status(self, tracked_order: InFlightOrder) -> OrderUpdate:
trading_pair = await self.exchange_symbol_associated_to_pair(trading_pair=tracked_order.trading_pair)
symbol = await self.exchange_symbol_associated_to_pair(trading_pair=tracked_order.trading_pair)
Comment on lines -276 to +237

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again might be more appropriate to use ticker here

updated_order_data = await self._api_get(
path_url=CONSTANTS.ORDER_PATH_URL,
params={
"symbol": trading_pair,
"origClientOrderId": tracked_order.client_order_id},
path_url=f"{CONSTANTS.ORDER_PATH_URL}/{tracked_order.exchange_order_id}",
is_auth_required=True)

new_state = CONSTANTS.ORDER_STATE[updated_order_data["status"]]

order_update = OrderUpdate(
client_order_id=tracked_order.client_order_id,
exchange_order_id=str(updated_order_data["orderId"]),
exchange_order_id=str(updated_order_data["transaction_id"]),
trading_pair=tracked_order.trading_pair,
update_timestamp=updated_order_data["updateTime"] * 1e-3,
update_timestamp=self._time_synchronizer.time(),
new_state=new_state,
)

Expand All @@ -301,11 +259,10 @@ async def _update_balances(self):
path_url=CONSTANTS.ACCOUNTS_PATH_URL,
is_auth_required=True)

balances = account_info["balances"]
for balance_entry in balances:
asset_name = balance_entry["asset"]
free_balance = Decimal(balance_entry["free"])
total_balance = Decimal(balance_entry["free"]) + Decimal(balance_entry["locked"])
balances = account_info
for asset_name, balance in balances.items():
free_balance = Decimal(balance)
total_balance = free_balance
self._account_available_balances[asset_name] = free_balance
self._account_balances[asset_name] = total_balance
remote_asset_names.add(asset_name)
Expand All @@ -317,14 +274,14 @@ async def _update_balances(self):

def _initialize_trading_pair_symbols_from_exchange_info(self, exchange_info: Dict[str, Any]):
mapping = bidict()
for symbol_data in filter(genius_yield_utils.is_exchange_information_valid, exchange_info["symbols"]):
mapping[symbol_data["symbol"]] = combine_to_hb_trading_pair(base=symbol_data["baseAsset"],
quote=symbol_data["quoteAsset"])
for symbol_data in exchange_info:
mapping[symbol_data["market_id"]] = combine_to_hb_trading_pair(base=symbol_data["base_asset"],
quote=symbol_data["target_asset"])
self._set_trading_pair_symbol_map(mapping)

async def _get_last_traded_price(self, trading_pair: str) -> float:
params = {
"symbol": await self.exchange_symbol_associated_to_pair(trading_pair=trading_pair)
"market-id": await self.exchange_symbol_associated_to_pair(trading_pair=trading_pair)
}

resp_json = await self._api_request(
Expand All @@ -333,4 +290,5 @@ async def _get_last_traded_price(self, trading_pair: str) -> float:
params=params
)

return float(resp_json["lastPrice"])
return float(resp_json["price"])

Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,9 @@ def snapshot_message_from_exchange(cls,
:param metadata: a dictionary with extra information to add to the snapshot data
:return: a snapshot message with the snapshot information received from the exchange
"""
if metadata:
msg.update(metadata)
return OrderBookMessage(OrderBookMessageType.SNAPSHOT, {
"trading_pair": msg["trading_pair"],
"update_id": msg["lastUpdateId"],
"market_id": msg["market_pair_id"],
"update_id": msg["timestamp"],
"bids": msg["bids"],
"asks": msg["asks"]
}, timestamp=timestamp)
Expand All @@ -43,14 +41,12 @@ def diff_message_from_exchange(cls,
:param metadata: a dictionary with extra information to add to the difference data
:return: a diff message with the changes in the order book notified by the exchange
"""
if metadata:
msg.update(metadata)
return OrderBookMessage(OrderBookMessageType.DIFF, {
"trading_pair": msg["trading_pair"],
"first_update_id": msg["U"],
"update_id": msg["u"],
"bids": msg["b"],
"asks": msg["a"]
"market_id": msg["market_pair_id"],
"first_update_id": msg["first_update_id"],
"update_id": msg["last_update_id"],
"bids": msg["bids"],
"asks": msg["asks"]
}, timestamp=timestamp)

@classmethod
Expand All @@ -61,14 +57,12 @@ def trade_message_from_exchange(cls, msg: Dict[str, any], metadata: Optional[Dic
:param metadata: a dictionary with extra information to add to trade message
:return: a trade message with the details of the trade as provided by the exchange
"""
if metadata:
msg.update(metadata)
ts = msg["E"]
ts = msg["timestamp"]
return OrderBookMessage(OrderBookMessageType.TRADE, {
"trading_pair": msg["trading_pair"],
"trade_type": float(TradeType.SELL.value) if msg["m"] else float(TradeType.BUY.value),
"trade_id": msg["t"],
"market_id": msg["market_pair_id"],
"trade_type": float(TradeType.SELL.value) if msg["is_sell"] else float(TradeType.BUY.value),
"trade_id": msg["transaction_id"],
"update_id": ts,
"price": msg["p"],
"amount": msg["q"]
"price": msg["price"],
"amount": msg["amount"]
}, timestamp=ts * 1e-3)
Loading