Introduction
Welcome to Levidge trader and developer documentation. These documents outline exchange functionality, market details, and APIs.
APIs are separated into two categories: trading and feed. Trading APIs require authentication and provide access to placing orders and other account information. Feed APIs provide market data and are public.
General
Market overview and general information.
Matching Engine
Levidge operates a continuous first-come, first-serve order book. Orders are executed in price-time priority as received by the matching engine.
Example
User A places a Buy order for 1 BTC at 100 USD. User B then wishes to sell 1 BTC at 80 USD. Because User A’s order was first to the trading engine, they will have price priority and the trade will occur at 100 USD.
Fees
Trading Fees
Levidge operates a maker-taker model. Orders which provide liquidity are charged different fees from orders taking liquidity. The fee is assessed as a percentage of the match amount (price * size). Maker and taker fees are calculated hourly based on the user’s 30d USD-equivalent trading volume:
Crypto and Perpetual Contracts
Contract | Tier | Maker | Taker | |||
Crypto | 1 | 0.15 % | 0.30 % | |||
Crypto | 2 | 0.10 % | 0.20 % | |||
Crypto | 3 | 0 % | 0.15 % |
Other Perpetual Contracts
Contract | Tier | Maker | Taker | |||
Other | 1 | 0.25 % | 0.50 % | |||
Other | 2 | 0.15 % | 0.35 % | |||
Other | 3 | 0 % | 0.25 % |
Perpetual Swap Funding Rate
Levidge charges a perpetual swap funding rate, calculated from the contract premium over an index price converted to BTC. Perpetual swap fees are paid to the counterparty contract holders.HOW TAKER FEES ARE CALCULATED
Your taker fee is based upon total USD trading volume across all Order Books over the trailing 30 day period. For example, a purchase of 1 BTC for $10,000 on the BTC-USD book will count as $10,000 towards your 30 day USD volume.
HOW VOLUMES ARE CALCULATED ON NON-USD BOOKS
Transactions made on non-USD books are converted to USD based on the most recent fill price on the corresponding USD book. For example, a purchase of 1 ETH for 0.1 BTC on the ETH-BTC book will count as the most recent fill price of 1 ETH on the ETH-USD Order Book.
Deposit/Withdraw Fees
Levidge does not charge any additional deposit fees, and only charges network transaction fees to withdraw
Client Libraries
Client libraries can help you integrate with our API quickly.
Official
Unofficial
API
The REST API has endpoints for account and order management as well as public market data.
REST API Endpoint URL
https://levidge.com/api/
There is also a FIX API for order management.
Requests
All requests and responses are application/json
content type and follow typical HTTP response status codes for success and failure.
Errors
{
"error": "Invalid userId"
}
Unless otherwise stated, errors to bad requests will respond with HTTP 4xx or status codes. The body will also contain a message
parameter indicating the cause. Your language’s http library should be configured to provide message bodies for non-2xx requests so that you can read the message field from the body.
Common error codes
Status Code | Reason |
---|---|
400 | Bad Request – Invalid request format |
401 | Unauthorized – Invalid API Key |
403 | Forbidden – You do not have access to the requested resource |
404 | Not Found |
500 | Internal Server Error – We had a problem with our server |
Success
A successful response is indicated by HTTP status code 200 and may contain an optional body. If the response has a body it will be documented under each resource below.
Rate Limits
When a rate limit is exceeded, a status of 429 Too Many Requests
will be returned.
REST API
Public endpoints
We throttle public endpoints by IP: 3 requests per second, up to 6 requests per second in bursts.
Private endpoints
We throttle private endpoints by user ID: 5 requests per second, up to 10 requests per second in bursts.
Financial Information eXchange API
The FIX API throttles the number of incoming messages to 50 commands per second.
Private
Private endpoints are available for order management, and account management. Every private request must be signed using the described authentication scheme.
Authentication
Generating an API Key
Before being able to sign any requests, you must create an API key via the Levidge website. Upon creating a key you will have 4 pieces of information which you must remember:
- Username
- Password
- RequestToken
- RequestSecret
The RequestToken and RequestSecret will be randomly generated and provided by Levidge.
API Key Permissions
A separate paid of RequestToken, and RequestSecret will be generated for withdrawal requests
- View - Allows a key read permissions. This includes all GET endpoints.
- Transfer - Allows a key to transfer currency on behalf of an account, including deposits and withdraws. Enable with caution - API key transfers WILL BYPASS two-factor authentication.
- Trade - Allows a key to enter orders, as well as retrieve trade data. This includes POST /orders and several GET endpoints.
Please refer to documentation below to see what API key permissions are required for a specific route.
Creating a Request
All REST requests must contain the following headers using HTTP POST:
requestToken
The API RequestTokensignature
hex(HMAC_SHA384(base64(payload), key=RequestSecret))
All request bodies should have content type application/json
and be valid JSON.
Signing a Message
import CryptoJS from 'crypto-js';
import hmacSHA384 from 'crypto-js/hmac-sha384';
const paramsString = JSON.stringify(params);
const signature = hmacSHA384(
paramsString,
this.state.requestSecret
).toString();
Utils.postData('/order/', params, this.state.requestToken, signature)
.then(data => {
console.log(data);
})
.catch(error => {
alert(error.message);
});
};
To sign the payload, decode it to base64, and generate the HMAC SHA using the private key
Selecting a Timestamp
The CB-ACCESS-TIMESTAMP
header MUST be number of seconds since Unix Epoch in UTC. Decimal values are allowed.
Your timestamp must be within 30 seconds of the api service time or your request will be considered expired and rejected. We recommend using the time endpoint to query for the API server time if you believe there many be time skew between your server and the API servers.
Accounts
Logon
{"login":"test","password":"123456"}
Logon
HTTP Request
POST /logon
API Key Permissions
This endpoint requires either the “view” or “trade” permission.
Logon Input Fields
Field | Type | Description |
---|---|---|
id | int | User ID |
login | String | the user login |
password | String | the user password |
code | int | *required for 2fa users, otherwise ignored |
Logon Output Fields
Field | Type | Description |
---|---|---|
id | int | User ID |
requestToken | String | the user requestToken |
requestSecret | String | the user requestSecret |
Get User Profile
{
"id": 10250474,
"login": "traderabc",
"password": "pwd123",
"firstname": "John",
"middlename": "Lee",
"lastname": "Doe",
"email": "johndoe555@gmail.com"
}
Get the user's profile information
HTTP request
POST /getUserProfile
API Key Permissions
This endpoint requires either the “view” or “trade” permission.
Input Fields
Field | Type | Description |
---|---|---|
userId | int | User ID |
Output Fields
Field | Type | Description |
---|---|---|
id | int | User ID |
login | String | |
password | String | |
firstname | String | |
middlename | String | |
lastname | String | |
String | ||
type | String | |
status | String | |
createDate | String | |
referralCode | String | |
referredByCode | String | |
phone | String | |
company | String | |
country | String | |
city | String | |
state | String | |
zip | String | |
address1 | String | |
address2 | String | |
address3 | String | |
registeredIP | String | |
lastIP | String | |
firmId | int | |
feeTier | int | |
birthdate | String | |
taxid | String | |
String | ||
String | ||
skype | String | |
photoUrl | String |
Orders
Place a New Order
{
"userId": 1045725,
"instrumentId": 5,
"side": 1,
"ordType": 2,
"symbol": "SPY/USD",
"quantity": 125,
"quantity_scale": 2,
"price": 245,
"price_scale": 2
}
Response
Data fields use enums defined in the FIX 4.4 standard
You can place two types of orders: limit
and market
. Orders can only be placed if your account has sufficient funds. Once an order is placed, your account funds will be put on hold for the duration of the order. How much and which funds are put on hold depends on the order type and parameters specified. See the Holds
details below.
HTTP Request
POST /order
API Key Permissions
This endpoint requires the “trade” permission.
Parameters
These parameters are common to all order types. Depending on the order type, additional parameters will be required (see below).
Field | Type | Description |
---|---|---|
userId | int | User ID |
instrumentId | int | |
symbol | String | |
clOrdId | String | |
side | int | 1 = Buy, 2 = Sell |
ordType | int | 1 = Market, 2 = Limit, 3 = Stop, 4 = Stop limit |
price | int | |
price_scale | int | |
quantity | int | |
quantity_scale | int | |
stopPx | int | (not required unless stop order) |
stopPx_scale | int | (not required unless stop order) |
timeInForce | int | (not required, defaults to 1 = Good Till Cancel (GTC)) |
Cancel Order
{
"origOrderId": 954751144,
"userId": 1045725,
"instrumentId": 5,
"side": 1,
"ordType": 2,
"symbol": "SPY/USD",
"quantity": 125,
"quantity_scale": 2,
"price": 245,
"price_scale": 2
}
Response
Cancel a previously placed order.
Data fields use enums defined in the FIX 4.4 standard
You can place two types of orders: limit
and market
. Orders can only be placed if your account has sufficient funds. Once an order is placed, your account funds will be put on hold for the duration of the order. How much and which funds are put on hold depends on the order type and parameters specified. See the Holds
details below.
HTTP Request
POST /cancelOrder
API Key Permissions
This endpoint requires the “trade” permission.
Parameters
These parameters are common to all order types. Depending on the order type, additional parameters will be required (see below).
Field | Type | Description |
---|---|---|
origOrderId | int | original order Id |
userId | int | User ID |
instrumentId | int | |
symbol | String | |
clOrdId | String | |
side | int | 1 = Buy, 2 = Sell |
ordType | int | 1 = Market, 2 = Limit, 3 = Stop, 4 = Stop limit |
price | int | |
price_scale | int | |
quantity | int | |
quantity_scale | int | |
stopPx | int | (not required unless stop order) |
stopPx_scale | int | (not required unless stop order) |
timeInForce | int | (not required, defaults to 1 = Good Till Cancel (GTC)) |
Cancel Reject
If the order could not be canceled (already filled or previously canceled, etc), then an error response will indicate the reason in the message
field.
Cancel Replace
With best effort, cancel an order and replace it.
{
"origOrderId": 954751144,
"userId": 1045725,
"instrumentId": 5,
"side": 1,
"ordType": 2,
"symbol": "SPY/USD",
"quantity": 125,
"quantity_scale": 2,
"price": 245,
"price_scale": 2,
"quantity2": 125,
"quantity2_scale": 2,
"price2": 245,
"price2_scale": 2
}
Response
Cancel a previously placed order.
Data fields use enums defined in the FIX 4.4 standard
You can place two types of orders: limit
and market
. Orders can only be placed if your account has sufficient funds. Once an order is placed, your account funds will be put on hold for the duration of the order. How much and which funds are put on hold depends on the order type and parameters specified. See the Holds
details below.
HTTP Request
POST /cancelOrder
API Key Permissions
This endpoint requires the “trade” permission.
Parameters
These parameters are common to all order types. Depending on the order type, additional parameters will be required (see below).
Field | Type | Description |
---|---|---|
origOrderId | int | original order Id |
userId | int | User ID |
instrumentId | int | |
symbol | String | |
clOrdId | String | |
side | int | 1 = Buy, 2 = Sell |
ordType | int | 1 = Market, 2 = Limit, 3 = Stop, 4 = Stop limit |
price | int | |
price_scale | int | |
quantity | int | |
quantity_scale | int | |
price2 | int | |
price2_scale | int | |
quantity2 | int | |
quantity2_scale | int | |
stopPx | int | (not required unless stop order) |
stopPx_scale | int | (not required unless stop order) |
timeInForce | int | (not required, defaults to 1 = Good Till Cancel (GTC)) |
Cancel Reject
If the order could not be canceled (already filled or previously canceled, etc), then an error response will indicate the reason in the message
field.
Get Orders List
Return a list of orders, ordered by id descending
{
"userId": 1045725,
"limit": 2
}
Response
{
"origOrderId": 954751144,
"userId": 1045725,
"instrumentId": 5,
"side": 1,
"ordType": 2,
"symbol": "SPY/USD",
"quantity": 125,
"quantity_scale": 2,
"price": 245,
"price_scale": 2
}
Data fields use enums defined in the FIX 4.4 standard
All types or orders are returned with their statuses
HTTP Request
POST /getOrders
API Key Permissions
This endpoint requires the “trade” permission.
Input Parameters
These parameters are common to all order types.
Field | Type | Description |
---|---|---|
userId | int | user Id |
orderId | int | optional filter |
limit | int | optional filter |
Output Parameters
These parameters are common to all order types. Depending on the order type, additional parameters will be required (see below).
Field | Type | Description |
---|---|---|
origOrderId | int | original order Id |
userId | int | User ID |
instrumentId | int | |
symbol | String | |
clOrdId | String | |
side | int | 1 = Buy, 2 = Sell |
ordType | int | 1 = Market, 2 = Limit, 3 = Stop, 4 = Stop limit |
price | int | |
price_scale | int | |
quantity | int | |
quantity_scale | int | |
stopPx | int | (not required unless stop order) |
stopPx_scale | int | (not required unless stop order) |
timeInForce | int | (not required, defaults to 1 = Good Till Cancel (GTC)) |
execType | String | |
ordStatus | int | |
execId | int | |
leavesQty | int | |
leavesQty_scale | int | |
cumQty | int | |
cumQty_scale | int | |
lastPx | int | |
lastPx_scale | int | |
lastQty | int | |
lastQty_scale | int |
Get Order
Return an order with its status
{
"orderId": 954751144,
"userId": 1045725
}
Response
{
"origOrderId": 954751144,
"userId": 1045725,
"instrumentId": 5,
"side": 1,
"ordType": 2,
"symbol": "SPY/USD",
"quantity": 125,
"quantity_scale": 2,
"price": 245,
"price_scale": 2
}
Data fields use enums defined in the FIX 4.4 standard
All types or orders are returned with their statuses
HTTP Request
POST /getOrder
API Key Permissions
This endpoint requires the “trade” permission.
Input Parameters
These parameters are common to all order types.
Field | Type | Description |
---|---|---|
userId | int | user Id |
orderId | int | required |
Output Parameters
These parameters are common to all order types. Depending on the order type, additional parameters will be required (see below).
Field | Type | Description |
---|---|---|
origOrderId | int | original order Id |
userId | int | User ID |
instrumentId | int | |
symbol | String | |
clOrdId | String | |
side | int | 1 = Buy, 2 = Sell |
ordType | int | 1 = Market, 2 = Limit, 3 = Stop, 4 = Stop limit |
price | int | |
price_scale | int | |
quantity | int | |
quantity_scale | int | |
stopPx | int | (not required unless stop order) |
stopPx_scale | int | (not required unless stop order) |
timeInForce | int | (not required, defaults to 1 = Good Till Cancel (GTC)) |
execType | String | |
ordStatus | int | |
execId | int | |
leavesQty | int | |
leavesQty_scale | int | |
cumQty | int | |
cumQty_scale | int | |
lastPx | int | |
lastPx_scale | int | |
lastQty | int | |
lastQty_scale | int |
Positions
List Positions
Return list of all postitions / balances
{
"userId": 1045725
}
Response
{
"origOrderId": 954751144,
"userId": 1045725,
"instrumentId": 5,
"side": 1,
"ordType": 2,
"symbol": "SPY/USD",
"quantity": 125,
"quantity_scale": 2,
"price": 245,
"price_scale": 2
}
Data fields use enums defined in the FIX 4.4 standard
All types or orders are returned with their statuses
HTTP Request
POST /getPositions
API Key Permissions
This endpoint requires the “trade” permission.
Input Parameters
These parameters are common to all order types.
Field | Type | Description |
---|---|---|
userId | int | user Id |
Output Parameters
If the position is a coin instrumentId will be returned. If the position is a contract the pairId will be returned
Field | Type | Description |
---|---|---|
symbol | String | |
userId | int | User ID |
instrumentId | int | either coindId or pairId |
quantity | int | |
quantity_scale | int | |
availableQuantity | int |
Deposits
Get Deposit Addresses
Return list of addresses for each coin
{
"userId": 1045725,
"instrumentId": 5
}
Response
{
"origOrderId": 954751144,
"userId": 1045725,
"instrumentId": 5,
"side": 1,
"ordType": 2,
"symbol": "SPY/USD",
"quantity": 125,
"quantity_scale": 2,
"price": 245,
"price_scale": 2
}
HTTP Request
POST /getDepositAddresses
API Key Permissions
This endpoint requires the “trade” permission.
Input Parameters
These parameters are common to all order types.
Field | Type | Description |
---|---|---|
userId | int | user Id |
instrumentId | int | optional filter |
Output Parameters
Field | Type | Description |
---|---|---|
symbol | String | |
userId | int | User ID |
instrumentId | int | coindId |
address | String | |
status | int | |
Withdrawals
List Withdraw Requests
Return list of withdrawals
{
"userId": 1045725,
"instrumentId": 5
}
Response
{
"origOrderId": 954751144,
"userId": 1045725,
"instrumentId": 5,
"side": 1,
"ordType": 2,
"symbol": "SPY/USD",
"quantity": 125,
"quantity_scale": 2,
"price": 245,
"price_scale": 2
}
HTTP Request
POST /getWithdrawRequests
API Key Permissions
This endpoint requires the “trade” permission.
Input Parameters
These parameters are common to all order types.
Field | Type | Description |
---|---|---|
userId | int | user Id |
instrumentId | int | optional filter |
Output Parameters
Field | Type | Description |
---|---|---|
symbol | String | |
userId | int | User ID |
instrumentId | int | coindId |
quantity | int | |
quantity_scale | int | |
address | String | |
status | int | |
Send Withdraw Request
Request to send coins to an external address
{
"userId": 1045725,
"instrumentId": 5,
"address": "atd42iv4u62wrs743g64wsf8f",
"quantity": 125,
"quantity_scale": 2
}
Response
{
"origOrderId": 954751144,
"userId": 1045725,
"instrumentId": 5,
"side": 1,
"ordType": 2,
"symbol": "SPY/USD",
"quantity": 125,
"quantity_scale": 2,
"price": 245,
"price_scale": 2
}
HTTP Request
POST /sendWithdrawRequest
API Key Permissions
This endpoint requires the “trade” permission.
Input Parameters
These parameters are common to all order types.
Field | Type | Description |
---|---|---|
userId | int | user Id |
instrumentId | int | |
quantity | int | |
quantity_scale | int | |
address | String |
Output Parameters
Field | Type | Description |
---|---|---|
symbol | String | |
userId | int | User ID |
instrumentId | int | coindId |
quantity | int | |
quantity_scale | int | |
address | String | |
status | int | |
Risk
Get Risk
Return real-time risk metrics
{
"userId": 1045725
}
Response
{
"userId": 1045725,
"usdValue": 56542.25,
"usdPositionValue": 56542.25,
"usdOpenOrdersValue": 56542.25,
"usdOpenOrdersRequiredValue": 56542.25,
"usdMarginValue": 56542.25,
"usdMarginRequiredValue": 56542.25,
"usdMarginMaintValue": 56542.25,
"leverageRatio": 56542.25,
"usdUnrealized": 56542.25,
"usdPositionValue": 56542.25,
"usdPositionValue": 56542.25,
"riskUpdateDate": "2018-12-12 4:44:20"
}
Data fields use enums defined in the FIX 4.4 standard
HTTP Request
POST /getRisk
API Key Permissions
This endpoint requires the “trade” permission.
Input Parameters
These parameters are common to all order types.
Field | Type | Description |
---|---|---|
userId | int | user Id |
Output Parameters
If the position is a coin instrumentId will be returned. If the position is a contract the pairId will be returned
Field | Type | Description |
---|---|---|
userId | int | User ID |
usdValue | double | |
usdPositionValue | double | |
usdOpenOrdersValue | double | |
usdOpenOrdersRequiredValue | double | |
usdMarginValue | double | |
usdMarginRequiredValue | double | |
usdMarginMaintValue | double | |
leverageRatio | double | |
usdUnrealized | double | |
riskUpdateDate | String | Timestamp these risk metrics were calculated |
Reports
Create a new report
Request
{
"type": "fills",
"start_date": "2014-11-01T00:00:00.000Z",
"end_date": "2014-11-30T23:59:59.000Z"
}
Response
{
"id": "0428b97b-bec1-429e-a94c-59232926778d",
"type": "fills",
"status": "pending",
"created_at": "2015-01-06T10:34:47.000Z",
"completed_at": undefined,
"expires_at": "2015-01-13T10:35:47.000Z",
"file_url": undefined,
"params": {
"start_date": "2014-11-01T00:00:00.000Z",
"end_date": "2014-11-30T23:59:59.000Z"
}
}
Reports provide batches of historic information about your account in various human and machine readable forms.
HTTP request
POST /reports
API Key Permissions
This endpoint requires either the “view” or “trade” permission.
Parameters
Param | Description |
---|---|
type | fills or account |
start_date | Starting date for the report (inclusive) |
end_date | Ending date for the report (inclusive) |
product_id | ID of the product to generate a fills report for. E.g. BTC-USD. Required if type is fills |
account_id | ID of the account to generate an account report for. Required if type is account |
format | pdf or csv (defualt is pdf ) |
Email address to send the report to (optional) |
The report will be generated when resources are available. Report status can be queried via the /reports/:report_id
endpoint. The file_url
field will be available once the report has successfully been created and is available for download.
Expired reports
Reports are only available for download for a few days after being created. Once a report expires, the report is no longer available for download and is deleted.
Get report status
Response (creating report)
{
"id": "0428b97b-bec1-429e-a94c-59232926778d",
"type": "fills",
"status": "creating",
"created_at": "2015-01-06T10:34:47.000Z",
"completed_at": undefined,
"expires_at": "2015-01-13T10:35:47.000Z",
"file_url": undefined,
"params": {
"start_date": "2014-11-01T00:00:00.000Z",
"end_date": "2014-11-30T23:59:59.000Z"
}
}
Response (finished report)
{
"id": "0428b97b-bec1-429e-a94c-59232926778d",
"type": "fills",
"status": "ready",
"created_at": "2015-01-06T10:34:47.000Z",
"completed_at": "2015-01-06T10:35:47.000Z",
"expires_at": "2015-01-13T10:35:47.000Z",
"file_url": "https://example.com/0428b97b.../fills.pdf",
"params": {
"start_date": "2014-11-01T00:00:00.000Z",
"end_date": "2014-11-30T23:59:59.000Z"
}
}
HTTP request
GET /reports/:report_id
Once a report request has been accepted for processing, the status is available by polling the report resource endpoint.
The final report will be uploaded and available at file_url
once the status
indicates ready
API Key Permissions
This endpoint requires either the “view” or “trade” permission.
Status
Status | Description |
---|---|
pending | The report request has been accepted and is awaiting processing |
creating | The report is being created |
ready | The report is ready for download from file_url |
User Account
Trailing Volume
[
{
"product_id": "BTC-USD",
"exchange_volume": "11800.00000000",
"volume": "100.00000000",
"recorded_at": "1973-11-29T00:05:01.123456Z"
},
{
"product_id": "LTC-USD",
"exchange_volume": "51010.04100000",
"volume": "2010.04100000",
"recorded_at": "1973-11-29T00:05:02.123456Z"
}
]
HTTP request
GET /users/self/trailing-volume
API Key Permissions
This endpoint requires either the “view” or “trade” permission.
This request will return your 30-day trailing volume for all products. This is a cached value that’s calculated every day at midnight UTC.
Public Market Data
Get Instruments
The Market Data API is an unauthenticated set of endpoints for retrieving market data. These endpoints provide snapshots of market data.
{"instruments":[[1,"USD",2,2],[2,"ETH",2,6],[3,"BTC",2,6],[6,"BTCTEST",2,6],[7,"ETHTEST",2,6],[10,"LTC",2,6],[24,"LMM",2,6]]}
Get a list of available currency pairs for trading.
HTTP Request
GET /getInstruments
Details
getInstruments Input Fields
Field | Type | Description |
---|
getInstruments Output Fields (comma deliminated)
Field | Type | Description |
---|---|---|
instrumentId | int | instrument ID |
symbol | String | the symbol |
price_scale | int | scale of the price |
quantity_scale | int | scale of the quantity |
Pairs
Get Pairs
{"instrumentPairs":[[4,"ETH/USD",2,1,2,6],[5,"BTC/USD",3,1,2,6],[11,"GOLD/USD[F]",3,1,2,6],[12,"BTC/USD[F]",3,1,2,6],[13,"SILVER/USD[F]",3,1,2,6],[14,"SPY/USD[F]",3,1,2,6],[15,"ETH/BTC",2,3,6,6],[16,"LTC/BTC",10,3,6,6],[17,"QQQ/USD[F]",3,1,2,6],[18,"AAPL/USD[F]",3,1,2,6],[19,"GOOG/USD[F]",3,1,2,6],[20,"FB/USD[F]",3,1,2,6],[21,"USO/USD[F]",3,1,2,6],[22,"EWJ/USD[F]",3,1,2,6],[23,"MCHI/USD[F]",3,1,2,6],[25,"LMM/USD",24,1,2,6],[26,"LMM/BTC",24,3,2,6],[27,"LMM/ETH",24,2,2,6]]}
Get a list of available currency pairs for trading.
HTTP Request
GET /getInstrumentPairs
Details
getInstrumentPairs Input Fields
Field | Type | Description |
---|
getInstrumentPairs Output Fields (comma deliminated)
Field | Type | Description |
---|---|---|
instrumentId | int | instrument ID |
symbol | String | the symbol |
price_scale | int | scale of the price |
quantity_scale | int | scale of the quantity |
Get Order Book Snap
Example Response for /getOrderBook?pairId=5
{"bids":[[373217,500000,1544056823375],[373191,100000,1544056823375],[372449,200000,1544056823375],[322200,1000000,1544056823375]], "asks":[[373318,500000,1544056823375],[373426,500000,1544056823375],[373441,100000,1544056823375]]}
Get open orders for a product. The amount of detail shown can be customized with the optional limit
parameter.
HTTP Request
GET /getOrderBook?pairId=<pairId>
Details
Polling is discouraged in favor of connecting via the websocket stream and listening for match
messages.
Order book data is returned as an array or order levels
[price,quantity,timestamp]
Price and quantity are integer numbers. To convert them to double, use the price_scale and quantity_scale defined in getInstrumentPairs
Get Trade History
Example Response for /getTradeHistory?pairId=5
{"trades":[[366309,200000,"20181206-15:04:18.278",9636],[365489,500000,"20181206-15:04:18.162",9635],[366406,500000,"20181206-15:04:18.143",9634],[366067,100000,"20181206-15:04:13.660",9633],[366329,200000,"20181206-15:04:11.726",9632],[366198,500000,"20181206-15:04:11.462",9631],[366174,500000,"20181206-15:04:11.426",9630],[366274,100000,"20181206-15:04:10.040",9629],[366461,500000,"20181206-15:04:06.168",9628],[366474,100000,"20181206-15:04:01.879",9627],[366666,200000,"20181206-15:04:01.877",9626],[366739,100000,"20181206-15:04:00.418",9625],[365872,500000,"20181206-15:03:59.309",9624],[366789,500000,"20181206-15:03:59.121",9623],[366792,500000,"20181206-15:03:54.032",9622],[367084,500000,"20181206-15:03:53.359",9621],[367224,500000,"20181206-15:03:51.088",9620],[367388,300000,"20181206-15:03:49.111",9619]]}
Most recent trades for the specified pair
HTTP request
GET /api/getTradeHistory?pairId=<pairId>
Real-time updates
Polling is discouraged in favor of connecting via the websocket stream and listening for match
messages.
[price,quantity,timestamp,seqNumber]
Price and quantity are integer numbers. To convert them to double, use the price_scale and quantity_scale defined in getInstrumentPairs
Get Chart Data
Example Response for /getChart?pairId=5&timespan=2
{"chart":[[1544112300000,361446,361500,359286,360130,8,417],[1544112000000,363074,363074,359435,361055,18,416],[1544111700000,363790,364335,362300,363185,33,415],[1544111400000,362860,364258,361143,363789,63,414],[1544111100000,360807,364890,360542,363054,29,413],[1544110800000,361501,361501,359710,360746,34,412],[1544110500000,361135,362134,357187,361608,50,411],[1544110200000,360414,360414,358312,360027,25,410],[1544109900000,359116,361838,357769,360474,47,409],[1544109600000,361132,361977,358621,359430,61,408],[1544109300000,362963,365340,360316,360316,35,407],[1544109000000,365543,366009,361300,362989,34,406],[1544108700000,366159,366247,364199,364679,32,405],[1544108400000,367693,369459,364712,366219,41,404],[1544108100000,368979,370778,367027,367737,53,403],[1544107800000,357505,370673,355075,367584,70,402],[1544107500000,359257,360819,355142,356737,55,401],[1544107200000,361928,362205,358492,358875,44,400],[1544106900000,363513,364069,358780,361546,70,399],[1544106600000,367300,367654,363701,363701,26,398]]}
Returns an array of chart data points in OHLC format
HTTP request
GET /getChart?pairId=<pairId>×pan=<timespan>
[<timestamp>,<open>,<high>,<low>,<close>,<volume>,<seqNumber>]
timespan enum values
ONE_MINUTE=1
FIVE_MINUTE=2
FIFTEEN_MINUTE=3
ONE_HOUR=4
SIX_HOUR=5
ONE_DAY=6
ONE_WEEK=7
Websocket Feed
The websocket feed provides real-time market data updates for orders and trades.
wss://levidge.com
Overview
Real-time market data updates provide the fastest insight into order flow and trades. This however means that you are responsible for reading the message stream and using the message relevant for your needs which can include building real-time order books or tracking real-time trades.
The websocket feed is publicly available, but connections to it are rate-limited to 1 per 4 seconds per IP.
Protocol overview
The websocket feed uses a bidirectional protocol, which encodes all messages as JSON objects. All messages have a type
attribute that can be used to handle the message appropriately.
Please note that new message types can be added at any point in time. Clients are expected to ignore messages they do not support.
Error messages: Most failure cases will cause an error
message (a message with the type
"error"
) to be emitted. This can be helpful for implementing a client or debugging issues.
Subscribe
{"login":"trader123","password":"pwd","userId":53234}
To begin receiving feed messages, you must first send a subscribe
message to the server indicating which channels and products to receive. This message is mandatory — you will be disconnected if no subscribe
has been received within 5 seconds.
Once a subscribe
message is received the server will respond with a subscriptions
message that lists all channels you are subscribed to.
Subsequent subscribe messages will add to the list of subscriptions. In case you already subscribed to a channel without being authenticated you will remain in the unauthenticated channel.
If you want to unsubscribe from channel/product pairs, send an unsubscribe
param with the value 1. The structure is equivalent to subscribe
messages.
Authentication
Authentication is required for private data channels
Channels
The Orderbook Channel
{"bids":[[373217,500000,1544056823375],[373191,100000,1544056823375],[372449,200000,1544056823375],[322200,1000000,1544056823375]], "asks":[[373318,500000,1544056823375],[373426,500000,1544056823375],[373441,100000,1544056823375]]}
Get realtime snapshot of orderbook
Public Websocket Request
WS /orderbook
Details
orderbook Input Fields
Field | Type | Description |
---|---|---|
pairId | int | pair ID |
orderbook Output Fields (comma deliminated)
[price,quantity,timestamp]
Price and quantity are integer numbers. To convert them to double, use the price_scale and quantity_scale defined in getInstrumentPairs
The Trade History Channel
{"trades":[[366309,200000,"20181206-15:04:18.278",9636],[365489,500000,"20181206-15:04:18.162",9635],[366406,500000,"20181206-15:04:18.143",9634],[366067,100000,"20181206-15:04:13.660",9633],[366329,200000,"20181206-15:04:11.726",9632],[366198,500000,"20181206-15:04:11.462",9631],[366174,500000,"20181206-15:04:11.426",9630],[366274,100000,"20181206-15:04:10.040",9629],[366461,500000,"20181206-15:04:06.168",9628],[366474,100000,"20181206-15:04:01.879",9627],[366666,200000,"20181206-15:04:01.877",9626],[366739,100000,"20181206-15:04:00.418",9625],[365872,500000,"20181206-15:03:59.309",9624],[366789,500000,"20181206-15:03:59.121",9623],[366792,500000,"20181206-15:03:54.032",9622],[367084,500000,"20181206-15:03:53.359",9621],[367224,500000,"20181206-15:03:51.088",9620],[367388,300000,"20181206-15:03:49.111",9619]]}
Get realtime trade history
Public Websocket Request
WS /tradehistory
Details
tradehistory Input Fields
Field | Type | Description |
---|---|---|
pairId | int | pair ID |
orderbook Output Fields (comma deliminated)
[price,quantity,timestamp,seqNumber]
Price and quantity are integer numbers. To convert them to double, use the price_scale and quantity_scale defined in getInstrumentPairs
The User Orders Channel
{"orderId":1025401,"clOrdId":"1234","instrumentId":5,"side":2,"userId":123456}
Get user order updates streamed through a websocket channel
This private channel must be subscribed to by authenticating
Example: {"login":"trader123","password":"pwd","userId":53234}
Private Websocket Request
WS /userorders
Details
user orders Input Fields
Field | Type | Description |
---|---|---|
login | String | user login |
password | String | user password |
userId | int |
Data fields use enums defined in the FIX 4.4 standard
user orders output Fields
Field | Type | Description |
---|---|---|
orderId | int | |
clOrdId | String | client order id |
symbol | String | |
instrumentId | int | |
side | int | |
userId | int | |
account | int | (same as userId) |
execType | String | |
ordType | int | |
ordStatus | int | |
timeInForce | String | (not required) |
execId | int | (only for fills) |
price | int | |
price_scale | int | |
quantity | int | |
quantity_scale | int | |
stopPx | int | (not required unless stop order) |
stopPx_scale | int | (not required unless stop order) |
leavesQty | int | remaining quantity of order |
leavesQty_scale | int | |
cumQty | int | |
cumQty_scale | int | |
lastPx | int | |
lastPx_scale | int | |
lastQty | int | |
lastQty_scale | int |
The User Risk Channel
{"orderId":1025401,"clOrdId":"1234","instrumentId":5,"side":2,"userId":123456,"userId":53234}
Get user risk updates streamed through a websocket channel
This private channel must be subscribed to by authenticating
Example: {"login":"trader123","password":"pwd","userId":53234}
Private Websocket Request
WS /userrisk
Details
user risk Input Fields
Field | Type | Description |
---|---|---|
login | String | user login |
password | String | user password |
userId | int |
user risk output Fields
Field | Type | Description |
---|---|---|
userId | int | |
usdValue | double | |
usdPositionValue | double | |
usdOpenOrdersValue | double | |
usdOpenOrdersRequiredValue | double | |
usdMarginValue | double | |
usdMarginValue | double | |
usdMarginRequiredValue | double | |
usdMarginMaintValue | double | |
leverageRatio | double | |
usdUnrealized | double | |
riskUpdateDate | String |
The User Position Channel
{"userId":1025401,"instrumentId":5,"quantity":2,"quantity_scale":2,"availableQuantity":54}
Get user risk updates streamed through a websocket channel
This private channel must be subscribed to by authenticating
Example: {"login":"trader123","password":"pwd","userId":53234}
Private Websocket Request
WS /userrisk
Details
user position Input Fields
Field | Type | Description |
---|---|---|
login | String | user login |
password | String | user password |
userId | int |
user position output Fields
Field | Type | Description |
---|---|---|
userId | int | |
instrumentId | int | |
quantity | int | |
quantity_scale | int | |
availableQuantity | int | |
symbol | String |
FIX API
FIX (Financial Information eXchange) is a standard protocol which can be used to enter orders, submit cancel requests, and receive fills. Users of the FIX API will typically have existing software using FIX for order management. Users who are not familiar with FIX should first consider using the REST API.
FIX API Endpoint URL
tcp+ssl://fix.levidge.com:4198
Connectivity
Before logging onto a FIX session, clients must establish a secure connection to the FIX gateway (fix.levidge.com:4198
). If your FIX implementation does not support establishing a TCP SSL connection natively, you will need to setup a local proxy such as stunnel to establish a secure connection to the FIX gateway. See the SSL Tunnels section for more details and examples.
Messages
The baseline specification for this API is FIX 4.2. There are additional tags from later versions of FIX, and custom tags in the high number range as allowed by the standard.
A standard header must be present at the start of every message in both directions.
Tag | Name | Description |
---|---|---|
8 | BeginString | Must be FIX.4.2 |
49 | SenderCompID | Client API key (on messages from the client) |
56 | TargetCompID | Must be Levidge (on messages from the client) |
Logon (A)
// create a new Logon message
var logon = new Msgs.Logon();
logon.SendingTime = new Date();
logon.HeartBtInt = 30;
logon.EncryptMethod = 0;
logon.passphrase = '...';
var presign = [
logon.SendingTime,
logon.MsgType,
session.outgoing_seq_num,
session.sender_comp_id,
session.target_comp_id,
passphrase
].join('\x01');
// add the presign string to the RawData field of the Logon message
logon.RawData = sign(presign, secret);
// send the logon message to the server
session.send(logon);
function sign(what, secret) {
var key = Buffer(secret, 'base64');
var hmac = crypto.createHmac('sha256', key);
return hmac.update(what).digest('base64');
}
Sent by the client to initiate a session, and by the server as an acknowledgement. Only one session may exist per connection; sending a Logon message within an established session is an error.
Tag | Name | Description |
---|---|---|
98 | EncryptMethod | Must be 0 (None) |
108 | HeartBtInt | Must be 30 (seconds) |
554 | Password | Client API passphrase |
96 | RawData | Client message signature (see below) |
8013 | CancelOrdersOnDisconnect | Y : Cancel all open orders for the current profile; S : Cancel open orders placed during session |
9406 | DropCopyFlag | If set to Y , execution reports will be generated for all user orders (defaults to Y ) |
The Logon message sent by the client must be signed for security. The signing method is described in Signing a Message. The prehash string is the following fields joined by the FIX field separator (ASCII code 1):
SendingTime, MsgType, MsgSeqNum, SenderCompID, TargetCompID, Password
.
There is no trailing separator. The RawData field should be a base64
encoding of the HMAC signature.
Logout (5)
Sent by either side to initiate session termination. The side which receives this message first should reply with the same message type to confirm session termination. Closing a connection without logging out of the session first is an error.
New Order Single (D)
Sent by the client to enter an order.
Tag | Name | Description |
---|---|---|
21 | HandlInst | Must be 1 (Automated) |
11 | ClOrdID | UUID selected by client to identify the order |
55 | Symbol | E.g. BTC-USD |
54 | Side | Must be 1 to buy or 2 to sell |
44 | Price | Limit price (e.g. in USD) (Limit order only) |
38 | OrderQty | Order size in base units (e.g. BTC) |
152 | CashOrderQty | Order size in quote units (e.g. USD) (Market order only) |
40 | OrdType | Must be 1 for Market, 2 for Limit, 3 for Stop Market, or 4 for Stop Limit |
99 | StopPx | Stop price for order |
59 | TimeInForce | Must be a valid TimeInForce value. See the table below (Limit order only) |
7928 | SelfTradePrevention | Optional, see the table below |
SelfTradePrevention Values
Value | Description |
---|---|
D | Decrement and cancel (the default) |
O | Cancel resting order |
N | Cancel incoming order |
B | Cancel both orders |
If an order is decremented due to self-trade prevention, an Execution Report will be sent to the client with ExecType=D indicating unsolicited OrderQty reduction (i.e. partial cancel).
See the self-trade prevention documentation for more details about this field.
TimeInForce Values
Value | Description |
---|---|
1 | Good Till Cancel |
3 | Immediate or Cancel |
4 | Fill or Kill |
P | Post-Only |
The post-only flag (P
) indicates that the order should only make liquidity. If any part of the order results in taking liquidity, the order will be rejected and no part of it will execute. Open Post-Only orders will be treated as Good Till Cancel.
See the time in force documentation for more details about these values.
Errors
If a trading error occurs (e.g. user has insufficient funds), an ExecutionReport with ExecType=8
is sent back, signifying that the order was rejected.
Order Cancel Request (F)
Sent by the client to cancel an order.
Tag | Name | Description |
---|---|---|
11 | ClOrdID | UUID selected by client for the order |
37 | OrderID | OrderID from the ExecutionReport with OrdStatus=New (39=0) |
41 | OrigClOrdID | ClOrdID of the order to cancel (originally assigned by the client) |
55 | Symbol | Symbol of the order to cancel (must match Symbol of the Order) |
ClOrdID
Use of the ClOrdID is not available after reconnecting or starting a new session. You should use the OrderID obtained via the ExecutionReport once available.
Order Status Request (H)
Sent by the client to obtain information about pending orders.
Tag | Name | Description |
---|---|---|
37 | OrderID | OrderID of order(s) to be sent back. Can be equal to * (wildcard) to send back all pending orders |
Response
The response to an Order Status Request is a series of ExecutionReports with ExecType=I
, each representing one open order belonging to the user. If the user has no open orders, a single ExecutionReport is sent back with OrderID=0
.
Execution Report (8)
Sent by the server when an order is accepted, rejected, filled, or canceled. Also sent when the user sends an OrderStatusRequest
.
Tag | Name | Description |
---|---|---|
11 | ClOrdID | Only present on order acknowledgements, ExecType=New (150=0) |
37 | OrderID | OrderID from the ExecutionReport with ExecType=New (150=0) |
55 | Symbol | Symbol of the original order |
54 | Side | Must be 1 to buy or 2 to sell |
32 | LastShares | Amount filled (if ExecType=1). Also called LastQty as of FIX 4.3 |
44 | Price | Price of the fill if ExecType indicates a fill, otherwise the order price |
38 | OrderQty | OrderQty as accepted (may be less than requested upon self-trade prevention) |
152 | CashOrderQty | Order size in quote units (e.g. USD) (Market order only) |
60 | TransactTime | Time the event occurred |
150 | ExecType | May be 1 (Partial fill) for fills, D for self-trade prevention, etc. |
39 | OrdStatus | Order status as of the current message |
103 | OrdRejReason | Insufficient funds=3 , Post-only=8 , Unknown error=0 |
136 | NoMiscFees | 1 (Order Status Request response only) |
137 | MiscFeeAmt | Fee (Order Status Request response only) |
139 | MiscFeeType | 4 (Exchange fees) (Order Status Request response only) |
1003 | TradeID | Product unique trade id |
1057 | AggressorIndicator | Y for taker orders, N for maker orders |
ExecType Values
ExecType | Description |
---|---|
0 | New Order |
1 | Fill |
3 | Done |
4 | Canceled |
7 | Stopped |
8 | Rejected |
D | Order Changed |
I | Order Status |
Order Cancel Reject (9)
Sent by the server when an Order Cancel Request cannot be satisfied, e.g. because the order is already canceled or completely filled.
Tag | Name | Description |
---|---|---|
11 | ClOrdID | As on the cancel request |
37 | OrderID | As on the cancel request |
41 | OrigClOrdID | As on the cancel request |
39 | OrdStatus | 4 if too late to cancel |
102 | CxlRejReason | 1 if the order is unknown |
434 | CxlRejResponseTo | 1 (Order Cancel Request) |
Reject (3)
Sent by either side upon receipt of a message which cannot be processed, e.g. due to missing fields or an unsupported message type.
Tag | Name | Description |
---|---|---|
45 | RefSeqNum | MsgSeqNum of the rejected incoming message |
371 | RefTagID | Tag number of the field which caused the reject (optional) |
372 | RefMsgType | MsgType of the rejected incoming message |
58 | Text | Human-readable description of the error (optional) |
373 | SessionRejectReason | Code to identify reason for reject |
SessionRejectReason Values
The following values can be sent by the server.
Value | Description |
---|---|
1 | Required tag missing |
5 | Value is incorrect (out of range) for this tag |
6 | Incorrect data format for value |
11 | Invalid MsgType (35) |
Heartbeat (0)
Sent by both sides if no messages have been sent for HeartBtInt seconds as agreed during logon. May also be sent in response to a Test Request.
Tag | Name | Description |
---|---|---|
112 | TestReqID | Copied from the Test Request, if any |
Test Request (1)
May be sent at any time by either side.
Tag | Name | Description |
---|---|---|
112 | TestReqID | Free text |
SSL Tunnels
fix.levidge.com:4198
only accepts TCP connections secured by SSL. If your FIX client library cannot establish an SSL connection natively, you will need to run a local proxy that will establish a secure connection and allow unencrypted local connections.
Stunnel Configuration
This is an example configuration file for stunnel to listen on a port locally and proxy unencrypted TCP connections to the encrypted SSL connection. The service name (Levidge
) and the accept port (4197
) may be changed to any suitable values.
[Levidge]
client = yes
accept = 4197
connect = fix.levidge.com:4198
verify = 4
CAfile = /example/path/to/fix.levidge.com.pem
When stunnel is started with the above configuration file, it will run in the background. On Unix-like systems the option foreground = yes
may be specified at the top of the file to avoid running in the background. For testing it may be easier to use foreground mode, or to specify the top-level output
option as a file path where stunnel will write log messages.
If your system has OpenSSL installed, you can run this command to download the certificate:
openssl s_client -showcerts -connect fix.levidge.com:4198 < /dev/null | openssl x509 -outform PEM > fix.levidge.com.pem