Premium APIsForeign ExchangeFX Listed Rates API v2

Listed Rates API Documentation

Overview

Listed Rates API is designed for use cases where you you need an FX rate that is valid for a longer period of time. An example use case could be an eCommerce solution with a shopping cart that needs to have a tradable FX conversion rate available, to be able to show the price in the customer’s home currency for the entire duration of the customer checkout process.

Through this API, you are able to retrieve tradable FX rates valid for a duration of up to 24 hours at a time for FX SPOT, TODAY and TOM value dates.

Onboarding Process

In the developer portal, no additional onboarding is needed and you can start testing your workflow right away. When you are ready to migrate your solution to production, a Nordea FX Sales onboarding process is required to map the correct company IDs and other data before live trading can begin. Additionally, it is possible to be onboarded on a pre-production system based on live market data from our UAT environment, however this requires a whitelisting of your IP ranges. In both cases, please contact e-TradingSystems@nordea.com for more information.

Listed Rates API Endpoints

The API has four endpoints. The developer portal version is based on mocked data only and does not reflect live FX rates on the market. However, the behaviour of the API is designed to be as close to production functionality as possible.

EndpointSupported HTTP Methods
/companiesGET
/prices/{company-id}/{currency-pair}GET
/orders/POST
/orders/status/{company-id}/{client-order-id}GET

Companies

The companies endpoint can be used to retrieve all currently configured company id’s for your user. In the Sandbox environment this is a static list of a few pre-configured companies, but in the production environment this list will correspond to your Nordea Markets company ids as mapped and provided to you by FX Sales during the onboarding process.

Prices

The prices endpoint is used to retrieve list rates for a given company id and currency combination.

Each rate will contain a valid_until field which indicates the expiration time of your rate. In sandbox, this is currently configured to be two hours from the time of your request, but in a live production environment, this setting can vary from five minutes to 24-hours depending on your requirements.

Each price retrieved is a firm tradable price*. You are able to trade on the same rate id in both directions (buy or sell) until its expiration time. Your system should be configured to retrieve new rates immediately after your rates have expired.

Significant Market Moves

If the FX market for a given currency pair moves more than 3% in either direction (client’s favour or bank’s favour) from the time that the rate was generated, the rates are automatically expired. This event is referred to as a *Significant Market Move. In this rare scenario, you may have retrieved a tradable rate and will experience a rejection upon sending a trade indicating an expired rate. In this case, your system should be configured to retrieve a new rate for the affected currency pair and execute the trade using the new rate instead.

Typically the risk of a market move of this magnitude is minimal and unlikely to affect regular use cases. However, use cases with emerging markets currencies combined with long validity periods may experience this behaviour.

Orders

The orders endpoint can be used to send orders based on retrieved prices. For instance, if you have retrieved prices for EUR/USD that are still valid, you can use the rate_id of that price to perform a currency conversion at the specified rate. An example could be selling EUR and thus buying USD.

The orders endpoint will respond with a trade confirmation or rejection within a given timeout period.

On occasion, the orders endpoint will respond with a PENDING order status. This simply means that Nordea has received your order and it will be processed as soon as possible. However, from your point of view, you can treat it simply as a confirmation of the trade that will be done at a later stage.

Orders enter PENDING status during periods where we are unable to book the trade immediately, for example during weekends when the market is closed, or during nightly dateroll. At such a time the order is queued for immediate execution when our booking engine is able to do so.

Order Lifecycle

The order lifecycle is simple: trade entered -> (PENDING) -> CONFIRMED / REJECTED. The order will only enter a PENDING stage in special circumstances. The order can never change from CONFIRMED or REJECTED back to PENDING.

Order Status

By querying the Order Status endpoint, you can request a status of a submitted order. This can be especially useful if you have received a PENDING trade confirmation and want to check on the order status at a later point in time. The endpoint will return the exact same trade confirmation or rejection as the orders endpoint, but with the current status of the order.

