From f405c03eb05c95dc6cdc5b6abc41f947a7ba7362 Mon Sep 17 00:00:00 2001 From: Blaine Costello Date: Thu, 20 Jan 2022 01:14:35 -0500 Subject: [PATCH] Added 'public client' & enhanced orders At the end of client.py I've added some function definitions for more user-friendly order functionality to handle all the bs we've been dealing with (timestamps, storageID, etc.) I've also added a bunch of global functions that serve as a 'public client' where users can go to get public information without impacting their rate limit on the authenticated client. These are just a bunch of simple requests packaged as easy-to-use functions that return simple json data. More documentation needs to be added, as it will be copied from the REST api doc provided by loopring. --- loopring/client.py | 231 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 231 insertions(+) diff --git a/loopring/client.py b/loopring/client.py index 5eef3cf..c70da1c 100644 --- a/loopring/client.py +++ b/loopring/client.py @@ -4,6 +4,7 @@ from asyncio.events import AbstractEventLoop from datetime import datetime from typing import List, Sequence, Union +import requests import aiohttp from py_eth_sig_utils.signing import v_r_s_to_signature @@ -1419,3 +1420,233 @@ async def update_api_key(self) -> str: return self.api_key + + + + +#======================================================================================================================== +#=========================================={Enhanced Order Functionality & Testing}====================================== +#======================================================================================================================== + + # update parameters to take only the client - 1. replace get_balance function with client-based solution + async def market_order(self, + *, + asset1: float = None, + asset2: str = None, + trade_pair: str = None, + funding: float = None, + size: int = None + ) -> str: + + """Market Order - allow user to execute market order at strike price, that exhibits bi-directional market orders + by switching the order of assets. + Args: + asset1 (str): asset to buy + asset2 (str): asset to sell + trade_pair (str): trade pair + funding (float): amount of asset 2 to sell (units: asset2) + size (float): amount of asset 1 to buy (units: asset1) + + Returns: + str: transaction hash of specified order. + + Raises: + EmptyAPIKey: ... + EmptySignature: ... + + + ############################ + #### UNDER CONSTRUCTION #### + ############################ + """ + + # Does Exchange represent the address of the exchange for the client? + EXC = Exchange + + # get token ids + tid1 = int(get_token_id(asset1)) + tid2 = int(get_token_id(asset2)) + trade_pair = asset1 + "-" + asset2 + + + # get relayer timestamp and calculate some future time... (the added 60x60x1000 'seconds?' is virtual duct tape) + time_now = get_relayer_time() + time_now = datetime.datetime.utcfromtimestamp(time_now / 1000) + print(time_now) + in5min = time_now + datetime.timedelta(days=100) + print(in5min) + print(datetime.datetime.timestamp(in5min)) + in5min = int(datetime.datetime.timestamp(in5min)) + time_now = int(datetime.datetime.timestamp(time_now)) + + # get print lowest ask (for debug) + bp = get_asks(trade_pair) + bp = float(bp[0][0]) + sp = get_asks(trade_pair) + sp = float(sp[0][0]) + print("sp,bp = " + str(sp) + ", " + str(bp)) + + # get volumes based on 'funding' (print for debug) + va1 = int(funding/float(bp) * 10 ** 18) + va2 = int(funding * 10 ** 18) + # print("~ Volumes (1), (2), (2/1) ~") + print("Trading " + str(va1) + " " + str(asset1)+ " for " + str(va2) + " " + str(asset2) + " (i.e. price = " + str(va2/va1) + ")") + # print(va2) + # print(va2 / va1) + # print() + # Define Tokens + buyTok = self.token.Token(id=tid1, volume=va1) + sellTok = self.token.Token(id=tid2, volume=va2) + + # Execute trades + sid = await self.get_next_storage_id(sell_token_id=get_token_id(asset1),) + print(sid) + msg = await self.submit_order(buy_token=buyTok, sell_token=sellTok, exchange=EXC, fill_amount_b_or_s=False, + max_fee_bips=50, order_type="TAKER_ONLY", storage_id=str(sid['orderId']+2), + trade_channel="MIXED",valid_since=time_now,valid_until=in5min, all_or_none=False) + return msg + + +# Future work: + async def limit_order(self) -> str: + return -1 + +#======================================================================================================================== +#=================================={Public Client Data & Utilities}====================================================== +#======================================================================================================================== + +# The public client consists of a set of global functions that facilitate interaction with the loopring ecosystem. +# most functions are built for data retrieval, some for data conversion. + +def get_depth(trade_pair, level=2, limit=50): + """ get depth - gets the market depth at the current time + Args: + trade_pair (str): the trade pair or market you wish to check + level: ... + limit: ... + + Returns: + json object representing current market depth + """ + API_URL = "https://api3.loopring.io/api/v3/depth?market=" + trade_pair + "&level=" + str(level) + "&limit=" + str(limit) + data = { + "Host": "api3.loopring.io", + "Connection": "keep-alive", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Accept-Language": "zh,en;q=0.9", + } + response = requests.get(API_URL, data) + depth = response.json() + return depth + + +# ---------------------------------------------------- + +def get_bids(trade_pair, level=1): + API_URL = "https://api3.loopring.io/api/v3/depth?market=" + trade_pair + "&level=" + str(level) + "&limit=50" + data = { + "Host": "api3.loopring.io", + "Connection": "keep-alive", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Accept-Language": "zh,en;q=0.9", + } + response = requests.get(API_URL, data) + depth = response.json() + bids = depth['bids'] + return bids + + +# ---------------------------------------------------- + +def get_asks(trade_pair, level=1): + API_URL = "https://api3.loopring.io/api/v3/depth?market=" + trade_pair + "&level=" + str(level) + "&limit=50" + data = { + "Host": "api3.loopring.io", + "Connection": "keep-alive", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Accept-Language": "zh,en;q=0.9", + } + response = requests.get(API_URL, data) + depth = response.json() + asks = depth['asks'] + return asks + + +# ================================================================== + +def get_ticker(trade_pair): + API_URL = "https://api3.loopring.io/api/v3/ticker?market=" + trade_pair + "" + data = { + "Host": "api3.loopring.io", + "Connection": "keep-alive", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Accept - Language": "zh, enq = 0.9", + } + response = requests.get(API_URL, data) + tickers = response.json() + ticker = tickers['tickers'][0] + return ticker + +# ================================================================== + +def get_relayer_time(): + API_URL = "https://api3.loopring.io/api/v3/timestamp" + data = { + "Host": "api3.loopring.io", + "Connection": "keep-alive", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Accept - Language": "zh, enq = 0.9", + } + response = requests.get(API_URL, data) + timestamp = response.json() + return timestamp['timestamp'] + + +# ================================================================== + +def get_trade_pairs(): + # get trade pairs on loopring exchange + API_URL = "https://api3.loopring.io/api/v3/exchange/markets" + data = { + "Host": "api3.loopring.io", + "Connection": "keep-alive", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Accept - Language": "zh, enq = 0.9", + } + response = requests.get(API_URL, data) + mkts = response.json() + mkt_list = mkts['markets'] + pairs = [''] * len(mkt_list) + for i in range(0, len(mkt_list)): + pairs[i] = mkt_list[i]['market'] + return pairs + + +# ================================================================== + +def get_token_id(asset): + # get trade pairs on loopring exchange + API_URL = "https://api3.loopring.io/api/v3/exchange/tokens" + data = { + "Host": "api3.loopring.io", + "Connection": "keep-alive", + "Accept": "*/*", + "Accept-Encoding": "gzip, deflate, br", + "Accept - Language": "zh, enq = 0.9", + } + response = requests.get(API_URL, data) + tkns = response.json() + tokenid = -1 + # tkn_list = tkns['tokens'] + for i in range(0, len(tkns)): + if tkns[i]['symbol'] == asset: + tokenid = tkns[i]['tokenId'] + + return tokenid +