Source code for idex.client

# coding=utf-8

import binascii
import codecs
import re
import requests
import time

from decimal import Decimal

from .exceptions import IdexException, IdexAPIException, IdexRequestException, IdexCurrencyNotFoundException
from .decorators import require_address, require_private_key
from .utils import sha3, ecsign, encode_int32


[docs]class BaseClient(object): API_URL = 'https://api.idex.market' _wallet_address = None _private_key = None _contract_address = None _currency_addresses = {}
[docs] def __init__(self, api_key=None, requests_params=None): """IDEX API Client constructor https://github.com/AuroraDAO/idex-api-docs :param api_key: optional - Wallet address :type api_key: address string :param requests_params: optional - Dictionary of requests params to use for all calls :type requests_params: dict. """ self._start_nonce = None self._client_started = int(time.time() * 1000) self._requests_params = requests_params self._last_response = None self._api_key = api_key self.session = self._init_session()
def _get_headers(self): return { 'Accept': 'application/json', 'User-Agent': 'python-idex', 'API-Key': self._api_key } def _get_nonce(self): """Get a unique nonce for request """ return self._start_nonce + int(time.time() * 1000) - self._client_started def _generate_signature(self, data): """Generate v, r, s values from payload """ # pack parameters based on type sig_str = b'' for d in data: val = d[1] if d[2] == 'address': # remove 0x prefix and convert to bytes val = val[2:].encode('utf-8') elif d[2] == 'uint256': # encode, pad and convert to bytes val = binascii.b2a_hex(encode_int32(int(d[1]))) sig_str += val # hash the packed string rawhash = sha3(codecs.decode(sig_str, 'hex_codec')) # salt the hashed packed string salted = sha3(u"\x19Ethereum Signed Message:\n32".encode('utf-8') + rawhash) # sign string v, r, s = ecsign(salted, codecs.decode(self._private_key[2:], 'hex_codec')) # pad r and s with 0 to 64 places return {'v': v, 'r': "{0:#0{1}x}".format(r, 66), 's': "{0:#0{1}x}".format(s, 66)} def _create_uri(self, path): return '{}/{}'.format(self.API_URL, path) def _get_request_kwargs(self, signed, **kwargs): kwargs['json'] = kwargs.get('json', {}) kwargs['headers'] = kwargs.get('headers', {}) # set default requests timeout kwargs['timeout'] = 10 # add our global requests params if self._requests_params: kwargs.update(self._requests_params) if signed: # generate signature e.g. {'v': 28 (or 27), 'r': '0x...', 's': '0x...'} kwargs['json'].update(self._generate_signature(kwargs['hash_data'])) # put hash_data into json param for name, value, _param_type in kwargs['hash_data']: kwargs['json'][name] = value # filter out contract address, not required if 'contract_address' in kwargs['json']: del (kwargs['json']['contract_address']) # remove the passed hash data del (kwargs['hash_data']) return kwargs def _init_session(self): pass
[docs] def set_wallet_address(self, address, private_key=None): """Set the wallet address. Optionally add the private_key, this is only required for trading. :param address: Address of the wallet to use :type address: address string :param private_key: optional - The private key for the address :type private_key: string .. code:: python client.set_wallet_address('0x925cfc20de3fcbdba2d6e7c75dbb1d0a3f93b8a3', 'priv_key...') :returns: nothing """ self._wallet_address = address.lower() nonce_res = self.get_my_next_nonce() self._start_nonce = nonce_res['nonce'] if private_key: if re.match(r"^0x[0-9a-zA-Z]{64}$", private_key) is None: raise(IdexException("Private key in invalid format must satisfy 0x[0-9a-zA-Z]{64}")) self._private_key = private_key
[docs] def get_wallet_address(self): """Get the wallet address .. code:: python address = client.get_wallet_address() :returns: address string """ return self._wallet_address
[docs] def get_last_response(self): """Get the last response object for inspection .. code:: python response = client.get_last_response() :returns: response objects """ return self._last_response
@staticmethod def _num_to_decimal(number): if type(number) == float: number = Decimal(repr(number)) elif type(number) == int: number = Decimal(number) elif type(number) == str: number = Decimal(number) return number @staticmethod def _parse_from_currency_quantity(currency_details, quantity): if currency_details is None: return None f_q = Decimal(quantity) if 'decimals' not in currency_details: return f_q # divide by currency_details['decimals'] d_str = "1{}".format(("0" * currency_details['decimals'])) res = f_q / Decimal(d_str) return res def _convert_to_currency_quantity(self, currency_details, quantity): if currency_details is None: return None f_q = self._num_to_decimal(quantity) if 'decimals' not in currency_details: return f_q # multiply by currency_details['decimals'] m_str = "1{}".format(("0" * currency_details['decimals'])) res = (f_q * Decimal(m_str)).to_integral_exact() return '{:d}'.format(int(res))
[docs]class Client(BaseClient):
[docs] def __init__(self, api_key, address=None, private_key=None): """ :param api_key: :type api_key: string :param address: optional - Wallet address :type address: address string :param private_key: optional - The private key for the address :type private_key: string .. code:: python api_key = 'kjdfiaadmad' client = Client(api_key=api_key) # with wallet address and private key address = '0x925cfc20de3fcbdba2d6e7c75dbb1d0a3f93b8a3' private_key = 'priv_key...' client = Client(api_key=api_key, address=address, private_key=private_key) """ super(Client, self).__init__(api_key) if address: self.set_wallet_address(address, private_key)
def _init_session(self): session = requests.session() session.headers.update(self._get_headers()) return session def _request(self, method, path, signed, **kwargs): kwargs = self._get_request_kwargs(signed, **kwargs) uri = self._create_uri(path) response = getattr(self.session, method)(uri, **kwargs) self._last_response = response return self._handle_response(response) @staticmethod def _handle_response(response): """Internal helper for handling API responses from the Quoine server. Raises the appropriate exceptions when necessary; otherwise, returns the response. """ if not str(response.status_code).startswith('2'): raise IdexAPIException(response, response.status_code, response.text) try: res = response.json() if 'error' in res: raise IdexAPIException(response, response.status_code, response.text) return res except ValueError: raise IdexRequestException('Invalid Response: %s' % response.text) def _get(self, path, signed=False, **kwargs): return self._request('get', path, signed, **kwargs) def _post(self, path, signed=False, **kwargs): return self._request('post', path, signed, **kwargs) def _put(self, path, signed=False, **kwargs): return self._request('put', path, signed, **kwargs) def _delete(self, path, signed=False, **kwargs): return self._request('delete', path, signed, **kwargs) # Market Endpoints
[docs] def get_tickers(self): """Get all market tickers Please note: If any field is unavailable due to a lack of trade history or a lack of 24hr data, the field will be set to 'N/A'. percentChange, baseVolume, and quoteVolume will never be 'N/A' but may be 0. https://github.com/AuroraDAO/idex-api-docs#returnticker .. code:: python tickers = client.get_tickers() :returns: API Response .. code-block:: python { ETH_SAN: { last: '0.000981', high: '0.0010763', low: '0.0009777', lowestAsk: '0.00098151', highestBid: '0.0007853', percentChange: '-1.83619353', baseVolume: '7.3922603247161', quoteVolume: '7462.998433' }, ETH_LINK: { last: '0.001', high: '0.0014', low: '0.001', lowestAsk: '0.002', highestBid: '0.001', percentChange: '-28.57142857', baseVolume: '13.651606265667369466', quoteVolume: '9765.891979953083752189' } # all possible markets follow ... } :raises: IdexResponseException, IdexAPIException """ return self._post('returnTicker')
[docs] def get_ticker(self, market): """Get ticker for selected market Please note: If any field is unavailable due to a lack of trade history or a lack of 24hr data, the field will be set to 'N/A'. percentChange, baseVolume, and quoteVolume will never be 'N/A' but may be 0. https://github.com/AuroraDAO/idex-api-docs#returnticker :param market: Name of market e.g. ETH_SAN :type market: string .. code:: python ticker = client.get_ticker('ETH_SAN') :returns: API Response .. code-block:: python { last: '0.000981', high: '0.0010763', low: '0.0009777', lowestAsk: '0.00098151', highestBid: '0.0007853', percentChange: '-1.83619353', baseVolume: '7.3922603247161', quoteVolume: '7462.998433' } :raises: IdexResponseException, IdexAPIException """ data = { 'market': market } return self._post('returnTicker', False, json=data)
[docs] def get_24hr_volume(self): """Get all market tickers https://github.com/AuroraDAO/idex-api-docs#return24volume .. code:: python volume = client.get_24hr_volume() :returns: API Response .. code-block:: python { ETH_REP: { ETH: '1.3429046745', REP: '105.29046745' }, ETH_DVIP: { ETH: '4', DVIP: '4' }, totalETH: '5.3429046745' } :raises: IdexResponseException, IdexAPIException """ return self._post('return24Volume')
[docs] def get_order_book(self, market, count=1): """Get order book for selected market Each market returned will have an asks and bids property containing all the sell orders and buy orders sorted by best price. Order objects will contain a price amount total and orderHash property but also a params property which will contain additional data about the order useful for filling or verifying it. https://github.com/AuroraDAO/idex-api-docs#returnorderbook :param market: Name of market e.g. ETH_SAN :type market: string :param count: Number of items to return :type count: int .. code:: python orderbook = client.get_order_book('ETH_SAN') :returns: API Response .. code-block:: python { asks: [ { price: '2', amount: '1', total: '2', orderHash: '0x6aee6591def621a435dd86eafa32dfc534d4baa38d715988d6f23f3e2f20a29a', params: { tokenBuy: '0x0000000000000000000000000000000000000000', buySymbol: 'ETH', buyPrecision: 18, amountBuy: '2000000000000000000', tokenSell: '0xf59fad2879fb8380ffa6049a48abf9c9959b3b5c', sellSymbol: 'DVIP', sellPrecision: 8, amountSell: '100000000', expires: 190000, nonce: 164, user: '0xca82b7b95604f70b3ff5c6ede797a28b11b47d63' } } ], bids: [ { price: '1', amount: '2', total: '2', orderHash: '0x9ba97cfc6d8e0f9a72e9d26c377be6632f79eaf4d87ac52a2b3d715003b6536e', params: { tokenBuy: '0xf59fad2879fb8380ffa6049a48abf9c9959b3b5c', buySymbol: 'DVIP', buyPrecision: 8, amountBuy: '200000000', tokenSell: '0x0000000000000000000000000000000000000000', sellSymbol: 'ETH', sellPrecision: 18, amountSell: '2000000000000000000', expires: 190000, nonce: 151, user: '0xca82b7b95604f70b3ff5c6ede797a28b11b47d63' } } ] } :raises: IdexResponseException, IdexAPIException """ data = { 'market': market, 'count': count } return self._post('returnOrderBook', False, json=data)
[docs] def get_open_orders(self, market, address, count=10, cursor=None): """Get the open orders for a given market and address Output is similar to the output for get_order_book() except that orders are not sorted by type or price, but are rather displayed in the order of insertion. As is the case with get_order_book( there is a params property of the response value that contains details on the order which can help with verifying its authenticity. https://github.com/AuroraDAO/idex-api-docs#returnopenorders :param market: Name of market e.g. ETH_SAN :type market: string :param address: Address to return open orders associated with :type address: address string :param count: amount of results to return :type count: int :param cursor: For pagination. Provide the value returned in the idex-next-cursor HTTP header to request the next slice (or page) :type cursor: str .. code:: python orders = client.get_open_orders( 'ETH_SAN', '0xca82b7b95604f70b3ff5c6ede797a28b11b47d63') :returns: API Response .. code-block:: python [ { orderNumber: 1412, orderHash: '0xf1bbc500af8d411b0096ac62bc9b60e97024ad8b9ea170340ff0ecfa03536417', price: '2.3', amount: '1.2', total: '2.76', type: 'sell', params: { tokenBuy: '0x0000000000000000000000000000000000000000', buySymbol: 'ETH', buyPrecision: 18, amountBuy: '2760000000000000000', tokenSell: '0xf59fad2879fb8380ffa6049a48abf9c9959b3b5c', sellSymbol: 'DVIP', sellPrecision: 8, amountSell: '120000000', expires: 190000, nonce: 166, user: '0xca82b7b95604f70b3ff5c6ede797a28b11b47d63' } }, { orderNumber: 1413, orderHash: '0x62748b55e1106f3f453d51f9b95282593ef5ce03c22f3235536cf63a1476d5e4', price: '2.98', amount: '1.2', total: '3.576', type: 'sell', params:{ tokenBuy: '0x0000000000000000000000000000000000000000', buySymbol: 'ETH', buyPrecision: 18, amountBuy: '3576000000000000000', tokenSell: '0xf59fad2879fb8380ffa6049a48abf9c9959b3b5c', sellSymbol: 'DVIP', sellPrecision: 8, amountSell: '120000000', expires: 190000, nonce: 168, user: '0xca82b7b95604f70b3ff5c6ede797a28b11b47d63' } } ] :raises: IdexResponseException, IdexAPIException """ data = { 'market': market, 'address': address, 'count': count } if cursor: data['cursor'] = cursor return self._post('returnOpenOrders', False, json=data)
@require_address def get_my_open_orders(self, market, count=10, cursor=None): """Get your open orders for a given market Output is similar to the output for get_order_book() except that orders are not sorted by type or price, but are rather displayed in the order of insertion. As is the case with get_order_book( there is a params property of the response value that contains details on the order which can help with verifying its authenticity. https://github.com/AuroraDAO/idex-api-docs#returnopenorders :param market: Name of market e.g. ETH_SAN :type market: string :param count: amount of results to return :type count: int :param cursor: For pagination. Provide the value returned in the idex-next-cursor HTTP header to request the next slice (or page) :type cursor: str .. code:: python orders = client.get_my_open_orders('ETH_SAN') :returns: API Response .. code-block:: python [ { orderNumber: 1412, orderHash: '0xf1bbc500af8d411b0096ac62bc9b60e97024ad8b9ea170340ff0ecfa03536417', price: '2.3', amount: '1.2', total: '2.76', type: 'sell', params: { tokenBuy: '0x0000000000000000000000000000000000000000', buySymbol: 'ETH', buyPrecision: 18, amountBuy: '2760000000000000000', tokenSell: '0xf59fad2879fb8380ffa6049a48abf9c9959b3b5c', sellSymbol: 'DVIP', sellPrecision: 8, amountSell: '120000000', expires: 190000, nonce: 166, user: '0xca82b7b95604f70b3ff5c6ede797a28b11b47d63' } }, { orderNumber: 1413, orderHash: '0x62748b55e1106f3f453d51f9b95282593ef5ce03c22f3235536cf63a1476d5e4', price: '2.98', amount: '1.2', total: '3.576', type: 'sell', params:{ tokenBuy: '0x0000000000000000000000000000000000000000', buySymbol: 'ETH', buyPrecision: 18, amountBuy: '3576000000000000000', tokenSell: '0xf59fad2879fb8380ffa6049a48abf9c9959b3b5c', sellSymbol: 'DVIP', sellPrecision: 8, amountSell: '120000000', expires: 190000, nonce: 168, user: '0xca82b7b95604f70b3ff5c6ede797a28b11b47d63' } } ] :raises: IdexWalletAddressNotFoundException, IdexResponseException, IdexAPIException """ return self.get_open_orders(market, self._wallet_address, count, cursor)
[docs] def get_order_status(self, order_hash): """Returns a single order https://docs.idex.market/#operation/returnOrderStatus :param order_hash: The order hash to query for associated trades :type order_hash: 256-bit hex string .. code:: python status = client.get_order_status('0xca82b7b95604f70b3ff5c6ede797a28b11b47d63') :returns: API Response .. code-block:: python { "timestamp": 1516415000, "market": "ETH_AURA", "orderNumber": 2101, "orderHash": "0x3fe808be7b5df3747e5534056e9ff45ead5b1fcace430d7b4092e5fcd7161e21", "price": "0.000129032258064516", "amount": "3100", "total": "0.4", "type": "buy", "params": { "tokenBuy": "0x7c5a0ce9267ed19b22f8cae653f198e3e8daf098", "buyPrecision": 18, "amountBuy": "3100000000000000000000", "tokenSell": "0x0000000000000000000000000000000000000000", "sellPrecision": 18, "amountSell": "400000000000000000", "expires": 100000, "nonce": "1", "user": "0x57b080554ebafc8b17f4a6fd090c18fc8c9188a0" }, "filled": "1900", "initialAmount": "5000", "status": "open" } :raises: IdexResponseException, IdexAPIException """ data = { 'orderHash': order_hash } return self._post('returnOrderStatus', False, json=data)
[docs] def get_trade_history(self, market=None, address=None, start=None, end=None, count=10, sort='desc', cursor=None): """Get the past 200 trades for a given market and address, or up to 10000 trades between a range specified in UNIX timetsamps by the "start" and "end" properties of your JSON input. https://github.com/AuroraDAO/idex-api-docs#returntradehistory :param market: optional - will return an array of trade objects for the market, if omitted, will return an object of arrays of trade objects keyed by each market :type market: string :param address: optional - If specified, return value will only include trades that involve the address as the maker or taker. :type address: address string :param start: optional - The inclusive UNIX timestamp (seconds since epoch) marking the earliest trade that will be returned in the response, (Default - 0) :type start: int :param end: optional - The inclusive UNIX timestamp marking the latest trade that will be returned in the response. (Default - current timestamp) :type end: int :param count: optional - Number of records to be returned per request. Valid range: 1 .. 100 :type count: int :param sort: optional - Possible values are asc (oldest first) and desc (newest first). Defaults to desc. :type sort: string :param cursor: optional - For pagination. Provide the value returned in the idex-next-cursor HTTP header to request the next slice (or page). This endpoint uses the tid property of a record for the cursor. :type cursor: string .. code:: python trades = client.get_trade_history() # get trades for the last 2 hours for ETH EOS market start = int(time.time()) - (60 * 2) # 2 hours ago trades = client.get_trade_history(market='ETH_EOS', start=start) :returns: API Response .. code-block:: python { ETH_REP: [ { date: '2017-10-11 21:41:15', amount: '0.3', type: 'buy', total: '1', price: '0.3', orderHash: '0x600c405c44d30086771ac0bd9b455de08813127ff0c56017202c95df190169ae', uuid: 'e8719a10-aecc-11e7-9535-3b8451fd4699', transactionHash: '0x28b945b586a5929c69337929533e04794d488c2d6e1122b7b915705d0dff8bb6' } ] } :raises: IdexResponseException, IdexAPIException """ data = {} if market: data['market'] = market if address: data['address'] = address if start: data['start'] = start if end: data['end'] = end if count: data['count'] = count if sort: data['sort'] = sort if cursor: data['cursor'] = cursor return self._post('returnTradeHistory', False, json=data)
@require_address def get_my_trade_history(self, market=None, start=None, end=None, count=10, sort='desc', cursor=None): """Get your past 200 trades for a given market, or up to 10000 trades between a range specified in UNIX timestamps by the "start" and "end" properties of your JSON input. https://github.com/AuroraDAO/idex-api-docs#returntradehistory :param market: optional - will return an array of trade objects for the market, if omitted, will return an object of arrays of trade objects keyed by each market :type market: string :param start: optional - The inclusive UNIX timestamp (seconds since epoch) marking the earliest trade that will be returned in the response, (Default - 0) :type start: int :param end: optional - The inclusive UNIX timestamp marking the latest trade that will be returned in the response. (Default - current timestamp) :type end: int :param count: optional - Number of records to be returned per request. Valid range: 1 .. 100 :type count: int :param sort: optional - Possible values are asc (oldest first) and desc (newest first). Defaults to desc. :type sort: string :param cursor: optional - For pagination. Provide the value returned in the idex-next-cursor HTTP header to request the next slice (or page). This endpoint uses the tid property of a record for the cursor. :type cursor: string .. code:: python trades = client.get_my_trade_history() # get trades for the last 2 hours for ETH EOS market start = int(time.time()) - (60 * 2) # 2 hours ago trades = client.get_my_trade_history(market='ETH_EOS', start=start) :returns: API Response .. code-block:: python { ETH_REP: [ { date: '2017-10-11 21:41:15', amount: '0.3', type: 'buy', total: '1', price: '0.3', orderHash: '0x600c405c44d30086771ac0bd9b455de08813127ff0c56017202c95df190169ae', uuid: 'e8719a10-aecc-11e7-9535-3b8451fd4699', transactionHash: '0x28b945b586a5929c69337929533e04794d488c2d6e1122b7b915705d0dff8bb6' } ] } :raises: IdexWalletAddressNotFoundException, IdexResponseException, IdexAPIException """ return self.get_trade_history(market, self._wallet_address, start, end, count, sort, cursor)
[docs] def get_currencies(self): """Get token data indexed by symbol https://github.com/AuroraDAO/idex-api-docs#returncurrencies .. code:: python currencies = client.get_currencies() :returns: API Response .. code-block:: python { ETH: { decimals: 18, address: '0x0000000000000000000000000000000000000000', name: 'Ether' }, REP: { decimals: 8, address: '0xc853ba17650d32daba343294998ea4e33e7a48b9', name: 'Reputation' }, DVIP: { decimals: 8, address: '0xf59fad2879fb8380ffa6049a48abf9c9959b3b5c', name: 'Aurora' } } :raises: IdexResponseException, IdexAPIException """ return self._post('returnCurrencies')
[docs] def get_currency(self, currency): """Get the details for a particular currency using it's token name or address :param currency: Name of the currency e.g. EOS or '0x7c5a0ce9267ed19b22f8cae653f198e3e8daf098' :type currency: string or hex string .. code:: python # using token name currency = client.get_currency('REP') # using the address string currency = client.get_currency('0xc853ba17650d32daba343294998ea4e33e7a48b9') :returns: .. code-block:: python { decimals: 8, address: '0xc853ba17650d32daba343294998ea4e33e7a48b9', name: 'Reputation' } :raises: IdexCurrencyNotFoundException, IdexResponseException, IdexAPIException """ if currency not in self._currency_addresses: self._currency_addresses = self.get_currencies() res = None if currency[:2] == '0x': for token, c in self._currency_addresses.items(): if c['address'] == currency: res = c break # check if we found the currency if res is None: raise IdexCurrencyNotFoundException(currency) else: if currency not in self._currency_addresses: raise IdexCurrencyNotFoundException(currency) res = self._currency_addresses[currency] return res
[docs] def get_balances(self, address, complete=False): """Get available balances for an address (total deposited minus amount in open orders) indexed by token symbol. https://github.com/AuroraDAO/idex-api-docs#returnbalances :param address: Address to query balances of :type address: address string :param complete: Include available balances along with the amount you have in open orders for each token (Default False) :param complete: bool .. code:: python balances = client.get_balances('0xca82b7b95604f70b3ff5c6ede797a28b11b47d63') :returns: API Response .. code-block:: python # Without complete details { REP: '25.55306545', DVIP: '200000000.31012358' } # With complete details { REP: { available: '25.55306545', onOrders: '0' }, DVIP: { available: '200000000.31012358', onOrders: '0' } } :raises: IdexResponseException, IdexAPIException """ data = { 'address': address } path = 'returnBalances' if complete: path = 'returnCompleteBalances' return self._post(path, False, json=data)
@require_address def get_my_balances(self, complete=False): """Get your available balances (total deposited minus amount in open orders) indexed by token symbol. https://github.com/AuroraDAO/idex-api-docs#returnbalances :param complete: Include available balances along with the amount you have in open orders for each token (Default False) :param complete: bool .. code:: python balances = client.get_my_balances() :returns: API Response .. code-block:: python # Without complete details { REP: '25.55306545', DVIP: '200000000.31012358' } # With complete details { REP: { available: '25.55306545', onOrders: '0' }, DVIP: { available: '200000000.31012358', onOrders: '0' } } :raises: IdexWalletAddressNotFoundException, IdexResponseException, IdexAPIException """ return self.get_balances(self._wallet_address, complete)
[docs] def get_transfers(self, address, start=None, end=None): """Returns the deposit and withdrawal history for an address within a range, specified by the "start" and "end" properties of the JSON input, both of which must be UNIX timestamps. Withdrawals can be marked as "PENDING" if they are queued for dispatch, "PROCESSING" if the transaction has been dispatched, and "COMPLETE" if the transaction has been mined. https://github.com/AuroraDAO/idex-api-docs#returndepositswithdrawals :param address: Address to query deposit/withdrawal history for :type address: address string :param start: optional - Inclusive starting UNIX timestamp of returned results (Default - 0) :type start: int :param end: optional - Inclusive ending UNIX timestamp of returned results (Default - current timestamp) :type end: int .. code:: python transfers = client.get_transfers('0xca82b7b95604f70b3ff5c6ede797a28b11b47d63') :returns: API Response .. code-block:: python { deposits: [ { depositNumber: 265, currency: 'ETH', amount: '4.5', timestamp: 1506550595, transactionHash: '0x52897291dba0a7b255ee7a27a8ca44a9e8d6919ca14f917616444bf974c48897' } ], withdrawals: [ { withdrawalNumber: 174, currency: 'ETH', amount: '4.5', timestamp: 1506552152, transactionHash: '0xe52e9c569fe659556d1e56d8cca2084db0b452cd889f55ec3b4e2f3af61faa57', status: 'COMPLETE' } ] } :raises: IdexResponseException, IdexAPIException """ data = { 'address': address } if start: data['start'] = start if end: data['end'] = end return self._post('returnDepositsWithdrawals', False, json=data)
@require_address def get_my_transfers(self, start=None, end=None): """Returns your deposit and withdrawal history within a range, specified by the "start" and "end" properties of the JSON input, both of which must be UNIX timestamps. Withdrawals can be marked as "PENDING" if they are queued for dispatch, "PROCESSING" if the transaction has been dispatched, and "COMPLETE" if the transaction has been mined. https://github.com/AuroraDAO/idex-api-docs#returndepositswithdrawals :param start: optional - Inclusive starting UNIX timestamp of returned results (Default - 0) :type start: int :param end: optional - Inclusive ending UNIX timestamp of returned results (Default - current timestamp) :type end: int .. code:: python transfers = client.get_transfers('0xca82b7b95604f70b3ff5c6ede797a28b11b47d63') :returns: API Response .. code-block:: python { deposits: [ { depositNumber: 265, currency: 'ETH', amount: '4.5', timestamp: 1506550595, transactionHash: '0x52897291dba0a7b255ee7a27a8ca44a9e8d6919ca14f917616444bf974c48897' } ], withdrawals: [ { withdrawalNumber: 174, currency: 'ETH', amount: '4.5', timestamp: 1506552152, transactionHash: '0xe52e9c569fe659556d1e56d8cca2084db0b452cd889f55ec3b4e2f3af61faa57', status: 'COMPLETE' } ] } :raises: IdexWalletAddressNotFoundException, IdexResponseException, IdexAPIException """ return self.get_transfers(self._wallet_address, start, end)
[docs] def get_order_trades(self, order_hash): """Get all trades involving a given order hash, specified by the order_hash https://github.com/AuroraDAO/idex-api-docs#returnordertrades :param order_hash: The order hash to query for associated trades :type order_hash: 256-bit hex string .. code:: python trades = client.get_order_trades('0x62748b55e1106f3f453d51f9b95282593ef5ce03c22f3235536cf63a1476d5e4') :returns: API Response .. code-block:: python [ { date: '2017-10-11 21:41:15', amount: '0.3', type: 'buy', total: '1', price: '0.3', uuid: 'e8719a10-aecc-11e7-9535-3b8451fd4699', transactionHash: '0x28b945b586a5929c69337929533e04794d488c2d6e1122b7b915705d0dff8bb6' } ] :raises: IdexResponseException, IdexAPIException """ data = { 'orderHash': order_hash } return self._post('returnOrderTrades', False, json=data)
[docs] def get_next_nonce(self, address): """Get the lowest nonce that you can use from the given address in one of the trade functions https://github.com/AuroraDAO/idex-api-docs#returnnextnonce :param address: The address to query for the next nonce to use :type address: address string .. code:: python nonce = client.get_next_nonce('0xf59fad2879fb8380ffa6049a48abf9c9959b3b5c') :returns: API Response .. code-block:: python { nonce: 2650 } :raises: IdexResponseException, IdexAPIException """ data = { 'address': address } return self._post('returnNextNonce', False, json=data)
@require_address def get_my_next_nonce(self): """Get the lowest nonce that you can use in one of the trade functions https://github.com/AuroraDAO/idex-api-docs#returnnextnonce .. code:: python nonce = client.get_next_nonce('0xf59fad2879fb8380ffa6049a48abf9c9959b3b5c') :returns: API Response .. code-block:: python { nonce: 2650 } :raises: IdexWalletAddressNotFoundException, IdexResponseException, IdexAPIException """ return self.get_next_nonce(self._wallet_address) def _get_contract_address(self): """Get a cached contract address value """ if not self._contract_address: res = self.get_contract_address() self._contract_address = res['address'] return self._contract_address
[docs] def get_contract_address(self): """Get the contract address used for depositing, withdrawing, and posting orders https://github.com/AuroraDAO/idex-api-docs#returncontractaddress .. code:: python trades = client.get_contract_address() :returns: API Response .. code-block:: python { address: '0x2a0c0dbecc7e4d658f48e01e3fa353f44050c208' } :raises: IdexResponseException, IdexAPIException """ return self._post('returnContractAddress')
# Trade Endpoints
[docs] def parse_from_currency_quantity(self, currency, quantity): """Convert a quantity string to a float :param currency: Name of currency e.g EOS :type currency: string :param quantity: Quantity value as string '3100000000000000000000' :type quantity: string :returns: decimal """ currency_details = self.get_currency(currency) return self._parse_from_currency_quantity(currency_details, quantity)
[docs] def convert_to_currency_quantity(self, currency, quantity): """Convert a float quantity to the correct decimal places :param currency: Name or address of currency e.g EOS or '0x7c5a0ce9267ed19b22f8cae653f198e3e8daf098' :type currency: string :param quantity: Quantity value 4.234298924 prefer Decimal or string, int or float should work :type quantity: Decimal, string, int, float """ currency_details = self.get_currency(currency) return self._convert_to_currency_quantity(currency_details, quantity)
@require_address def create_order(self, token_buy, token_sell, price, quantity): """Create a limit order :param token_buy: The name or address of the token you will receive as a result of the trade e.g. ETH or '0x7c5a0ce9267ed19b22f8cae653f198e3e8daf098' :type token_buy: string :param token_sell: The name or address of the token you will lose as a result of the trade e.g. EOS or '0x7c5a0ce9267ed19b22f8cae653f198e3e8daf098' :type token_sell: string :param price: The price in token_sell you want to purchase the new token for :type price: Decimal, string, int or float :param quantity: The amount of token_buy you will receive when the order is fully filled :type quantity: Decimal, string, int or float .. code:: python ticker = client.create_order( 'EOS', 'ETH', '0.000123', '31200.324') :returns: API Response .. code-block:: python { orderNumber: 2101, orderHash: '0x3fe808be7b5df3747e5534056e9ff45ead5b1fcace430d7b4092e5fcd7161e21', price: '0.000129032258064516', amount: '3100', total: '0.4', type: 'buy', params: { tokenBuy: '0x7c5a0ce9267ed19b22f8cae653f198e3e8daf098', buyPrecision: 18, amountBuy: '3100000000000000000000', tokenSell: '0x0000000000000000000000000000000000000000', sellPrecision: 18, amountSell: '400000000000000000', expires: 100000, nonce: 1, user: '0x57b080554ebafc8b17f4a6fd090c18fc8c9188a0' } } :raises: IdexWalletAddressNotFoundException, IdexPrivateKeyNotFoundException, IdexResponseException, IdexAPIException """ # convert buy and sell amounts based on decimals price = self._num_to_decimal(price) quantity = self._num_to_decimal(quantity) sell_quantity = price * quantity amount_buy = self.convert_to_currency_quantity(token_buy, quantity) amount_sell = self.convert_to_currency_quantity(token_sell, sell_quantity) return self.create_order_wei(token_buy, token_sell, amount_buy, amount_sell) @require_address @require_private_key def create_order_wei(self, token_buy, token_sell, amount_buy, amount_sell): """Create a limit order using buy and sell amounts as integer value precision matching that token :param token_buy: The name or address of the token you will receive as a result of the trade e.g. ETH or '0x7c5a0ce9267ed19b22f8cae653f198e3e8daf098' :type token_buy: string :param token_sell: The name or address of the token you will lose as a result of the trade e.g. EOS or '0x7c5a0ce9267ed19b22f8cae653f198e3e8daf098' :type token_sell: string :param amount_buy: The amount of token_buy you will receive when the order is fully filled :type amount_buy: Decimal, string :param amount_sell: The amount of token_sell you are selling :type amount_sell: Decimal, string .. code:: python ticker = client.create_order_gwei( 'EOS', 'ETH', '3100000000000000000000', '400000000000000000') :returns: API Response .. code-block:: python { orderNumber: 2101, orderHash: '0x3fe808be7b5df3747e5534056e9ff45ead5b1fcace430d7b4092e5fcd7161e21', price: '0.000129032258064516', amount: '3100', total: '0.4', type: 'buy', params: { tokenBuy: '0x7c5a0ce9267ed19b22f8cae653f198e3e8daf098', buyPrecision: 18, amountBuy: '3100000000000000000000', tokenSell: '0x0000000000000000000000000000000000000000', sellPrecision: 18, amountSell: '400000000000000000', expires: 100000, nonce: 1, user: '0x57b080554ebafc8b17f4a6fd090c18fc8c9188a0' } } :raises: IdexWalletAddressNotFoundException, IdexPrivateKeyNotFoundException, IdexResponseException, IdexAPIException """ contract_address = self._get_contract_address() buy_currency = self.get_currency(token_buy) sell_currency = self.get_currency(token_sell) hash_data = [ ['contractAddress', contract_address, 'address'], ['tokenBuy', buy_currency['address'], 'address'], ['amountBuy', amount_buy, 'uint256'], ['tokenSell', sell_currency['address'], 'address'], ['amountSell', amount_sell, 'uint256'], ['expires', '10000', 'uint256'], ['nonce', self._get_nonce(), 'uint256'], ['address', self._wallet_address, 'address'], ] return self._post('order', True, hash_data=hash_data) @require_address @require_private_key def create_trade(self, order_hash, token, amount): """Make a trade TODO: Allow multiple orders to be filled :param order_hash: This is the raw hash of the order you are filling. The orderHash property of an order can be retrieved from the API calls which return orders, for higher security the has can be derived from the order parameters. :type order_hash: string e.g ‘0xcfe4018c59e50e0e1964c979e6213ce5eb8c751cbc98a44251eb48a0985adc52’ :param token: The name or address of the token you are filling the order with. In the order it's the tokenBuy token :type token: string or hex string e.g 'EOS' or '0x7c5a0ce9267ed19b22f8cae653f198e3e8daf098' :param amount: This is the amount of the order you are filling e.g 0.2354 :type amount: Decimal, string, int or float .. code:: python trades = client.create_trade( '0xcfe4018c59e50e0e1964c979e6213ce5eb8c751cbc98a44251eb48a0985adc52', 'ETH', '1.23') :returns: API Response .. code-block:: python [ { amount: '0.07', date: '2017-10-13 16:25:36', total: '0.01', market: 'ETH_DVIP', type: 'buy', price: '7', orderHash: '0xcfe4018c59e50e0e1964c979e6213ce5eb8c751cbc98a44251eb48a0985adc52', uuid: '250d51a0-b033-11e7-9984-a9ab79bb8f35' } ] :raises: IdexWalletAddressNotFoundException, IdexPrivateKeyNotFoundException, IdexResponseException, IdexAPIException """ amount_trade = self.convert_to_currency_quantity(token, amount) hash_data = [ ['orderHash', order_hash, 'address'], ['amount', amount_trade, 'uint256'], ['address', self._wallet_address, 'address'], ['nonce', self._get_nonce(), 'uint256'], ] return self._post('trade', True, hash_data=hash_data) @require_address @require_private_key def cancel_order(self, order_hash): """Cancel an order :param order_hash: The raw hash of the order you are cancelling :type order_hash: .. code:: python status = client.cancel_order('0xcfe4018c59e50e0e1964c979e6213ce5eb8c751cbc98a44251eb48a0985adc52') :returns: API Response .. code-block:: python { success: 1 } :raises: IdexWalletAddressNotFoundException, IdexPrivateKeyNotFoundException, IdexResponseException, IdexAPIException """ hash_data = [ ['orderHash', order_hash, 'address'], ['nonce', self._get_nonce(), 'uint256'], ] json_data = { 'address': self._wallet_address } return self._post('cancel', True, hash_data=hash_data, json=json_data) # Withdraw Endpoints @require_address @require_private_key def withdraw(self, amount, token): """Withdraw funds from IDEX to your wallet address :param amount: The amount of token you want to withdraw :type amount: Decimal, string :param token: The name or address of the token you are withdrawing. In the order it's the tokenBuy token :type token: string or hex string e.g 'EOS' or '0x7c5a0ce9267ed19b22f8cae653f198e3e8daf098' .. code:: python status = client.withdraw('1000.32', 'EOS') :returns: API Response :raises: IdexWalletAddressNotFoundException, IdexPrivateKeyNotFoundException, IdexResponseException, IdexAPIException """ contract_address = self._get_contract_address() currency = self.get_currency(token) # convert amount amount = self.convert_to_currency_quantity(token, amount) hash_data = [ ['contractAddress', contract_address, 'address'], ['token', currency['address'], 'address'], ['amount', amount, 'uint256'], ['address', self._wallet_address, 'address'], ['nonce', self._get_nonce(), 'uint256'], ] return self._post('withdraw', True, hash_data=hash_data)