Table of Operations

Each operation is based on simple conversion from currency A to currency B. For example, if you have retrieved a list rate for EUR/USD because you have some Euros that you would like to sell against US Dollars, you would do a conversion from EUR to USD. Likewise, if you have some US Dollars that you would like to convert to Euros, you would do a conversion from USD to EUR.

Additionally it is possible to do more complex conversions such as selling X Euros by buying 1000 USD (from X EUR to 100 USD).

This leads to the following table of operations. We assume the order amount to be 1000 each time:

CURRENCY_PAIRFROM_CURRENCYTO_CURRENCYAMOUNT_CURRENCYRESULTING TRADE
EUR/USDEURUSDEURSELL 1000 EUR - BUY X USD
EUR/USDUSDEURUSDSELL 1000 USD - BUY X EUR
EUR/USDEURUSDUSDSELL X EUR - BUY 1000 USD
EUR/USDUSDEUREURSELL X USD - BUY 1000 EUR

The X amount will be determined by the FX rate referenced by the rate id.

Supported FX Tenors

A tenor refers to a fixed value date in the future that takes into account all banking holidays in the two currencies involved. For example during easter, a EUR/DKK TOM trade executed on Wednesday will have settlement on the following Tuesday due to the bank holidays of Thursday, Friday and Monday in Denmark.

Available tenors

The available tenors will be configured to you by your FX sales manager based on your needs.

  • TODAY
  • TOM
  • SPOT

Opening Hours / Daily Cut-Off Hours

It is possible to execute FX trades 24 hours a day. However, settlement for TODAY is always limited to 17:00 CET, after which you must execute on TOM or SPOT tenor until the nightly dateroll, which takes place at 5pm New York Time nightly.

API Timeout Values

You must specify a REST timeout of minimum 15 seconds before abandoning each call. While nearly all transactions will complete immediately with few milliseconds of latency, there are cases where we must perform a credit check that can add delay. Before 15 seconds have passed you are guaranteed either a confirmation of trade (which may or may not be in a PENDING state) or a rejection of a trade.

Client Order ID requirements

The client order ID is your unique reference to a given trade. You can use this to track market orders on your side. When specifying a client order ID, you should specify an ID that is maximum 24 characters long. The ID must be unique for each trading day.

Example Usage

The basic flow for trading on listed rates is as follows, using EUR/USD as an example.

  1. Retrieve latest list rates for EUR/USD by requesting them from the prices endpoint.
  2. Store the rate id for EUR/USD in your application together with the expiration timestamp (UTC)
  3. Send BUY/SELL trades using the specific rate id
  4. If necessary, retrieve the order status of a PENDING order at a later stage from the order status request endpoint.
  5. Retrieve new list rates for EUR/USD when the expiration time has passed.

TLS Cipher Suites

Nordea uses secure HTTPS TLS1.2 cipher suites for all endpoints. In practice, any modern platform should support the below HTTPS ciphers out-of-box, but applications running on older operating systems (for example: older than Windows 8.1) may have to use additional software to wrap your connection with.

The minimum cipher suites levels are:

  • TLS1.2-ECDHE-RSA-AES256-GCM-SHA384 (TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384)
  • TLS1.2-ECDHE-RSA-AES128-GCM-SHA256 (LS_ECDH_RSA_WITH_AES_128_GCM_SHA256)

Migrate to Production

Once you have completed your integration to our FX Trade Retriever API, you are ready to migrate to production. This requires an onboarding process together with Nordea FX Sales.

In order to migrate to Production, please contact e-TradingSystems@nordea.com or your Nordea representative for additional information.

API Examples

Here you can find examples on how to use the FX List Rate API endpoints. All examples can also be tried in the API Console.

Note that in all of the example cURL commands you must change the x-ibm-client-id, and x-ibm-client-secret header values to the correct ones. However, the sandbox will not enforce the x-ibm-client-id so any value will work here. In a production environment the valid id is strictly enforced.

