Treasury

FX Trade

Receive real-time foreign exchange rates and initiate currency conversions.

Tutorials

This tutorial describes the functionality of the FX Trade API and also how to connect to the sandbox environment.

You can also:

For additional information, see Technical.

  • To download the OAS 3.0 FX Trade API, in .YAML format, click here
  • To view the reference content and understand the full semantics of this service, open the .YAML file in a tool such as Swagger Editor
  • In this tutorial we will show how to invoke the service with curl.

Important: before starting this tutorial, you must complete everything mentioned in the Getting Started section of the Overview.

Fictional Company FxConversion

In this tutorial, you are a developer of fictional company called FxConversion.

The company FxConversion provides a foreign exchange payment service to small business through multiple channels.

FxConversion does not want to be exposed to currency risks itself and wants to hedge their client transactions immediately with ABN AMRO. Their internal systems are connected to the FX Trade API.

Getting an access token

To invoke an API you must obtain an access token. The token obtained, together with the API key that you obtained from the developer portal during onboarding for sandbox, is required for every call to the FX Trade API.

The easiest way to obtain an access token in sandbox, is to use the basic authentication mechanism. To use the basic authentication mechanism, execute the following:

curl -u FXAPI:z5l2KRWEXnmDvBhssPq7 \
     -X POST https://auth-sandbox.connect.abnamro.com/as/token.oauth2 \
     -d 'grant_type=client_credentials&scope=fxtrade:allowedcurrencypairs:read fxtrade:settlementaccountgroups:read fxtrade:rates:read fxtrade:quotes:read fxtrade:quotes:write fxtrade:orders:read fxtrade:orders:write'

To obtain an access token using certificate-based authentication, which is the required mechanism for production, execute the following:

curl -v -X POST \
     https://auth-sandbox.connect.abnamro.com:8443/as/token.oauth2 \
     --cert {location_of_sandbox_certificate} \
     --key {location_of_sandbox_private_key} \
     -d 'grant_type=client_credentials&client_id={your_client_id}&scope=fxtrade:allowedcurrencypairs:read fxtrade:settlementaccountgroups:read fxtrade:rates:read fxtrade:quotes:read fxtrade:quotes:write fxtrade:orders:read fxtrade:orders:write'

How the private key apiclient.key and public certificate apiclient.crt are obtained is described in Sandbox. For a complete overview of all possible scopes and how to use them, see Technical.

GET Allowed Currency Pairs

As FxConversion supports transactions in multiple currencies it must verify whether it can trade all these currencies with ABN AMRO. If an FX conversion is not possible,it must contact the account manager to arrange for this.

To determine the currency pairs that you can obtain indicative rates and perform currency conversions on, you must perform the following request:

curl -X GET \
     https://api-sandbox.abnamro.com/v1/fxtrade/allowedcurrencypairs \
     -H "Accept: application/json" \
     -H "Authorization: Bearer {your_access_token}" \
     -H "API-Key: {your_api_key}"

You will receive a response that provides you with an array of currency pairs. A response may look like:

[
  "EURUSD",
  "USDEUR",
  "USDJPY",
  "GBPUSD",
  "USDGBP",
  "EURRON"
]

GET Settlement Account Groups

FxConversion has many currency accounts for the same currency with ABN AMRO that they use for managing their cash flows. When hedging a client transaction they need to indicate which currency account must be debited or credited. To hide the burden of specifying the explicit accounts in a transaction with ABN AMRO, settlement account groups are setup which provide instructions on which accounts must be used for the settlement of a sum of money. Small companies will most likely have one single settlement account group only.

To determine the settlement account groups that are set up and can be used when performing a currency conversion, perform the following request:

curl -X GET \
     https://api-sandbox.abnamro.com/v1/fxtrade/settlementaccountgroups \
     -H "Accept: application/json" \
     -H "Authorization: Bearer {your_access_token}" \
     -H "API-Key: {your_api_key}" 

You will receive a response that provides you with an array of settlement account group identifiers. This enables you to verify whether the setup is according to your expectations. A response may look like:

[
  "Client Account",
  "House Account"
]

GET Indicative Rates

Clients of FxConversion want to know the exchange rates that are applicable for their transactions. For this purposes FxConversion publishes the rates for the most traded currencies on their website. To do this, they first obtain the indicative rates from ABN AMRO and then apply a spread to them. One way is to request the rate for a specific currency pair, another way is to ask for a collection of currency pairs. By default the indicative rates are for SPOT, but other tenors are possible, such as: TODAY, TOMORROW and ASAP (As Soon As Possible).

