OpenAPI

It is possible to place orders on all asset types except for Fx Options, which can only be traded by accepting a quote. When placing orders using OpenAPI, it is important to understand that not all order types and parameters are supported for all asset classes or even across one asset type traded on different exchanges. In order to place an order it is usually required to look up some of the underlying instrument's constraints through instrument services in Reference Data and to get a price through a separate request or through a subscription already set up.

Order Properties

AccountKey (recommended)

Used to indicate what account the resulting position is on. For clients with sub clients, the order can also be placed on a sub client's account.

Amount (mandatory)

When choosing an amount there are certain rules to obey

  • If the amount is very small or very large the order may fail
  • For exchange based asset types, if the value of the order is too small, the order will fail
  • For asset types traded in lots, there may be rules around whether odd lot sizes are allowed - this can be looked up by the instrument's LotSizeType
  • In general the amount is always in the base unit of the instrument


OrderType (mandatory)

Most common types are market, limit and stop. See the full overview in the article on Core Business Concepts.

OrderPrice

Must be provided on all order types other than market orders. See section on rounding.

StopLimitPrice

Must be provided for StopLimitOrders. Controls the stop level part of the stoplimit.

ToOpenClose

This value is only relevant and mandatory for StockIndexOption, StockOption and FuturesOption. By setting this value you indicate the desired netting behavioir of the resulting position. 

Note on Examples in this Article

Values used in the different examples are often dynamic and must be looked up to get the specific ones. The examples illustrate rules by using values that can change.

The examples in this article pertain to version 2 of the orders endpoint. Almost all of the concepts explained are also valid for version 1. The main benefit of the new version 2 endpoint is better support for placing related orders and OCO orders in particular.

Placing Single Orders

Not all instruments support all order types or even all duration types. To find out what order types are supported, look at the instrument data returned by Reference Data. Most instrument support market orders with a duration of the current day. This is demonstrated by this example placing a market order on the EURUSD instrument.

Market

POST https://gateway.saxobank.com/sim/openapi/trade/v2/orders
{
	"AccountKey": "1ZsMyKSda2eB2GCMrUCKHw==",
	"Amount": 10000.0,
	"BuySell": "Buy",
	"OrderType": "Market",
	"Uic": 21,
	"AssetType": "FxSpot",
	"OrderDuration": {
		"DurationType": "DayOrder"
	}
}

Market orders are executed when the market is open and on whatever price there is in the market at the time of execution. This makes market orders unsuited for instruments with low liquidity. Especially when also having a long duration time.

Limit

In order to place a limit order we need a target order price at a reasonable distance to the market. A price can be obtained from a price subscription or simply by requesting:

GET https://gateway.saxobank.com/sim/openapi/trade/v1/infoprices?uic=21&AssetType=FxSpot 
{
	"AccountKey": "1ZsMyKSda2eB2GCMrUCKHw==",
	"AssetType": "FxSpot",
	"Quote": {
		"Ask": 1.06337,
		"Bid": 1.06317,
		"DelayedByMinutes": 0
	},
	"Uic": 21
}

Let's say we want to place a limit order 50 pips away from the market, then the target price would be 1.06287. However for EURUSD valid tick sizes are rounded to nearest 0.00005, so the target order price becomes 1.06285. The order would then be:

POST https://gateway.saxobank.com/sim/openapi/trade/v2/orders
{
	"AccountKey": "1ZsMyKSda2eB2GCMrUCKHw==",
	"Amount": 10000.0,
	"BuySell": "Buy",
	"OrderType": "Limit",
    "OrderPrice": 1.06285,
	"Uic": 21,
	"AssetType": "FxSpot",
	"OrderDuration": {
		"DurationType": "DayOrder"
	}
}

Decimals, TickSize and Tick Size Schemes

Calculating a correct OrderPrice value is easy for Fx, but complicated for other asset types where there are explicit rules for what values are valid and how they are rounded. Those rules are controlled by the instrument's tick size and tick size scheme. For instruments that does not have a tick size or tick size scheme, order prices simply need to be rounded to the instrument's number of decimals. For the few instruments are quoted in fractions, the order price must conform to the tick size on the instrument.

Decimals

A price is always denoted in the the number of decimals supported by the instrument. The number of decimals can be found on the instrument data looked up in the Reference Data. However for certain Fx crosses the number is one higher if the format has the flag AllowDecimalPips.

When there is no tick size scheme

The order price has to be rounded to the number of decimals of the instrument and then adjusted to the tick size of the instrument. In the above example EURUSD has 4 (+1) decimals and a tick size of 0.00005. This means that a price like 1.062873 should be rounded to 1.06287 due to the number of decimals, but since the tick size is 0.00005, the correct rounding is 1.06285.

When there is a tick size scheme

Certain instruments have more complex price rounding rules. That is indicated by the tick size scheme on the instrument. It gives specific tick sizes depending on a price range. If the actual price of the instrument is above the maximum price value in the tick size scheme, the above normal rounding rules apply. The use of tick size schemes are illustrated by this example:


