Skip to main content
Version: nightly

Betfair

Founded in 2000, Betfair operates the world’s largest online betting exchange, with its headquarters in London and satellite offices across the globe.

NautilusTrader provides an adapter for integrating with the Betfair REST API and Exchange Streaming API.

Installation

Install NautilusTrader with Betfair support:

uv pip install "nautilus_trader[betfair]"

To build from source with Betfair extras:

uv sync --all-extras

Examples

You can find live example scripts here.

Betfair documentation

Betfair provides documentation for developers:

Application keys

Betfair requires an Application Key to authenticate API requests. After registering and funding your account, obtain your key using the API-NG Developer AppKeys Tool.

Two App Keys are assigned per account: a Live key (requires a one-time activation fee) and a Delayed key for development and testing.

info

See the Application Keys documentation for detailed setup instructions.

API credentials

Supply your Betfair credentials via environment variables or client configuration:

export BETFAIR_USERNAME=<your_username>
export BETFAIR_PASSWORD=<your_password>
export BETFAIR_APP_KEY=<your_app_key>
export BETFAIR_CERTS_DIR=<path_to_certificate_dir>
tip

We recommend using environment variables to manage your credentials.

SSL Certificates

Betfair recommends non-interactive (bot) login with SSL certificates for automated trading systems. The certs_dir configuration is optional, but certificates are recommended for production deployments.

Generating certificates

Create a 2048-bit RSA certificate using OpenSSL:

# Generate private key and certificate signing request
openssl genrsa -out client-2048.key 2048
openssl req -new -key client-2048.key -out client-2048.csr

# Self-sign the certificate (valid for 365 days)
openssl x509 -req -days 365 -in client-2048.csr -signkey client-2048.key -out client-2048.crt

Uploading to Betfair

Before using the certificate, attach it to your Betfair account:

  1. Navigate to My Betfair Account Security.
  2. Scroll to Automated Betting Program Access and click Edit.
  3. Upload your client-2048.crt file.

Directory structure

Place your certificate files in a directory and set BETFAIR_CERTS_DIR to that path:

/path/to/certs/
├── client-2048.crt
└── client-2048.key
info

SSL certificates are used for the Exchange Streaming API connection. The REST API uses username/password authentication with your Application Key.

warning

Enabling 2-Step Authentication on the Betfair website does not affect API access. Certificate-based login remains functional regardless of 2FA settings.

Overview

The Betfair adapter provides three primary components:

  • BetfairInstrumentProvider: loads Betfair markets and converts them into Nautilus instruments.
  • BetfairDataClient: streams real-time market data from the Exchange Streaming API.
  • BetfairExecutionClient: submits orders (bets) and tracks execution status via the REST API.

Orders capability

Betfair operates as a betting exchange with unique characteristics compared to traditional financial exchanges:

Order types

Order TypeSupportedNotes
MARKET-Not applicable to betting exchange.
LIMITOrders placed at specific odds.
STOP_MARKET-Not supported.
STOP_LIMIT-Not supported.
MARKET_IF_TOUCHED-Not supported.
LIMIT_IF_TOUCHED-Not supported.
TRAILING_STOP_MARKET-Not supported.

Execution instructions

InstructionSupportedNotes
post_only-Not applicable to betting exchange.
reduce_only-Not applicable to betting exchange.

Time in force options

Time in forceSupportedNotes
GTCMaps to Betfair PERSIST persistence.
GTD-Not supported.
DAYMaps to Betfair LAPSE persistence.
FOKMaps to Betfair FILL_OR_KILL.
IOCMaps to FILL_OR_KILL with partial fills.
note

Betfair uses a persistence model rather than traditional time-in-force. The adapter maps FOK to Betfair's FILL_OR_KILL, while IOC uses FILL_OR_KILL with min_fill_size=0 to allow partial fills.

Advanced order features

FeatureSupportedNotes
Order ModificationLimited to non-exposure changing fields.
Bracket/OCO Orders-Not supported.
Iceberg Orders-Not supported.

Batch operations

OperationSupportedNotes
Batch Submit-Not supported.
Batch Modify-Not supported.
Batch Cancel-Not supported.

Position management

FeatureSupportedNotes
Query positions-Betting exchange model differs.
Position mode-Not applicable to betting exchange.
Leverage control-No leverage in betting exchange.
Margin mode-No margin in betting exchange.

Order querying

FeatureSupportedNotes
Query open ordersList all active bets.
Query order historyHistorical betting data.
Order status updatesReal-time bet state changes.
Trade historyBet matching and settlement reports.

Contingent orders

FeatureSupportedNotes
Order lists-Not supported.
OCO orders-Not supported.
Bracket orders-Not supported.
Conditional orders-Basic bet conditions only.

Tick scheme and pricing

Betfair uses a tiered tick scheme with varying increments across price ranges:

Price RangeTick Size
1.01 - 2.000.01
2.00 - 3.000.02
3.00 - 4.000.05
4.00 - 6.000.10
6.00 - 10.000.20
10.00 - 20.000.50
20.00 - 30.001.00
30.00 - 50.002.00
50.00 - 100.005.00
100.00 - 1000.0010.00

