Changelog

Version

Date

Description

1.0.0

August 2019

Initial version

1.0.1

August 2019

Add initialization and user trades chapters

1.0.2

August 2019

Add javascript documentation, error codes and accounting chapter

1.1.0

September 2019

Add live http api documentation

1.1.1

October 2019

Add Javascript and Java examples for unencrypted orders

1.1.2

January 2020

Add timestamps on user feeds and market data

1.2.0

February 2020

Add withdrawal request and cancellation endpoints

1.2.1

February 2020

Add deposit address request and operation endpoint

1.2.2

April 2020

Add websocket unexpected error details

1.2.3

May 2020

Adds unsubscribe messages documentation (websocket) and remove unnecessary headers usage

Introduction

We offer multiple ways to connect to the LGO trading platform:

1) Write your own code: Sign/encrypt your requests by yourself and send to our

  1. HTTP API endpoints

  2. Websocket API

or

2) Use one of our turnkey solutions to facilitate signature/encryption of your requests to our HTTP and Websocket APIs

  1. By using our Javascript SDK in your code

  2. By using our Java library (implementation of XChange and xchange-stream) in your code

This document is a global documentation to help you choose between these ways.

We also offer to install a proxy application, you can find more informations here.

LGO recommends to use the websocket API (whether directly, via the proxy, the Javascript SDK or the XChange-stream API) Websocket API will give you real time market data and order placement without http roundtrips.

LGO provides a public API available without authentication to allow viewing of orders and trades of the platform. You can find it’s documentation here: https://doc.exchange.lgo.markets/index-public.html

General design principles

The HTTP API is rougthly divided in three parts :

Sandbox URL

Production URL

Accounting

All your wallets and accounts informations

https://account-api.sandbox.lgo.markets

https://account-api.exchange.lgo.markets

History

Exchange archives. Retrieving your trades, orders, etc. Data are behind live by at least 10sc

https://exchange-api.sandbox.lgo.markets/v1/history

https://exchange-api.exchange.lgo.markets/v1/history

Live

Exchange live activity.

https://exchange-api.sandbox.lgo.markets/v1/live

https://exchange-api.exchange.lgo.markets/v1/live

The sandbox is a complete test environment. Every features are available, and LGO is running a few bots to simulate activity.

Authentication

API keys

Authentication on LGO uses 2 keys:

  • A RSA keypair

  • An access key provided when you register your RSA public key on LGO

Generate a RSA keypair

With OpenSSL for example:

openssl genrsa -out private_key.pem 2048

openssl rsa -in private_key.pem -pubout > public_key.crt

Some softwares may need your private key as pkcs#8 format:

openssl pkcs8 -topk8 -in private_key.pem -inform pem -out private_key_pkcs8.pem -outform pem -nocrypt

Register an API key

API keys should be registered via the LGO Desktop app.

Keys used in this documentation

Each example of this documentation will use these values:

Access key

60715d6f-adc9-4792-9b4d-d705135a6173

RSA keypair
-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAojp7SlwPi4Ql5JPAbxP6
+aG+z8HOGT9BsvSDHR/1gQ8xDXlMcrd7VtrhyQ31jR3rnc6aPx+lVxSn2YvQWyl/
4pBQyV39hzJMoMLDz6TJjmPZOUGlo2yAku9TntE70RaQcjf2bQA4ikZkMVesJ8Tf
GVsun7vaYTjJkXRkwdz4MXpXapqHteWNVTjgteDZn3Z+MEA9W5OazmPVD/NsDzD9
qBxDW0lwtvkHljjNQFit+dtN6zv8th5aLcrcnXuSoXbe6RXRMSxCC0whTKfsveyB
DCA+kSSPOgiBJyUuv863KSsvEd0kkVB8Ox9U8z/V7/xpDLTNXkcfvIlUvC+3Hqi4
3wIDAQAB
-----END PUBLIC KEY-----
-----BEGIN RSA PRIVATE KEY-----
MIIEpQIBAAKCAQEAojp7SlwPi4Ql5JPAbxP6+aG+z8HOGT9BsvSDHR/1gQ8xDXlM
crd7VtrhyQ31jR3rnc6aPx+lVxSn2YvQWyl/4pBQyV39hzJMoMLDz6TJjmPZOUGl
o2yAku9TntE70RaQcjf2bQA4ikZkMVesJ8TfGVsun7vaYTjJkXRkwdz4MXpXapqH
teWNVTjgteDZn3Z+MEA9W5OazmPVD/NsDzD9qBxDW0lwtvkHljjNQFit+dtN6zv8
th5aLcrcnXuSoXbe6RXRMSxCC0whTKfsveyBDCA+kSSPOgiBJyUuv863KSsvEd0k
kVB8Ox9U8z/V7/xpDLTNXkcfvIlUvC+3Hqi43wIDAQABAoIBADgttlpGzR9MUO75
946/xY7C41gAzkVR8YduQyVH1vWtdBgtZDrprS2juMKuMdV/ggNw81tesxwXzBR6
5VlcYqvru/4vrUcvNPgK2lJCx4WmsCeywxB314KKnFOIM4Wxoa3cEVsn02yW+cVY
jgZrl7KpL9ki7Xnzd2IGg4na4pwHK6EU+bD8+DV7IsMusum6veuEeKxJk2v6FG/Q
HZozl6aWQhOcYt2aLYue+Cx+qFnLV+zoCuB6lr2EmtqW5GDoSTk6ANJ3DJkX3Btk
jApRU5BOkipqWy2u7J8A6v9th0VQCXUA1/9ZD+Mq5PszK4pW4SW9RL2IVSRygLL5
nnslcXECgYEA0aJ9Itf5c0Te6py8H2eMFFAzj0b13PoXnQ3IgMIaTfVVf1i8Ksqy
VhWzFQNoy03WxCNyWduEqxTLXL2WLLCXnrsrArl8FE/PZnhH1U/RLxKl+O5xIN3z
90x70xLeQr/qBNRSOClhzToINNToHUQCgAEvDtZsNAERD5OJ99RdfBkCgYEAxhvZ
0WdT90s051oFW0rQSB9ZPD7M9oQ0NdJENy2gXzO97QlwT9xcC3ZjF6Oh2h3ZC62k
E32DKqV/gD6g6ANXW92z08Co+zUjFdKY1h7N1s/oxAc+vm+jfJnXKo72W4pehKkb
A6+PTvPLLOi0WDMG8c13ZALC662qxOZLd9V9e7cCgYEAsRGKmS/L5+04TPrue6g+
zbmgk1jguzITV/kYbomFJmwfN12AMrTbRZM2nH0wuuiYiztVj0i4GrmJvF/2xPC0
YMK/ZaG+iHmROYBHTIoKqrQZtNXSgGx5cV0NChBZ5A+uXz3n5MAvd+WYoOdk17Nm
WTCmyuWap6JvArUgSFD8VaECgYEAio1x8lVM0ThlAKTh+C5Dqx1ZoJvfZ02g4j9z
fA/KCKs8WqpuRTw9l7qtpRvJF64mXVeM2CDA+rOSj5O9n2au004j9aXZyQ8pwZpv
T9ltZp40Ed1rUW7srTk+1cH0pKMKZceLYDGJjdNNttPtRX4yjiyAIo8X2hK0y06x
W1cRktMCgYEAkIwoKjIDfj2EaKzCcxBSpxIrx2+6gB1qmC9v8PLRG4JDJTqj9gj+
HNI5gCSXj3NLzLFpnC/obG7G9jommp8uh0ojNzuyb0sc5y9SujeNdNbBKGkObprD
3LydCtIeSZ2KdhxBpWNEqa3PafBE+M/F0RnCGtm+cMm+oLw8VvktHhg=
-----END RSA PRIVATE KEY-----

HTTP API

The only exception for the mechanism described here is the unencrypted order placement and the withdrawal request, where the body should be included in the signature.

Details will be covered in order placement and withdrawal documentation section.

Each request to the HTTP API should have these headers set:

Name

Type

Description

X-LGO-DATE

long

Timestamp

Authorization

String

LGO specific signed String

Timestamp header

The X-LGO-DATE header value should contain the timestamp of now, in milliseconds. This value should be used in the string to sign for the authorization header.

Authorization header

The authorization header value format is: LGO {your-api-key}:{signed-string}.

The signed string is made of the called URL and the timestamp (same as the header X-LGO-DATE value): {timestamp}\n{url-without-protocol}. You should remove the protocol part of the called URL before adding the value to the string to sign.

The bytes to sign should be encoded with UTF-8.

The signature should use RSA encryption algorithm with SHA-256 hash and PKCS#1 v1.5 padding.

Examples of algorithm constant name for some known technologies:

Java Signature

SHA256withRSA

SoftHSM

SHA256_RSA_PKCS

Python PKCS#11

SHA256_RSA_PKCS

NodeJS crypto

RSA-SHA256

The signed bytes then should be encoded in hexadecimal before concatenation to the rest of the header value.

Example

This example uses default api keys available above.

Timestamp: 1565598764774
Url: https://exchange-api.exchange.lgo.markets/v1/live/currencies
Signature sha256Rsa(1565598764774\nexchange-api.exchange.lgo.markets/v1/live/currencies): 4e38ef44534eba534bffaee4f78dbd9256f6662a314490b938ee389cedac390b7c66c7699cad9d996528900beb19390c01f7d36e9c2d16cd39cce8e730de44c297c3ebdb76f915e831fef0ee8e632fe0829bc577962ef7a70c88869a6aeca6417ff5a6b54b5da7c242b37d8dbfd9131f80d8fcba34084344bd7922d5b2088920953c20a89dc90ae712803712cdf29ee2aacd08664e4ae94ce219c562d5425f8f3ae02b97b1dc9c571a89ee119207b84e0210c45fb49c0e680e400c386c5fffa3ad37aefbae786fb192937b0cb4858ee303d4e5da29c18e94cc3a5ee9f3501d5461126e5f92bc8dbafb375966ab6a44fbf4fa37cf99d604baaf504e1add77c116

Expected request headers:

X-LGO-DATE: 1565598764774
Authorization: LGO 60715d6f-adc9-4792-9b4d-d705135a6173:4e38ef44534eba534bffaee4f78dbd9256f6662a314490b938ee389cedac390b7c66c7699cad9d996528900beb19390c01f7d36e9c2d16cd39cce8e730de44c297c3ebdb76f915e831fef0ee8e632fe0829bc577962ef7a70c88869a6aeca6417ff5a6b54b5da7c242b37d8dbfd9131f80d8fcba34084344bd7922d5b2088920953c20a89dc90ae712803712cdf29ee2aacd08664e4ae94ce219c562d5425f8f3ae02b97b1dc9c571a89ee119207b84e0210c45fb49c0e680e400c386c5fffa3ad37aefbae786fb192937b0cb4858ee303d4e5da29c18e94cc3a5ee9f3501d5461126e5f92bc8dbafb375966ab6a44fbf4fa37cf99d604baaf504e1add77c116

Websocket API

The connection request to the LGO websocket should have these headers set:

Name

Type

Description

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

These headers are the same as the ones described in the HTTP API chapter previously.

The only thing that differs is that, for the Authorization header, the signed string URL part should end with a trailing slash: {timestamp}\n{url-without-protocol}/.

Example

This example uses default api keys available above.