The following request enables you to request the indicative rate for the EURUSD for SPOT:

curl -X GET \
     https://api-sandbox.abnamro.com/v1/fxtrade/rates/EURUSD \
     -H "Accept: application/json" \
     -H "Authorization: Bearer {your_access_token}" \
     -H "API-Key: {your_api_key}"

The response contains the currency pair for the requested indicative rate, the bid or ask rate, mid rate, and a timestamp that indicates the moment the rate was calculated.It also shows the settlement date for a transaction performed at this moment in time. The following is an example response:

{
  "currencyPair": "EURUSD",
  "allInRate": {
    "bidRate": 1.14346,
    "askRate": 1.14686,
    "midRate": 1.14516,
    "effectiveDateTime": "2019-04-05T10:42:31.390Z"
  },
  "settlementDate": "2019-04-09"
}

Use the following to request the indicative rate for the EURUSD, USDGBP, and the USDJPY for the tenor TODAY:

curl -X GET \
     "https://api-sandbox.abnamro.com/v1/fxtrade/rates?currencyPairs=EURUSD,USDGBP,USDJPY&fxRateTenor=TODAY" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer {your_access_token}" \
     -H "API-Key: {your_api_key}"

The response will contain the indicative rates for all the requested currency pairs for which a indicate rate can be calculated. The following is an example response:

[
  {
    "currencyPair": "EURUSD",
    "spotRate": {
      "bidRate": 1.14346,
      "askRate": 1.14686,
      "midRate": 1.14516,
      "effectiveDateTime": "2019-04-05T10:57:39.208Z"
    },
    "swapPoints": {
      "bidPoints": 0.00003,
      "askPoints": 0.00003
    },
    "allInRate": {
      "bidRate": 1.14349,
      "askRate": 1.14689,
      "midRate": 1.14519,
      "effectiveDateTime": "2019-04-05T10:57:39.370Z"
    },
    "settlementDate": "2019-04-05"
  },
  {
    "currencyPair": "USDGBP",
    "spotRate": {
      "bidRate": 0.78792,
      "askRate": 0.79029,
      "midRate": 0.78911,
      "effectiveDateTime": "2019-04-05T10:57:39.370Z"
    },
    "swapPoints": {
      "bidPoints": 0.00002,
      "askPoints": 0.00002
    },
    "allInRate": {
      "bidRate": 0.78794,
      "askRate": 0.79031,
      "midRate": 0.78913,
      "effectiveDateTime": "2019-04-05T10:57:39.370Z"
    },
    "settlementDate": "2019-04-05"
  }
]

In the above response the currency pair USDJPY is not listed, because the moment of requesting the cutoff for today's settlement of JPY has been reached. Also note that the rate contains swap points and an all-in-rate because we are dealing with an outright.

POST Quote

The indicative rates do not represent tradable rates. Therefore, when FXConversion wants to hedge a transaction that it has performed with its client, it will ask for a quote. A quote must be requested to ensure that FXConversion can inspect the bid or ask price to see whether it is dealing with normal market conditions.

Note: FxConversion uses the quote facility in most of its channels first to provide a quote to its clients too.

The following request shows a quote request for buying 25.000,- EUR while selling USD for the tenor ASAP, which means that the settlement should take place as soon as possible:

curl -X POST \
     https://api-sandbox.abnamro.com/v1/fxtrade/quotes \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer {your_access_token}" \
     -H "API-Key: {your_api_key}" \
     -d '{
           "consumerQuoteReference": "SP_2019-04-05_131",
           "quoteRequest": {
             "buyCurrency": "EUR",
             "sellCurrency": "USD",
             "sellAmount": 25000,
             "settlement": "ASAP"
           },
           "settlementAccountGroup": "House Account"
         }'

The response will contain the status of the quote request. If the status is QUOTED all the price details, the effective rate and the contra amount — which is the amount that you must pay or will be buying — as well as the settlement date will be available, and an order can be performed.

The response will contain the status of the quote request. If the status is QUOTED all the price details, the effective rate and the contra amount — which is the amount that you must pay or will be buying — as well as the settlement date will be available, and an order can be performed. The following is an example response:

{
  "quoteId": "0169ed41-ca23-4e5f-9e06-802b61f3ac97",
  "submittedDateTime": "2019-04-05T11:28:25.635Z",
  "quoteStatus": "QUOTED",
  "expirationDateTime": "2019-04-05T11:28:55.635Z",
  "consumerQuoteReference": "SP_2019-04-05_131",
  "buyCurrency": "EUR",
  "sellCurrency": "USD",
  "sellAmount": 25000.00,
  "settlement": "ASAP",
  "settlementAccountGroup": "House Account",
  "currencyPair": "EURUSD",
  "allInRate": {
    "bidRate": 1.1435,
    "askRate": 1.1469,
    "midRate": 1.1452,
    "effectiveDateTime": "2019-04-05T11:28:25.460Z"
  },
  "contraAmount": 21862.70,
  "rate": 1.1435,
  "settlementDate": "2019-04-09",
  "quoteSignature": "AAAAAEZHDGklAuHzbnpAT7hzXEdAGXBn2Hp9VEfdJWcot0LOSsWrprpOa+Bcn3c+vDWwEZi8y4KF0y0BOGE5edvzaqAeOgY+/PHQGeT6pudG1XgaaNRXKCmXWSW2v/cG71eXljVOHS1pb6Yug16POQ=="
}

When the quote status is QUOTED, the response will also contain the expiration date, and the time till when the quote is tradable. It also contains the quote signature, a cryptographic token that must be sent to perform an order on a quote.

If a quote request is REJECTED, a message property will indicate the reason. If a system cannot provide a response on short notice, you will receive a PENDING_NEW status and will need to request for the quote status on a regular basis. For more information, see getting the quote status. Important messages that can impact your workflow are described in Technical.

POST Order

After analysis on the quote, FxConversion decides to trade upon it. The request data used in the quote must be repeated in the order request, together with the quote signature from the offer response, and the settlement account group identifier.

The following request shows an order request for buying 25.000,- EUR while selling USD for the tenor ASAP and based upon the quote that has ben received earlier:

Important:: replace the value for the quoteSignature property with the value from the response for the quote request.

curl -X POST \
     https://api-sandbox.abnamro.com/v1/fxtrade/orders \
     -H "Content-Type: application/json" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer {your_access_token}" \
     -H "API-Key: {your_api_key}" \
     -d '{
           "consumerQuoteReference": "SP_2019-04-05_71",
           "quoteRequest": {
             "buyCurrency": "EUR",
             "sellCurrency": "USD",
             "sellAmount": 25000,
             "settlement": "ASAP"
           },
           "settlementAccountGroup": "House Account",
           "quoteSignature": "{quoteSignature}"
         }'

If the time of submission is before the expiration time of the quote the response will be:

{
  "orderId": "0169eda1-4d02-449f-a76b-a984db12919a",
  "submittedDateTime": "2019-04-05T11:28:31.125Z",
  "orderStatus": "FILLED",
  "consumerOrderReference": "SP_2019-04-05_71",
  "buyCurrency": "EUR",
  "sellCurrency": "USD",
  "sellAmount": 25000.00,
  "settlement": "ASAP",
  "settlementAccountGroup": "House Account",
  "quoteId": "0169ed41-ca23-4e5f-9e06-802b61f3ac97",
  "currencyPair": "EURUSD",
  "allInRate": {
    "bidRate": 1.1435,
    "askRate": 1.1469,
    "midRate": 1.1452,
    "effectiveDateTime": "2019-04-05T11:28:25.460Z"
  },
  "filledAmount": 25000.00,
  "contraAmount": 21862.70,
  "rate": 1.1435,
  "settlementDate": "2019-04-09"
}

If the quote is expired and is no longer tradable, performing an order with the quote signature will result in the following response:

{
  "orderId": "0169eda1-4d02-449f-a76b-a984db12919a",
  "submittedDateTime": "2019-04-05T11:29:51.724Z",
  "orderStatus": "REJECTED_EXPIRED",
  "consumerOrderReference": "SP_2019-04-05_71",
  "buyCurrency": "EUR",
  "sellCurrency": "USD",
  "sellAmount": 25000.00,
  "settlement": "ASAP",
  "settlementAccountGroup": "House Account",
  "quoteId": "0169ed41-ca23-4e5f-9e06-802b61f3ac97"
}

GET quotes