Example: Suggesting an OrderPrice for a CFD on International Consolidated Airlines Group SA (IAG:xlon)

Assume you want to place a buy limit order for IAG:xlon at 2% away from the market, you need to know the tick size scheme for IAG:xlon as well as the current market price. Right now it is traded at bid 446.1, ask 446.4. 

The tick size scheme for the instrument looked up in reference data is this:

High Price
Tick Size
0.49990.0001
0.99950.0005
4.9990.001
9.9950.005
49.990.01
99.950.05
499.90.1
999.50.5
4999.01.0
9995.05.0

Since the desired price is in the ]99.99; 499.9] range, we need to round the price to nearest 0.1. Also known as the tick size relevant for the desired order price. In the end the formula for selecting a price n percent away from the market becomes: 

orderPrice = Round((currentPrice- currentPrice*n/100)/tickSize)*tickSize

So filling in the values for a buy order we get

currentPrice = 446.4 // currentPrice is the ask side since we are placing an order to buy
n = 2 
tickSize = 0.1 
orderPrice = Round((446.4-446.5*2/100)/0.1)*0.1 => orderPrice = 437.5

Support for Related Orders

It is possible to relate orders to an entry order or position for most asset types. The exceptions are StockIndex (which is not tradable), FutureContracts and FutureStrategies. For the other asset types support for related orders can be deduced from the supported order types retrieved on the instrument. Related orders can be placed together with the entry order or one by one.

Throttling of order placement is per request

Placing Multiple Related Orders

It is possible to place up to three orders together in a relation. For instance to place an entry order together with a non-working order to guard the resulting position against a drop-off in price.

POST https://gateway.saxobank.com/sim/openapi/trade/v2/orders
{
	"Amount": 10000.0,
	"BuySell": "Buy",
	"OrderPrice": 1.06375,
	"OrderType": "Limit",
	"Uic": 21,
	"AssetType": "FxSpot",
	"OrderDuration": {
		"DurationType": "DayOrder"
	},
	"Orders": [
	{
		"BuySell": "Sell",
		"OrderPrice": 1.05875,
		"OrderType": "StopIfOffered",
		"OrderDuration": {
			"DurationType": "GoodTillCancel"
		}
	}
	]
}

The service support placing two related orders to an entry order, but the related orders have to be one limit order and one of the various supported stop order types. The result of succesfully placing the above structure is a result containing the resulting two order ids:

HTTP/1.1 200 OK
{
	"OrderId": 49036233,
	"Orders": [
	{
		"OrderId": 49036234
	}
	]
}

Adding a Related Order to an Existing Order

It is recommended to place multiple related orders together as one operation. However, if there already is an existing order it too can be done:

POST https://gateway.saxobank.com/sim/openapi/trade/v2/orders
{
	"OrderId": 49036233,
	"Orders": [
	{
		"Uic": 21,
		"BuySell": "Sell",
		"Amount": 10000,
		"OrderPrice": 1.06575,
		"AssetType": "FxSpot",
		"OrderType": "Limit",
		"OrderDuration": {
			"DurationType": "GoodTillCancel"
		}
	}
	]
}

The above example adds a second related order to the entry order + stop order from the previous example. It does so, by providing a new order in the related orders collection and referring to the entry order instead of having a full order structure. If it is desired to add two related orders to an entry order it is recommended to do that as one operation, but a second order can be appended later. The request to add the second related order needs to refer to the order id of the entry order as well the first related order. The request that needs to be sent contains the entire structure of a three way order, but where the two first orders are only referred to by id.

{
	"OrderId": 49036233,
	"Orders": [
	{
		"OrderId": 49026234
	},
	{
		"Uic": 21,
		"BuySell": "Sell",
		"Amount": 10000,
		"OrderPrice": 1.06075,
		"AssetType": "FxSpot",
		"OrderType": "StopIfOffered",
		"OrderDuration": {
			"DurationType": "GoodTillCancel"
		}
	}
	]
}

It is important to be aware of some pitfalls of doing this:

  • It is an error to provide an orderid together with any other parameters. You are either providing a reference to an order or an order placement request
  • Relating orders to an already existing order increases the risk that the entry order has been executed when the new related order is entered
  • You can only add two related orders and only one of them can be a limit order
  • The above example does not explicitly use an accountkey to identify the account the order should be on. In this case the client's default account is then used. However, if the entry order had explicitly been placed on another account, the related order should also explicitly be placed on that account. In general it is better to be very explicit about what account a given order is placed on
  • While amount on the related order can be different between entry and related order, that is not recommended. The related orders are intended to guard the entire resulting position and cannot be used to control netting.

Adding a Related Order to an Existing Position

This only works for clients on End-of-Day netting mode.

This is very similar to how an order is added to an existing entry order. The only difference is that instead of providing a reference to the entry order id, a reference to the PositionID must be provided instead. The same concerns as for related orders apply here as well.