You can find the client id, and client secret from your app console. As a part of the production onboarding you must specify the client id** value to your FX sales manager who will map it to Nordea’s FX systems accordingly. Failing to do so will result in a rejection of your request.

If you need to replace your client id you must contact your FX sales representative or contact e-TradingSystems@nordea.com

cURL Examples

Retrieve available companies for trading.

GET listed-rates-pricing/v2/companies HTTP/1.1

The following cURL can be used to request availabe companies for your client id.

$ curl --request GET 'https://api.nordeaopenbanking.com/listed-rates-pricing/v2/companies' \
    -H 'accept: application/json' \
    -H 'x-ibm-client-id: CLIENT_ID' \
    -H 'x-ibm-client-secret: CLIENT_SECRET'

Example response for companies in the sandbox environment.

[
    "COMPANY_A",
    "COMPANY_B",
    "COMPANY_C"
]

Retrieve list rates for e.g. EUR/USD

In this example we are retrieving list rates for EUR/USD for all for COMPANY_A

GET listed-rates-pricing/v2/prices/{company-id}/{currency-pair} HTTP/1.1
$ curl --request GET 'https://api.nordeaopenbanking.com/listed-rates-pricing/v2/prices/COMPANY_A/EURUSD' -i \
    -H 'accept: application/json' \
    -H 'x-ibm-client-id: CLIENT_ID' \
    -H 'x-ibm-client-secret: CLIENT_SECRET'

Example response for COMPANY_A from the sandbox. Note the UTC expiration time in the valid_until** field. The buy_rate refers to the price you can BUY EUR with, and the sell_rate refers to the price that you can SELL EUR with.

[
    {
        "rate_id": "b51a1b84-4de3-40c5-ba5a-99b9da21e50c",
        "company_id": "COMPANY_A",
        "currency_pair": "EUR/USD",
        "tenor": "SPOT",
        "valid_until": "2019-04-05T12:19:01.31Z",
        "buy_rate": 1.12474,
        "sell_rate": 1.12471
    },
    {
        "rate_id": "542de971-b047-48d6-8252-bd44a78be300",
        "company_id": "COMPANY_A",
        "currency_pair": "EUR/USD",
        "tenor": "TOM",
        "valid_until": "2019-04-05T12:19:01.31Z",
        "buy_rate": 1.12474,
        "sell_rate": 1.12471
    },
    {
        "rate_id": "33745130-ff60-40db-94da-5c26a1f34ae5",
        "company_id": "COMPANY_A",
        "currency_pair": "EUR/USD",
        "tenor": "TODAY",
        "valid_until": "2019-04-05T12:19:01.31Z",
        "buy_rate": 1.12474,
        "sell_rate": 1.12471
    }
]

Send a trade for EUR/USD on a given list rate: SELL 10000 EUR (buy X USD) trade

POST listed-rates-trading/v2/orders HTTP/1.1
$ curl 'https://api.nordeaopenbanking.com/listed-rates-trading/v2/orders' -i \
    -H 'accept: application/json' \
	-H 'Content-Type: application/json' \
    -H 'x-ibm-client-id: CLIENT_ID' \
    -H 'x-ibm-client-secret: CLIENT_SECRET' \
	-d '{ "company_id": "COMPANY_A",
  "amount_currency": "EUR",
  "client_order_id": "594df134-739b-4c46-bc71b",
  "from_currency": "EUR",
  "order_amount": 10000,
  "rate_id": "b51a1b84-4de3-40c5-ba5a-99b9da21e50c",
  "to_currency": "USD"
}}' 

Example response for a confirmed SELL 10000 USD (buy X EUR) trade:

