Skip to content Why GitHub? Enterprise Explore Marketplace Pricing Sign in Sign up 4 5 4 betfair-datascientists/API Code Issues 0 Pull requests 0 Projects 0 Insights Join GitHub today GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together. API/python/API Tutorial.html @JamesHWard JamesHWard Added API Tutorial in HTML 89b5227 on Oct 29, 2018 14095 lines (13788 sloc) 337 KB
This tutorial will walk you through the process of connecting to Betfair's API, grabbing data and placing a bet in python. It will utilise the betfairlightweight python library.
Other useful links are outlined below.
To use the API securely, Betfair recommends generating certificates. The betfairlight package requires this to login non-interactively. For detailed instructions on how to generate certificates on a windows machine, follow the instructions outlined here. For alternate instructions for windows, or for Mac/Linux machines, follow the instructions outlined here. You should then create a folder for your certs, perhaps named 'certs' and grab the path loation.
We also need to install betfairlightweight. To do this, simply use pip install betfairlightweight
in the cmd prompt/terminal. If this doesn't work, you will have to google your error. If you're just starting out with python, you may have to add python to your environment variables.
Now we're finally ready to log in and use the API. First, we create an APIClient object and then log in. To log in, we'll need to specify where we put our certs. In this example, I'll put them in a folder named 'certs', on my desktop.
You'll also need to change the username, password and app_key variables to your own.
# Import libraries
import betfairlightweight
from betfairlightweight import filters
import pandas as pd
import numpy as np
import os
import datetime
import json
# Change this certs path to wherever you're storing your certificates
certs_path = r'C:\Users\wardj\Desktop\certs'
# Change these login details to your own
my_username = "your_username"
my_password = "your_password"
my_app_key = "your_app_key"
trading = betfairlightweight.APIClient(username=my_username,
password=my_password,
app_key=my_app_key,
certs=certs_path)
trading.login()
Betfair's API has a number of operations. For example, if you want to list the market book for a market, you would use the listMarketBook operation. These endpoints are shown in the Sports API Visualiser and in the docs. They are also listed below:
Sports API
The Account Operations API operations/endpoints can be found here.
First we need to grab the 'Event Type Id'. Each sport has a different ID. Below we will find the ids for all sports by requesting the event_type_ids without a filter.
# Grab all event type ids. This will return a list which we will iterate over to print out the id and the name of the sport
event_types = trading.betting.list_event_types()
sport_ids = pd.DataFrame({
'Sport': [event_type_object.event_type.name for event_type_object in event_types],
'ID': [event_type_object.event_type.id for event_type_object in event_types]
}).set_index('Sport').sort_index()
sport_ids
If we just wanted to get the event id for horse racing, we could use the filter function from betfairlightweight as shown in the examples and below.
# Filter for just horse racing
horse_racing_filter = betfairlightweight.filters.market_filter(text_query='Horse Racing')
# This returns a list
horse_racing_event_type = trading.betting.list_event_types(
filter=horse_racing_filter)
# Get the first element of the list
horse_racing_event_type = horse_racing_event_type[0]
horse_racing_event_type_id = horse_racing_event_type.event_type.id
print(f"The event type id for horse racing is {horse_racing_event_type_id}")
Sometimes you may want to get markets based on the competition. An example may be the Brownlow medal, or the EPL. Let's have a look at all the soccer competitions over the next week and filter to only get the EPL Competition ID.
# Get a datetime object in a week and convert to string
datetime_in_a_week = (datetime.datetime.utcnow() + datetime.timedelta(weeks=1)).strftime("%Y-%m-%dT%TZ")
# Create a competition filter
competition_filter = betfairlightweight.filters.market_filter(
event_type_ids=[1], # Soccer's event type id is 1
market_start_time={
'to': datetime_in_a_week
})
# Get a list of competitions for soccer
competitions = trading.betting.list_competitions(
filter=competition_filter
)
# Iterate over the competitions and create a dataframe of competitions and competition ids
soccer_competitions = pd.DataFrame({
'Competition': [competition_object.competition.name for competition_object in competitions],
'ID': [competition_object.competition.id for competition_object in competitions]
})
# Get the English Premier League Competition ID
soccer_competitions[soccer_competitions.Competition.str.contains('English Premier')]
Say you want to get all the upcoming events for Thoroughbreads for the next 24 hours. We will use the listEvents operation for this. First, as before, we define a market filter, and then using the betting method from our trading object which we defined earlier.
# Define a market filter
thoroughbreds_event_filter = betfairlightweight.filters.market_filter(
event_type_ids=[horse_racing_event_type_id],
market_countries=['AU'],
market_start_time={
'to': (datetime.datetime.utcnow() + datetime.timedelta(days=1)).strftime("%Y-%m-%dT%TZ")
}
)
# Print the filter
thoroughbreds_event_filter
# Get a list of all thoroughbred events as objects
aus_thoroughbred_events = trading.betting.list_events(
filter=thoroughbreds_event_filter
)
# Create a DataFrame with all the events by iterating over each event object
aus_thoroughbred_events_today = pd.DataFrame({
'Event Name': [event_object.event.name for event_object in aus_thoroughbred_events],
'Event ID': [event_object.event.id for event_object in aus_thoroughbred_events],
'Event Venue': [event_object.event.venue for event_object in aus_thoroughbred_events],
'Country Code': [event_object.event.country_code for event_object in aus_thoroughbred_events],
'Time Zone': [event_object.event.time_zone for event_object in aus_thoroughbred_events],
'Open Date': [event_object.event.open_date for event_object in aus_thoroughbred_events],
'Market Count': [event_object.market_count for event_object in aus_thoroughbred_events]
})
aus_thoroughbred_events_today
Say we want to know what market types a certain event is offering. To do this, we use the listMarketTypes operation. Let's take the Moonee Valley event from above (ID - 28971066). As this is a horse race we would expect that it would have Win and Place markets.
# Define a market filter
market_types_filter = betfairlightweight.filters.market_filter(event_ids=['28971066'])
# Request market types
market_types = trading.betting.list_market_types(
filter=market_types_filter
)
# Create a DataFrame of market types
market_types_mooney_valley = pd.DataFrame({
'Market Type': [market_type_object.market_type for market_type_object in market_types],
})
market_types_mooney_valley
If we want to know the various market names that there are for a particular event, as well as how much has been matched on each market, we want to request data from the listMarketCatalogue operation. We can provide a number of filters, including the Competition ID, the Event ID, the Venue etc. to the filter.
We must also specify the maximum number of results, and if we want additional data like the event data or runner data, we can also request that.
For a more comprehensive understanding of the options for filters and what we can request, please have a look at the Sports API Visualiser. The options listed under market filter should be put into a filter, whilst the others should be arguments to the relevant operation function in betfairlightweight.
For example, if we want all the markets for Moonee Valley, we should use the following filters and arguments.
market_catalogue_filter = betfairlightweight.filters.market_filter(event_ids=['28971066'])
market_catalogues = trading.betting.list_market_catalogue(
filter=market_catalogue_filter,
max_results='100',
sort='FIRST_TO_START'
)
# Create a DataFrame for each market catalogue
market_types_mooney_valley = pd.DataFrame({
'Market Name': [market_cat_object.market_name for market_cat_object in market_catalogues],
'Market ID': [market_cat_object.market_id for market_cat_object in market_catalogues],
'Total Matched': [market_cat_object.total_matched for market_cat_object in market_catalogues],
})
market_types_mooney_valley
If we then want to get the prices available/last traded for a market, we should use the listMarketBook operation. Let's Look at the Market book for Moonee Valley R7. We will need to define a function which processes the runnerbooks and collates the data into a DataFrame.
def process_runner_books(runner_books):
'''
This function processes the runner books and returns a DataFrame with the best back/lay prices + vol for each runner
:param runner_books:
:return:
'''
best_back_prices = [runner_book.ex.available_to_back[0].price
if runner_book.ex.available_to_back[0].price
else 1.01
for runner_book
in runner_books]
best_back_sizes = [runner_book.ex.available_to_back[0].size
if runner_book.ex.available_to_back[0].size
else 1.01
for runner_book
in runner_books]
best_lay_prices = [runner_book.ex.available_to_lay[0].price
if runner_book.ex.available_to_lay[0].price
else 1000.0
for runner_book
in runner_books]
best_lay_sizes = [runner_book.ex.available_to_lay[0].size
if runner_book.ex.available_to_lay[0].size
else 1.01
for runner_book
in runner_books]
selection_ids = [runner_book.selection_id for runner_book in runner_books]
last_prices_traded = [runner_book.last_price_traded for runner_book in runner_books]
total_matched = [runner_book.total_matched for runner_book in runner_books]
statuses = [runner_book.status for runner_book in runner_books]
scratching_datetimes = [runner_book.removal_date for runner_book in runner_books]
adjustment_factors = [runner_book.adjustment_factor for runner_book in runner_books]
df = pd.DataFrame({
'Selection ID': selection_ids,
'Best Back Price': best_back_prices,
'Best Back Size': best_back_sizes,
'Best Lay Price': best_lay_prices,
'Best Lay Size': best_lay_sizes,
'Last Price Traded': last_prices_traded,
'Total Matched': total_matched,
'Status': statuses,
'Removal Date': scratching_datetimes,
'Adjustment Factor': adjustment_factors
})
return df
# Create a price filter. Get all traded and offer data
price_filter = betfairlightweight.filters.price_projection(
price_data=['EX_BEST_OFFERS']
)
# Request market books
market_books = trading.betting.list_market_book(
market_ids=['1.150038686'],
price_projection=price_filter
)
# Grab the first market book from the returned list as we only requested one market
market_book = market_books[0]
runners_df = process_runner_books(market_book.runners)
runners_df
Now that we have the market book in an easy to read DataFrame, we can go ahead and start placing orders based on the market book. Although it is a simple (and probably not profitable) strategy, in the next few sections we will be backing the favourite and adjusting our orders.
To place an order we use the placeOrders operation. A handy component of placeOrders is that you can send your strategy along with the runner that you want to back, so it is extremely easy to analyse how your strategy performed later. Let's place a 5 dollar back bet on the favourite at $7 call this strategy 'back_the_fav'.
Note that if you are placing a limit order you must specify a price which is allowed by Betfair. For example, the price 6.3 isn't allowed, whereas 6.4 is, as prices go up by 20c increments at that price range.
# Get the favourite's price and selection id
fav_selection_id = runners_df.loc[runners_df['Best Back Price'].idxmin(), 'Selection ID']
fav_price = runners_df.loc[runners_df['Best Back Price'].idxmin(), 'Best Back Price']
# Define a limit order filter
limit_order_filter = betfairlightweight.filters.limit_order(
size=5,
price=7,
persistence_type='LAPSE'
)
# Define an instructions filter
instructions_filter = betfairlightweight.filters.place_instruction(
selection_id=str(fav_selection_id),
order_type="LIMIT",
side="BACK",
limit_order=limit_order_filter
)
instructions_filter
# Place the order
order = trading.betting.place_orders(
market_id='1.150038686', # The market id we obtained from before
customer_strategy_ref='back_the_fav',
instructions=[instructions_filter] # This must be a list
)
Now that we've placed the other, we can check if the order placing was a success and if any has been matched.
order.__dict__
As we can see, the status is 'SUCCESS', whilst the sizeMatched is 0. Let's now look at our current orders.
To get our current orders, we need to use the listCurrentOrders operation. We can then use either the bet id, the market id, or the bet strategy to filter our orders.
trading.betting.list_current_orders(customer_strategy_refs=['back_the_fav']).__dict__
As we can see, we have one order which is unmatched for our strategy 'back_the_fav'
Let's now cancel this bet. To do this, we will use the cancelOrders operation. It takes in a market ID and cancels all orders for that specific market ID, like you can do on the website.
cancelled_order = trading.betting.cancel_orders(market_id='1.150038686')
# Create a DataFrame to view the insturction report
pd.Series(cancelled_order.cancel_instruction_reports[0].__dict__).to_frame().T
If we want to go back and look at past orders we have made, there are two main operations for this:
Alternatively, we can use the getAccountStatement operation from the Account Operations API.
Let's now use both Sports API operations based on our previous orders and then compare it to the getAccountStatement operation.
# listClearedOrders
cleared_orders = trading.betting.list_cleared_orders(bet_status="SETTLED",
market_ids=["1.150038686"])
# Create a DataFrame from the orders
pd.DataFrame(cleared_orders._data['clearedOrders'])
Note that we can also filter for certain dates, bet ids, event ids, selection ids etc. We can also group by the event type, the event, the market, the runner, the side, the bet and the strategy, which is extremely useful if you're looking for a quick summary of how your strategy is performing.
Now let's find the Profit and Loss for the market. To do this we will use the listMarketProfitAndLoss operation. Note that this function only works with market IDs, and once the website clears the market, the operation will no longer work. However the market is generally up for about a minute after the race, so if your strategy is automated, you can check once if your bet is settled and if it is, hit the getMarketProfitAndLoss endpoint.
Because of this, we will check a different market ID to the example above.
# Get the profit/loss - this returns a list
pl = trading.betting.list_market_profit_and_loss(market_ids=["1.150318913"],
include_bsp_bets='true',
include_settled_bets='true')
# Create a profit/loss DataFrame
pl_df = pd.DataFrame(pl[0]._data['profitAndLosses']).assign(marketId=pl[0].market_id)
pl_df
Another method is to use the getAccountStatement, which provides an overview of all your bets over a certain time period. You can then filter this for specific dates if you wish.
# Define a date filter - get all bets for the past 4 days
four_days_ago = (datetime.datetime.utcnow() - datetime.timedelta(days=4)).strftime("%Y-%m-%dT%TZ")
acct_statement_date_filter = betfairlightweight.filters.time_range(from_=four_days_ago)
# Request account statement
account_statement = trading.account.get_account_statement(item_date_range=acct_statement_date_filter)
# Create df of recent transactions
recent_transactions = pd.DataFrame(account_statement._data['accountStatement'])
recent_transactions
# Create df of itemClassData - iterate over the account statement list and convert to json so that the DataFrame function
# can read it correctly
class_data = [json.loads(account_statement.account_statement[i].item_class_data['unknownStatementItem'])
for i in range(len(account_statement.account_statement))]
class_df = pd.DataFrame(class_data)
class_df
As we can see, this DataFrame provides a much more comprehensive view of each of our bets. However, it lacks the ability to filter by strategy like the listClearedOrders operation in the Sports API.