Timestamp: 1565598764774
Url: wss://ws.exchange.lgo.markets/
Signature sha256Rsa(1565598764774\nws.exchange.lgo.markets/): 4abf53082d46696b9320af2c60e48933ceb68ce363ec0c467b10f72b5e07565c522f24e06d151606be5ec7f1e69ae5a1a14149171b81b5ae6e999834a5339e7b62be166c9015a52d2660e90642622e58f597af3873081cff204d66bc18faa2874b868cbc3cef03595b6347cc149c79c2e0fc946563e5c6f5c9c2557b453383465095eeefa9f36590e1892d718e03ee620c065a5b18d92e7e8fbc4302f682ec4783c6d5a04f7d5730593ef4e07be59370858384aed74c5d7fbf33726618fb664b801971ad09868b4895cfe51c5e807b25e65419a50f185829f87c4ac7305af54b47e3424b3d1f1ccf43ffc913149612c88ca3937a9a8b262edc51408f50404090

Expected request headers:

X-LGO-DATE: 1565598764774
Authorization: LGO 60715d6f-adc9-4792-9b4d-d705135a6173:4abf53082d46696b9320af2c60e48933ceb68ce363ec0c467b10f72b5e07565c522f24e06d151606be5ec7f1e69ae5a1a14149171b81b5ae6e999834a5339e7b62be166c9015a52d2660e90642622e58f597af3873081cff204d66bc18faa2874b868cbc3cef03595b6347cc149c79c2e0fc946563e5c6f5c9c2557b453383465095eeefa9f36590e1892d718e03ee620c065a5b18d92e7e8fbc4302f682ec4783c6d5a04f7d5730593ef4e07be59370858384aed74c5d7fbf33726618fb664b801971ad09868b4895cfe51c5e807b25e65419a50f185829f87c4ac7305af54b47e3424b3d1f1ccf43ffc913149612c88ca3937a9a8b262edc51408f50404090

XChange (Java), Javascript SDK

XChange Java library and the Javascript SDK will add these headers for you, see the corresponding documentation.

Initialization of your client

Initialization of our Java library

Java library: XChange

LGO integration is available in the XChange library since version 4.3.21.

See https://github.com/knowm/XChange for Getting started documentation.

You can then initialize the LGO exchange object with the following source code:

import org.apache.commons.io.IOUtils;
import org.knowm.xchange.Exchange;
import org.knowm.xchange.ExchangeFactory;
import org.knowm.xchange.ExchangeSpecification;
import org.knowm.xchange.dto.trade.UserTrades;
import org.knowm.xchange.service.trade.TradeService;

import java.io.IOException;
import java.io.InputStream;

public class Example {

  public static void main(String[] args) throws IOException {
    ExchangeSpecification spec = LgoEnv.sandbox(); // or LgoEnv.prod();
    InputStream stream = Example.class.getResourceAsStream("/your-path/your-private_key.pem");
    spec.setSecretKey(IOUtils.toString(stream, "utf8"));
    spec.setApiKey("your-api-key");
    Exchange exchange = ExchangeFactory.INSTANCE.createExchange(spec);
  }
}

Java streaming library: XChange-stream

LGO integration is not yet supported by xchange-stream, you have to use our public repository to build your own package for the moment: https://github.com/lgo-public/xchange-stream

Checkout and install the develop branch:

git clone --branch develop git@github.com:lgo-public/xchange-stream.git
mvn install

Starting websocket connection:

import org.apache.commons.io.IOUtils;
import org.knowm.xchange.ExchangeSpecification;
import org.knowm.xchange.currency.CurrencyPair;
import org.knowm.xchange.dto.Order;
import org.knowm.xchange.dto.trade.LimitOrder;
import org.knowm.xchange.dto.trade.MarketOrder;
import org.knowm.xchange.lgo.LgoEnv;

import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.util.Date;

public class Example {

    public static void main(String[] args) throws IOException {
        LgoStreamingExchange exchange = new LgoStreamingExchange();
        ExchangeSpecification spec = LgoEnv.sandbox(); // or LgoEnv.prod();
        InputStream stream = Example.class.getResourceAsStream("/your-path/your-private_key.pem");
        spec.setSecretKey(IOUtils.toString(stream, "utf8"));
        spec.setApiKey("your-api-key");
        spec.setShouldLoadRemoteMetaData(false);
        exchange.applySpecification(spec);
        exchange.connect().blockingAwait();
    }
}

Initialization of our Javascript library

Client

To use the library instantiate a new Client.

import { Client } from '@lgo/sdk';

const client = new Client(/* options */);

You can also use require syntax:

const { Client } = require('@lgo/sdk');