The minimum price is 1.01 and the maximum is 1000.00.

Order modification

Order modification on Betfair has specific constraints:

  • Price and size cannot be changed atomically - these require separate operations.
  • Price modification uses ReplaceOrders (cancel + new order at new price).
  • Size reduction uses CancelOrders with a size_reduction parameter.
  • Size increase is not supported - submit a new order instead.
warning

A replace operation generates both a cancel event for the original order and an accepted event for the replacement order. The adapter tracks pending replacements to suppress synthetic cancel events.

Order stream fill handling

The execution client processes order updates from the Betfair Exchange Streaming API. Two configuration options control how updates are filtered:

  • stream_market_ids_filter: Filters at the market level (early exit, silent skip).
  • ignore_external_orders: Filters at the order level (controls warning vs debug logging).

Note that stream_market_ids_filter is independent of reconciliation scope (reconcile_market_ids_only). Stream filtering affects live updates only; reconciliation uses its own market filter.

The same stream_market_ids_filter is also applied during full-image reconciliation in check_cache_against_order_image.

When ignore_external_orders=True, the client silently skips orders and fills not found in cache:

ScenarioDescription
Unknown order in stream updateNo venue-to-client order ID mapping exists.
Unknown order in full imageOrder not found in cache during image sync.
Unknown fill in full imageFill does not match any known order during sync.
info

For multi-node setups sharing a Betfair account, set both stream_market_ids_filter (your markets only) and ignore_external_orders=True to avoid warnings about orders managed by other nodes.

Fill handling

The adapter handles several edge cases when processing fills from the stream:

  • Incremental fills: Betfair reports cumulative matched sizes. The adapter calculates incremental fills by tracking the last known filled quantity per order.
  • Overfill protection: Fills that would exceed the order quantity are rejected.
  • Deduplication: A cache of published trade IDs prevents duplicate fill events from late messages or stream reconnection replays.
  • Race conditions: When stream fills arrive before the HTTP order response, the adapter caches the venue order ID immediately to ensure correct order matching.

Rate limiting

The adapter enforces a default rate limit of 5 requests/second across all endpoints, aligned with Betfair's best practice recommendation. Order status and fill report queries retry once on TOO_MANY_REQUESTS errors after a 1-second delay; order operations reject with the error message.

Betfair's actual API limits are more nuanced:

CategoryLimitNotes
Order operations1,000 transactions/sTotal instructions across placeOrders, cancelOrders, replaceOrders.
Order projection queries3 concurrentlistMarketBook (with OrderProjection), listCurrentOrders, listMarketProfitAndLoss.
Best practice5 requests/sRecommended for listMarketBook per market.

Custom data types

The Betfair adapter provides several custom data types that flow through the market stream. All custom data is delivered automatically when subscribed to markets - no explicit subscription is required, though strategies can register handlers for specific data types.

BetfairTicker

Real-time ticker data for a betting selection.

FieldTypeDescription
instrument_idstrNautilus instrument identifier.
last_traded_pricefloatLast matched price (odds).
traded_volumefloatTotal matched volume.
starting_price_nearfloatNear-side BSP indicator.
starting_price_farfloatFar-side BSP indicator.

BetfairStartingPrice

The realized Betfair Starting Price (BSP) after market close.

FieldTypeDescription
instrument_idstrNautilus instrument identifier.
bspfloatFinal starting price (odds).

BetfairRaceRunnerData

Live GPS tracking data for individual horses (Total Performance Data). Available for supported UK and Irish races.

FieldTypeDescription
race_idstrBetfair race identifier.
market_idstrBetfair market identifier.
selection_idintBetfair selection (runner) identifier.
latitudefloatGPS latitude.
longitudefloatGPS longitude.
speedfloatCurrent speed in m/s (Doppler-derived).
progressfloatDistance to finish line in meters.
stride_frequencyfloatStride frequency in Hz.

BetfairRaceProgress

Race summary data with sectional times and running order.

FieldTypeDescription
race_idstrBetfair race identifier.
market_idstrBetfair market identifier.
gate_namestrTiming gate (e.g., "1f", "2f", "Finish").
sectional_timefloatTime for this section in seconds.
running_timefloatTotal time since race start in seconds.
speedfloatLead horse speed in m/s.
progressfloatLead horse distance to finish in meters.
orderlist[int]Selection IDs in current race position order.
jumpslist[dict]Jump obstacle data for National Hunt races.

Subscribing to custom data

Custom data flows automatically through the Betfair market stream when you subscribe to markets. To receive custom data in your strategy, register a handler with the Betfair client ID:

from nautilus_trader.adapters.betfair.constants import BETFAIR_CLIENT_ID
from nautilus_trader.adapters.betfair.data_types import BetfairRaceRunnerData
from nautilus_trader.adapters.betfair.data_types import BetfairRaceProgress
from nautilus_trader.adapters.betfair.data_types import BetfairTicker
from nautilus_trader.model.data import DataType

