...
minLevel | 1 |
---|---|
maxLevel | 2 |
location | top |
Overview
The Exchange Streaming API provides low latency access to Betfair Exchange market data allowing you to subscribe to and efficiently track changes to market, price, and order data.
The protocol is based on ssl sockets (normal) with a CRLF JSON protocol. We publish a definition of the schema of the JSON messages in the Swagger format.
We maintain sample code in Java, C#, and Node.js here: https://github.com/betfair/stream-api-sample-code
Sample Application - C#, Java & Node.js
...
Table of Content Zone | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
OverviewThe Exchange Streaming API provides low latency access to Betfair Exchange market data allowing you to subscribe to and efficiently track changes to market, price, and order data. The protocol is based on ssl sockets (normal) with a CRLF JSON protocol. We publish a definition of the schema of the JSON messages in the Swagger format. We maintain sample code in Java, C#, and Node.js here: https://github.com/betfair/stream-api-sample-code Sample Application - C#, Java & Node.jsA console-based C#, Java and Node.js sample application is available for the Market & Order Streaming API and is available via https://github.com/betfair/stream-api-sample-code Users wishing to interact with the Streaming API using one of these languages are strongly advised to make use of this sample code. Swagger DefinitionFor users wishing to use other languages or develop their own implementation, we provide a swagger schema to allow browsing & code generation. We recommend using Swagger Code Gen (http://swagger.io/swagger-codegen/) for generation,
Users wishing to interact with the Streaming API using one of these languages are strongly advised to make use of this sample code. Swagger DefinitionFor users wishing to use other languages or develop their own implementation, we provide a swagger schema to allow browsing & code generation. We recommend using Swagger Code Gen (http://swagger.io/swagger-codegen/) for generation,
The Swagger editor can also be used to view the domain model
Typical Interactions with Stream API:The typical API interactions are documented below (detail is below this). Market Stream: Order Stream: ConnectionProtocolEvery message is in json & terminated with a line feed (CRLF):
TCP / SSL ConnectionConnection is established with an SSL socket to the following address: External (SSL):
Pre-production (beta) endpoint
Integration Endpoint
Basic Message ProtocolTwo base message classes exist:
Every child message type has:
RequestMessageRequestMessage is the base class for requests from the client; the discriminator is op=<message type> Key fields:
ResponseMessageResponseMessage is the base class for responses back to the client; the discriminator is op=<message type> Key fields:
Status / StatusMessageEvery request receives a status response with a matching id. Key fields:
ErrorCodeThis categorizes the various error codes that could be expected (these are subject to change and extension)
Connection / ConnectionMessageThis is received by the client when it successfully opens a connection to the server Key fields:
Authentication / AuthenticationMessageThis message is the first message that the client must send on connecting to the server - you must be authenticated before any other request is processed. Key fields:
Subscription / SubscriptionMessageThis message changes the client's subscription - there are currently two subscription message types:
On creating a subscription you will receive:
It is possible to subscribe multiple times - each replaces the previous (each will send a new initial image and deltas) - they are not additive. Key fields on a SubscriptionMessage:
ChangeMessageThis message is the payload that delivers changes (both initial image & updates) to a client - there are currently two change message types:
Key fields on a ChangeMessage:
Change Message SegmentationThe below shows the key interactions for subscription & changes with segmentation applied: Typically on changing your subscription, you will want to clear any local cache you maintain.
MarketSubscriptionMessageThis subscription type is used to receive price changes for one or more markets; your subscription criteria determine what you see.
A Market Subscription has two types of filter:
Limiting the amount of data that you consume will make your initial image much smaller (and faster) & suppress changes that are uninteresting to you. Market Filtering / MarketFilterAs with the APING API users have the ability to filter the market data they get from the new Exchange Stream API (ESA). All subscriptions are evaluated with a few default criteria:
Users can then specify the following filters when they subscribe to ESA:
Example marketSubscriptionFor example a subscription message with almost all filters enabled will look something like this:
Market data field filtering / MarketDataFilterA market data filter restricts the fields that you get back (and only if the fields have changed). Key fields:
The field filter flags are defined as shown below.
ExamplesMultiple field filters may be combined; a subscription message that contains data fields should look like the following:
The below example shows how to correctly use the ladderLevels marketDataFilter:
MC / MarketChangeMessageThis is the ChangeMessage stream of data we send back to you once you subscribe to the market stream. Key fields:
Building a price cacheMost of the change-based data (RunnerChange) is delta based - this means a few rules:
MarketDefinition FieldsThe following fields are returned within the marketDefintition.
|
Filter name | Type | Mandatory | Default | Description |
---|---|---|---|---|
accountIds | Set<Integer> | No | null | This is for internal use only & should not be set on your filter (your subscription is already locked to your account). |
includeOverallPosition | Boolean | No | true | Returns overall / net position (OrderRunnerChange.mb / OrderRunnerChange.ml) |
customerStrategyRefs | Set<String> | No | null | Restricts to specified customerStrategyRefs (specified in placeOrders) ; this will filter orders and StrategyMatchChanges accordingly (Note: overall position is not filtered) |
partitionMatchedByStrategyRef | Boolean | No | false | Returns strategy positions (OrderRunnerChange.smc=Map<customerStrategyRef, StrategyMatchChange>) - these are sent in delta format as per overall position. |
Example
Code Block |
---|
{"op":"orderSubscription","orderFilter":{"includeOverallPosition":false,"customerStrategyRefs":["betstrategy1"],"partitionMatchedByStrategyRef":true},"segmentationEnabled":true} |
OCM / OrderChangeMessage
This is the ChangeMessage stream of data we send back to you once you subscribe to the order stream.
Key fields:
<as for ChangeMessage>
oc / OrderAccountChange - the modifications to account's orders (will be null on a heartbeat)
closed - indicates when the market is closed
id / Market Id - the id of the market the order is on
fullImage - replace existing data at market level with the data supplied: it is not a delta (or null if delta)
orc / Order Changes - a list of changes to orders on a runner
fullImage - replace existing data at runner level with the data supplied: it is not a delta (or null if delta)
id / Selection Id - the id of the runner (selection)
hc / Handicap - the handicap of the runner (selection) (null if not applicable)
uo / Unmatched Orders - orders on this runner that are unmatched
Every order change is sent in full; the transient on a change to EXECUTION_COMPLETE is sent (but it would not be sent on the initial image)
id / Bet Id - the id of the order
p / Price - the original placed price of the order
s / Size - the original placed size of the order
bsp / BSP Liability - the BSP liability of the order (null if the order is not a BSP order)
side / Side - the side of the order
status / Status - the status of the order (E = EXECUTABLE, EC = EXECUTION_COMPLETE)
pt / Persistence Type - whether the order will persist at in play or not (L = LAPSE, P = PERSIST, MOC = Market On Close)
ot / Order Type - the type of the order (L = LIMIT, MOC = MARKET_ON_CLOSE, LOC = LIMIT_ON_CLOSE)
pd / Placed Date - the date the order was placed
md / Matched Date - the date the order was matched (null if the order is not matched)
cd / Cancelled Date - the date the order was cancelled (null if the order is not cancelled)
ld / Lapsed Date - the date the order was lapsed (null if the order is not lapsed)
lsrc/Lapse Status Reason Code - the reason that some or all of this order has been lapsed (null if no portion of the order is lapsed)
avp / Average Price Matched - the average price the order was matched at (null if the order is not matched
sm / Size Matched - the amount of the order that has been matched
sr / Size Remaining - the amount of the order that is remaining unmatched
sl / Size Lapsed - the amount of the order that has been lapsed
sc / Size Cancelled - the amount of the order that has been cancelled
sv / Size Voided - the amount of the order that has been voided
rac / Regulator Auth Code - the auth code returned by the regulator
rc / Regulator Code - the regulator of the order
rfo / Reference Order - the customer supplied order reference
rfs / Reference Strategy - the customer-supplied strategy reference used to group orders together - default is ""
Price point / full depth Ladders (price, size - tuples - keyed by price) of matches:
mb / Matched Backs - matched amounts by distinct matched price on the Back side for this runner
ml / Matched Lays - matched amounts by distinct matched price on the Lay side for this runner
Building an order cache
An order cache is somewhat simpler as orders are sent in full (on change) and only matches need delta merging
fullImage - if the market or runner's fullImage value is set to true then you should replace this item in your cache N.B. it is possible for the fulllImage flag to be sent with an empty update for a market/runner which indicates you no longer have any position on that market/runner and it can be removed from your cache completely
Orders - replace each order according to order id.
Price point / full depth ladders
[1.2, 20] -> Insert / Update price 1.2 with size 20
[1.2, 0] -> Remove price 1.2 i.e. there is no size at this price
An empty list of points also means the ladder is now empty
Tip |
---|
CurrenciesMarket subscriptions - are always in underlying exchange currency - GBP. The default roll-up for GBP is £1 for batb / batl and bdatb / bdatl, This means that stakes of less than £1 (or currency equivalent) are rolled up to the next available price on the odds ladder. For atb / atl there is no roll-up. Available volume is displayed at all prices including those with less than £2 available. Orders subscriptions - are provided in the currency of the account that the orders are placed in. |
Tip |
---|
Unmatched OrdersNew subscriptions: Will receive an initial image with only E - Executable orders (unmatched). Live subscriptions: Will receive a transient of the order to EC - Execution Complete as the order transits into that state (allowing you to remove the order from your cache). Please note: EXECUTION_COMPLETE (fully matched) orders are only returned when transitioning from EXECUTABLE to EXECUTION_COMPLETE. The full details of EXECUTION_COMPLETE orders can only be viewed using listCurrentOrders/listMarketBook using orderProjections. |
Tip |
---|
Market Level SnapshotsDuring normal streaming, you may on rare occasions receive a market-level snapshot, in which case you should replace the item in your cache. The update will be a fullImage, as shown in the example below {"clk": "AIElAJgiAIYjAMAhAOsm", "oc": [{"orc": [{"uo": [{"status": "E", "rfs": "", "sm": 0, "pt": "L", "sr": 2, "rc": "REG_GGC", "sv": 0, "side": "B", "p": 990, "s": 2, "pd": 1603894536000, "sl": 0, "sc": 0, "ot": "L", "rfo": "", "id": "215144775671", "rac": ""}], "id": 30246, "fullImage": true}], "id": "1.174743281", "fullImage": true}], "pt": 1603895058618, "op": "ocm"} |
Example Output of Order Stream Message on Connection/Re-connection
Here's an example showing the data provided following a connection/re-connection to the Order Stream API. The example shows matched backs on two separate markets one of which has a size remaining of 0.25.
Example of Order Stream Output (reconnection) - with size remaining
Code Block |
---|
{ "op": "ocm", "id": 6, "initialClk": "GpOH0JwBH762w50BHKKomJ0BGpzR5ZoBH5mWsJwB", "clk": "AAAAAAAAAAAAAA==", "conflateMs": 0, "heartbeatMs": 5000, "pt": 1468943673782, "ct": "SUB_IMAGE", "oc": [{ "id": "1.125657695", "orc": [{ "fullImage": true, "id": 48756, "mb": [ [1.4, 2] ] }] }, { "id": "1.125657760", "orc": [{ "fullImage": true, "id": 151478, "uo": [{ "id": "71352090695", "p": 12, "s": 5, "side": "B", "status": "E", "pt": "L", "ot": "L", "pd": 1468919099000, "md": 1468933833000, "avp": 12, "sm": 4.75, "sr": 0.25, "sl": 0, "sc": 0, "sv": 0 }], "mb": [ [12, 4.75] ] }] }] } |
Remaining 0.25 is then matched on marketId 1.125657760
Example of Order Stream Output - with size remaining matched
Code Block |
---|
{ "op": "ocm", "id": 10, "initialClk": "GtD10ZwBH5OJxZ0BHK75mZ0BGsKq6JoBH4THsZwB", "clk": "AAAAAAAAAAAAAA==", "conflateMs": 0, "heartbeatMs": 5000, "pt": 1468944647413, "ct": "SUB_IMAGE", "oc": [{ "id": "1.125670254", "orc": [{ "fullImage": true, "id": 5643663 }] }, { "id": "1.125657760", "orc": [{ "fullImage": true, "id": 151478, "mb": [ [12, 5] ] }] }, { "id": "1.125657695", "orc": [{ "fullImage": true, "id": 48756, "mb": [ [1.4, 2] ] }] }] } |
Heartbeat / HeartbeatMessage
This is an explicit heartbeat request (in addition to the server heartbeat interval which is automatic).
This functionality should not normally be necessary unless you need to keep a firewall open.
Tip |
---|
Do I need to use HeartbeatMessage?No - under normal circumstances the subscription level ChangeType.HEARTBEAT is an acceptable guarantee of connection health. Use the HeartbeatMessage only if you need to keep a firewall open - as it will incur some performance penalty (as a response will block your connection) |
Re-connection / Re-subscription
Although maintaining long-lived connections is actively encouraged (for the Stream API for example), for a number of reasons within & beyond our direct control, we cannot guarantee that keep-alive connection won't be forcibly closed. We, therefore, advise all customers to ensure that they have reconnection logic in place to handle any connection termination scenarios.
If a client is disconnected a client may connect, authenticate and re-subscribe.
Prerequisite steps:
Store your subscription criteria (re-subscribe will only work correctly with identical subscription criteria.
Store initialClk (normally only initial image) & Clk (normally on every non-segmented message or a SEG_END) on any change message they are sent on.
The connection is broken.
Connect & Authenticate as normal
Subscribe setting initialClk and Clk to the last values sent on the subscription
Change message with ChangeType.RESUB_DELTA is sent - this will patch your cache
Some markets might have img=true set indicating they are either new or can't be patched.
Tip |
---|
Easiest way to implement re-subscribe
|
Performance Considerations
Here are a few tips on performance which are worth bearing in mind:
Tip |
---|
Performance tips
|
Currency Support
The Exchange Stream API supports GBP currency only.
Those looking to convert data from GBP to a different currency should use listCurrencyRates to do so.
Tip |
---|
CurrenciesMarket subscriptions - are always in underlying exchange currency - GBP. The default roll-up for GBP is £1 for batb / batl and bdatb / bdatl, This means that stakes of less than £1 (or currency equivalent) are rolled up to the next available price on the odds ladder. For atb / atl there is no roll-up. Available volume is displayed at all prices including those with less than £2 available. Orders subscriptions - are provided in the currency of the account that the orders are placed in. |
Runner Removals on the Order Stream
When a Rule 4 Runner Removal occurs in a Horse Race the price of matched bets on remaining runners are reduced by a Reduction Factor.
For these matched bets, you will receive on the Order Stream both a uo for the affected bet and the relevant updates to mb or ml (reducing the matched volume at the original matched price and adding volume at the new reduced price).
Initial bet placement at price 12
|
Bet fully matched at price 12
|
Runner removed (and so bet was reduced in price to 9.47)
|
See the avp in the uo record showing the new price of 9.47 and see the two entries in mb, one to remove the previously added size of 2 at a price point 12 and one to add the size of 2 into the new price point of size 9.47.
Bets placed on the actual removed runner will be voided/lapsed (for matched/unmatched bets respectively) and these will also be sent through on the Order Stream.
Identifying Cancelled BSP Bets
Whilst BSP bets cannot be cancelled in general, in the scenario where a Limit Price applied to the BSP bet is updated this is modelled as a Cancellation of the original bet with the original Limit Price and a Place of a new bet with the new Limit Price.
In this scenario, the cancellation of the original bet can be identified by looking at the "Cancelled Date" field ("cd") on the "Unmatched Orders" object ("uo") - N.B. there will be no "Size Cancelled" ("sc") because a BSP bet does not have any Size before reconciliation.
Line Markets
Line markets being sent on the Market Stream can be identified by the bettingType field of MarketDefinition (with value of "LINE").
The MarketDefinition of Line markets provides some additional fields that will be null for all other types,
lineMaxUnit - maximum value for the outcome, in market units for this market (eg 100 runs).
lineMinUnit - minimum value for the outcome, in market units for this market (eg 0 runs).
lineInterval - the odds ladder on this market will be between the range of lineMinUnit and lineMaxUnit, in increments of the interval value.e.g. If lineMinUnit=10 runs, lineMaxUnit=20 runs, lineInterval=0.5 runs, then valid odds include 10, 10.5, 11, 11.5 up to 20 runs.
For updates for Orders on Line markets received on the Order Stream be aware of how the following properties behave,
price - line markets operate at even-money odds of 2.0. However, the price for these markets refers to the line positions available as defined by the markets min-max range and interval steps.
side - for Line markets a 'B' bet refers to a SELL line and an 'L' bet refers to a BUY line.
averagePriceMatched - this value is not meaningful for activity on Line markets and is not guaranteed to be returned or maintained for these markets.
Stream API Status - latency
If any latency occurs, the ChangeMessage for the Order and Market Stream will contain a 'status' field which will give an indication of the health of the stream data provided by the service. This feature will be used in addition to the heartbeat mechanism which only gives an indication that the service is up but doesn't provide an indication of the latency of the data provided.
By default, when the stream data is up to date the value is set to null and will be set to 503 when the stream data is unreliable (i.e. not all bets and market changes will be reflected on the stream) due to an increase in push latency. Clients shouldn't disconnect if status 503 is returned; when the stream recovers updates will be sent containing the latest data. The status is sent per subscription on heartbeats and change messages.
Example of message containing the status field:
{"op":"ocm","id":3,"clk":"AAAAAAAA","status":503,"pt":1498137379766,"ct":"HEARTBEAT"} {"op":"mcm","id":2,"clk":"AAAAAAAA","status":503,"pt":1498137381621,"ct":"HEARTBEAT" |
Stream Health
In addition to the Stream API status field, we'd recommend the below as best practice for monitoring the health of the Stream API:
Use heartbeat messages to confirm Stream API is healthy and that you are still connected
Messages with ChangeType.HEARTBEAT will be sent at the requested interval if no change has occurred.
If no message of any kind is received for 2 x heartbeat intervals then you may no longer be connected to initiate a fresh connection (use re-subscribe to continue where you left off)
Re-connect code should contain back-offs to avoid spamming the service if you are unable to connect for a prolonged period for any reason
Conflation
Conflation set to true (con=true) in the stream message means that multiple stream updates have been pushed in the same cycle.
This can happen due to the following reasons:
The client socket buffer for the connection needs to be read (cleared) by the client in order for the Stream API to push the next cycle, if not the current update is skipped, and pushed with the next cycle resulting in "con = true".
The client subscription message has the "conflateMs" parameter set to a value greater than '0'.
The Stream API has a slow publishing cycle resulting in multiple updates being pushed in the same message.
Lapse Status Reason Code Possible Values
This field will now be present in some cases on the Order object of the Order Stream to denote the reason that some or all of the order is lapsed. It will be null if no portion of the order is lapsed or if the order lapsed for some reason other than those listed below.
The full list of currently supported values for this field is:
Code | Description |
---|---|
MKT_UNKNOWN | The market was unknown, presumably removed from the matcher (closed) between bet placement and matching. |
MKT_INVALID | The market was known about but in an invalid state. |
RNR_UNKNOWN | The runner was unknown, presumably removed between bet placement and matching. |
TIME_ELAPSED | The bet was waiting in the queue too long, so was lapsed for safety. |
CURRENCY_UNKNOWN | The bet's currency ID was not recognised by the matcher. |
PRICE_INVALID | The bet's price was invalid, e.g. outside the defined ladder for the market. |
MKT_SUSPENDED | The market was suspended at the time the bet came to be matched. |
MKT_VERSION | The bet had a maximum market version set, and the market's version on matching was greater than this. |
LINE_TARGET | The bet was on a line market, but was requested targeting profit or payout. |
LINE_SP | The bet was on a line market, but was either a BSP bet directly or requested to PERSIST_TO_SP. |
SP_IN_PLAY | The bet was a BSP bet that had somehow come to be placed after turn-in-play. |
SMALL_STAKE | The bet's stake was worth less than half a penny in GBP. |
PRICE_IMP_TOO_LARGE | When the bet came to be matched, the price available was better than its best-permitted price, suggesting a significant shift in the market, presumably due to a major incident, which may have rendered the bet unwanted. |
Offline Documentation
An offline version of the Exchange Stream API is available via ExchangeStreamAPI-March2018.pdf
Please note, the full Exchange Stream API specification is available online here
Known Issues
Markets moved under a new eventId - In certain circumstances, a market may move from one eventId to another due to actions performed by our Exchange Operations team. This will cause the Exchange Stream API to hold two copies of the market in its cache and the initial image of the market provided will therefore contain both copies of the market. In these circumstances, further Stream API updates will only be sent for the latest version of the market. You can identify the latest version of the market using the "version" parameter returned in the initial image and should only store the market with the higher version number.Trades with volume = 0 for all traded price points- e.g. [{"trd":[[1.75,0],[1.5,0],[1.25,0],[1.32,0],[1.57,0],[2.86,0],[1.82,0],[2.36,0],[1.76,0],[2.48,0],[1.51,0],[2.98,0],[1.26,0]... - This is an artefact of settlement kicking off that moves the bets away from trading DB into the longer-term store. Any price change notifications triggered during this process will result in attempts to reconstruct the market view based on no bets being available and would result in this kind of notification being sent.both copies of the market. In these circumstances, further Stream API updates will only be sent for the latest version of the market. You can identify the latest version of the market using the "version" parameter returned in the initial image and should only store the market with the higher version number.
Trades with volume = 0 for all traded price points- e.g. [{"trd":[[1.75,0],[1.5,0],[1.25,0],[1.32,0],[1.57,0],[2.86,0],[1.82,0],[2.36,0],[1.76,0],[2.48,0],[1.51,0],[2.98,0],[1.26,0]... - This is an artefact of settlement kicking off that moves the bets away from trading DB into the longer-term store. Any price change notifications triggered during this process will result in attempts to reconstruct the market view based on no bets being available and would result in this kind of notification being sent.
Appanvil karma designer | ||||
---|---|---|---|---|
|