Introduction
Leverj REST and Websocket API enable access to all features of the platform. The Kovan testnet, the Ropsten testnet, and the live sites are built entirely using this API and should be seen as one of the many possible platform implementations. The API could be leveraged to power a comprehensive set of tools and utilities, including automated trading clients and bots.
Language SDK/Libraries
Currently we support Python and Javascript (with node.js) for programmatic access.
Python
Javascript (Node.js)
Usage of these libraries will be illustrated in the following sections.
Spot & Futures Markets
Leverj provides access to two markets, the spot market and the derivatives market. The spot market supports ERC20 pairs. The derivatives market supports futures products. Perpetual swaps on BTC, ETH, and DEFI index are currently offered.
The API is very similar for both spot and futures markets, with minor differences to accomodate for product variations, market structure differences, and differing trading strategy possibilities between the two.
Scheme
The URL scheme like most REST apis consists of a BASE_URL and an endpoint.
REST URL = BASE_URL + ENDPOINT
Base URL
The BASE_URL is of the following format:
<protocol>://<host>/<market>/api/v1
For REST calls https
is the protocol, host depends on the environment, and market could be spot
or futures
.
The base url for spot REST API for the live site is https://live.leverj.io/spot/api/v1
. For Kovan testnet use https://kovan.leverj.io/spot/api/v1
.
For futures, the live site base url is https://live.leverj.io/futures/api/v1
and for Kovan it is https://kovan.leverj.io/futures/api/v1
.
To request an endpoint, append it to the base url and make a request.
Getting All Info
For example to access the /all/info
endpoint for the spot
market use https://live.leverj.io/spot/api/v1/all/info
. For futures
use https://live.leverj.io/futures/api/v1/all/info
.
curl https://live.leverj.io/spot/api/v1/all/info
import requests
response = requests.get('https://live.leverj.io/spot/api/v1/all/info')
response.status_code
response.json()
const axios = require('axios');
axios.get("https://live.leverj.io/spot/api/v1/all/info")
.then(function (response) {
console.log({ response });
});
URL parameters
Url parameters are denoted by prepending a colon:
/instrument/:symbol/chart/:timeframe
The parameters :symbol
and :timeframe
need to be filled in when making a REST call to server.
To get chart for a specific symbol with id L2ETH
for timeframe 5
, the actual url would be https://live.leverj.io/api/v1/instrument/L2ETH/chart/5
https://live.leverj.io/api/v1/instrument/LEVETH/chart/5
HTTP headers
HTTP 1.1 requires Host
header. In the examples here, we have used testnet host kovan.leverj.io
. For production use, you should change it to live.leverj.io
. You need Authorization
and Nonce
headers for protected resources. You may also add other appropriate headers, which are omitted here for brevity.
Authentication
Leverj uses a zero-knowledge authentication system. Leverj replaces the typical username and password based authentication scheme with a triplet of your account id, an apikey and a secret associated with the apikey. There is absolutely no need for the system to know about your account's private key. The apikey's secret is used to sign and confirm your identity but is not transfered over to the server either. The loginless system relies on ECDSA (Elliptic Curve Digital Signature Algorithm). The scheme involves signing message payload using an apikey's secret and subsequently using the elliptic curve signature elements to derive or recover the apikey. The recovered apikey is matched against the registered apikey. This pair of actions involving signing and recovery establishes trust and identity to facilitate authentication. Every request is authenticated using this mechanism.
You need to register online with the exchange to setup and download an apikey and its corresponding secret. When you register, you will be given an option to download your api key. You can also download the key at a later time by accessing it from your Leverj Gluon wallet. The api key contains your accountId, apikey, and secret. It will download as a json formatted file. An account can have multiple pairs of apikeys and their corresponding secrets.
Zero knowledge Authentication avoids setting session cookies and eliminates the following classes of attacks: session hijacking, some kinds of replay attacks, cookie sniffing, and some XSS and CSRF attacks. Not having the password or session id on the server mitigates some kinds of attacks due to server breach. Zero knowledge systems never send passwords or cookies and are also safer in case of information leak from TLS issues such as the Heartbleed bug.
Overview
Leverj authentication requires you to set Authorization
and Nonce
headers in HTTP for protected endpoints. This requires your account id, apikey and secret, which are in a JSON key file that you saved when you registered with the exchange and setup your apikey.
Authorization and Nonce headers
The syntax for Authorization
and Nonce
headers is as below.
Authorization: SIGN <account_id>.<apiKey>.<v>.<r>.<s>
Nonce: <unix_time>
v, r, and s are ECDSA related elements. r and s are normal outputs of an ECDSA signature and v is the extra byte or header byte that helps in public key recovery.
For example, to get all configuration information:
GET /api/v1/all/config HTTP/1.1
Host: test.leverj.io
Authorization: 'SIGN 0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F.0x12d80a4b0803Cf7D462EDF36963429B6aCfA3fFa.27.0x50eef94b262808ea42072dbe4375d0e908efea6e713b37b8ed729a63e56e4c23.0x4cf4c460baf43092b5350692179a833feb9dd855d6421570a2dc7d1f3325a952'
Nonce: 1550791374642
/all/config
is an open endpoint and you don't need an API key to access it but similar header and signature structures are used when getting protected information.
We provide transparent and seamless authentication support for node.js via an easy to use javascript module and suggest you use it instead of rolling out your own.
zka node module
The zka node module will do all the handshake and authentication and enables interaction with REST and socket API transparently. This may be used from a browser using browserify.
Remember to initialize and configure your zka instance before making calls to the API endpoints.
const zka = require("zka")(baseUrl, "/api/v1")
zka.init(accountId, apiKey, secret)
Authentication with zka
Nonce
To prevent replay attacks, all requests should include a nonce. The server expects UNIX time as the nonce. This requires a reasonably accurate clock on your client machine.
Nonce: 1550791374642
Clients with inaccurate clocks
If your client does not have an accurate clock or you are on an unusually slow network connection, you can compute the skew and apply it to all future requests using the Server-Time
header in the HTTP responses. The zka node module does skew adjustment automatically. The non-cacheable HTTP methods PUT
, POST
, DELETE
and OPTIONS
return a Server-Time
header.
Futures REST API
Unprotected REST API Endpoints
Unprotected endpoints do not require an Authorization
header.
General Exchange Data
Method | Rest Endpoint | Description |
---|---|---|
GET | /all/info | Last price, 24Hr volume, etc |
Exchange Configuration
Method | Rest Endpoint | Description |
---|---|---|
GET | /all/config | Get exchange configuration parameters |
Market Data
Method | Rest Endpoint | Description |
---|---|---|
GET | /instrument/:symbol/trade | Get recent trade data |
GET | /instrument/:symbol/chart/:timeframe | Get chart data for instrument |
GET | /instrument/:symbol/orderbook | Get order book for the instrument |
Futures WebSocket API
Leverj provides high speed and high throughput websocket based connectivity for all its endpoints. Use socket.io libraries to connect via websocket.
Here are a few language binding to consider:
- Python: python-socketio
- Javascript socket.io
When using websocket remember that the conversation occurs by means of emitting events and messages and listening to topics for data. When a client needs to communicate to the server it will emit an event with some data pertaining to the event. To receive data from the server the client listens on topics it is interested in and attaches callback functions to these listeners. When new data is published on such a topic, the client receives it and the client callback function is invoked. Client programs on receiving data could then proceed to use that data.
Connect & Register
As a first step, make sure to connect. You will need to connect to receive all and any data from websocket, including data from open endpoints. For protected endpoints, register in addition to connecting. You could think of registration as a login or authorization step.
Disconnect and unregister are actions that help cleanup and close the websocket connection.
Format & Helper Methods for Protected Endpoints
We will discuss details about each of the open and protected endpoints in the next few sections. In this section we touch upon an important pre-requisite for fetching protected data, i.e. the data that needs authentication.
Start by registering and downloading an APIKey. You will need an APIKey for each environment. You will have separate keys for testnet and the live environments. It's not the same. The APIKey is a JSON formatted text file that contains information on the following:
- accountId -- the Ethereum mainnet address
- apiKey -- identifier used to authorize and sign requests
- secret -- private or secret key for the apiKey
Collectively, this triplet are the credentials. Sometimes, it's also referred to in this documentation and in the example code as originator credentials. These credentials are available only to the user or caller of the API. Leverj has no access to the apiKey secret and cannot help in recovering it if you lose it. However, you are able to generate a new APIKey if you lose one.
When emitting a message to a server to fetch data from a protected endpoint you need to send event
and data
. event
is a concatenation of the corrsponding method
and uri
. As an example to create an order you use the event POST /order
. The data
part of the message that is emitted needs adherering to certain requirements in terms of format and signatures. There is a helper method that provides a utility method to generate event
and data
by passing in the request payload and the credentials.
Look at the protected_endpoint_request
function in leverj.websocket.util.py for an example implementation. You are welcome to use this method as is in your code or rewrite it in a a way that suits you.
Connect & Disconnect
Method | Action | Description |
---|---|---|
connect | Connect to websocket | |
disconnect | Disconnect |
Register & Un-Register
Method | Action | Description |
---|---|---|
GET | register | Register/authorize |
GET | unregister | Un-register/logout |
Open Websocket Topics
Unprotected endpoints do not require Authorization
.
Market Data
Method | Action | Description |
---|---|---|
orderbook | Order book data, updated every minute | |
difforderbook | Get change in order book | |
index | Fetch indices |
Protected Websocket Endpoints
All user specific endpoints require Authorization
and Nonce
information. These are included in the payload as headers.
Order
Method | Endpoint/Topic | Description |
---|---|---|
POST | /order | Create orders |
DELETE | /order | Delete specified orders |
order_execution | Order fills |
Listen to these topics to be notified as you add, update, and delete orders. Also, listen to order_error
.
Topic | Description |
---|---|
order_add | Create order |
order_update | Update order |
order_del | Cancel order |
order_error | Order error |
Position
Method | Endpoint/Topic | Description |
---|---|---|
position | Position updates | |
liquidation | Liquidation updates | |
adl | ADL updates |
Account
Method | Endpoint/Topic | Description |
---|---|---|
account_balance | Receive data on account balance |
Spot REST API
Unprotected REST API Endpoints
Unprotected endpoints do not require an Authorization
header.
General Exchange Data
Method | Rest Endpoint | Description |
---|---|---|
GET | /all/info | Last price, 24Hr volume, etc |
Exchange Configuration
Method | Rest Endpoint | Description |
---|---|---|
GET | /all/config | Get exchange configuration parameters |
Market Data
Method | Rest Endpoint | Description |
---|---|---|
GET | /instrument/:symbol/trade | Get recent trade data |
GET | /instrument/:symbol/chart/:timeframe | Get chart data for instrument |
GET | /instrument/:symbol/orderbook | Get order book for the instrument |
Protected REST API endpoints
All user specific endpoints require Authorization
and Nonce
headers as described in the Loginless or ZKA section
Open Orders
Method | Rest Endpoint | Description |
---|---|---|
GET | /order | Get all open orders |
POST | /order | Create orders |
PUT | /order | Update Orders |
DELETE | /order/:uuids | Delete specified orders |
Get account information: Orders and Trades
Method | Rest Endpoint | Description |
---|---|---|
GET | /account | |
GET | /account/execution | GET User's recent executions |
Examples (Futures)
Examples are structured as follows:
Section | Description |
---|---|
Endpoint | The /api endpoint |
Client Request | Request using the zka client |
Server Response | JSON Response payload (abbreviated to partial data sets for readability) |
Exchange Basic Info
GET /all/info
curl https://live.leverj.io/futures/api/v1/all/info
response = requests.get('https://live.leverj.io/futures/api/v1/all/info')
response.json()
zka.rest.get('/all/info').then(function(result) {console.log(result)})
Response
Returns information on
- last 24 hours volume,
- last price,
- bid,
- ask,
- funding rate, and
- index
for each of the perpetual swap instruments.
BTCUSD perpetual swap has id 1 and ETHUSD perpetual swap has id 2.
{
"1": {
"vol24H": {
"qty": 0,
"eth": 0,
"priceChange": 0,
"priceChangePercent": 0,
"instrument": "1"
},
"lastPrice": 15072,
"bid": 15532,
"ask": 15688,
"fundingRate": {
"rate": "2098600000000000000",
"start": 1605142800000,
"end": 1605146400000,
"instrument": "1",
"indexPrice": 15596,
"futurePrice": 15619
},
"index": {
"date": 1605143249,
"price": 15585,
"topic": "index_BTCUSD"
}
},
"2": {
"vol24H": {
"qty": 1.3,
"eth": 596.4,
"priceChange": -2.7,
"priceChangePercent": -0.5869565217391,
"instrument": "2"
},
"lastPrice": 457.3,
"bid": 455.1,
"ask": 459.7,
"fundingRate": {
"rate": "22000000000000000",
"start": 1605142800000,
"end": 1605146400000,
"instrument": "2",
"indexPrice": 457.8,
"futurePrice": 458.4
},
"index": {
"date": 1605143249,
"price": 457.52,
"topic": "index_ETHUSD"
}
}
}
Exchange config (format subject to frequent change)
GET /all/config
curl https://live.leverj.io/futures/api/v1/all/config
response = requests.get('https://live.leverj.io/futures/api/v1/all/config')
response.json()
zka.rest.get('/all/config').then(function(result) {console.log(result)})
Response
Returns configuration information about
- fee,
- smart contracts,
- instruments, and
- assets
Response
{
"config": {
"maxInputs": 50,
"maxOrdersCreateUpdate": 20,
"fee": {
"default": {
"maker": 0,
"taker": 5
},
"factor": 10000
},
"estimatedTokenTransferFee": "100000",
"network": {
"provider": "mainnet",
"etherscan": "https://etherscan.io",
"id": 1,
"appId": 3,
"gluon": "0x75ACe7a086eA0FB1a79e43Cc6331Ad053d8C67cB",
"legacyTokensExtension": "0xDA88EfA53c85Afa30564bb651A2E76b99a232082",
"lev": "0x0F4CA92660Efad97a9a70CB0fe969c755439772C",
"registryLogic": "0x385827aC8d1AC7B2960D4aBc303c843D9f87Bb0C",
"registryData": "0x0fC25C7931679B838209c484d49Df0Cb9E633C41",
"stakeLogic": "0xe517af2457E0dD285ed22Ee4440b265f203D1B0d",
"stakeLogicV1": "0x88Ac1E78b8a7D2B457Cb030978F71EdeE541bD5b",
"stakeData": "0xaB3AC436D66CBEeDc734ed2c1562c3a213c9bc77",
"derivativesLogic": "0xDfBFe895e07e5115773Cb9631CB2148114589caC",
"derivativesData": "0x563052914Fd973a2305763269A106a7B0B6D50Cc"
},
"minimumDepositConfirmations": 25,
"geofence": {
"block": ["US", "PR", "AS", "VI", "GU", "UM", "SC", "CU", "SY", "SD", "IR", "KP", "UA:40", "UA:43"]
},
"noSignup": false,
"maintenance": false,
"tradingDisabled": false,
"depositGasLimit": 160000,
"gasPriceMultiplier": 1.5,
"default": {
"instrument": "1"
},
"markets": {
"DAI": true
},
"legacyTokensExtensionSupport": {
"USDT": true
},
"defaultDisplayPair": "DAIUSDC"
},
"instruments": {
"1": {
"id": "1",
"symbol": "BTCUSD",
"name": "BTC/USD",
"status": "active",
"tickSize": 1,
"quoteSignificantDigits": 0,
"baseSignificantDigits": 4,
"baseSymbol": "BTC",
"quoteSymbol": "DAI",
"maxLeverage": 100,
"topic": "index_BTCUSD",
"quote": {
"name": "DAI",
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"symbol": "DAI",
"decimals": 18
}
},
"2": {
"id": "2",
"symbol": "ETHUSD",
"name": "ETH/USD",
"status": "active",
"tickSize": 0.1,
"quoteSignificantDigits": 1,
"baseSignificantDigits": 2,
"baseSymbol": "ETH",
"quoteSymbol": "DAI",
"maxLeverage": 50,
"topic": "index_ETHUSD",
"quote": {
"name": "DAI",
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"symbol": "DAI",
"decimals": 18
}
}
},
"assets": {
"DAI": {
"name": "DAI",
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"symbol": "DAI",
"decimals": 18
},
"L2": {
"name": "L2",
"address": "0xBbff34E47E559ef680067a6B1c980639EEb64D24",
"symbol": "L2",
"decimals": 18
}
},
"user": {
"ip": "<IP Address>",
"country": ["<country>", "<province>"]
}
}
Recent trade data for an instrument (e.g. LEVETH)
GET /instrument/:symbol/trade
zka.rest.get('/instrument/LEVETH/trade').then(function(result) {console.log(result)})
Response
[ { date: 1550710792,
price: 0.000102,
volume: 0.2,
instrument: 'LEVETH',
side: 'buy' },
{ date: 1550708634,
price: 0.000101,
volume: 1,
instrument: 'LEVETH',
side: 'buy' },
{ date: 1550687614,
price: 0.0001,
volume: 1,
instrument: 'LEVETH',
side: 'buy' },
{ date: 1550682148,
price: 0.0001,
volume: 92,
instrument: 'LEVETH',
side: 'sell' },
{ date: 1550682147,
price: 0.0001,
volume: 6,
instrument: 'LEVETH',
side: 'buy' },
{ date: 1550682142,
price: 0.0001,
volume: 94,
instrument: 'LEVETH',
side: 'sell' }
]
Chart for instrument
GET /instrument/:symbol/chart/:timeframe
zka.rest.get('/instrument/LEVETH/chart/5').then(function(result) {console.log(result)})
Response
[ { v: 0,
s: 0,
t: 1550794200,
o: 0.000102,
h: 0.000102,
l: 0.000102,
c: 0.000102,
i: 'LEVETH' },
{ v: 0,
s: 0,
t: 1550793900,
o: 0.000102,
h: 0.000102,
l: 0.000102,
c: 0.000102,
i: 'LEVETH' },
{ v: 0,
s: 0,
t: 1550793600,
o: 0.000102,
h: 0.000102,
l: 0.000102,
c: 0.000102,
i: 'LEVETH' },
{ v: 0,
s: 0,
t: 1550793300,
o: 0.000102,
h: 0.000102,
l: 0.000102,
c: 0.000102,
i: 'LEVETH' },
{ v: 0,
s: 0,
t: 1550793000,
o: 0.000102,
h: 0.000102,
l: 0.000102,
c: 0.000102,
i: 'LEVETH' },
{ v: 0,
s: 0,
t: 1550792700,
o: 0.000102,
h: 0.000102,
l: 0.000102,
c: 0.000102,
i: 'LEVETH' }
]
Order book for the instrument
GET /instrument/:symbol/orderbook
zka.rest.get('/instrument/LEVETH/orderbook').then(function(result) {console.log(result)})
Response
{ bid: 0.000093,
ask: 0.000102,
buy:
[ { price: 0.000093,
numberOfOrders: 1,
totalQuantity: 100,
instrument: 'LEVETH' },
{ price: 0.000092,
numberOfOrders: 1,
totalQuantity: 100,
instrument: 'LEVETH' }],
sell:
[ { price: 0.000102,
numberOfOrders: 1,
totalQuantity: 0.2,
instrument: 'LEVETH' },
{ price: 0.000103,
numberOfOrders: 1,
totalQuantity: 100,
instrument: 'LEVETH' }]
}
Get all open orders
GET /order
zka.rest.get('/order').then(function(result) {console.log(result)})
Response
[ { uuid: '77bb9a50-3639-11e9-aa67-1b934017d41a',
accountId: '0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F',
side: 'buy',
quantity: 9,
filled: 0,
averagePrice: 0,
cancelled: 0,
price: 0.001209,
entryTime: 1550795606133888,
eventTime: 1550795606133888,
status: 'open',
orderType: 'LMT',
instrument: 'LEVETH',
timestamp: 1550795605937000,
signature:
'0x2de825f6315f3a712f0b69c36b690918466889edcebbd86e086c4b5e8715c9fa2bd4e74bf0cb263d2c5464b7eba5777f94246989d4f98707c9f44aa891b4743900',
token: '0xAa7127e250E87476FdD253f15e86A4Ea9c4c4BD4',
clientOrderId: 'ac60c4f4-bd6b-40a4-ae1d-bbef604d0bb7',
originator: '0x12d80a4b0803Cf7D462EDF36963429B6aCfA3fFa' },
{ uuid: 'b7bf15a0-3639-11e9-b2cc-fe32bd3894ee',
accountId: '0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F',
side: 'buy',
quantity: 8,
filled: 0,
averagePrice: 0,
cancelled: 0,
price: 0.001213,
entryTime: 1550795713530585,
eventTime: 1550795713530585,
status: 'open',
orderType: 'LMT',
instrument: 'LEVETH',
timestamp: 1550795713339000,
signature:
'0x962f348852da325d8663d25ee922f67b278929d500b68234ee0e294d0b4f7e601a957c8798a7fb6d26fb2bc477d3c30293790d9a64e68a3e87d6ae433432b33d01',
token: '0xAa7127e250E87476FdD253f15e86A4Ea9c4c4BD4',
clientOrderId: 'a0da5818-b0a3-4e39-b8eb-f5f7f7384bfe',
originator: '0x12d80a4b0803Cf7D462EDF36963429B6aCfA3fFa' }
]
Fetch all futures order books
Listen on the topic: 'orderbook'
Connect to the socket endpoint and listen on "orderbook". A simple code illustration in python and a sample response is provided.
The entire orderbook is returned when listening on this topic. Filter down to the specific instrument as required. Remember that this feed updates every minute and gives you the entire order book. If you want to fetch this and also listen to order books as soon as there is any chance then listen to difforderbook
in addition to this topic.
The instruments currently supported are:
- 1 - BTCUSD (DAI)
- 2 - ETHUSD (DAI)
- 3 - BTCUSD (USDT)
- 4 - ETHUSD (USDT)
- 5 - DEFIUSD (USDT)
sio = socketio.Client(logger=False, engineio_logger=False)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')
sio.on("orderbook", on_orderbook)
def on_orderbook(data):
print(f'orderbook data: {data}')
Response
{
'1': {
'buy': [],
'sell': []
},
'2': {
'buy': [],
'sell': []
},
'3': {
'bid': 56904,
'ask': 56914,
'buy': [{
'price': 56904,
'numberOfOrders': 1,
'totalQuantity': 0.01,
'instrument': '3'
}, {
'price': 56899,
'numberOfOrders': 1,
'totalQuantity': 0.01,
'instrument': '3'
}, {
'price': 56894,
'numberOfOrders': 1,
'totalQuantity': 0.01,
'instrument': '3'
}, {
'price': 56889,
'numberOfOrders': 1,
'totalQuantity': 0.01,
'instrument': '3'
}, {
'price': 56884,
'numberOfOrders': 1,
'totalQuantity': 0.01,
'instrument': '3'
}],
'sell': [{
'price': 56914,
'numberOfOrders': 1,
'totalQuantity': 0.01,
'instrument': '3'
}, {
'price': 56919,
'numberOfOrders': 1,
'totalQuantity': 0.01,
'instrument': '3'
}, {
'price': 56924,
'numberOfOrders': 1,
'totalQuantity': 0.01,
'instrument': '3'
}, {
'price': 56929,
'numberOfOrders': 1,
'totalQuantity': 0.01,
'instrument': '3'
}, {
'price': 56934,
'numberOfOrders': 1,
'totalQuantity': 0.01,
'instrument': '3'
}]
},
'4': {
'bid': 1718.5,
'ask': 1720.5,
'buy': [{
'price': 1718.5,
'numberOfOrders': 1,
'totalQuantity': 1.65,
'instrument': '4'
}, {
'price': 1717.9,
'numberOfOrders': 1,
'totalQuantity': 0.57,
'instrument': '4'
}, {
'price': 1717.4,
'numberOfOrders': 1,
'totalQuantity': 0.22,
'instrument': '4'
}, {
'price': 1716.9,
'numberOfOrders': 1,
'totalQuantity': 0.04,
'instrument': '4'
}],
'sell': [{
'price': 1720.5,
'numberOfOrders': 1,
'totalQuantity': 1.65,
'instrument': '4'
}, {
'price': 1721,
'numberOfOrders': 1,
'totalQuantity': 0.57,
'instrument': '4'
}, {
'price': 1721.6,
'numberOfOrders': 1,
'totalQuantity': 0.22,
'instrument': '4'
}, {
'price': 1722.1,
'numberOfOrders': 1,
'totalQuantity': 0.04,
'instrument': '4'
}]
}
}
Fetch all updates to order book
Listen on the topic: 'difforderbook'
Connect to the socket endpoint and listen on "difforderbook". A simple code illustration in python and a sample response is provided.
Updates to orderbook are emitted as soon as it occurs. You could use the orderbook
feed to get snapshots of order books every minute and then use difforderbook
to listen to every update in the book between updates from the order book feed. Remember, you will only receive the delta. Filter down to the specific instrument as required.
The instruments currently supported are:
- 1 - BTCUSD (DAI)
- 2 - ETHUSD (DAI)
- 3 - BTCUSD (USDT)
- 4 - ETHUSD (USDT)
- 5 - DEFIUSD (USDT)
sio = socketio.Client(logger=False, engineio_logger=False)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')
sio.on("difforderbook", on_difforderbook)
def on_difforderbook(data):
print(f'difforderbook data: {data}')
Response
On testnet only the DEFIUSD market maker was quoting so you get difforderbook
updates as it adjusts the quotes.
{
'instrument': '5',
'buy': {
'490': {
'price': 490,
'instrument': {
'id': '5',
'symbol': 'DEFIUSDT',
'name': 'DEFI/USD (USDT)',
'status': 'active',
'tickSize': 0.1,
'quoteSignificantDigits': 1,
'baseSignificantDigits': 2,
'baseSymbol': 'DEFI',
'quoteSymbol': 'USDT',
'maxLeverage': 50,
'topic': 'index_DEFI',
'quote': {
'name': 'USDT',
'address': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44',
'symbol': 'USDT',
'decimals': 6
}
},
'numberOfOrders': 0,
'totalQuantity': 0
},
'490.5': {
'price': 490.5,
'instrument': {
'id': '5',
'symbol': 'DEFIUSDT',
'name': 'DEFI/USD (USDT)',
'status': 'active',
'tickSize': 0.1,
'quoteSignificantDigits': 1,
'baseSignificantDigits': 2,
'baseSymbol': 'DEFI',
'quoteSymbol': 'USDT',
'maxLeverage': 50,
'topic': 'index_DEFI',
'quote': {
'name': 'USDT',
'address': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44',
'symbol': 'USDT',
'decimals': 6
}
},
'numberOfOrders': 0,
'totalQuantity': 0
},
'489.4': {
'price': 489.4,
'instrument': {
'id': '5',
'symbol': 'DEFIUSDT',
'name': 'DEFI/USD (USDT)',
'status': 'active',
'tickSize': 0.1,
'quoteSignificantDigits': 1,
'baseSignificantDigits': 2,
'baseSymbol': 'DEFI',
'quoteSymbol': 'USDT',
'maxLeverage': 50,
'topic': 'index_DEFI',
'quote': {
'name': 'USDT',
'address': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44',
'symbol': 'USDT',
'decimals': 6
}
},
'numberOfOrders': 1,
'totalQuantity': 0.17
},
'489.5': {
'price': 489.5,
'instrument': {
'id': '5',
'symbol': 'DEFIUSDT',
'name': 'DEFI/USD (USDT)',
'status': 'active',
'tickSize': 0.1,
'quoteSignificantDigits': 1,
'baseSignificantDigits': 2,
'baseSymbol': 'DEFI',
'quoteSymbol': 'USDT',
'maxLeverage': 50,
'topic': 'index_DEFI',
'quote': {
'name': 'USDT',
'address': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44',
'symbol': 'USDT',
'decimals': 6
}
},
'numberOfOrders': 0,
'totalQuantity': 0
},
'489.9': {
'price': 489.9,
'instrument': {
'id': '5',
'symbol': 'DEFIUSDT',
'name': 'DEFI/USD (USDT)',
'status': 'active',
'tickSize': 0.1,
'quoteSignificantDigits': 1,
'baseSignificantDigits': 2,
'baseSymbol': 'DEFI',
'quoteSymbol': 'USDT',
'maxLeverage': 50,
'topic': 'index_DEFI',
'quote': {
'name': 'USDT',
'address': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44',
'symbol': 'USDT',
'decimals': 6
}
},
'numberOfOrders': 1,
'totalQuantity': 0.5
},
'490.4': {
'price': 490.4,
'instrument': {
'id': '5',
'symbol': 'DEFIUSDT',
'name': 'DEFI/USD (USDT)',
'status': 'active',
'tickSize': 0.1,
'quoteSignificantDigits': 1,
'baseSignificantDigits': 2,
'baseSymbol': 'DEFI',
'quoteSymbol': 'USDT',
'maxLeverage': 50,
'topic': 'index_DEFI',
'quote': {
'name': 'USDT',
'address': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44',
'symbol': 'USDT',
'decimals': 6
}
},
'numberOfOrders': 1,
'totalQuantity': 1.5
}
},
'sell': {
'493': {
'price': 493,
'instrument': {
'id': '5',
'symbol': 'DEFIUSDT',
'name': 'DEFI/USD (USDT)',
'status': 'active',
'tickSize': 0.1,
'quoteSignificantDigits': 1,
'baseSignificantDigits': 2,
'baseSymbol': 'DEFI',
'quoteSymbol': 'USDT',
'maxLeverage': 50,
'topic': 'index_DEFI',
'quote': {
'name': 'USDT',
'address': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44',
'symbol': 'USDT',
'decimals': 6
}
},
'numberOfOrders': 0,
'totalQuantity': 0
},
'492.4': {
'price': 492.4,
'instrument': {
'id': '5',
'symbol': 'DEFIUSDT',
'name': 'DEFI/USD (USDT)',
'status': 'active',
'tickSize': 0.1,
'quoteSignificantDigits': 1,
'baseSignificantDigits': 2,
'baseSymbol': 'DEFI',
'quoteSymbol': 'USDT',
'maxLeverage': 50,
'topic': 'index_DEFI',
'quote': {
'name': 'USDT',
'address': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44',
'symbol': 'USDT',
'decimals': 6
}
},
'numberOfOrders': 1,
'totalQuantity': 1.5
}
},
'bid': 490.4,
'ask': 492.4
}
Fetch indices
Listen on the topic: 'index'
Connect to the socket endpoint and listen on "index". A simple code illustration in python and a sample response is provided.
The value for all indices is returned in real time, as available. Filter down to the specific index as required.
The indices currently supported are:
- index_BTCUSD
- index_ETHUSD
- index_DEFI
sio = socketio.Client(logger=False, engineio_logger=False)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')
sio.on("index", on_index)
def on_index(data):
print(f'index data: {data}')
Response
on_index data: {'date': 1618952192, 'topic': 'index_BTCUSD'}
on_index data: {'date': 1618952192, 'topic': 'index_DEFI'}
on_index data: {'date': 1618952192, 'topic': 'index_ETHUSD'}
on_index data: {'date': 1618952192, 'topic': 'index_LINK'}
on_index data: {'date': 1618952193, 'price': 2309.6, 'topic': 'index_ETHUSD', 'stale': False}
on_index data: {'date': 1618952193, 'price': 56854.1, 'topic': 'index_BTCUSD', 'stale': False}
on_index data: {'date': 1618952194, 'price': 56852.6, 'topic': 'index_BTCUSD', 'stale': False}
on_index data: {'date': 1618952195, 'price': 2309.2, 'topic': 'index_ETHUSD', 'stale': False}
on_index data: {'date': 1618952195, 'price': 56847.6, 'topic': 'index_BTCUSD', 'stale': False}
on_index data: {'date': 1618952196, 'price': 451.1, 'topic': 'index_DEFI', 'stale': False}
Connect via websocket
Connect to Leverj socket.io endpoint
Connect using one of the socket.io language bindings. An example in Python is included.
As a first step instantiate a socket.io client. Register an event listener for the "connect" event. This would allow you to be alerted on a successful connection to the websocket endpoint.
Use the Leverj host and path to connect to the websocket endpoint.
For kovan testnet the host value is https://kovan.leverj.io
and path is /futures/socket.io
. For livenet the path is the same but the host changes to https://live.leverj.io
.
sio = socketio.Client(logger=False, engineio_logger=False)
sio.on("connect", on_connect)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')
def on_connect(data):
print('connected!)
Response
If connected successfully, you should see the message "connected!".
**** response ****
connected!
Disconnect from a websocket
Disconnect from a connected Leverj socket.io endpoint
Disconnect using one of the socket.io language bindings. An example in Python is included.
Register an event listener for the "disconnect" event. This would allow you to be alerted when you successfully disconnect from an existing websocket connection.
sio = socketio.Client(logger=False, engineio_logger=False)
sio.on("disconnect", on_disconnect)
sio.disconnect()
def on_disconnect(data):
print('disconnected!)
Response
If the socket connection is disconnected successfully, you should see the message "disconnected!".
**** response ****
disconnected!
Register
Register on a connected Leverj socket.io endpoint
Registration allows you to authenticate and listen to your own personal events from protected endpoints.
As a first step instantiate a socket.io client. Connect and then register.
Please look at the websocket client example and the utility functions in leverj.websocket.util.py.
def register(originator_credentials):
request_body = {"accountId": originator_credentials.get(
'accountId'), "apiKey": originator_credentials.get('apiKey')}
request = {
"method": "GET",
"uri": "/register",
"headers": {},
"body": json.dumps(request_body),
"params": {}
}
return request
request = register(originator_credentials)
# protected_endpoint_request util method adds required signature headers and formats the request
protected_request_payload = protected_endpoint_request(request, originator_credentials)
Response
You will not receive an explicit response on successful registration. If the registration fails you will not receive data from protected endpoints.
Unregister
Stop listening for events from Leverj socket.io protected endpoint
Unregister is analogous to logout.
Once you successfully unregister, you will stop listening to data from events for protected endpoints that need authorization.
def unregister(originator_credentials):
request_body = {"accountId": originator_credentials.get(
'accountId'), "apiKey": originator_credentials.get('apiKey')}
request = {
"method": "GET",
"uri": "/unregister",
"headers": {},
"body": json.dumps(request_body),
"params": {}
}
return request
request = unregister(originator_credentials)
# protected_endpoint_request util method adds required signature headers and formats the request
protected_request_payload = protected_endpoint_request(request, originator_credentials)
Response
You will not receive an explicit response on unregistration.
Create Order
Create an order and send it via websocket
Before you create an order, take a moment to look at the product specification at https://leverj.io/. Leverj Futures offers perpetual swap contracts. These are linear contracts, where margin and settelment is in stablecoins. Both DAI and USDT margined and settled contracts are available for BTCUSD and ETHUSD. The DEFIUSD contract is offered only with USDT as the margin and settlement currency.
Leverj supports a number of advanced order types. Please read Leverj Futures Order Types for details.
The following attributes are supported for an order:
Attribute/Property | Description |
---|---|
accountId | Account address of the trader. Credentials or APIKey has this information |
originator | APIKey. Credentials or APIKey has this information |
instrument | The instrument id |
price | Price at which you want to buy or sell. Adjust price precision basis supported quoteSignificantDigits for the instrument |
quantity | Amount you want to buy or sell. Adjust quantity precision basis supported baseSignificantDigits for the instrument |
marginPerFraction | Perpetual swaps are leveraged contracts so you could put down substantially less margin than the notional value you buy or sell. This is the margin in lowest denomination of base significant digits |
side | Order side. Choices are "buy" or "sell" |
orderType | Multiple order types are supported. LMT, MKT, SMKT, SLMT are supported |
timestamp | Timestamp when order is created. Make sure to have this in milliseconds |
quote | Address of the quote currency |
isPostOnly | Boolean value to flag is it's a post only order |
reduceOnly | Boolean value to flag is it's a reduce only order |
clientOrderId | User generated order id that could be used to query the status of an order |
triggerPrice | Trigger price when an order is activated. For example, a stop-limit orders gets activated only when the stop order trigger price is reached |
indexSanity | A sanity check to define what variance from the index should be allowed |
Once you create an order. You need to sign it. A signed order becomes the body of a request that needs to be transformed for a protected websocket request.
Once you have a protected request ready, you emit it from the websocket client to the Leverj endpoint for creating orders. Multiple orders can be created in a single call.
Example code for Python is included.
Start by looking at the order creation and signing functions. Then look at the method for created a protected request order creation payload. A working example is available in websocket_client_example.py.
def get_margin_per_fraction(self, orderInstrument, price, leverage):
estimated_entry_price = price
max_leverage = orderInstrument['maxLeverage']
if leverage > max_leverage:
self.logger.info(
f'You have specified a leverage of {leverage} but the max leverage allowed on this instrument is {max_leverage}.')
base_significant_digits = orderInstrument['baseSignificantDigits']
decimals = orderInstrument['quote']['decimals']
multiplier = Decimal(
pow(Decimal(10), Decimal(decimals - base_significant_digits)))
intermediate_value = Decimal((Decimal(
estimated_entry_price) * multiplier) / Decimal(leverage)).to_integral_exact()
return int(Decimal(intermediate_value) * Decimal(pow(Decimal(10), Decimal(base_significant_digits))))
def create_futures_order(self, side, price, triggerPrice, quantity, orderInstrument, orderType, leverage, orderAccountId, originatorApiKey, secret, reduceOnly=False):
price_precision = orderInstrument.get('quoteSignificantDigits')
quantity_precision = orderInstrument.get('baseSignificantDigits')
# default leverage is set to 1.0 which means you aren't using any leverage. If you want 5K DAI position to control 10K DAI worth of BTC, use leverage of 2
order = {
'accountId': orderAccountId,
'originator': originatorApiKey,
'instrument': orderInstrument['id'],
'price': round_with_precision(price, price_precision),
'quantity': round_with_precision(quantity, quantity_precision),
'marginPerFraction': str(self.get_margin_per_fraction(orderInstrument, price, leverage)),
'side': side,
'orderType': orderType,
'timestamp': int(time.time()*1000000),
'quote': orderInstrument['quote']['address'],
'isPostOnly': False,
'reduceOnly': reduceOnly,
'clientOrderId': 1,
'triggerPrice': round_with_precision(triggerPrice, price_precision),
'indexSanity': MAX_INDEX_VARIANCE
}
order['signature'] = futures.sign_order(
order, orderInstrument, secret)
return order
def create_order(originator_credentials):
client = Client('./resources/config/kovan.leverj.io/c21b18-64bdd3.json')
client.set_api_url('https://kovan.leverj.io/futures/api/v1')
all_config = client.get_all_config()
logging.debug(f'all info: {all_config}')
instruments = all_config.get('instruments')
BTCDAI_instrument = instruments.get('1')
print(f'BTCDAI_instrument: {BTCDAI_instrument}')
futures_order = client.create_futures_order('buy', 55394, 55394, 0.02, BTCDAI_instrument, 'LMT', 2.0, originator_credentials.get(
'accountId'), originator_credentials.get('apiKey'), originator_credentials.get('secret'))
print(f'futures_order: {futures_order}')
body = json.dumps([futures_order], separators=(',', ':'))
request = {
"method": "POST",
"uri": "/order",
"headers": {"requestid": str(uuid.uuid4())},
"body": body,
"params": {"instrument": BTCDAI_instrument.get('id')}
}
return request
request = create_order(originator_credentials)
# protected_endpoint_request util method adds required signature headers and formats the request
protected_request_payload = protected_endpoint_request(request, originator_credentials)
Response
On successful order creation you should receive the newly created order as data on the order_add
topic.
**** response ****
on_order_add: {'result': [{'uuid': '9c8e9a00-a304-11eb-be7d-fc18fda03052', 'accountId': '0xc21b183A8050D1988117B86408655ff974d021A0', 'side': 'buy', 'quantity': 0.02, 'filled': 0, 'averagePrice': 0, 'cancelled': 0, 'price': 53863, 'triggerPrice': 53863, 'marginPerFraction': '26931500000000000000000', 'entryTime': 1619052576672616, 'eventTime': 1619052576672616, 'status': 'open', 'orderType': 'LMT', 'instrument': '1', 'timestamp': 1619052575006805, 'signature': '0x015f2a9a3841cefc6d49e41d82f0f31fee1d6bea8bc77a14aebed36c834040ca70ebd4f94c4f50ca3bd2c93efda5bd36194924be978701d76742f84635bea4221c', 'clientOrderId': 1, 'originator': '0x64bdd35077Ce078aC7B87Cfb381d7F50059BDb16', 'isPostOnly': False, 'quote': '0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa', 'triggered': False, 'reduceOnly': False}]}
Cancel Order
Create an order cancel request and send it via websocket
You will need the uuid
for the orders you want to cancel. Once you have the uuids
of the orders, create a request body with the following payload:
request_body = [{"op": "remove", "value": uuids}]
uuids
are in a list. In the example python code there is only 1 element in this list.
Then generate a request dictionary or map as follows:
request = {
"method": "PATCH",
"uri": "/order",
"headers": {"requestid": str(uuid.uuid4())},
"body": json.dumps(request_body),
"params": {"instrument":
Format this request using the protected_endpoint_request function in leverj.websocket.util.py and then emit the event and data to send an order cancellation request to the server.
def cancel_order(uuids, originator_credentials):
client = Client('./resources/config/kovan.leverj.io/c21b18-64bdd3.json')
client.set_api_url('https://kovan.leverj.io/futures/api/v1')
all_config = client.get_all_config()
instruments = all_config.get('instruments')
BTCDAI_instrument = instruments.get('1')
request_body = [{"op": "remove", "value": uuids}]
request = {
"method": "PATCH",
"uri": "/order",
"headers": {"requestid": str(uuid.uuid4())},
"body": json.dumps(request_body),
"params": {"instrument": BTCDAI_instrument.get('symbol')}
}
return request
uuids = ['9c8e9a00-a304-11eb-be7d-fc18fda03052']
request = cancel_order(uuids, originator_credentials)
# protected_endpoint_request util method adds required signature headers and formats the request
protected_request_payload = protected_endpoint_request(request, originator_credentials)
print(protected_request_payload)
# emit from client to the connected websocket
websocket_client.sio.emit(protected_request_payload.get('event'), protected_request_payload.get('data'))
Response
Once the order is cancelled, you will receive a confirmation if you are listening to order_del
**** response ****
on_order_del: {'result': '9c8e9a00-a304-11eb-be7d-fc18fda03052'}
Position
Listen to position creation and any updates to an existing position
Make sure you are listening to position
after connecting and registering successfully. You will be notified when a new position is created or any of your position parameters is modified. For example, you will get a message when your margin or ranking changes.
sio = socketio.Client(logger=False, engineio_logger=False)
sio.on("position", on_account_balance)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')
def on_position(self, data):
print(f'on_position: {data}')
Response
If listening to position
you should see information about your current positions when you receive data on this topic.
**** response ****
on_position: {'accountId': '0xc21b183A8050D1988117B86408655ff974d021A0', 'instrument': '1', 'notional': '538630000000000000000', 'margin': '269315000000000000000', 'size': '0.01', 'liquidationPrice': '27201', 'ranking': 269315000000, 'bankruptcyPrice': '26932', 'eventTime': 1619114054305, 'reservedFees': '215452000000000000'}
Liquidation
Listen to liquidation events
Make sure you are listening to liquidation
after connecting and registering successfully. You will be notified when any of your positions is liquidated.
sio = socketio.Client(logger=False, engineio_logger=False)
sio.on("liquidation", on_account_balance)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')
def on_liquidation(self, data):
print(f'on_liquidation: {data}')
Response
If listening to liquidation
you should see information when positions are liquidated.
**** response ****
TODO
Auto Deleveraging (ADL)
Listen to ADL events
Make sure you are listening to adl
after connecting and registering successfully. You will be notified when any of your positions is auto-deleveraged.
sio = socketio.Client(logger=False, engineio_logger=False)
sio.on("adl", on_account_balance)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')
def on_adl(self, data):
print(f'on_adl: {data}')
Response
If listening to adl
you should see information when positions are auto-deleveraged.
**** response ****
TODO
Order Execution
Listen for fills as orders are partially or fully matched
Make sure you are listening to order_execution
after connecting and registering successfully.
sio = socketio.Client(logger=False, engineio_logger=False)
sio.on("order_execution", on_account_balance)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')
def on_order_execution(self, data):
print(f'on_order_execution: {data}')
Response
If listening to order_execution
you should see a message or data point every time an existing order is partially or fully matched. You could track each and every fill by listening on this topic.
Sample response shows data for a partial match. Order was for same price but for 0.04 as the quantity. 0.01 of that order was matched in this case. Use orderId
to match against open orders and track fills.
**** response ****
on_order_execution: {'instrument': '1', 'accountId': '0xc21b183A8050D1988117B86408655ff974d021A0', 'executionId': 'c0120ae0-a393-11eb-be58-92c3aa195f67', 'side': 'buy', 'orderType': 'LMT', 'orderId': '97ac02b0-a391-11eb-9afb-ab76d3b77c82', 'price': 53863, 'quantity': 0.01, 'commission': 0.107726, 'pnl': 0, 'eventTime': 1619114054286342}
Account Balance
Listen to account_balance
Make sure you are listening to account_balance
after connecting and registering successfully. You will receive an update on this topic whenever any of the balances changes. This happenes whenever funds are transferred in or out or deposits and withdrawals are made between the Gluon wallet and the mainnet account. It also happens when you lock funds to open orders.
sio = socketio.Client(logger=False, engineio_logger=False)
sio.on("account_balance", on_account_balance)
sio.connect('https://kovan.leverj.io', socketio_path='/futures/socket.io')
def on_account_balance(self, data):
print(f'on_account_balance: {data}')
Response
If listening to account_balance
you should see the entire set of balances when you receive data on this topic.
**** response ****
on_account_balance: {'0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa': {'accountId': '0xc21b183A8050D1988117B86408655ff974d021A0', 'assetAddress': '0x4F96Fe3b7A6Cf9725f59d353F723c1bDb64CA6Aa', 'symbol': 'DAI', 'plasma': '17558903955615588889151', 'available': '17558903955615588889151', 'pending': '0'}, '0x6a4480B1c08A822Fed4b907AD09798ED79312a44': {'accountId': '0xc21b183A8050D1988117B86408655ff974d021A0', 'assetAddress': '0x6a4480B1c08A822Fed4b907AD09798ED79312a44', 'symbol': 'USDT', 'plasma': '16507310354', 'available': '16507310354', 'pending': '0'}, '0xF9990Bf4FFbc423b8a492771658eAade8A1E72D6': {'accountId': '0xc21b183A8050D1988117B86408655ff974d021A0', 'assetAddress': '0xF9990Bf4FFbc423b8a492771658eAade8A1E72D6', 'symbol': 'L2', 'plasma': '5500000000000000000000', 'available': '5500000000000000000000', 'pending': '0'}}
Examples (Spot)
Examples are structured as follows:
Section | Description |
---|---|
Endpoint | The /api endpoint |
Client Request | Request using the zka client |
Server Response | JSON Response payload (abbreviated to partial data sets for readability) |
Exchange Basic Info
GET /all/info
curl https://live.leverj.io/spot/api/v1/all/info
response = requests.get('https://live.leverj.io/spot/api/v1/all/info')
response.json()
zka.rest.get('/all/info').then(function(result) {console.log(result)})
Response
Returns information on
- last 24 hours volume,
- last price,
- bid, and
- ask
for each of the spot pairs.
{ FEEETH:
{ vol24H: { qty: 0, eth: 0, instrument: 'FEEETH' },
bid: 0.000002 },
LEVETH:
{ vol24H: { qty: 1.2, eth: 0.0001214, instrument: 'LEVETH' },
lastPrice: 0.000102,
bid: 0.000093,
ask: 0.000102 }
}
Exchange config (format subject to frequent change)
GET /all/config
curl https://live.leverj.io/spot/api/v1/all/config
response = requests.get('https://live.leverj.io/spot/api/v1/all/config')
response.json()
zka.rest.get('/all/config').then(function(result) {console.log(result)})
Response
Returns configuration information about
- fee,
- smart contracts,
- instruments, and
- assets
{
"config": {
"maxInputs": 50,
"maxOrdersCreateUpdate": 50,
"fee": {
"default": {
"maker": 0,
"taker": 0
},
"factor": 10000,
"weiPerNanoFEE": 1000000,
"DAIUSDC": {
"maker": 0,
"taker": 0
}
},
"estimatedTokenTransferFee": "100000",
"network": {
"provider": "mainnet",
"etherscan": "https://etherscan.io",
"id": 1,
"appId": 2,
"gluon": "0x75ACe7a086eA0FB1a79e43Cc6331Ad053d8C67cB",
"legacyTokensExtension": "0xDA88EfA53c85Afa30564bb651A2E76b99a232082",
"lev": "0x0F4CA92660Efad97a9a70CB0fe969c755439772C",
"registryLogic": "0x385827aC8d1AC7B2960D4aBc303c843D9f87Bb0C",
"registryData": "0x0fC25C7931679B838209c484d49Df0Cb9E633C41",
"stakeLogic": "0xe517af2457E0dD285ed22Ee4440b265f203D1B0d",
"stakeLogicV1": "0x88Ac1E78b8a7D2B457Cb030978F71EdeE541bD5b",
"stakeData": "0xaB3AC436D66CBEeDc734ed2c1562c3a213c9bc77",
"spotLogic": "0x463cd03Db739B8A8c67adC8732f708A649478681",
"spotData": "0x0d283D685F0A741C463846176e4c8EFF90D3F9EC"
},
"geofence": {
"block": ["US", "PR", "AS", "VI", "GU", "UM", "SC", "CU", "SY", "SD", "IR", "KP", "UA:40", "UA:43"]
},
"noSignup": false,
"maintenance": false,
"tradingDisabled": false,
"depositGasLimit": 160000,
"gasPriceMultiplier": 1.5,
"default": {
"instrument": "LEVETH"
},
"minimumDepositConfirmations": 25,
"markets": {
"DAI": true,
"ETH": true
},
"legacyTokensExtensionSupport": {
"USDT": false
},
"defaultDisplayPair": "DAIUSDC"
},
"instruments": {
"L2ETH": {
"id": "L2ETH",
"symbol": "L2ETH",
"name": "L2/ETH",
"status": "active",
"ticksize": 6,
"ticksperpoint": 1000000,
"baseSignificantDigits": 1,
"quoteSignificantDigits": 6,
"base": {
"name": "L2",
"address": "0xBbff34E47E559ef680067a6B1c980639EEb64D24",
"symbol": "L2",
"decimals": 18
},
"quote": {
"name": "ETH",
"address": "0x0000000000000000000000000000000000000000",
"symbol": "ETH",
"decimals": 18
},
"convertSymbol": "ETH"
},
"LINKETH": {
"id": "LINKETH",
"symbol": "LINKETH",
"name": "LINK/ETH",
"status": "active",
"ticksize": 6,
"ticksperpoint": 1000000,
"baseSignificantDigits": 1,
"quoteSignificantDigits": 6,
"base": {
"name": "LINK",
"address": "0x514910771AF9Ca656af840dff83E8264EcF986CA",
"symbol": "LINK",
"decimals": 18
},
"quote": {
"name": "ETH",
"address": "0x0000000000000000000000000000000000000000",
"symbol": "ETH",
"decimals": 18
},
"convertSymbol": "ETH"
},
"ETHDAI": {
"id": "ETHDAI",
"symbol": "ETHDAI",
"name": "ETH/DAI",
"status": "active",
"ticksize": 2,
"ticksperpoint": 100,
"baseSignificantDigits": 4,
"quoteSignificantDigits": 2,
"base": {
"name": "ETH",
"address": "0x0000000000000000000000000000000000000000",
"symbol": "ETH",
"decimals": 18
},
"quote": {
"name": "DAI",
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"symbol": "DAI",
"decimals": 18
},
"convertSymbol": "ETH"
},
"DAIUSDC": {
"id": "DAIUSDC",
"symbol": "DAIUSDC",
"name": "DAI/USDC",
"status": "active",
"ticksize": 4,
"ticksperpoint": 10000,
"baseSignificantDigits": 4,
"quoteSignificantDigits": 4,
"base": {
"name": "DAI",
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"symbol": "DAI",
"decimals": 18
},
"quote": {
"name": "USDC",
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"symbol": "USDC",
"decimals": 6
},
"convertSymbol": "DAI"
}
},
"assets": {
"ETH": {
"name": "ETH",
"address": "0x0000000000000000000000000000000000000000",
"symbol": "ETH",
"decimals": 18
},
"LINK": {
"name": "LINK",
"address": "0x514910771AF9Ca656af840dff83E8264EcF986CA",
"symbol": "LINK",
"decimals": 18
},
"DAI": {
"name": "DAI",
"address": "0x6B175474E89094C44Da98b954EedeAC495271d0F",
"symbol": "DAI",
"decimals": 18
},
"USDC": {
"name": "USDC",
"address": "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48",
"symbol": "USDC",
"decimals": 6
},
"L2": {
"name": "L2",
"address": "0xBbff34E47E559ef680067a6B1c980639EEb64D24",
"symbol": "L2",
"decimals": 18
}
},
"user": {
"ip": "<IP Address>",
"country": ["<country>", "<province>"]
}
}
Recent trade data for an instrument (e.g. LEVETH)
GET /instrument/:symbol/trade
zka.rest.get('/instrument/LEVETH/trade').then(function(result) {console.log(result)})
Response
[ { date: 1550710792,
price: 0.000102,
volume: 0.2,
instrument: 'LEVETH',
side: 'buy' },
{ date: 1550708634,
price: 0.000101,
volume: 1,
instrument: 'LEVETH',
side: 'buy' },
{ date: 1550687614,
price: 0.0001,
volume: 1,
instrument: 'LEVETH',
side: 'buy' },
{ date: 1550682148,
price: 0.0001,
volume: 92,
instrument: 'LEVETH',
side: 'sell' },
{ date: 1550682147,
price: 0.0001,
volume: 6,
instrument: 'LEVETH',
side: 'buy' },
{ date: 1550682142,
price: 0.0001,
volume: 94,
instrument: 'LEVETH',
side: 'sell' }
]
Chart for instrument
GET /instrument/:symbol/chart/:timeframe
zka.rest.get('/instrument/LEVETH/chart/5').then(function(result) {console.log(result)})
Response
[ { v: 0,
s: 0,
t: 1550794200,
o: 0.000102,
h: 0.000102,
l: 0.000102,
c: 0.000102,
i: 'LEVETH' },
{ v: 0,
s: 0,
t: 1550793900,
o: 0.000102,
h: 0.000102,
l: 0.000102,
c: 0.000102,
i: 'LEVETH' },
{ v: 0,
s: 0,
t: 1550793600,
o: 0.000102,
h: 0.000102,
l: 0.000102,
c: 0.000102,
i: 'LEVETH' },
{ v: 0,
s: 0,
t: 1550793300,
o: 0.000102,
h: 0.000102,
l: 0.000102,
c: 0.000102,
i: 'LEVETH' },
{ v: 0,
s: 0,
t: 1550793000,
o: 0.000102,
h: 0.000102,
l: 0.000102,
c: 0.000102,
i: 'LEVETH' },
{ v: 0,
s: 0,
t: 1550792700,
o: 0.000102,
h: 0.000102,
l: 0.000102,
c: 0.000102,
i: 'LEVETH' }
]
Order book for the instrument
GET /instrument/:symbol/orderbook
zka.rest.get('/instrument/LEVETH/orderbook').then(function(result) {console.log(result)})
Response
{ bid: 0.000093,
ask: 0.000102,
buy:
[ { price: 0.000093,
numberOfOrders: 1,
totalQuantity: 100,
instrument: 'LEVETH' },
{ price: 0.000092,
numberOfOrders: 1,
totalQuantity: 100,
instrument: 'LEVETH' }],
sell:
[ { price: 0.000102,
numberOfOrders: 1,
totalQuantity: 0.2,
instrument: 'LEVETH' },
{ price: 0.000103,
numberOfOrders: 1,
totalQuantity: 100,
instrument: 'LEVETH' }]
}
Get all open orders
GET /order
zka.rest.get('/order').then(function(result) {console.log(result)})
Response
[ { uuid: '77bb9a50-3639-11e9-aa67-1b934017d41a',
accountId: '0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F',
side: 'buy',
quantity: 9,
filled: 0,
averagePrice: 0,
cancelled: 0,
price: 0.001209,
entryTime: 1550795606133888,
eventTime: 1550795606133888,
status: 'open',
orderType: 'LMT',
instrument: 'LEVETH',
timestamp: 1550795605937000,
signature:
'0x2de825f6315f3a712f0b69c36b690918466889edcebbd86e086c4b5e8715c9fa2bd4e74bf0cb263d2c5464b7eba5777f94246989d4f98707c9f44aa891b4743900',
token: '0xAa7127e250E87476FdD253f15e86A4Ea9c4c4BD4',
clientOrderId: 'ac60c4f4-bd6b-40a4-ae1d-bbef604d0bb7',
originator: '0x12d80a4b0803Cf7D462EDF36963429B6aCfA3fFa' },
{ uuid: 'b7bf15a0-3639-11e9-b2cc-fe32bd3894ee',
accountId: '0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F',
side: 'buy',
quantity: 8,
filled: 0,
averagePrice: 0,
cancelled: 0,
price: 0.001213,
entryTime: 1550795713530585,
eventTime: 1550795713530585,
status: 'open',
orderType: 'LMT',
instrument: 'LEVETH',
timestamp: 1550795713339000,
signature:
'0x962f348852da325d8663d25ee922f67b278929d500b68234ee0e294d0b4f7e601a957c8798a7fb6d26fb2bc477d3c30293790d9a64e68a3e87d6ae433432b33d01',
token: '0xAa7127e250E87476FdD253f15e86A4Ea9c4c4BD4',
clientOrderId: 'a0da5818-b0a3-4e39-b8eb-f5f7f7384bfe',
originator: '0x12d80a4b0803Cf7D462EDF36963429B6aCfA3fFa' }
]
Create New Order
POST /order
// install @leverj/adapter
// npm i @leverj/adapter
const adapter = require("@leverj/adapter/src/OrderAdapter")
// create new order object
function createNewOrder(side, price, quantity, orderInstrument, orderAccountId, secret) {
let order = {
orderType: 'LMT',
side,
price: price.toFixed(orderInstrument.baseSignificantDigits) - 0,
quantity: quantity.toFixed(orderInstrument.termSignificantDigits) - 0,
timestamp: Date.now() * 1e3,
accountId: orderAccountId,
token: orderInstrument.address,
instrument: orderInstrument.symbol
}
order.signature = adapter.sign(order, orderInstrument, secret)
return order
}
// get instrument information for LEVETH
// create LEVETH buy order
zka.rest.get('/all/config').then(function (result) {
const instruments = result.instruments
const LEVETH_instrument = instruments['LEVETH']
console.log(LEVETH_instrument)
const newOrder = createNewOrder('buy', 0.001229, 20, LEVETH_instrument, accountId, secret)
try {
zka.rest.post('/order', {}, [newOrder]).catch(console.error)
} catch (e) {
console.error(e)
}
})
User account
GET /account
zka.rest.get('/account').then(function(result) {console.log(result)})
Response
{ orders:
{ FEEETH: {},
LEVETH:
{ '77bb9a50-3639-11e9-aa67-1b934017d41a': [Object],
'b7bf15a0-3639-11e9-b2cc-fe32bd3894ee': [Object] } },
accountId: '0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F',
apiKeys: { '0x12d80a4b0803Cf7D462EDF36963429B6aCfA3fFa': true },
preference: {},
exited: {}
}
User Executions
GET /account/execution
zka.rest.get('/account/execution').then(function(result) {console.log(result)})
Response
[ { accountId: '0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F',
executionid: '7dd3a952-3639-11e9-bd86-99376ce577bd',
side: 'buy',
orderType: 'LMT',
orderid: '7d30b290-3639-11e9-8dbe-58e2283f8f2a',
price: 0.001228,
quantity: 1,
liquidity: 'commission',
commission: 0.000002456,
eventTime: 1550795616358063,
instrument: 'LEVETH' },
{ accountId: '0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F',
executionid: '7d9a22c2-3639-11e9-92a4-74585b482779',
side: 'buy',
orderType: 'LMT',
orderid: '7d30b290-3639-11e9-8dbe-58e2283f8f2a',
price: 0.001227,
quantity: 1,
liquidity: 'commission',
commission: 0.000002454,
eventTime: 1550795615980826,
instrument: 'LEVETH' },
{ accountId: '0xE239Caeb4A6eCe2567fa5307f6b5D95149a5188F',
executionid: '7d396520-3639-11e9-805a-71319d7431df',
side: 'buy',
orderType: 'LMT',
orderid: '7d30b290-3639-11e9-8dbe-58e2283f8f2a',
price: 0.001226,
quantity: 1,
liquidity: 'commission',
commission: 0.000002452,
eventTime: 1550795615346724,
instrument: 'LEVETH' }
]
Gluon
Errors
The Leverj API uses the standard HTTP error codes:
Error Code | Meaning |
---|---|
400 | Bad Request -- Usually syntax error in your HTTP request URI or headers |
401 | Unauthorized -- You are using an unregistered private key or your clock is way off |
404 | Not Found -- The specified resource could not be found |
405 | Method Not Allowed -- Resource does not support that method. Eg. PUT on a GET only resource |
406 | Not Acceptable -- You requested a format that isn't JSON |
410 | Gone -- Resource no longer supported |
429 | Too Many Requests -- You have exceed the rate limit |
500 | Internal Server Error -- We had a problem with our server. Try again later. |
503 | Service Unavailable -- We're temporarily offline for maintenance. Please try again later. |