If the POST request for a quote results in a PENDING_NEW state, you can query the quotes endpoint to find out the current status:

Important: replace the value for the quoteId with the value you obtained from the quote response earlier.

curl -X GET \
     https://api-sandbox.abnamro.com/v1/fxtrade/quotes/{quoteId} \
     -H "Accept: application/json" \
     -H "Authorization: Bearer {your_access_token}" \
     -H "API-Key: {your_api_key}"

The following response shows a quote that has EXPIRED in case it was QUOTED before:

{
  "quoteId": "0169ed41-ca23-4e5f-9e06-802b61f3ac97",
  "submittedDateTime": "2019-04-05T11:28:25.635Z",
  "quoteStatus": "EXPIRED",
  "expirationDateTime": "2019-04-05T11:28:55.635Z",
  "consumerQuoteReference": "SP_2019-04-05_131",
  "buyCurrency": "EUR",
  "sellCurrency": "USD",
  "sellAmount": 25000.00,
  "settlement": "ASAP",
  "currencyPair": "EURUSD",
  "allInRate": {
    "bidRate": 1.1435,
    "askRate": 1.1469,
    "midRate": 1.1452,
    "effectiveDateTime": "2019-04-05T11:28:25.460Z"
  },
  "contraAmount": 21862.70,
  "rate": 1.1435,
  "settlementDate": "2019-04-09",
  "quoteSignature": "AAAAAEZHDGklAuHzbnpAT7hzXEdAGXBn2Hp9VEfdJWcot0LOSsWrprpOa+Bcn3c+vDWwEZi8y4KF0y0BOGE5edvzaqAeOgY+/PHQGeT6pudG1XgaaNRXKCmXWSW2v/cG71eXljVOHS1pb6Yug16POQ=="
}

If the POST request is still in the PENDING_NEW state it will continue to query the service for a final state. This will happen with a substantial interval, such as every two seconds, to prevent from receiving an 429 error, which is the 'Too Many Requests' error.

At FxConversion they want to keep an eye on the quote requests that have been submitted, but not yet priced after 3 seconds. For this purpose the monitoring application that was developed performs the following query at a regular interval.

Important: to ensure appropriate response times, you must choose a parameter value for the startDateTime query that represents a short period of time, and is before the current time. This will limit the amount of data that is searched. In this example, the parameter value for the startDateTime query is set to five minutes.

curl -X GET \
     "https://api-sandbox.abnamro.com/v1/fxtrade/quotes?quoteStatus=PENDING_NEW&startDateTime=2019-04-05T11%3A23%3A30Z" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer {your_access_token}" \
     -H "API-Key: {your_api_key}"

A response could look like the one below. It contains two records, one due to dealer intervention, and one for a slow response:

{
  "records": [
    {
      "quoteId": "016a1181-094f-49f5-aa22-1a65f473239a",
      "submittedDateTime": "2019-04-05T11:28:01.125Z",
      "quoteStatus": "PENDING_NEW",
      "message": "quote went into dealer intervention as the amount exceeds EUR 10000.00",
      "consumerQuoteReference": "SP_2019-04-05_79",
      "buyCurrency": "EUR",
      "sellCurrency": "USD",
      "sellAmount": 25000.00,
      "settlement": "ASAP"
    },
    {
      "quoteId": "016a1181-28d2-4c64-8cd1-aed62b72e307",
      "submittedDateTime": "2019-04-05T11:28:28.541Z",
      "quoteStatus": "PENDING_NEW",
      "message": "quote not priced in the specified maximum wait time of '500' ms",
      "consumerQuoteReference": "SP_2019-04-05_87",
      "buyCurrency": "EUR",
      "sellCurrency": "USD",
      "sellAmount": 2500.00,
      "settlement": "ASAP"
    }
  ]
}

By comparing the submitted date time with your local time, assuming you are properly time synchronized, you can check if the quote has been submitted more than three seconds ago. If it has been submitted more than three seconds ago, it can be included in the monitoring overview.

GET orders

If the POST request for an order results in a PENDING_NEW state one can query the orders endpoint to find out the current status:

Important: replace the value for the orderId with the value you obtained from the order response earlier.

curl -X GET \
     https://api-sandbox.abnamro.com/v1/fxtrade/orders/{orderId} \
     -H "Accept: application/json" \
     -H "Authorization: Bearer {your_access_token}" \
     -H "API-Key: {your_api_key}"