{
    "client_order_id": "594df134-739b-4c46-bc71b",
    "order_status": "FILLED",
    "company_id": "COMPANY_A",
    "fx_rate": 1.12471,
    "transaction_time": "2019-04-05T11:29:30.995Z",
    "value_date": "SPOT",
    "order_reject_reason": ""
}

Send a trade for EUR/USD on a given list rate: SELL 10000 USD

POST listed-rates-trading/v2/orders HTTP/1.1
$ curl 'https://api.nordeaopenbanking.com/listed-rates-trading/v2/orders' -i \
    -H 'accept: application/json' \
	-H 'Content-Type: application/json' \
    -H 'x-ibm-client-id: CLIENT_ID' \
    -H 'x-ibm-client-secret: CLIENT_SECRET' \
	-d '{ "company_id": "COMPANY_A",
  "amount_currency": "EUR",
  "client_order_id": "594df134-739b-4c46-bc71b",
  "from_currency": "USD",
  "order_amount": 10000,
  "rate_id": "b51a1b84-4de3-40c5-ba5a-99b9da21e50c",
  "to_currency": "EUR"
}}' 

Example response for a confirmed SELL 10000 USD (buy X EUR) trade:

{
    "client_order_id": "594df134-739b-4c46-bc71b",
    "order_status": "FILLED",
    "company_id": "COMPANY_A",
    "fx_rate": 1.12474,
    "transaction_time": "2019-04-05T11:26:02.868Z",
    "value_date": "SPOT",
    "order_reject_reason": ""
}

Note: operation is always on the base currency. So in this EUR/USD example we are selling USD, i.e buying EUR, hence we are dealing on the buy_rate** of the rate_id=b51a1b84-4de3-40c5-ba5a-99b9da21e50c**.

Request status of order

GET listed-rates-trading/v2/orders/status/{company-id}/{client-order-id}?order-status-req-id=XYZ HTTP/1.1
$ curl --request GET 'https://api.nordeaopenbanking.com/listed-rates-trading/v2/orders/status/COMPANY_A/594df134-739b-4c46-bc71b?order-status-req-id=XYZ' -i \
    -H 'accept: application/json' \
    -H 'x-ibm-client-id: CLIENT_ID' \
    -H 'x-ibm-client-secret: CLIENT_SECRET' 

The response will be the same filled execution report as in the original trade, with the added OrderStatusRequest id.

{
    "client_order_id": "594df134-739b-4c46-bc71a",
    "order_status": "FILLED",
	"order_status_req_id" : "XYZ",
    "company_id": "COMPANY_A",
    "fx_rate": 1.12471,
    "transaction_time": "2019-04-05T11:32:45.891Z",
    "value_date": "SPOT",
    "order_reject_reason": ""
}

Java examples

Retrieve available companies for trading.

GET listed-rates-pricing/v2/companies HTTP/1.1

The following Java snippet can be used to retrieve available companies

// OkHttpClient from http://square.github.io/okhttp/
 
OkHttpClient client = new OkHttpClient();
 
Request request = new Request.Builder()
  .url("https://api.nordeaopenbanking.com/listed-rates-pricing/v2/companies")
  .get()
  .addHeader("x-ibm-client-id", "REPLACE_THIS_KEY")
  .addHeader("accept", "*/*")
  .build();
 
Response response = client.newCall(request).execute();

Example response for companies in the sandbox environment.

[
    "COMPANY_A",
    "COMPANY_B",
    "COMPANY_C"
]

Retrieve list rates for e.g. EUR/USD

In this example we are retrieving list rates for EUR/USD for all for COMPANY_A

GET listed-rates-pricing/v2/prices/{company-id}/{currency-pair} HTTP/1.1
// OkHttpClient from http://square.github.io/okhttp/
 
OkHttpClient client = new OkHttpClient();
 
Request request = new Request.Builder()
  .url("https://api.nordeaopenbanking.com/listed-rates-pricing/v2/prices/COMPANY_A/EURUSD")
  .get()
  .addHeader("x-ibm-client-id", "REPLACE_THIS_KEY")
  .addHeader("accept", "application/json")
  .build();
 
