OpenAPI

The order activities resource expose events related to order placement, modification, execution and cancellation. 

Compared to the Event Notification Service (/ens/v1/clientactivities), this resource stores entries much further back in time (2+ years). On the other hand it does not support streaming, thus:

  • If you want to request the entire history of an order, you should use this resource
  • If you want near real-time information about order events, you should use /ens/v1/clientactivities and use the streaming capability.

Using the Audit/OrderActivities resource

With the /cs/v1/audit/orderactivities endpoint, an individual client can:

  • get the latest execution state of his or her orders by setting EntryType=Last
  • audit how the orders were executed by getting all the historical order activities by setting EntryType=All

A partner client, who usually owns a lot of end clients, can set IncludeSubAccounts=true and thereby:

  • get the latest execution status of all its end trading clients' orders
  • get the whole order history of all its end trading clients

Get the Execution History of a single Order

The example below illustrates the simple example of the placement and execution of a market order. This usually results in two entries: "Placed" and "FinalFill".

To retrieve the order history,  the client can query the /audit/orderactivities endpoint as shown below:

GET https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities/?OrderId=1000001&EntryType=All
200 - OK

{
  "__count": 2,
  "Data": [
    {
      "ActivityTime": "2018-05-15T15:14:43.908000Z",
      "LogId": "34139673",
      "OrderId": "1000001",
	  "Amount": 500,
	  "OrderType": "Market",
      "Status": "Placed",
      "SubStatus": "Confirmed",
	  ...
      ...
    },
    {
      "ActivityTime": "2018-05-15T15:14:44.588000Z",
      "LogId": "34139674",
      "OrderId": "1000001",
	  "FillAmount": 500,
	  "FilledAmount": 500,
	  "AveragePrice": 99.9,
	  "PositionId": "19968201",
	  "OrderType": "Market",
      "Status": "FinalFill",
      "SubStatus": "Confirmed"
	  ...
	  ...
    }
  ]
}

The first field is called "__count" field. Just as its name indicates, this field shows how many order activities are in the response. There were 2 events in our example.