The response below shows that an order has been filled:

{
  "orderId": "0169eda1-4d02-449f-a76b-a984db12919a",
  "submittedDateTime": "2019-04-05T11:28:31.125Z",
  "orderStatus": "FILLED",
  "consumerOrderReference": "SP_2019-04-05_71",
  "buyCurrency": "EUR",
  "sellCurrency": "USD",
  "sellAmount": 25000.00,
  "settlement": "ASAP",
  "settlementAccountGroup": "House Account",
  "quoteId": "0169ed41-ca23-4e5f-9e06-802b61f3ac97",
  "currencyPair": "EURUSD",
  "allInRate": {
    "bidRate": 1.1435,
    "askRate": 1.1469,
    "midRate": 1.1452,
    "effectiveDateTime": "2019-04-05T11:28:25.460Z"
  },
  "filledAmount": 25000.00,
  "contraAmount": 21862.70,
  "rate": 1.1435,
  "settlementDate": "2019-04-09"
}

FxConversion can use the service to generate full quote and order reports on demand however, by the end of each trading day they obtain all the orders regardless of their status and uses that for reconciliation purposes. A typical query to obtain this report would be:

curl -X GET \
     "https://api-sandbox.abnamro.com/v1/fxtrade/orders?startDateTime=2019-04-05T00%3A00%3A00Z&endDateTime=2019-04-06T00%3A00%3A00Z" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer {your_access_token}" \
     -H "API-Key: {your_api_key}"

As a result it will give the orders on the day of April 5 2009:

{
  "nextPageKey": "2019-04-05T10:43:27.719Z",
  "records": [
    {
      "orderId": "016a11cf-f3e3-45a5-8f57-a94bab405cf4",
      "submittedDateTime": "2019-04-05T08:21:02.211Z",
      "orderStatus": "FILLED",
      "consumerOrderReference": "SP_2019-04-05_1",
      "buyCurrency": "EUR",
      "sellCurrency": "USD",
      "buyAmount": 75932.00,
      "settlement": "SPOT",
      "settlementAccountGroup": "House Account",
      "quoteId": "016a11cf-f337-4318-9099-e51e958e3686",
      "currencyPair": "EURUSD",
      "allInRate": {
        "bidRate": 1.1435,
        "askRate": 1.1469,
        "midRate": 1.1452,
        "effectiveDateTime": "2019-04-05T08:21:00.607"
      },
      "filledAmount": 75932.00,
      "contraAmount": 87086.41,
      "rate": 1.1469,
      "settlementDate": "2019-04-09"
    },
    ...
    {
      "orderId": "016a11d1-656f-4d17-97ef-f642a19d5d27",
      "submittedDateTime": "2019-04-05T10:43:26.012Z",
      "orderStatus": "FILLED",
      "consumerOrderReference": "SP_2019-04-05_100",
      "buyCurrency": "CHF",
      "sellCurrency": "USD",
      "buyAmount": 29868,
      "settlement": "SPOT",
      "settlementAccountGroup": "House Account",
      "quoteId": "016a11d1-5f9f-457f-863c-c92da9f70761",
      "currencyPair": "USDCHF",
      "allInRate": {
        "bidRate": 0.9957,
        "askRate": 0.9987,
        "midRate": 0.9972,
        "effectiveDateTime": "2019-04-05T10:43:23.838Z"
      },
      "filledAmount": 29868,
      "contraAmount": 29996.99,
      "rate": 0.9957,
      "settlementDate": "2019-04-09"
    }
  ]
}

If the response contains a nextPageKey property, it indicates that the result set for the request is larger than what can be returned based upon the pageSize query property, which defaults to 100. The value of the nextPageKey property can be used as the value for the nextPageKey query parameter. This enables you to get the complete result set by performing multiple queries. An example of a subsequent query is:

curl -X GET \
     "https://api-sandbox.abnamro.com/v1/fxtrade/orders?startDateTime=2019-04-05T00%3A00%3A00Z&endDateTime=2019-04-06T00%3A00%3A00Z&nextPageKey=2019-04-05T10%3A43%3A27.719Z" \
     -H "Accept: application/json" \
     -H "Authorization: Bearer {your_access_token}" \
     -H "API-Key: {your_api_key}"

The above query must be repeated with a nextPageKey query parameter, that has the value from the property with the same name in the response. When the response no longer contains this property we know that we have the records for that day.