Response response = client.newCall(request).execute();

Example response for COMPANY_A from the sandbox. Note the UTC expiration time in the valid_until** field. The buy_rate refers to the price you can BUY EUR with, and the sell_rate refers to the price that you can SELL EUR with.

[
    {
        "rate_id": "b51a1b84-4de3-40c5-ba5a-99b9da21e50c",
        "company_id": "COMPANY_A",
        "currency_pair": "EUR/USD",
        "tenor": "SPOT",
        "valid_until": "2019-04-05T12:19:01.31Z",
        "buy_rate": 1.12474,
        "sell_rate": 1.12471
    },
    {
        "rate_id": "542de971-b047-48d6-8252-bd44a78be300",
        "company_id": "COMPANY_A",
        "currency_pair": "EUR/USD",
        "tenor": "TOM",
        "valid_until": "2019-04-05T12:19:01.31Z",
        "buy_rate": 1.12474,
        "sell_rate": 1.12471
    },
    {
        "rate_id": "33745130-ff60-40db-94da-5c26a1f34ae5",
        "company_id": "COMPANY_A",
        "currency_pair": "EUR/USD",
        "tenor": "TODAY",
        "valid_until": "2019-04-05T12:19:01.31Z",
        "buy_rate": 1.12474,
        "sell_rate": 1.12471
    }
]

Send a trade for EUR/USD on a given list rate: SELL 10000 USD

POST listed-rates-trading/v2/orders HTTP/1.1
// OkHttpClient from http://square.github.io/okhttp/
 
OkHttpClient client = new OkHttpClient();
 
MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{\"amount_currency\":\"USD\",\"client_order_id\":\"clientOrderId1\",\"company_id\":\"COMPANY_A\",\"from_currency\":\"USD\",\"order_amount\":10000,\"rate_id\":\"b51a1b84-4de3-40c5-ba5a-99b9da21e50c\",\"to_currency\":\"EUR\"}");
Request request = new Request.Builder()
  .url("https://api.nordeaopenbanking.com/listed-rates-trading/v2/orders")
  .post(body)
  .addHeader("x-ibm-client-id", "REPLACE_THIS_KEY")
  .addHeader("content-type", "application/json")
  .addHeader("accept", "application/json")
  .build();
 
Response response = client.newCall(request).execute();

Example response for a confirmed SELL 10000 USD (buy X EUR) trade:

{
    "client_order_id": "clientOrderId1",
    "order_status": "FILLED",
    "company_id": "COMPANY_A",
    "fx_rate": 1.12474,
    "transaction_time": "2019-04-05T11:26:02.868Z",
    "value_date": "SPOT",
    "order_reject_reason": ""
}

Request status of order

GET listed-rates-trading/v2/orders/status/{company-id}/{client-order-id}?order-status-req-id=XYZ HTTP/1.1
// OkHttpClient from http://square.github.io/okhttp/
 
OkHttpClient client = new OkHttpClient();
 
Request request = new Request.Builder()
  .url(https://api.nordeaopenbanking.com/listed-rates-trading/v2/orders/status/COMPANY_A/clientOrderId1?order-status-req-id=XYZ")
  .get()
  .addHeader("x-ibm-client-id", "REPLACE_THIS_KEY")
  .addHeader("accept", "application/json")
  .build();
 
Response response = client.newCall(request).execute();

The response will be the same filled execution report as in the original trade, with the added OrderStatusRequest id.

{
    "client_order_id": "clientOrderId1",
	"order_status_req_id" : "XYZ",
    "order_status": "FILLED",
    "company_id": "COMPANY_A",
    "fx_rate": 1.12474,
    "transaction_time": "2019-04-05T11:26:02.868Z",
    "value_date": "SPOT",
    "order_reject_reason": ""
}