Then the "Data" field contains the details of each order activity. Here we have condensed each activity into 5 fields to simplify the example. 

  • "ActivityTime" logs the time when this event happened
  • "LogId" is the identifier of each event or order activity
  • "OrderId" shows which order this entry relates to
  • "Price" and "Amount" refers to the order price and order amount
  • "FillAmount" shows how many has been filled this time if it is a partial fill
  • "FilledAmount" shows how many has been filled collectively if there are several partial fills
  • "Average Price" calculates the average filled price
  • "PositionId" gives the Position id if order got filled
  • "Status + SubStatus": could be Placed – Confirmed, Cancelled – Confirmed/Rejected, Changed – Confirmed/Rejected, Fill – Confirmed for partial fill, FinalFill – Confirmed for total fill etc
  • "Status" specifies what kind of event has happened on an order:
    1. Placed: Orders has been placed with Saxo.
    2. Changed: Order has been changed.
    3. Fill - Partial fill
    4. FinalFill: Order is 100%filled
    5. Cancelled: Order is cancelled
    6. Expired: Order is expired.
    7. DoneForDay: Order has been cancelled for today, will be replaced tomorrow.
  • The most common values of "SubStatus" are "Confirmed" and "Rejected". For example,
    1. A {"OrderId": 10002,  Status": "Placed", "SubStatus": "Confirmed" } order activity means the order has been successfully placed;
    2. A {"OrderId": 10002, Status": "Cancelled", "SubStatus": "Confirmed" } order activity means that the cancellation has been successfully executed;
    3. In case that the cancellation cannot be executed if the order has already been filled, there would be a {"OrderId": 10002, Status": "Cancelled", "SubStatus": "Rejected" } entry.
    4. Occasionally you will see entries having {Status": "Cancelled", "SubStatus": "Requested"}  for some instruments. Such entries are legacy ones which records the time when SAXO receives the requests from the end clients. SAXO is in the process of removing them so that there will be no "Requested" entries in the future.

Getting the Last State of My Orders

It is common that after placing an order, a client needs to know about the latest order status after placing an order. In this case, it is not necessary for the client to know the whole order history but the order activity entry having the last status.

A query to retrieve the last entry for the above order is shown below:

GET https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities/?OrderId=1000001&EntryType=Last
200 - OK

{
  "__count": 1,
  "Data": [
    {
      "ActivityTime": "2018-05-15T15:14:44.588000Z",
      "LogId": "34139673",
      "OrderId": "1000001",
      "Status": "FinalFill",
      "SubStatus": "Confirmed"
	  ...
	  ...
    }
  ]
}

It is also important to notice that the order activity entry having the last status is not always the last event or entry in an order history. Here is an example

  1. A client requested to cancel their order => {"OrderId": 10003, Status": "Cancelled", "SubStatus": "Requested"}
  2. Unfortunately the order was just filled at the same time  => {"OrderId": 10003, Status": "FinalFill", "SubStatus": "Confirmed"}
  3. The cancelation order then got rejected  => {"OrderId": 10003, Status": "Cancelled", "SubStatus": "Rejected"}

In this case, there were 3 order events. The last entry in this order history is "Rejected-Canceled". But this entry does not represent the most recent (and current) order status. The last status is the FinalFill-Confirmed one, which happened prior to the Rejected-Canceled event.  By specifying EntryType=Last, Saxo will infer the last order status according to the whole history and return {"OrderId": 10003, Status": "FinalFill", "SubStatus": "Confirmed"} in the response, 

Get Order Activities of All my Trading Clients Periodically

It is a common requirement of a partner company that they need to store a completed order history of all their end clients in its own repository. This can be achieved either by using a user under the owner and

  • Subscribing to ENS and then receiving real time Order events for all underlying clients (preferred!), or
  • Periodically requesting the /orderactivities endpoint for the most recent order log entries of all underlying clients

The request to /orderactivities must

  • be originated by a user under the owner of the end clients
  • set IncludeSubAccounts=true to return order log entries of all the underlying clients
  • set EntryType=All for all the entries ( or just simply ignore the EntryType parameter since All is its default value)
  • set $top = <a number between 200 and 500>. This is for limiting the size of entries in the response. And by our testing, the performance reaches the best when the size is no more than 500 entries
  • set $skiptoken = <a number representing the logId from where the search begins>

The below example shows that at T time,  a partner wants to take no more than 200 order log entries of its end clients. As a result, two entries, 10000 and 10006 were found and returned. 

Period 1: GET https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities?top=200&skiptoken=9000&IncludeSubAccounts=true

{
  "__count": 2,
  "__nextPoll": "https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities?$top=200&$skiptoken=11117&FieldGroups=DisplayAndFormat&IncludeSubAccounts=true"
  "Data": [
      {
           "LogId": 10000
      }, 
     {
           "LogId": 10006
      }
   ]
}

Period 2: GET https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities?$top=200&$skiptoken=11117&FieldGroups=DisplayAndFormat&IncludeSubAccounts=true


Besides, SAXO also returns a field called __nextPoll. It is a URL containing skiptoken=11117 which means next time, SAXO recommends you to search from LogId 11117 because we have noticed that order logs before 11117 have been taken by other clients. Therefore, the partner should query /orderactivities?$top=200&skiptoken=11117&FieldGroups=DisplayAndFormat&IncludeSubAccounts=true on T + a few seconds. This time there is no new order log entries. But __nextPoll is still updated by the best starting index of next time.

Period 1: GET https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities?top=200&skiptoken=11117&IncludeSubAccounts=true

{
  "__count": 0,
  "__nextPoll": "https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities?$top=200&$skiptoken=11220&FieldGroups=DisplayAndFormat&IncludeSubAccounts=true"
  "Data": []
}

Period 2: GET https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities?$top=200&$skiptoken=11220&FieldGroups=DisplayAndFormat&IncludeSubAccounts=true


In case more than $top entries are foundein the result, the endpoint will only return the $top rows of them as the first page and the URL to fetch the next page will be available in the __next filed. Usually the $skiptoken parameter in the __next fields is the largest LogId in the Data array. By seeing the __next field, it means that you haven't yet received the complete dataset this period so that you may request the next page immediately. Or leave the next pages in the next periodical call. 

Period 1: GET https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities?top=200&skiptoken=9000&IncludeSubAccounts=true

{
  "__count": 200,
  "__next": "https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities?$top=200&$skiptoken=10206&FieldGroups=DisplayAndFormat&IncludeSubAccounts=true"
  "Data": [
      {
           "LogId": 10000
      }, 
     .....
     {
           "LogId": 10206
      }
   ]
}

Here we choose to get the next page immediately by sending the second request in period 1. You may also leave the next pages to Period 2.
GET https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities?$top=200&$skiptoken=10206&FieldGroups=DisplayAndFormat&IncludeSubAccounts=true
{
  "__count": 1,
  "__nextPoll": "https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities?$top=200&$skiptoken=10240&FieldGroups=DisplayAndFormat&IncludeSubAccounts=true"
  "Data": [
      {
           "LogId": 10221
      }
   ]
}


Period 2: https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities?$top=200&$skiptoken=10240&FieldGroups=DisplayAndFormat&IncludeSubAccounts=true


You can get the next page immediately by sending the second request in period 1.

Period 1: 
GET https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities?$top=200&$skiptoken=10206&FieldGroups=DisplayAndFormat&IncludeSubAccounts=true
{
  "__count": 1,
  "__nextPoll": "https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities?$top=200&$skiptoken=10240&FieldGroups=DisplayAndFormat&IncludeSubAccounts=true"
  "Data": [
      {
           "LogId": 10221
      }
   ]
}


Period 2: https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities?$top=200&$skiptoken=10240&FieldGroups=DisplayAndFormat&IncludeSubAccounts=true


Or wait and get the rests in the next period.

Period 2: 
GET https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities?$top=200&$skiptoken=10206&FieldGroups=DisplayAndFormat&IncludeSubAccounts=true
{
  "__count": 1,
  "__nextPoll": "https://gateway.saxobank.com/sim/openapi/cs/v1/audit/orderactivities?$top=200&$skiptoken=10290&FieldGroups=DisplayAndFormat&IncludeSubAccounts=true"
  "Data": [
      {
           "LogId": 10221
      },
	  {
           "LogId": 10222
      },
	  {
           "LogId": 10223
      }
   ]
}