class MyStrategy(Strategy):
def on_start(self):
# Register handlers for custom data types (client_id required)
self.subscribe_data(DataType(BetfairTicker), client_id=BETFAIR_CLIENT_ID)
self.subscribe_data(DataType(BetfairRaceRunnerData), client_id=BETFAIR_CLIENT_ID)
self.subscribe_data(DataType(BetfairRaceProgress), client_id=BETFAIR_CLIENT_ID)

def on_data(self, data):
if isinstance(data, BetfairRaceRunnerData):
self.log.info(f"Runner {data.selection_id}: speed={data.speed} m/s")
elif isinstance(data, BetfairRaceProgress):
self.log.info(f"Race order: {data.order}")
elif isinstance(data, BetfairTicker):
self.log.info(f"LTP: {data.last_traded_price}")
info

Race data (RCM messages) is only available for races with Total Performance Data coverage. Not all races have GPS tracking enabled.

Loading race data from files

For backtesting with recorded race data, use the file parser:

from nautilus_trader.adapters.betfair.parsing.core import parse_betfair_rcm_file

for data in parse_betfair_rcm_file("path/to/rcm_data.json"):
if isinstance(data, BetfairRaceRunnerData):
print(f"Runner {data.selection_id} at {data.latitude}, {data.longitude}")

Configuration

Data client configuration options

OptionDefaultDescription
account_currencyRequiredBetfair account currency for data and price feeds.
usernameNoneBetfair account username; taken from environment when omitted.
passwordNoneBetfair account password; taken from environment when omitted.
app_keyNoneBetfair application key used for API authentication.
certs_dirNoneDirectory containing Betfair SSL certificates for login.
instrument_configNoneOptional BetfairInstrumentProviderConfig to scope available markets.
subscription_delay_secs3Delay (seconds) before initial market subscription request is sent.
keep_alive_secs36,000Keep-alive interval (seconds) for the Betfair session.
stream_conflate_msNoneExplicit stream conflation interval in milliseconds (0 disables conflation).
proxy_urlNoneOptional proxy URL for HTTP requests.
warning

When stream_conflate_ms is None, Betfair applies its default conflation behavior (typically enabled). Set stream_conflate_ms=0 explicitly to guarantee no conflation and receive every price update.

Execution client configuration options

OptionDefaultDescription
account_currencyRequiredBetfair account currency for order placement and balances.
usernameNoneBetfair account username; taken from environment when omitted.
passwordNoneBetfair account password; taken from environment when omitted.
app_keyNoneBetfair application key used for API authentication.
certs_dirNoneDirectory containing Betfair SSL certificates for login.
instrument_configNoneOptional BetfairInstrumentProviderConfig to scope reconciliation.
calculate_account_stateTrueCalculate account state locally from events when True.
request_account_state_secs300Interval (seconds) to poll Betfair for account state (0 disables).
reconcile_market_ids_onlyFalseWhen True, reconciliation only covers instrument_config.market_ids (no effect if unset).
stream_market_ids_filterNoneList of market IDs to process from stream; others are silently skipped.
ignore_external_ordersFalseWhen True, ignore stream orders missing from the local cache.
proxy_urlNoneOptional proxy URL for HTTP requests.
warning

If you set stream_market_ids_filter, ensure it includes all markets you trade. Orders placed on markets excluded from this filter will miss live fill and cancel updates from the stream.

Session management

Betfair sessions typically expire every 12-24 hours. The adapter automatically handles session reconnection when NO_SESSION or INVALID_SESSION_INFORMATION errors occur:

  • The HTTP client reconnects and obtains a new session token.
  • The streaming client re-authenticates and resubscribes to markets.
  • The keep-alive mechanism (keep_alive_secs, default 10 hours) proactively extends sessions.
info

Session errors during account state polling or keep-alive trigger automatic reconnection. No manual intervention is required for normal session expiry.

Multi-node deployment

When multiple trading nodes share a single Betfair account across different markets, configure each node to avoid interference:

  1. Set stream_market_ids_filter to include only that node's markets.
  2. Set ignore_external_orders=True to suppress warnings about orders from other nodes.
  3. Set reconcile_market_ids_only=True to limit reconciliation scope.

This prevents warning spam and ensures each node processes only its own orders and fills.

Here is a minimal example showing how to configure a live TradingNode with Betfair clients:

from nautilus_trader.adapters.betfair import BETFAIR
from nautilus_trader.adapters.betfair import BetfairLiveDataClientFactory
from nautilus_trader.adapters.betfair import BetfairLiveExecClientFactory
from nautilus_trader.config import TradingNodeConfig
from nautilus_trader.live.node import TradingNode

# Configure Betfair data and execution clients (using AUD account currency)
config = TradingNodeConfig(
data_clients={BETFAIR: {"account_currency": "AUD"}},
exec_clients={BETFAIR: {"account_currency": "AUD"}},
)

# Build the TradingNode with Betfair adapter factories
node = TradingNode(config)
node.add_data_client_factory(BETFAIR, BetfairLiveDataClientFactory)
node.add_exec_client_factory(BETFAIR, BetfairLiveExecClientFactory)
node.build()

Contributing

info

For additional features or to contribute to the Betfair adapter, please see our contributing guide.