Options are provided as an object with following properties:

  • cryptoKi: CryptoKi - A CryptoKi compatible implementation. See [CryptoKi](#cryptoki).

  • env?: string - The target environment. Defaults to production. See [Environments](#environments).

  • logger?: Logger - A logger compatible implementation. Logs nothing by default. See [Logging](#logging).

  • accessKey: string - Your personal access key on LGO backend.

  • webSocket?: {} - WebSocket specific options.

    • connectionTimeoutDelay?: number - Delay to wait in ms before throwing an error if connection takes too much time. Defaults to 10000 ms.

    • reconnectionDelay?: number - Delay to wait in ms before trying to reconnect. Defaults to 3000 ms.

    • pingDelay?: number - Delay to wait in ms before sending each ping requests. Defaults to 5000 ms.

    • pongTimeoutDelay?: number - Delay to wait in ms before detecting a missing pong response and then forcing disconnection. Defaults to 10000 ms.

Options for advanced users or edge cases:

  • exchangeUrl: string - Overwrites the exchange backend url.

  • accountingUrl: string - Overwrites the accounting backend url.

  • wsGatewayUrl: string - Overwrites the WebSocket gateway backend url.

  • keysUrl: string - Overwrites the encryption keys host url.

Example:

const client = new Client({
  cryptoKi: myCryptoKi,
  accessKey: '971432e9-746c-4c29-ab68-24704347a1e3',
  webSocket: {
    connectionTimeoutDelay: 30000
  }
});

CryptoKi

You must provide a CryptoKi compatible implementation to the sdk.

The interface must be:

export interface CryptoKi {
  sign: (data: string) => Promise<string>;
}

The resulting signature must be a encoded to hexadecimal.

Available implementations:

Environments

LGO sdk can be configured to target LGO Markets or LGO Exchange with their own production or sandbox environments.

Available environments:

Name

Description

production

LGO production environment. Default environment.

sandbox

LGO sandbox environment to test api without real money.

Example:

const client = new Client({
  env: 'sandbox'
  /* ... other options */
});

Logging

You can provide a Logger compatible implementation and the sdk will use it to display information or error messages.

The signature must be:

interface Logger {
  log: (message: string) => void;
  error: (message: string) => void;
}

An obvious implementation is Node.js console object.

Example:

const client = new Client({
  logger: console
  /* ... other options */
});

Currencies

Using your own code

You can receive currencies list by sending a GET request to:

Sandbox

https://exchange-api.sandbox.lgo.markets/v1/live/currencies

Production

https://exchange-api.exchange.lgo.markets/v1/live/currencies

along with the following headers:

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Accept

String

application/json

You will receive this JSON payload as response:

{
  "currencies": [
    {
      "name": "Bitcoin",  (1)
      "code": "BTC",  (2)
      "decimals": 8  (3)
    },
    {
      "name": "United States Dollar",
      "code": "USD",
      "decimals": 4
    }
  ]
}
1 name name of the currency
2 code code of the currency
3 decimals maximum decimals allowed

Using our Java library (XChange)

You can receive the configuration LgoCurrencies through the MarketDataService:

exchange.getMarketDataService()
        .getCurrencies();

Using our Javascript library

client.getCurrencies(): Promise<{}>

  • returns: a promise of an object containing currencies.

Example:

client.getCurrencies().then(currencies => {
  // ...
});

Output example:

{
  currencies: [
    { name: 'Bitcoin', code: 'BTC', decimals: 8 },
    { name: 'United States Dollar', code: 'USD', decimals: 4 }
  ];
}

Products list

Using your own code

You can receive products list by sending a GET request to:

Sandbox

https://exchange-api.sandbox.lgo.markets/v1/live/products

Production

https://exchange-api.exchange.lgo.markets/v1/live/products

along with the following headers (previously described):

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Accept

String

application/json

You will receive this JSON payload as response:

{
  "products": [
    {
      "id": "BTC-USD",  (1)
      "total": {
        "limits": {  (2)
          "min": "10",
          "max": "50000000"
        }
      },
      "base": {
        "id": "BTC",
        "limits": {  (3)
          "min": "0.001",
          "max": "1000"
        }
      },
      "quote": {
        "id": "USD",
        "increment": "0.10",  (4)
        "limits": {  (5)
          "min": "10",
          "max": "1000000"
        }
      }
    }
  ]
}
1 id identifier of the product
2 total.limits minimum and maximum total price*quantity allowed (quote currency)
3 base.limits minimum and maximum quantity allowed
4 quote.increment price increment allowed
5 quote.limits minimum and maximum price allowed

Using our Java library (XChange)

You can receive the configuration LgoProducts through the MarketDataService:

exchange.getMarketDataService()
        .getProducts();

Using our Javascript library

client.getProducts(): Promise<{}>

  • returns: a promise of an object containing products.

Example:

client.getProducts().then(products => {
  // ...
});

Output example:

{
  products: [
    {
      id: 'BTC-USD',
      base: {
        id: 'BTC',
        limits: { min: 0.001, max: 1000 }
      },
      quote: {
        id: 'USD',
        increment: 0.1,
        limits: { min: 10, max: 1000000 }
      },
      total: {
        limits: { min: 10, max: 50000000 }
      }
    }
  ];
}

Placing unencrypted orders

If you don’t want to use the anti front running protection, you can place an order without encrypting its content.

You order will follow the same workflow as described here, but without the encryption part.

basics

Using your own code

If an order is received in the same batch as its order cancellation request, it will not be executed.

If a limit order is already in LGO order book, the order cancellation request will be executed at the start of the batch in which it was added, so that the orders in the batch will not be executed against the canceled order.

HTTP API

You should send the JSON payload as the request body of a POST on the following URL:

Sandbox

https://exchange-api.sandbox.lgo.markets/v1/live/orders

Production

https://exchange-api.exchange.lgo.markets/v1/live/orders

along with the following headers:

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String (with signed payload)

Content-Type

String

application/json

Payload signature

The order signature is included directly in the Authorization header, so the process here is slightly different than other endpoints.

The authorization header value format remains LGO {your-api-key}:{signed-string}.

Here the string to sign is made of the called URL, the timestamp (same as the header X-LGO-DATE value), and the request body (UTF-8 encoded string): {timestamp}\n{url-without-protocol}\n{request-body}. This string should be signed as described in the authentication chapter.

Place order
{
  "type": "L",  (1)
  "side": "B",  (2)
  "product_id": "BTC-USD", (3)
  "quantity": "2.5",  (4)
  "price": "9000.3",  (5)
  "reference": 34  (6)
}
1 type L or M for Limit order or Market order
2 side B or S for Buy order or Sell order
3 product_id the product code
4 quantity The order amount. For limit order, base currency amount to sell or buy. For market order, the max amount to spend (base currency for sell order and quote currency for buy order).
5 price the maximum price to pay, for a limit order. This field is not required for market orders.
6 reference An optional numerical reference that you can choose. It will be sent along the order identifier in the ACK message

You will then receive an HTTP status indicating if the LGO platform was available to receive your message, and the identifier of your order:

{
  "order_id": "129898898"
}

You will receive an HTTP status 400 in case of invalid payload, with a body containing more details. For instance:

{
  "message": "PRODUCT_NOT_AVAILABLE",
  "product_id": "BTC-EUR"
}

A 200 OK response doesn’t imply that the order complies to all rules. Most checks are only processed once the order is in a batch. You still need to check execution result with the proper websocket channel.

Cancel order

In the same way, you can choose to cancel an order without anti front running protection.

You should send a DELETE request to the proper url : 

Sandbox

https://exchange-api.sandbox.lgo.markets/v1/live/orders/{order_id}

Production

https://exchange-api.exchange.lgo.markets/v1/live/orders/{order_id}

and the following optional query parameter:

Name

Type

Description

reference

long

An optional reference to track this request via websocket

Since the request contains no body, you can use the standard auth mechanism.

You will then receive an HTTP status indicating if the LGO platform was available to receive your message, and the identifier of your cancellation request:

{
  "order_id": "129898898"
}

You will receive an HTTP status 400 in case of invalid payload, with a body containing more details. For instance :

{
  "message": "BACKPRESSURE"
}

WebSocket API

Place Order

You can send an unencrypted limit or market order via the socket with a message formated like this :

{
  "type": "placeunencryptedorder",
  "payload": {
    "order": {
      "type": "L",  (1)
      "side": "B",  (2)
      "product_id": "BTC-USD", (3)
      "quantity": "2.5",  (4)
      "price": "9000.3",  (5)
      "timestamp": 1565271479654 (6)
    }
    "signature": {
      "value": "2e01378e2ca1d40e3d575cc00bf1131e72d7735c083d3783ed28...",  (7)
      "source": "RSA"
    },
    "reference": 1565339956462  (8)
  }
}
1 order.type L or M for Limit order or Market order
2 order.side B or S for Buy order or Sell order
3 order.product_id the product code
4 order.quantity The order amount. For limit order, base currency amount to sell or buy. For morket order, the max amount to spend (base currency for sell order and quote currency for buy order).
5 order.price the maximum price to pay, for a limit order. This field is not required for market orders.
6 order.timestamp the timestamp of the current time (milliseconds)
7 signature.value Payload signature (see below)
8 reference An optional numerical reference that you can choose. It will be sent along the order identifier in the ACK message

In case of error you will receive this payload:

{
  "type": "error",
  "reference": 324242,  (1)
  "message": "..."  (2)
}
1 order reference
2 possible message:
  • INTERNAL_SERVER_ERROR (could not validate signature)

  • BAD_SIGNATURE (invalid signature)

  • BACK_PRESSURED (failed to send order to LGO order processor: back pressured)

  • UNEXPECTED (failed to send order to LGO order processor: closed/max position exceeded)

  • TIMEOUT (failed to send order to LGO order processor: timeout)

Payload signature

This payload requires a signature.

The bytes to sign should be encoded with UTF-8.

The signature should use RSA with SHA-256 hash and PKCS#1 v1.5 padding.

The key to use to sign is your personal RSA private key.

Examples of algorithm constant name for some known technologies:

Java Signature

SHA256withRSA

SoftHSM

SHA256_RSA_PKCS

Python PKCS#11

SHA256_RSA_PKCS

NodeJS crypto

RSA-SHA256

The signed bytes then should be encoded in hexadecimal.

The string to sign must be formatted this way, properties in the same order (no spaces, no tab, no new line):

In case of a limit order :

{"type":"L","side":"B","product_id":"BTC-USD","quantity":"3","price":"6000","timestamp":1565271479654}

In case of a market order (price is omitted):

{"type":"M","side":"B","product_id":"BTC-USD","quantity":"3","timestamp":1565271479654}
Cancel an order

You can send an unencrypted cancel order via the socket with a message formated like this :

{
  "type": "cancelorder"
  "payload": {
    "order_id": "129889" (1)
    "timestamp": 17878787 (2)
    "signature": {
      "value": "2e01378e2ca1d40e3d575cc00bf1131e72d7735c083d3783ed28...",  (3)
      "source": "RSA"
    },
    "reference": 1565339956462  (4)
  }
}
1 order_id The id of the order to cancel
2 timestamp the timestamp of the current time (milliseconds)
3 signature.value Payload signature (see below)
4 reference An optional numerical reference that you can choose. It will be sent along the order identifier in the ACK message

In case of error you will receive this payload:

{
  "type": "error",
  "reference": 324242,  (1)
  "message": "..."  (2)
}
1 order reference
2 possible message:
  • INTERNAL_SERVER_ERROR (could not validate signature)

  • BAD_SIGNATURE (invalid signature)

  • BACK_PRESSURED (failed to send order to LGO order processor: back pressured)

  • UNEXPECTED (failed to send order to LGO order processor: closed/max position exceeded)

  • TIMEOUT (failed to send order to LGO order processor: timeout)

Payload signature

This payload requires a signature.

The bytes to sign should be encoded with UTF-8.

The signature should use RSA with SHA-256 hash and PKCS#1 v1.5 padding.

The key to use to sign is your personal RSA private key.

Examples of algorithm constant name for some known technologies:

Java Signature

SHA256withRSA

SoftHSM

SHA256_RSA_PKCS

Python PKCS#11

SHA256_RSA_PKCS

NodeJS crypto

RSA-SHA256

The signed bytes then should be encoded in hexadecimal.

The string to sign must be formatted this way, properties in the same order (no spaces, no tab, no new line):

{"order_id":"129889","timestamp":17878787}

Using our Java streaming library (XChange-stream)

Place a limit order without encryption:

LimitOrder limitOrder = new LimitOrder(
        Order.OrderType.BID,  (1)
        new BigDecimal("0.5"),  (2)
        CurrencyPair.BTC_USD,  (3)
        null,  (4)
        new Date(),  (5)
        new BigDecimal("12000.10")  (6)
);
String ref = exchange
        .getStreamingTradeService()
        .placeUnencryptedLimitOrder(limitOrder);
System.out.println("Order was placed with reference: " + ref);
1 Order.OrderType.BID or Order.OrderType.ASK
2 amount (base currency)
3 product
4 ignored value
5 actual date
6 price (quote currency)

Place a market order without encryption:

MarketOrder marketOrder = new MarketOrder(
        Order.OrderType.BID,  (1)
        new BigDecimal("0.5"),  (2)
        CurrencyPair.BTC_USD,  (3)
        null,  (4)
        new Date()  (5)
);
String ref = exchange
        .getStreamingTradeService()
        .placeUnencryptedMarketOrder(marketOrder);
System.out.println("Order was placed with reference: " + ref);
1 Order.OrderType.BID or Order.OrderType.ASK
2 amount (base currency)
3 product
4 ignored value
5 actual date

Cancel a limit or market order without encryption:

exchange
    .getStreamingTradeService()
    .placeUnencryptedCancelOrder(
            "156406068135700001",  (1)
            new Date()  (2)
    );
1 identifier of the order to cancel
2 actual date

Using our Java library (XChange)

Place a limit order without encryption:

LimitOrder limitOrder = new LimitOrder(
        Order.OrderType.BID,  (1)
        new BigDecimal("0.5"),  (2)
        CurrencyPair.BTC_USD,  (3)
        null,  (4)
        new Date(),  (5)
        new BigDecimal("12000.10")  (6)
);
String orderId = exchange
        .getTradeService()
        .placeUnencryptedLimitOrder(limitOrder);
System.out.println("Order was placed with identifier: " + orderId);
1 Order.OrderType.BID or Order.OrderType.ASK
2 amount (base currency)
3 product
4 ignored value
5 actual date
6 price (quote currency)

Place a market order without encryption:

MarketOrder marketOrder = new MarketOrder(
        Order.OrderType.BID,  (1)
        new BigDecimal("0.5"),  (2)
        CurrencyPair.BTC_USD,  (3)
        null,  (4)
        new Date()  (5)
);
String orderId = exchange
        .getTradeService()
        .placeUnencryptedMarketOrder(marketOrder);
System.out.println("Order was placed with identifier: " + orderId);
1 Order.OrderType.BID or Order.OrderType.ASK
2 amount (base currency)
3 product
4 ignored value
5 actual date

Cancel a limit or market order without encryption:

String orderId = exchange
        .getTradeService()
        .placeUnencryptedCancelOrder("156406068135700001");  (1)
System.out.println("Cancellation request was placed with identifier: " + orderId);
1 identifier of the order to cancel

Using our Javascript library

With HTTP

Place an order:

client.placeOrder(order: {}): Promise<{order_id: number}>

  • order: {} - The order to place. Properties are:

    • type: string - L for limit or M for market.

    • side: string - B for buy or S for sell.

    • productId: string - The product id. Example: BTC-USD·.

    • quantity: number - The quantity. Example: 0.05.

    • price?: number - The price. Not available for a market order. Example: 8000.1.

    • reference?: number - A reference to track your order on afr WebSocket channel.

  • returns: a promise containing the order id.

Example:

import { Client, OrderType, OrderSide } from '@lgo/sdk';

const client = new Client(/* options */);

const order = {
  type: OrderType.limit,
  side: OrderSide.sell,
  productId: 'BTC-USD',
  quantity: 1,
  price: 8000,
  reference: Date.now()
};

client.placeOrder(order).then(() => console.log);

Cancel an order:

client.cancelOrder(cancellation: {}): Promise<void>

  • cancellation: {} - The cancellation description. Properties are:

    • orderId: string - The id of the order to cancel.

    • reference?: number - A reference to track your order on afr WebSocket channel.

  • returns: a promise containing nothing.

Example:

import { Client } from '@lgo/sdk';

const client = new Client(/* options */);

const cancellation = {
  reference: Date.now(),
  orderId: '155663606242700001'
};

client.cancelOrder(cancellation).then(() => console.log('done'));

With websocket

Place an order:

client.webSocket.placeOrder(order: {}): Promise<void>

  • order: {} - The order to place. Properties are:

    • type: string - L for limit or M for market.

    • side: string - B for buy or S for sell.

    • productId: string - The product id. Example: BTC-USD·.

    • quantity: number - The quantity. Example: 0.05.

    • price?: number - The price. Not available for a market order. Example: 8000.1.

    • reference?: number - A reference to track your order on afr WebSocket channel.

  • returns: a promise containing nothing.

Example:

import { Client, OrderType, OrderSide } from '@lgo/sdk';

const client = new Client(/* options */);

client.webSocket.on('open', async () => {
  await client.webSocket.placeOrder({
    type: OrderType.limit,
    side: OrderSide.sell,
    productId: 'BTC-USD',
    quantity: 1,
    price: 50000,
    reference: Date.now()
  });
  client.webSocket.disconnect();
});

client.webSocket.connect();

Cancel an order:

client.webSocket.cancelOrder(cancellation: {}): Promise<void>

  • cancellation: {} - The cancellation description. Properties are:

    • orderId: string - The id of the order to cancel.

    • reference?: number - A reference to track your order on afr WebSocket channel.

  • returns: a promise containing nothing.

Example:

const client = new Client(/* options */);

client.webSocket.on('open', async () => {
  await client.webSocket.cancelOrder({
    orderId: '155724024975600001',
    reference: Date.now()
  });

  client.webSocket.disconnect();
});

client.webSocket.connect();

Placing encrypted orders

If you want to make sure that no one, not even LGO, would be able to read your order, and front run it, you can activate our anti front running protection.

It uses strong cryptography to conceil your order until we publish a proof about the next batch to execute.

This mechanism ensures that LGO didn’t know the content of your orders before they reach the matching engine.

The process used by the LGO platform is:

basics

Using your own code

Order format

The LGO trading platform handles 3 types of orders: limit order, market order and cancel order (order cancellation).

Each order placed on the platform must have a specific format and must be encrypted with the public keys made available by the Certificate Authority.

If an order is received in the same batch as its order cancellation request, it will not be executed.

If a limit order is already in LGO order book, the order cancellation request will be executed at the start of the batch in which it was added, so that the orders in the batch will not be executed against the canceled order.

Limit order

L,B,BTC-USD,10.5,6001.50,gtc,1565271479654

Considering each field separated by a comma, this string contains:

  • L for limit order

  • B or S for Buy order or Sell order

  • The product code: BTC-USD

  • The order amount (base currency)

  • The order price (quote/counter currency)

  • The time of validity: gtc Good 'til canceled (currently gtc is the only type handled by LGO platform)

  • The timestamp of the current time (milliseconds)

Market order

M,S,BTC-USD,10.5,,,1565271519634

Considering each field separated by a comma, this string contains:

  • M for market order

  • B or S for Buy order or Sell order

  • The product code: BTC-USD

  • The order amount (base currency for sell order and quote currency for buy order)

  • nothing

  • nothing

  • The timestamp of the current time (milliseconds)

Cancel order

C,156389617430400001,1565271555140

Considering each field separated by a comma, this string contains:

  • C for cancel order

  • The identifier of the limit/market order to cancel

  • The timestamp of the current time (milliseconds)

Public keys retrieval

The Certificate Authority publishes a set of public/private key pairs.

Each public key has a validity period during which the private key is not published by the CA.

An order encrypted with an invalid public key (outdated, not valid yet) will be rejected by the LGO platform. This prevents using key pairs in which the private key has already been published and could be used to know the content of your order.

Shortly after the end of the validity period of the public key, the Certificate Authority releases the associated private key to allow transparency and fairness verification by third parties (see https://lgono.de).

Websocket and HTTP API

You should first retrieve a valid public key published by the Certificate Authority.

The URL of publication are:

Sandbox

https://storage.googleapis.com/lgo-sandbox_batch_keys

Production

https://storage.googleapis.com/lgo-markets-keys

On each URL, you can find:

  • /index.txt an index file of all published key pairs identifiers and their validity period

  • /{keyId}_public.pem the public key of identifier keyId

You should first download the last index.txt file. The files contains the following content:

837eea84-e9a2-4fd9-bdff-250796432e47 2019-03-01T15:33:54Z 2019-03-01T16:33:54Z true
7e26961f-4172-4936-807b-c42f604d8147 2019-03-01T16:23:54Z 2019-03-01T17:23:54Z true
...

The fields are, in order:

  • Key pair identifier

  • Public key validity start date (UTC date)

  • Public key validity end date (UTC date)

  • Private key was released by CA

Given this index file content, you should filter keys on their validity period and select a valid key, we recommend using the one with greater validity end date.

You can then download the corresponding public key, and use it for orders encryption until the end of it’s validity period.

Order encryption

Websocket and HTTP API

The order bytes should be encoded with UTF-8 and then encrypted via RSA encryption algorithm with OAEP padding.

Examples of algorithm constant name for some known technologies:

Java Cipher

RSA/ECB/OAEPWithSHA-1AndMGF1Padding

NodeJS crypto

RSA_PKCS1_OAEP_PADDING

Python PKCS#11

RSA_PKCS_OAEP

The encrypted bytes then should be encoded in base 64.

Order signature

Websocket and HTTP API

The bytes to sign should be encoded with UTF-8.

The signature should use RSA with SHA-256 hash and PKCS#1 v1.5 padding.

The key to use to sign is your personal RSA private key.

Examples of algorithm constant name for some known technologies:

Java Signature

SHA256withRSA

SoftHSM

SHA256_RSA_PKCS

Python PKCS#11

SHA256_RSA_PKCS

NodeJS crypto

RSA-SHA256

The signed bytes then should be encoded in hexadecimal.

Encrypted order sending

JSON Payload

The encrypted order and signed string will be used in a JSON payload sent to the API with the following format:

HTTP API
{
  "key_id": "01c610d3-fa5c-43b7-985d-f4c073049221",  (1)
  "order": "egSpygw+E8gRN1nOl4aJTL5xRgif9hLv/3vYt1qMMYOkQB0UfqGOAXE...",  (2)
  "signature": {
    "value": "2e01378e2ca1d40e3d575cc00bf1131e72d7735c083d3783ed28...",  (3)
    "source": "RSA"
  },
  "reference": 1565339956462  (4)
}
1 key_id Identifier of the public key used to encrypt the order
2 order Encrypted order
3 signature.value Encrypted order signature
4 reference An optional numerical reference that you can choose. It will be sent along the order identifier in the ACK message
Websocket API
{
  "order": {
    "key_id": "01c610d3-fa5c-43b7-985d-f4c073049221",  (1)
    "order": "egSpygw+E8gRN1nOl4aJTL5xRgif9hLv/3vYt1qMMYOkQB0UfqGOAXE...",  (2)
    "signature": {
      "value": "2e01378e2ca1d40e3d575cc00bf1131e72d7735c083d3783ed28...",  (3)
      "source": "RSA"
    },
    "reference": 1565339956462  (4)
  },
  "type": "placeorder"
}
1 key_id Identifier of the public key used to encrypt the order
2 order Encrypted order
3 signature.value Encrypted order signature
4 reference An optional numerical reference that you can choose. It will be sent along the order identifier in the ACK message

In case of error you will receive this payload:

{
  "type": "error",
  "reference": 324242,  (1)
  "message": "..."  (2)
}
1 order reference
2 possible message:
  • INTERNAL_SERVER_ERROR (could not validate signature)

  • BAD_SIGNATURE (invalid signature)

  • BACK_PRESSURED (failed to send order to LGO order processor: back pressured)

  • UNEXPECTED (failed to send order to LGO order processor: closed/max position exceeded)

  • TIMEOUT (failed to send order to LGO order processor: timeout)

HTTP API

You should POST the JSON payload as the request body on the following URL:

Sandbox

https://exchange-api.sandbox.lgo.markets/v1/live/orders/encrypted

Production

https://exchange-api.exchange.lgo.markets/v1/live/orders/encrypted

along with the following headers (previously described):

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Content-Type

String

application/json

You will then receive an HTTP status indicating if the LGO platform was available to receive your message. You will receive an HTTP status 400 if the payload does not contain the signature. In case of success, you will receive the generated orderd id :

{
  "order_id": "1989898"
}

A 200 OK response doesn’t imply that the order complies to all rules. Most checks are only processed once the order is in a batch. You still need to check with the proper websocket channel execution details.

Websocket API

You should first open a connection to the LGO websocket on one of these URL:

Sandbox

wss://ws.sandbox.lgo.markets

Production

wss://ws.exchange.lgo.markets

along with the following headers (previously described):

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Send a websocket message containing the JSON payload.

Using our Java library (XChange)

Place a limit order:

LimitOrder limitOrder = new LimitOrder(
        Order.OrderType.BID,  (1)
        new BigDecimal("0.5"),  (2)
        CurrencyPair.BTC_USD,  (3)
        null,  (4)
        new Date(),  (5)
        new BigDecimal("12000.10")  (6)
);
String orderId = exchange
        .getTradeService()
        .placeLimitOrder(limitOrder);
System.out.println("Order was placed with identifier: " + orderId);
1 Order.OrderType.BID or Order.OrderType.ASK
2 amount (base currency)
3 product
4 ignored value
5 actual date
6 price (quote currency)

Place a market order:

MarketOrder marketOrder = new MarketOrder(
        Order.OrderType.BID,  (1)
        new BigDecimal("0.5"),  (2)
        CurrencyPair.BTC_USD,  (3)
        null,  (4)
        new Date()  (5)
);
String orderId = exchange
        .getTradeService()
        .placeMarketOrder(marketOrder);
System.out.println("Order was placed with identifier: " + orderId);
1 Order.OrderType.BID or Order.OrderType.ASK
2 amount (base currency)
3 product
4 ignored value
5 actual date

Cancel a limit or market order:

exchange
        .getTradeService()
        .cancelOrder("156406068135700001");  (1)
1 identifier of the order to cancel

Using our Java streaming library (XChange-stream)

Place a limit order:

LimitOrder limitOrder = new LimitOrder(
        Order.OrderType.BID,  (1)
        new BigDecimal("0.5"),  (2)
        CurrencyPair.BTC_USD,  (3)
        null,  (4)
        new Date(),  (5)
        new BigDecimal("12000.10")  (6)
);
String ref = exchange
        .getStreamingTradeService()
        .placeLimitOrder(limitOrder);
System.out.println("Order was placed with reference: " + ref);
1 Order.OrderType.BID or Order.OrderType.ASK
2 amount (base currency)
3 product
4 ignored value
5 actual date
6 price (quote currency)

Place a market order:

MarketOrder marketOrder = new MarketOrder(
        Order.OrderType.BID,  (1)
        new BigDecimal("0.5"),  (2)
        CurrencyPair.BTC_USD,  (3)
        null,  (4)
        new Date()  (5)
);
String ref = exchange
        .getStreamingTradeService()
        .placeMarketOrder(marketOrder);
System.out.println("Order was placed with reference: " + ref);
1 Order.OrderType.BID or Order.OrderType.ASK
2 amount (base currency)
3 product
4 ignored value
5 actual date

Cancel a limit or market order:

exchange
    .getStreamingTradeService()
    .cancelOrder("156406068135700001");  (1)
1 identifier of the order to cancel

Using our Javascript library

With HTTP

Place an order:

client.placeOrder(order: {}): Promise<void>

  • order: {} - The order to place. Properties are:

    • type: string - L for limit or M for market.

    • side: string - B for buy or S for sell.

    • productId: string - The product id. Example: BTC-USD·.

    • quantity: number - The quantity. Example: 0.05.

    • price?: number - The price. Not available for a market order. Example: 8000.1.

    • reference?: number - A reference to track your order on afr WebSocket channel.

    • key: {} - A key object returned by client.getKeys to encrypt the order.

  • returns: a promise containing nothing.

Example:

import { Client, OrderType, OrderSide } from '@lgo/sdk';

const client = new Client(/* options */);

const keys = await client.getKeys();
const key = client.selectKey(keys);

const order = {
  type: OrderType.limit,
  side: OrderSide.sell,
  productId: 'BTC-USD',
  quantity: 1,
  price: 8000,
  key,
  reference: Date.now()
};

client.placeOrder(order).then(() => console.log('done'));

Cancel an order:

client.cancelOrder(cancellation: {}): Promise<void>

  • cancellation: {} - The cancellation description. Properties are:

    • orderId: string - The id of the order to cancel.

    • reference?: number - A reference to track your order on afr WebSocket channel.

    • key: {} - A key object returned by client.getKeys to encrypt the order.

  • returns: a promise containing nothing.

Example:

import { Client } from '@lgo/sdk';

const client = new Client(/* options */);

const keys = await client.getKeys();
const key = client.selectKey(keys);

const cancellation = {
  key,
  reference: Date.now(),
  orderId: '155663606242700001'
};

client.cancelOrder(cancellation).then(() => console.log('done'));

With websocket

Place an order:

client.webSocket.placeOrder(order: {}): Promise<void>

  • order: {} - The order to place. Properties are:

    • type: string - L for limit or M for market.

    • side: string - B for buy or S for sell.

    • productId: string - The product id. Example: BTC-USD·.

    • quantity: number - The quantity. Example: 0.05.

    • price?: number - The price. Not available for a market order. Example: 8000.1.

    • reference?: number - A reference to track your order on afr WebSocket channel.

    • key: {} - A key object returned by client.getKeys to encrypt the order.

  • returns: a promise containing nothing.

Example:

import { Client, OrderType, OrderSide } from '@lgo/sdk';

const client = new Client(/* options */);

client.webSocket.on('open', async () => {
  const keys = await client.getKeys();
  const key = client.selectKey(keys);
  await client.webSocket.placeOrder({
    type: OrderType.limit,
    side: OrderSide.sell,
    productId: 'BTC-USD',
    quantity: 1,
    price: 50000,
    key,
    reference: Date.now()
  });
  client.webSocket.disconnect();
});

client.webSocket.connect();

Cancel an order:

client.webSocket.cancelOrder(cancellation: {}): Promise<void>

  • cancellation: {} - The cancellation description. Properties are:

    • orderId: string - The id of the order to cancel.

    • reference?: number - A reference to track your order on afr WebSocket channel.

    • key: {} - A key object returned by client.getKeys to encrypt the order.

  • returns: a promise containing nothing.

Example:

const client = new Client(/* options */);

client.webSocket.on('open', async () => {
  const keys = await client.getKeys();
  const key = client.selectKey(keys);
  await client.webSocket.cancelOrder({
    orderId: '155724024975600001',
    key,
    reference: Date.now()
  });

  client.webSocket.disconnect();
});

client.webSocket.connect();

Receive order placement acknowledgement

Order placement can be acknowledged through the Websocket afr channel.

  • "received" events indicate the orderId associated to your order, if you set a reference on order placement you will have this reference in the event.

  • "failed" events indicate that the order could not be read or was invalid and not added to a batch.

Using your own code

Open a websocket connection as described in previous chapter, and connect to the afr channel by sending this message:

{
  "type": "subscribe",
  "channels": [
      {
        "name": "afr"
      }
  ]
}

You will first receive an empty snapshot message, because this channel backend does not keep data in memory.

{
  "payload": [],
  "type": "snapshot",
  "channel": "afr"
}

You will then receive this payload for a received order:

{
  "payload": [
    {
      "time": "2018-11-12T09:41:34.232Z",
      "type": "received",
      "order_id": "154201569423200001",
      "reference": "1565339956462"
    }
  ],
  "type": "update",
  "channel": "afr"
}

and this one for a failed order:

{
  "payload": [
    {
      "order_id": "156017773818900001",
      "type": "failed",
      "reference": "1565339956463",
      "time": "2019-06-10T14:42:18.972Z",
      "reason": "INVALID_PAYLOAD"
    }
  ],
  "type": "update",
  "channel": "afr"
}

You can unsubscribe from this feed by sending this message:

{
  "type": "unsubscribe",
  "channels": [
    {
      "name": "afr"
    }
  ]
}

Using our Java streaming library (XChange-stream)

You will receive LgoReceivedOrderEvent and LgoFailedOrderEvent through the StreamingTradeService:

exchange.getStreamingTradeService()
        .getReceivedOrderEvents()
        .blockingForEach(System.out::println);

Using our Javascript library

Example:

import { Client, Feed } from '@lgo/sdk';

const client = new Client(/* options */);

client.webSocket.on('message', console.log);
client.webSocket.on('open', () => client.webSocket.subscribe([{ name: Feed.afr }]));

client.webSocket.connect();

Update output example:

{
  payload: [
    {
      time: '2018-11-12T09:41:34.232Z',
      type: 'received',
      order_id: '154201569423200001',
      reference: '12345678899'
    }
  ],
  type: 'update',
  channel: 'afr'
}

Receive order updates

Order updates can be known through the Websocket user channel.

  • pending events indicate that the order was added to a batch and received by the execution engine.

  • invalid events indicate that the order was not suitable for execution.

  • match events indicate that the order did match against another order.

  • open events indicate that the order entered the order book.

  • done events indicate that the order was filled, canceled or rejected.

Using your own code

Websocket API

Order updates are grouped by batch and product.

Open a websocket connection as described in previous chapter, and connect to the user channel for BTC-USD product by sending this message:

{
  "type": "subscribe",
  "channels": [
    {
      "name": "user",
      "product_id": "BTC-USD"
    }
  ]
}

You will receive a snapshot message containing all your open orders for this product, and the identifier of the last executed batch:

{
  "payload": [
    {
      "quantity": "1.00000000",  (1)
      "order_creation_time": "2019-07-23T15:36:14.304Z",  (2)
      "price": "6000.0000",  (3)
      "side": "S",  (4)
      "remaining_quantity": "1.00000000",  (5)
      "order_id": "156389617430400001",  (6)
      "order_type": "L"  (7)
    },
    {
      "quantity": "1.00000000",
      "order_creation_time": "2019-07-18T12:24:21.891Z",
      "price": "6000.0000",
      "side": "S",
      "remaining_quantity": "1.00000000",
      "order_id": "156345266189100001",
      "order_type": "L"
    }
  ],
  "batch_id": 6317542,  (8)
  "timestamp": 1578665295204,  (9)
  "type": "snapshot",
  "channel": "user",
  "product_id": "BTC-USD"
}
1 quantity quantity of the order (base currency for limit orders and market sell orders, quote currency for market buy orders)
2 order_creation_time date of reception of the order by LGO
3 price price (quote currency)
4 side B for buy/bid order, S for sell/ask order
5 remaining_quantity remaining quantity of the order (base currency)
6 order_id identifier of the order, set by the LGO platform
7 order_type L for limit order, M for market order
8 batch_id identifier of the last batch
9 timestamp timestamp of the last batch

You will then receive an update payload for each batch, containing your orders updates, if any:

{
  "payload": [
    {
      "quantity": "2.00000000",
      "time": "2019-07-24T08:37:19.116Z",
      "side": "B",
      "price": "8000.0000",
      "type": "pending",
      "order_type": "L",
      "order_id": "156395743911600001"
    },
    {
      "time": "2019-07-24T08:37:19.849Z",
      "type": "open",
      "order_id": "156395743911600001"
    }
  ],
  "batch_id": 6317543,
  "timestamp": 1578665295204,
  "type": "update",
  "channel": "user",
  "product_id": "BTC-USD"
}

You can unsubscribe from this feed by sending this message:

{
  "type": "unsubscribe",
  "channels": [
    {
      "name": "user",
      "product_id": "BTC-USD"
    }
  ]
}
Order event payload
  • type type of event

  • time date of the event

  • side B for buy/bid order, S for sell/ask order

  • quantity quantity of the order (base currency for limit orders and market sell orders, quote currency for market buy orders)

  • price price of a limit order or price of a match (quote currency)

  • order_type L for limit order, M for market order

  • order_id identifier of the order, set by the LGO platform

  • reason reason for invalid or done events

  • liquidity liquidity of a trade, T for taker M for maker

  • remaining_quantity remaining quantity of the order after a match (base currency for limit orders and market sell orders, quote currency for market buy orders)

  • filled_quantity quantity filled by a match (amount of the trade), in base currency

  • trade_id identifier of the trade

  • fees fees of a trade (quote currency)

Pending order
{
  "quantity": "2.00000000",
  "time": "2019-07-24T08:37:19.116Z",
  "side": "B",
  "price": "8000.0000",
  "type": "pending",
  "order_type": "L",
  "order_id": "156395743911600001"
}
Invalid order
{
  "time": "2019-07-24T08:37:19.849Z",
  "type": "invalid",
  "reason": "InvalidAmount",
  "order_id": "156395743912700001"
}
Matched order
{
  "liquidity": "T",
  "time": "2019-08-06T10:00:05.658Z",
  "order_id": "156508560418400001",
  "remaining_quantity": "0.00000000",
  "filled_quantity": "0.50000000",
  "type": "match",
  "price": "955.3000",
  "trade_id": "4441691",
  "fees": "0.2388"
}
Open order
{
  "time": "2019-07-24T08:37:19.849Z",
  "type": "open",
  "order_id": "156395743911600001"
}
Done order
{
  "time": "2019-07-24T08:37:19.922Z",
  "type": "done",
  "reason": "canceled",
  "order_id": "156345266189100001"
}

HTTP API

You can receive your open orders by sending a GET request to:

Sandbox

https://exchange-api.sandbox.lgo.markets/v1/live/orders

Production

https://exchange-api.exchange.lgo.markets/v1/live/orders

along with the following headers (previously described):

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Accept

String

application/json

and the following query parameter:

Name

Type

Description

product_id

string

The product to retrieve orders for. Mandatory. Example: BTC-USD

You will receive this JSON payload as response:

{
  "batch_id": 345888, (1)
  "timestamp": 1578665295204, (2)
  "orders": [
    {
      "order_id": "154211289673900001",  (3)
      "order_type": "L",  (4)
      "quantity": "1.00000000",  (5)
      "price": "7000.0000",  (6)
      "remaining_quantity": "1.00000000",  (7)
      "side": "B",  (8)
      "order_creation_time": "2018-11-13T12:41:36.739"  (9)
    }
  ]
}
1 batch_id identifier of the last processed batch
2 timestamp timestamp of the last processed batch
3 order_id identifier of the order
4 order_type L for limit order, M for market order
5 quantity quantity of the order (base currency)
6 price price of the order (quote currency)
7 remaining_quantity remaining quantity of the order (base currency)
8 side B for buy/bid order, S for sell/ask order
9 order_creation_time date of the order

You will receive a response with HTTP status 400 and this body in case of invalid query parameters:

{
  "message": "PRODUCT_NOT_AVAILABLE"
}

Using our Java streaming library (XChange-stream)

Open orders

You can receive an up-to-date view of your open orders after each LGO batch execution through the StreamingTradeService:

exchange.getStreamingTradeService()
        .getOpenOrders(CurrencyPair.BTC_USD)
        .blockingForEach(System.out::println);

First response you’ll get is the current open orders list.

Order events

You can receive LgoPendingOrderEvent, LgoInvalidOrderEvent, LgoMatchOrderEvent, LgoOpenOrderEvent and LgoDoneOrderEvent, grouped by batch, through the StreamingTradeService:

exchange.getStreamingTradeService()
        .getBatchOrderEvents(Arrays.asList(CurrencyPair.BTC_USD))
        .blockingForEach(System.out::println);

You can also receive the flow of all events (received, failed and batch events):

exchange.getStreamingTradeService()
        .getAllOrderEvents(Arrays.asList(CurrencyPair.BTC_USD))
        .blockingForEach(System.out::println);

Updated orders

You can receive an Order flow of each new state of your placed orders through the StreamingTradeService:

exchange.getStreamingTradeService()
        .getOrderChanges(CurrencyPair.BTC_USD)
        .blockingForEach(System.out::println);

Using our Javascript library

Using the websocket

Example:

import { Client, Feed } from '@lgo/sdk';

const client = new Client(/* options */);

client.webSocket.on('message', console.log);
client.webSocket.on('open', () =>
  client.webSocket.subscribe([{ name: Feed.user, productId: 'BTC-USD' }])
);

client.webSocket.connect();

Snapshot output example:

{
  payload: [
    {
      quantity: '1.00000000',
      order_creation_time: '2018-11-12T09:45:26.475Z',
      side: 'S',
      remaining_quantity: '1.00000000',
      order_id: '154201592647500001',
      order_type: 'L',
      price: '7000.0000'
    },
    {
      quantity: '1.00000000',
      side: 'S',
      remaining_quantity: '1.00000000',
      order_creation_time: '2018-11-12T10:24:54.091Z',
      order_id: '154201829409100001',
      order_type: 'L',
      price: '7000.0000'
    }
  ],
  batch_id: 793,
  type: 'snapshot',
  channel: 'user',
  product_id: 'BTC-USD'
}

Update output example:

{
  payload: [
    {
      quantity: '1.00000000',
      time: '2018-11-12T10:24:54.091Z',
      side: 'S',
      order_id: '154201829409100001',
      type: 'pending',
      order_type: 'L',
      price: '7000.0000'
    },
    {
      order_id: '154201829409100001',
      time: '2018-11-12T10:24:57.104Z',
      type: 'open'
    }
  ],
  batch_id: 794,
  type: 'update',
  channel: 'user',
  product_id: 'BTC-USD'
}

Using Live API for open orders only

client.getMyOpenOrders(query: {}): Promise<{}>

  • query: {} - The query to filter order book. Properties are:

  • returns: a promise of an object containing user’s order.

Example:

client.getMyOpenOrders().then(order => {
  // ...
});

Output example:

{
  batchId: 9818248,
  orders:[
    {
      id: '156984767631800001',
      type: 'L',
      side: 'S',
      status: 'OPEN',
      quantity: 1,
      remainingQuantity: 1,
      price: 50000,
      creationDate: 2019-09-30T12:47:56.318Z,
      productId: 'BTC-USD'
    }
  ]
}

Level 2 Market Data

Level 2 market data of the LGO platform can be known by subscribing to the level2 channel on the websocket API.

Using your own code

Websocket API

Open a websocket connection and connect to the level2 channel for the product of your choice by sending this message:

{
  "type": "subscribe",
  "channels": [
    {
      "name": "level2",
      "product_id": "BTC-USD"
    }
  ]
}

On subscribe, the server will send a snapshot of the current order book, and the last executed batch identifier:

{
  "payload": {
    "bids": [
      ["7998.3000", "6.21470000"],
      ["7998.1000", "2.14420000"],
      ["7997.8000", "6.08540000"]
    ],
    "asks": [
      ["8000.5000", "1.34970000"],
      ["8000.9000", "8.16290000"],
      ["8001.2000", "1.16220000"]
    ]
  },
  "batch_id": 776,
  "timestamp": 1578665295204,
  "type": "snapshot",
  "channel": "level2",
  "product_id": "BTC-USD"
}

Each line (for example: ["7998.3000", "6.21470000"]) contains the price level (quote currency) and the quantity at this price level (base currency).

You will then receive updates for every batch:

{
  "payload": {
    "bids": [],
    "asks": [["7000.0000", "2.00000000"]]
  },
  "batch_id": 777,
  "timestamp": 1578665895204,
  "type": "update",
  "channel": "level2",
  "product_id": "BTC-USD"
}

In an update, a line with quantity 0 means that there is no more order at this price level in the order book.

You can subscribe to multiple product feeds once:

{
  "type": "subscribe",
  "channels": [
    {
      "name": "level2",
      "product_id": "BTC-USD"
    },
    {
      "name": "level2",
      "product_id": "LGO-USD"
    }
  ]
}

You can also unsubscribe from one or many feeds:

{
  "type": "unsubscribe",
  "channels": [
    {
      "name": "level2",
      "product_id": "BTC-USD"
    },
    {
      "name": "level2",
      "product_id": "LGO-USD"
    }
  ]
}

HTTP API

You can receive the current state of the order book with a GET request to:

Sandbox

https://exchange-api.sandbox.lgo.markets/v1/live/products/BTC-USD/book

Production

https://exchange-api.exchange.lgo.markets/v1/live/products/BTC-USD/book

along with the following headers:

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Accept

String

application/json

where BTC-USD is the product identifier you want to retrieve the book for.

You will receive this JSON payload as response:

{
  "batch_id": 345888, (1)
  "timestamp": 1578665295204, (2)
  "product_id": "BTC-USD", (3)
  "bids": [
      ["7998.3000", "6.21470000"],
      ["7998.1000", "2.14420000"],
      ["7997.8000", "6.08540000"]
    ],
    "asks": [
      ["8000.5000", "1.34970000"],
      ["8000.9000", "8.16290000"],
      ["8001.2000", "1.16220000"]
    ]
  ]
}
1 batch_id identifier of the last processed batch
2 timestamp timestamp of the last processed batch
3 product_id product id

Each line (for example: ["7998.3000", "6.21470000"]) contains the price level (quote currency) and the quantity at this price level (base currency).

You will receive a response with HTTP status 400 and this body in case of invalid path parameter:

{
  "message": "PRODUCT_NOT_AVAILABLE"
}

Using our Java streaming library (XChange-stream)

You can receive LGO level 2 market data after each batch execution through the StreamingMarketDataService:

exchange.getStreamingMarketDataService()
        .getOrderBook(CurrencyPair.BTC_USD)
        .blockingForEach(System.out::println);

Using our Javascript library

Example:

import { Client, Feed } from '@lgo/sdk';

const client = new Client(/* options */);

client.webSocket.on('message', console.log);
client.webSocket.on('open', () =>
  client.webSocket.subscribe([{ name: Feed.level2, productId: 'BTC-USD' }])
);

client.webSocket.connect();

Snapshot output example:

{
  payload: {
    bids: [
      ['7998.3000', '6.21470000'],
      ['7998.1000', '2.14420000'],
      ['7997.8000', '6.08540000']
    ],
    asks: [
      ['8000.5000', '1.34970000'],
      ['8000.9000', '8.16290000'],
      ['8001.2000', '1.16220000']
    ]
  },
  batch_id: 776,
  type: 'snapshot',
  channel: 'level2',
  product_id: 'BTC-USD'
}

Update output example:

{
  payload: {
    bids: [],
    asks: [['7998.1000', '1.32120000']]
  },
  batch_id: 777,
  type: 'update',
  channel: 'level2',
  product_id: 'BTC-USD'
}

Trades

Last trades of the LGO platform can be retrieved with the websocket trades channel, or the http live api.

Using your own code

WebSocket API

Open a websocket connection and connect to a trades channel by sending this message with the product of your choice:

{
  "type": "subscribe",
  "channels": [
    {
      "name": "trades",
      "product_id": "BTC-USD"
    }
  ]
}

On subscribe, the server will first send a snapshot of the 100 last trades for the selected product:

{
  "payload": [
    {
      "quantity": "1.00000000",
      "side": "S",
      "price": "100.0000",
      "trade_id": "222",
      "trade_creation_time": "2018-11-22T14:26:24.636Z"
    }
  ],
  "batch_id": 777,
  "timestamp": 1578665295204,
  "type": "snapshot",
  "channel": "trades",
  "product_id": "BTC-USD"
}

You will then receive updates for every batch. The update is limited to the last 100 trades of the batch for the given product.

{
  "payload": [
    {
      "quantity": "2.00000000",  (1)
      "side": "B",  (2)
      "price": "120.0000",  (3)
      "trade_id": "223",  (4)
      "trade_creation_time": "2018-11-22T14:27:23.735Z"  (5)
    },
    {
      "quantity": "0.50000000",
      "side": "S",
      "price": "153.0000",
      "trade_id": "224",
      "trade_creation_time": "2018-11-22T14:27:23.835Z"
    }
  ],
  "batch_id": 778,
  "timestamp": 1578665795204,
  "type": "update",
  "channel": "trades",
  "product_id": "BTC-USD"
}
1 quantity quantity of the match (base currency)
2 side B for buy/bid maker order, S for sell/ask maker order
3 price price of the match (quote currency)
4 trade_id identifier of the match
5 trade_creation_time instant of the match

You can unsubscribe from this feed by sending this message:

{
  "type": "unsubscribe",
  "channels": [
    {
      "name": "trades",
      "product_id": "BTC-USD"
    }
  ]
}

HTTP Api

You can receive last trades by sending a GET request to:

Sandbox

https://exchange-api.sandbox.lgo.markets/v1/live/live/products/BTC-USD/trades

Production

https://exchange-api.exchange.lgo.markets/v1/live/products/BTC-USD/trades

along with the following headers:

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Accept

String

application/json

where BTC-USD is the product id you want trades for.

You will receive this JSON payload as response:

{
  "batch_id": 98797,
  "timestamp": 1578665295204,
  "trades": [
    {
      "trade_id": "182",  (1)
      "side": "B",  (2)
      "price": "7422.6100",  (3)
      "quantity": "1.00000000", (4)
      "trade_creation_time": "2018-08-27T14:41:54.028" (5)
    }
  ]
}
1 trade_id identifier of the match
2 side maker side of this match
3 price price of the match (quote currency)
4 quantity quantity of the match (base currency)

You will receive a response with HTTP status 400 and this body in case of invalid product identifier:

{
  "code": "PRODUCT_NOT_AVAILABLE"
}

Using our Java streaming library (XChange-stream)

You can receive last LGO platform trades (of type Trade) through the StreamingMarketDataService:

exchange.getStreamingMarketDataService()
        .getTrades(CurrencyPair.BTC_USD)
        .blockingForEach(System.out::println);

In XChange API, the order type of a trade represents the taker order type, whereas the websocket API JSON payload sends the maker order type.

Using our Javascript library

Example:

import { Client, Feed } from '@lgo/sdk';

const client = new Client(/* options */);

client.webSocket.on('message', console.log);
client.webSocket.on('open', () =>
  client.webSocket.subscribe([{ name: Feed.trades, productId: 'BTC-USD' }])
);

client.webSocket.connect();

Snapshot output example:

{
  payload: [
    {
      quantity: '1.00000000',
      side: 'S',
      price: '8000.0000',
      trade_id: '222',
      trade_creation_time: '2018-11-22T14:26:24.636Z'
    }
  ],
  batch_id: 777,
  type: 'snapshot',
  channel: 'trades',
  product_id: 'BTC-USD'
}

Update output example:

{
  payload: [
    {
      quantity: '2.00000000',
      side: 'B',
      price: '8000.1000',
      trade_id: '223',
      trade_creation_time: '2018-11-22T14:27:23.735Z'
    },
    {
      quantity: '0.50000000',
      side: 'S',
      price: '8001.3000',
      trade_id: '224',
      trade_creation_time: '2018-11-22T14:27:23.835Z'
    }
  ],
  batch_id: 778,
  type: 'update',
  channel: 'trades',
  product_id: 'BTC-USD'
}

Trading balances

You can receive your trading balance updates by subscribing to the balance channel.

Using your own code

Websocket API

Open a websocket connection as described in previous chapter, and connect to the balance channel by sending this message:

{
  "type": "subscribe",
  "channels": [
    {
      "name": "balance"
    }
  ]
}

On subscribe, the server will first send a snapshot of your actual known balances:

{
  "payload": [
    [
      "BTC", (1)
      "98.76000000", (2)
      "0.00000000", (3)
    ],
    [
      "USD",
      "1003399.5912",
      "0.0000"
      ]
  ],
  "seq": 3,
  "timestamp": 1578665295204,
  "type": "snapshot",
  "channel": "balance"
}
1 currency code
2 available balance
3 in-order balance

You will then receive updates for every change of a balance:

{
  "seq": 3,
  "timestamp": 1578665295804,
  "payload": [["USD", "1003399.5912", "0.0000"]],
  "type": "update",
  "channel": "balance"
}

The sequencer number, seq should be checked to ensure no messages were lost. This number won’t always start at 0, and subsequent checks should be based on the initial sequence number received in the snapshot.

Balances with no data won’t be sent.

You can unsubscribe from this feed by sending this message:

{
  "type": "unsubscribe",
  "channels": [
    {
      "name": "balance"
    }
  ]
}

HTTP API

You can receive your current trading balances with a GET request to:

Sandbox

https://exchange-api.sandbox.lgo.markets/v1/live/balances

Production

https://exchange-api.exchange.lgo.markets/v1/live/balances

along with the following headers (previously described):

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Accept

String

application/json

You will receive this JSON payload as response:

{
  "timestamp": 1578665295204,
  "balances": [
    [
      "BTC", (1)
      "98.76000000", (2)
      "0.00000000", (3)
    ],
    [
      "USD",
      "1003399.5912",
      "0.0000"
    ]
  ]
}
1 currency code
2 available balance
3 in-order balance

Using our Java streaming library (XChange-stream)

You can receive your up-to-date Wallet through the StreamingAccountService:

exchange.getStreamingAccountService()
        .getWallet()
        .blockingForEach(System.out::println);

You can also receive all Balance changes for a currency:

exchange.getStreamingAccountService()
        .getBalanceChanges(Currency.BTC)
        .blockingForEach(System.out::println);

Using our Javascript library

Live balances with the websocket

Example:

import { Client, Feed } from '@lgo/sdk';

const client = new Client(/* options */);

client.webSocket.on('message', console.log);
client.webSocket.on('open', () => {
  client.webSocket.subscribe([{ name: Feed.balance }]);
});

client.webSocket.connect();

Snapshot output example:

{
  payload: [
    ['BTC', '2.76000000', '0.00000000'],
    ['USD', '1003.5912', '0.0000']
  ],
  seq: 3,
  type: 'snapshot',
  channel: 'balance'
}

Update output example:

{
  seq: 3,
  payload: [['USD', '990.5912', '0.0000']],
  type: 'update',
  channel: 'balance'
}

Live balances with http

Example:

import { Client } from '@lgo/sdk';

const client = new Client(/* options */);

client.getMyBalances().then(console.log);

Output example:

{
  BTC: {
    available: '99999991.00000000',
    inOrder: '8.00000000'
  },
  USD: {
    available: '23.0884',
    inOrder: '0.0000'
  }
}

Past Orders

Using your own code

You can retrieve your orders by sending a GET request to:

Sandbox

https://exchange-api.sandbox.lgo.markets/v1/history/orders

Production

https://exchange-api.exchange.lgo.markets/v1/history/orders

along with the following headers (previously described):

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Accept

String

application/json

and the following optional query parameters:

Name

Type

Description

product_id

string

The product to retrieve orders for. Optional. Example: BTC-USD

max_results

int

The maximum results to fetch. Optional, defaults to 100. Cannot exceed 100

page

string

Encoded page representation. Optional. Example: aGVsbG8=

sort

string

DESC or ASC sorting order, by creation date. Optional, defaults to DESC

You will receive this JSON payload as response:

{
  "result": {
    "orders": [
      {
        "id": "154211289673900001",  (1)
        "product_id": "BTC-USD",  (2)
        "type": "L",  (3)
        "quantity": "1.00000000",  (4)
        "price": "7000.0000",  (5)
        "remaining_quantity": "1.00000000",  (6)
        "status": "OPEN",  (7)
        "side": "B",  (8)
        "batch_id": "5",  (9)
        "creation_date": "2018-11-13T12:41:36.739"  (10)
      },
      {
        "remaining_quantity": "1.00000000",
        "status": "OPEN",
        "id": "154211289373800001",
        "batch_id": "4",
        "type": "L",
        "side": "B",
        "product_id": "BTC-USD",
        "quantity": "1.00000000",
        "price": "7000.0000",
        "creation_date": "2018-11-13T12:41:33.738"
      },
      {
        "id": "154241289373800001",
        "product_id": "BTC-USD",
        "type": "L",
        "quantity": "1.00000000",
        "price": "6900.0000",
        "remaining_quantity": "0.00000000",
        "status": "DONE",
        "side": "B",
        "batch_id": "12",
        "creation_date": "2018-11-13T12:41:33.738",
        "done_date": "2019-02-03T01:23:12.420",  (11)
        "status_reason": "Filled"  (12)
      }
    ]
  },
  "nextPage": "aGVsbG8="
}
1 id identifier of the order
2 product_id product code
3 type L for limit order, M for market order
4 quantity quantity of the order (base currency)
5 price price of the order (quote currency)
6 remaining_quantity remaining quantity of the order (base currency)
7 status status of your order (PENDING, INVALID, OPEN, DONE)
8 side B for buy/bid order, S for sell/ask order
9 batch_id identifier of the batch
10 creation_date date of the order
11 done_date date when the order was canceled/rejected/filled
12 status_reason reason of the actual order status:
  • for DONE status: Filled, Rejected, CanceledBySelfTradePrevention, CanceledByOwner, CanceledByAdministrator

  • for INVALID status: InvalidQuantity, InvalidPrice, InvalidAmount, InvalidPriceIncrement, InvalidProduct, InsufficientFunds

You will receive a response with HTTP status 400 and this body in case of invalid query parameters:

{
  "code": "PRODUCT_NOT_AVAILABLE"
}

or

{
  "code": "INVALID_ORDER_STATUS"
}

Using our Javascript library

client.getMyOrders(query: {}): Promise<{}>

  • query: {} - The query to filter orders. Properties are:

    • productId?: string - The product id to retrieve orders for. Example: BTC-USD.

    • status?: string - The status to filter orders. Use OrderStatus enum. Example: OrderStatus.done.

    • maxResults?: number - The maximum results to retrieve.

    • page?: string - The page to load. The next page is sent with result if any.

    • sort?: string - Sort direction. ASC for ascending or DESC for descending. Defaults to DESC.

  • returns: a promise of an object containing user’s orders.

Example:

client.getMyOrders({ productId: 'BTC-USD', maxResults: 3 }).then(orders => {
  // ...
});

Output example:

{
  results: [
    {
      id: '155853503445100001',
      type: 'L',
      side: 'S',
      status: 'OPEN',
      productId: 'BTC-USD',
      quantity: 0.1,
      remainingQuantity: 0.1,
      price: 8000,
      creationDate: 2019-05-22T14:23:54.451Z
    },
    // ...
  ],
  nextPage: 'MTU1ODUzMzE0NDkxMjAwMDAx'
}

Order

Using your own code

You can receive informations and status of one of your orders by sending a GET request to:

Sandbox

https://exchange-api.sandbox.lgo.markets/v1/history/orders/{orderId}

Production

https://exchange-api.exchange.lgo.markets/v1/history/orders/{orderId}

along with the following headers (previously described):

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Accept

String

application/json

You will receive this JSON payload as response if the order was found:

{
    "id": "154211289673900001",  (1)
    "product_id": "BTC-USD",  (2)
    "type": "L",  (3)
    "quantity": "1.00000000",  (4)
    "price": "7000.0000",  (5)
    "remaining_quantity": "1.00000000",  (6)
    "status": "OPEN",  (7)
    "side": "B",  (8)
    "batch_id": "5",  (9)
    "creation_date": "2018-11-13T12:41:36.739",  (10)
    "done_date": "2019-02-03T01:23:12.420",  (11)
    "status_reason": "Filled"  (12)
}
1 id identifier of the order
2 product_id product code
3 type L for limit order, M for market order
4 quantity quantity of the order (base currency)
5 price price of the order (quote currency)
6 remaining_quantity remaining quantity of the order (base currency)
7 status status of your order (PENDING, INVALID, OPEN, DONE)
8 side B for buy/bid order, S for sell/ask order
9 batch_id identifier of the batch
10 creation_date date of the order
11 done_date date when the order was canceled/rejected/filled
12 status_reason reason of the actual order status:
  • for DONE status: Filled, Rejected, CanceledBySelfTradePrevention, CanceledByOwner, CanceledByAdministrator

  • for INVALID status: InvalidQuantity, InvalidPrice, InvalidAmount, InvalidPriceIncrement, InvalidProduct, InsufficientFunds

You will receive an HTTP Status 400 if the order identifier was not a numerical value and 404 if the order was not found.

Using our Javascript library

client.getMyOrder(query: {}): Promise<{}>

  • query: {} - The query to filter order book. Properties are:

  • id: string - The order’s id to load.

  • returns: a promise of an object containing user’s order.

Example:

client.getMyOrder({ id: '155247507925500001' }).then(order => {
  // ...
});

Output example:

{
  id: '155247507925500001',
  type: 'L',
  side: 'S',
  status: 'OPEN',
  productId: 'BTC-USD',
  quantity: 0.1,
  remainingQuantity: 0.1,
  price: 8000,
  creationDate: 2019-05-22T14:23:54.451Z
}

Past User trades

Using your own code

With the HTTP API

You can receive your last trades by sending a GET request to:

Sandbox

https://exchange-api.sandbox.lgo.markets/v1/history/trades

Production

https://exchange-api.exchange.lgo.markets/v1/history/trades

along with the following headers (previously described):

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Accept

String

application/json

and the following optional query parameters:

Name

Type

Description

product_id

string

The product to retrieve trades for. Optional. Example: BTC-USD

max_results

int

The maximum results to fetch. Optional, defaults to 100. Cannot exceed 100

page

string

Encoded page representation. Optional. Example: aGVsbG8=

sort

string

DESC or ASC sorting order, by creation date. Optional, defaults to DESC

You will receive this JSON payload as response:

{
  "result": {
    "trades": [
      {
        "id": "182",  (1)
        "product_id": "BTC-USD",  (2)
        "quantity": "1.00000000",  (3)
        "price": "7422.6100",  (4)
        "fees": "148.4522",  (5)
        "creation_date": "2018-08-27T14:41:54.028",  (6)
        "side": "B",  (7)
        "order_id": "153538091223800000",  (8)
        "liquidity": "T"  (9)
      },
      {
        "id": "183",
        "product_id": "BTC-USD",
        "quantity": "0.10000000",
        "price": "7422.6100",
        "fees": "14.8452",
        "creation_date": "2018-08-27T14:42:45.032",
        "side": "B",
        "order_id": "153538096329100000",
        "liquidity": "T"
      }
    ]
  },
  "nextPage": "aGVsbG8="
}
1 id identifier of the match
2 product_id product code
3 quantity quantity of the match (base currency)
4 price price of the match (quote currency)
5 fees fees of the match (quote currency)
6 creation_date date of the match
7 side B for buy/bid order, S for sell/ask order
8 order_id identifier of your order in the match
9 liquidity liquidity of the trade, T for taker M for maker

You will receive a response with HTTP status 400 and this body in case of invalid product identifier:

{
  "code": "PRODUCT_NOT_AVAILABLE"
}

With the Websocket API

There is no endpoint to retrieve your live trades, but you can use the order updates (user channel) and build your trades from match payloads.

Using our Java streaming library (XChange-stream)

You can receive a flow of your live trades (of type UserTrade) through the StreamingTradeService:

exchange.getStreamingTradeService()
        .getUserTrades(CurrencyPair.BTC_USD)
        .blockingForEach(System.out::println);

Using our Java library (XChange)

You can receive the list of your last trades (of type UserTrade) through the TradeService:

exchange.getTradeService()
        .getTradeHistory(tradeService.createTradeHistoryParams())
        .getUserTrades().forEach(System.out::println);

You can send TradeHistoryParams of the current types (combined if you wish): TradeHistoryParamsSorted, TradeHistoryParamNextPageCursor, TradeHistoryParamCurrencyPair, TradeHistoryParamLimit.

Using our Javascript library

client.getMyTrades(query: {}): Promise<{}>

  • query: {} - The query to filter order book. Properties are:

    • productId: string - The product id to retrieve trades for. Example: BTC-USD.

    • maxResults?: number - The maximum results to retrieve.

    • page?: string - The page to load. The next page is sent with result if any.

    • sort?: string - Sort direction. ASC for ascending or DESC for descending. Defaults to DESC.

  • returns: a promise of an object containing trades.

Example:

client.getMyTrades({ productId: 'BTC-USD', maxResults: 3 }).then(trades => {
  // ...
});

Output example:

{
  results: [
    {
      id: '2002581',
      orderBuyerId: '155913499018100001',
      orderSellerId: '155913499421400001',
      productId: 'BTC-USD',
      quantity: 0.6031,
      price: 8602.9,
      creationDate: 2019-05-29T13:03:15.742Z,
      side: 'B',
      buyerFee: 0.151,
      sellerFee: 2.292
    },
    // ...
  ],
  nextPage: 'MjAwMjU3OQ=='
};

Accounting operations and addresses

Available tokens

In the accounting part, the token parameter can take the following values:

  • USDC

  • PAX

  • SIGNET

  • BTC

Request a deposit address

Using your own code

You can request a deposit address by sending a POST request to:

Sandbox

https://account-api.sandbox.lgo.markets/addresses/deposit-address-request

Production

https://account-api.exchange.lgo.markets/addresses/deposit-address-request

along with the following headers (previously described):

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Content-Type

String

application/json

Accept

String

application/json

and the following JSON payload :

{
  "token": "BTC"
}

You will receive your current deposit address for the given token or an address creation will be initiated if you have none yet.

The PENDING status indicates that the creation process is in progress.

You will receive this JSON payload as response:

{
    "status": "ACTIVE",
    "address": "1BoatSLRHtKNngkdXEeobR22b53LETtpzT"
}

or for a pending address :

{
    "status": "PENDING",
    "address": null
}

Please note that you cannot request a Signature Bank (SIGNET) deposit address with this endpoint. Please reach out to your LGO account manager to do so.

List your whitelisted withdrawal addresses

Using your own code

You can list the withdrawal addresses already whitelisted for your account by sending a GET request to:

Sandbox

https://account-api.sandbox.lgo.markets/addresses/withdrawals

Production

https://account-api.exchange.lgo.markets/addresses/withdrawals

along with the following headers (previously described):

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Accept

String

application/json

You will receive this JSON payload as response:

{
  "addresses": [
    {
        "id": "1d2ff698-0e2f-4b41-a6be-95cfca9ae5b1",
        "address": "1BoatSLRHtKNngkdXEeobR22b53LETtpzT",
        "state": "ACTIVE",
        "token": "BTC"
    }
  ]
}

The possible statuses are: ACTIVE, WAITING_AML_APPROVAL, AML_REJECTED.

WAITING_AML_APPROVAL indicates that our services are reviewing your address.

AML_REJECTED indicates that our services have rejected your withdrawal addresses because of an insufficient AML score.

ACTIVE indicates that you can request withdrawals to this address.

Operations statuses

Using your own code

You can know the status of your accounting operations by sending a GET request to:

Sandbox

https://account-api.sandbox.lgo.markets/operations

Production

https://account-api.exchange.lgo.markets/operations

along with the following headers (previously described):

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Accept

String

application/json

and the following optional query parameters:

Name

Type

Description

type

string

The type of operation to filter on. Optional. DEPOSIT or WITHDRAWAL

currency_code

string

The currency to filter on. Optional. Example: BTC

max_results

int

The maximum results to fetch. Optional, defaults to 100. Cannot exceed 100

page

string

Encoded page representation. Optional. Example: aGVsbG8=

The operations are returned in descending order on creation date.

You will receive this JSON payload as response:

{
  "result": {
    "operations": [
      {
        "id": "1d2ff698-0e2f-4b41-a6be-95cfca9ae5b1",
        "account_id": "10ed0e0d-4763-4cd7-aca3-af845a514330",
        "owner_id": 571503,
        "type": "DEPOSIT",
        "status": "RECONCILED",
        "quantity": "10.00000000",
        "currency": "USDC",
        "counter_party": "something",
        "created_at": 1559124433520,
        "protocol": "ONCHAIN",
        "transaction_id": "0x..."
      }
    ]
  },
  "next_page": ""
}

You will receive an HTTP status 400 if a query parameter is invalid.

Using our Javascript library

client.getMyOperations(query: {}): Promise<{}>

  • query?: {} - The query to filter trades. Properties are:

    • currencyCode?: string - The currency code to retrieve operations for. Example: BTC.

    • type?: string - The type to filter operations for. Possible values are DEPOSIT or WITHDRAWAL.

  • returns: a promise of an object containing user’s operations.

Example:

import { Client, OperationType } from '@lgo/sdk';

const client = new Client(/* options */);

client
  .getMyOperations({ type: OperationType.deposit, currencyCode: 'BTC' })
  .then(operations => {
    // ...
  });

Output example:

{
  results: [
    {
      id: 'bab458d5-577c-4545-9f94-e54ed49ef970',
      accountId: 'b70230f2-8902-4394-b802-34a32fcda558',
      ownerId: 764088,
      type: 'DEPOSIT',
      status: 'RECONCILED',
      quantity: 100,
      currency: 'USDC',
      counterParty: 'something',
      createdAt: 2019-03-20T16:35:09.810Z,
      protocol: 'ONCHAIN',
      transaction_id: '0x...'
    }
  ],
  nextPage: 'PYB8OFUzMzE0NDpsMjAwMBCw'
};

Operation status

Using your own code

You can know the status of one of your accounting operation by sending a GET request to:

Sandbox

https://account-api.sandbox.lgo.markets/operations/{id}

Production

https://account-api.exchange.lgo.markets/operations/{id}

along with the following headers (previously described):

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Accept

String

application/json

You will receive this JSON payload as response:

{
    "id": "1d2ff698-0e2f-4b41-a6be-95cfca9ae5b1",
    "account_id": "10ed0e0d-4763-4cd7-aca3-af845a514330",
    "owner_id": 571503,
    "type": "DEPOSIT",
    "status": "RECONCILED",
    "quantity": "10.00000000",
    "currency": "USDC",
    "counter_party": "something",
    "created_at": 1559124433520,
    "protocol": "ONCHAIN",
    "transaction_id": "0x..."
}

To retrieve the transaction identifier of a withdrawal, you can poll this endpoint until your withdrawal is in the final status PROCESSED.

Request a withdrawal

You can request a withdrawal via API with three conditions:

  • Your trading balance should be credited of all the requested funds

  • Your API key should have the withdrawal privilege (managed in the LGO Desktop Application)

  • The destination address should be whitelisted in the LGO Desktop Application

Using your own code

You can request a withdrawal by sending a POST request to:

Sandbox

https://account-api.sandbox.lgo.markets/withdrawals

Production

https://account-api.exchange.lgo.markets/withdrawals

along with the following headers:

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Content-Type

String

application/json

Accept

String

application/json

and the following JSON payload:

{
  "address": "1BoatSLRHtKNngkdXEeobR22b53LETtpzT",    (1)
  "amount": "2.45",   (2)
  "token": "BTC"   (3)
}
1 address The destination address
2 amount The amount to withdraw
3 token One of the following values: BTC, PAX, USDC, SIGNET

You will receive the operation identifier as response:

{
  "id": "929acd0d-20fd-4385-a983-b53fbc71b7c3"
}

You can then poll the Operation Status endpoint previously described to know the status of your withdrawal request.

Payload signature

The withdrawal request signature is included directly in the Authorization header.

The authorization header value format remains LGO {your-api-key}:{signed-string}.

Here the string to sign is made of the called URL, the timestamp (same as the header X-LGO-DATE value), and the request body (UTF-8 encoded string): {timestamp}\n{url-without-protocol}\n{request-body}. This string should be signed as described in the authentication chapter.

Using our Java library (XChange)

You can request a withdrawal with the AccountService:

    String id =
        exchange
            .getAccountService()
            .withdrawFunds(Currency.BTC, new BigDecimal("2.45"), "1BoatSLRHtKNngkdXEeobR22b53LETtpzT");
    System.out.println("Withdrawal request was placed with identifier: " + id);

Cancel a withdrawal request

You can cancel a withdrawal request via API with two conditions:

  • Your API key should have the withdrawal privilege (managed in the LGO Desktop Application)

  • The withdrawal should still be waiting screening approval

Using your own code

You can request a withdrawal cancellation by sending a DELETE request to:

Sandbox

https://account-api.sandbox.lgo.markets/withdrawals/{withdrawal_id}

Production

https://account-api.exchange.lgo.markets/withdrawals/{withdrawal_id}

along with the following headers:

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

You will receive an empty response with 200 status if it succeeded.

Account informations

Using your own code

You can retrieve your account fees level by sending a GET request to:

Sandbox

https://account-api.sandbox.lgo.markets/me

Production

https://account-api.exchange.lgo.markets/me

along with the following headers (previously described):

Name

Type

Value

X-LGO-DATE

long

Timestamp of current date

Authorization

String

LGO specific signed String

Accept

String

application/json

You will receive this JSON payload as response:

{
  "rates": {
      "taker_rate": "0.25",  (1)
      "maker_rate": "0.18",  (2)
      "default_rates": false  (3)
  },
  "no_restricted_tokens": true,  (4)
  "origin": "EXCHANGE"  (5)
}
1 taker_rate The fee rate applied if you are taker of a trade
2 maker_rate The fee rate applied if you are taker of a trade
3 default_rates Whether or not you benefit from the platform’s default rates
4 no_restricted_tokens Whether or not you have a restricted trading account
5 origin Either EXCHANGE or MARKETS

Errors

Websocket API

When sending message to a websocket you can receive these messages in case of error:

{
  "type": "error",
  "message": "..."  (1)
}
1 possible message:
  • Message type {type} invalid (If you sent another type than subscribe, unsubscribe or placeorder)

  • Message type missing

  • Order payload missing in placeorder message

  • Order signature missing in placeorder message

  • Unexpected error

For Unexpected error, error message will contain the initial payload sent (only if the payload is well formatted).

{
  "type": "error",
  "message": "Unexpected error",
  "content": {
    "payload": {
      "signature": {
        "source": "RSA",
        "value": "9faaa3636cb1bffc7"
      },
      "order": {
        "type": "M",
        "side": "B",
        "product_id": "BTC-USD",
        "quantity": "1",
        "price": null,
        "timestamp": 12344678900900
      },
      "reference": 12345678899
    }
  }
}

You can also receive this message if you used a product identifier that is not handled by the LGO platform:

{
  "code": "PRODUCT_NOT_AVAILABLE",
  "value": "USD-BTC"
}

XChange stream

You can receive the following exceptions:

Reason

Description

InternalServerException

Corresponding to HTTP 500 response from server

ExchangeSecurityException

Corresponding to HTTP 401 response from server

FrequencyLimitExceededException

Corresponding to HTTP 429 response from server

ExchangeException

Other errors