Place an Order to Close a Position

There are two ways of closing a position. The most simple one is to open a position for the same amount in the opposite direction and let the netting process remove them. If the client is on End-of-Day-netting it happens overnight - on Intraday-netting the process should be immediate. In some cases you'll want to explicitly control netting by relating an order to the position (again: this can only be done for clients on End-of-Day-netting).

An order to close is often something you want done now and is no different from placing any other related order. The rules that a position can only have two related orders where a maximum of one is a limit order still apply. So if the goal is to explicitly close a position now, the most simple way is to a) ensure that the position has no related orders and b) relate a market order (or aggressive limit order) to the position. Assuming the position has no related order, the request looks like this:

POST https://gateway.saxobank.com/sim/openapi/trade/v2/orders 
{
	"PositionId": 1275430,
	"Orders": [
	{
		"Uic": 21,
		"BuySell": "Sell",
		"Amount": 30000,
		"AssetType": "FxSpot",
		"OrderType": "Market",
		"OrderDuration": {
			"DurationType": "GoodTillCancel"
		}
	]
}

The catch is that a position can only be related to one other position, so if this method is used to partially close a position, it can never be fully explicitly closed.

Placing OCO Orders

OCO orders are a special case where there is no single entry order, but instead is a list of (two) equally valid entry orders.

POST https://gateway.saxobank.com/sim/openapi/trade/v2/orders
{
	"Orders": [
	{
		"Uic": 21,
		"BuySell": "Buy",
		"Amount": 30000,
		"OrderPrice": 1.05375,
		"AssetType": "FxSpot",
		"OrderType": "Limit",
		"OrderDuration": {
			"DurationType": "GoodTillCancel"
		}
	},
	{
		"Uic": 21,
		"BuySell": "Buy",
		"Amount": 30000,
		"OrderPrice": 1.06875,
		"AssetType": "FxSpot",
		"OrderType": "StopIfOffered",
		"OrderDuration": {
			"DurationType": "GoodTillCancel"
		}
	}
	]
}

Cancelling Orders

Cancelling orders involves calling DELETE with both an OrderId and and AccountKey of the order that is to be deleted

DELETE https://gateway.saxobank.com/sim/openapi/trade/v2/orders/49039256?AccountKey=1ZsMyKSda2eB2GCMrUCKHw==

If a delete is issued against an entry order, that can lead to a silent cascading delete on the related orders as well. Multiple orders can be cancelled in one call by providing a comma separated list of OrderIds as long as the orders are on the same account. However, the orders are attempted cancelled one by one and cancellation will stop if an error is encountered.

It is not guaranteed that an order can always be cancelled. Right before an order is about to be executed it is locked for a short while. While it is in that state cancelling will lead to an error.

Editing Orders

Editing orders involves calling PATCH and providing an OrderId in addition to the new values for the fields. For instance changing the above single order to a new target price of 1.06270 can look like this (assuming the order id of the order was 49039256)

PATCH https://gateway.saxobank.com/sim/openapi/trade/v2/orders
{
    "AccountKey": "zsnh|NO42RmzjC4oPovKGA==",
	"Amount": 10000.0,
	"BuySell": "Buy",
	"OrderType": "Limit",
    "OrderPrice": 1.06270,
    "OrderId": 49039256,
	"Uic": 21,
	"AssetType": "FxSpot",
	"OrderDuration": {
		"DurationType": "DayOrder"
	}
}

If an order is changed, all fields relevant for that order must be provided. Order editing can be done as a single operation by providing the entire order structure.

Handling Timeouts

For orders on exchange based products, order placement will once in a while timeout with a TradeNotCompleted. The status TradeNotCompleted simply means that the request terminated without knowing the status of the resulting order, which is often placed just fine. If an OrderId is also present in the the response, that serves as a reference to the order's status which can then be queried through the Portfolio.

Preventing duplicate orders

OpenAPI has some level of protection against duplicate orders. For example placing exactly the same order within 15 seconds without specifying a different x-request-id header will be rejected (see Rate Limiting). However, especially when providing an x-request-id header, the calling application should also implement de-bouncing ensure only requesting order placement, when the client actually intends to place an order. Finally, a calling application must wait until a call to OpenAPI returns. Most of the time the call to /trade/v2/orders will return within less than one second. But in the rare cases where the call does not return quickly, because we are awaiting confirmation from an external entity, the calling application should not make a second call to "re-try" placing the same order.

Common Error Messages

MessageComment
OrderRelatedPositionMissMatchUsually happens when a related order is placed on a different account from the position or the position does not exist
PriceExceedsAggressiveToleranceCan occur when placing a limit order where the condition for the order is already met by a huge distance
TooFarFromMarketCan occur when placing a limit order where the condition for the order is unlikely to be met
OnWrongSideOfMarketOccurs when relating an order to a position, and the order's target price is on the wrong side of the current market price
PriceNotInTickSizeIncrementsOccurs when an order price does not confirm to tick size or formatting rules