Getting TAB betting data using bettRtab
Jason Zivkovic
2024-07-31
Source:vignettes/using-bettRtab.Rmd
using-bettRtab.Rmd
Overview
This vignette will outline the ways in which the functions in
bettRtab
can be used to extract sports and racing betting
data from the TAB.
Installation
You can install the released version of bettRtab
from GitHub with:
# install.packages("remotes")
remotes::install_github("JaseZiv/bettRtab")
Usage
The functions in this package should work without hitch locally, but running these in an automated way (GitHub Actions, cloud, etc) may lead to requests being blocked.
To get around this, you will need access to proxies and would need to set configs at the start of your scripts similar to the following:
httr::set_config(httr::use_proxy(url = Sys.getenv("PROXY_URL"),
port = as.numeric(Sys.getenv("PROXY_PORT")),
username =Sys.getenv("PROXY_USERNAME"),
password= Sys.getenv("PROXY_PASSWORD")))
Sports Data
To get betting data for any currently available competition market,
use the get_sports_market()
function.
To get a valid list of values you can pass to the
competition_name
parameter, look at the
competitions.name
column in the file here.
Alternatively, the competition_name
needed as an
argument to this function can be found under the competitions page
selector on the TAB website.
epl_futures <- get_sports_market(competition_name = "English Premier League Futures")
glimpse(epl_futures)
#> Rows: 559
#> Columns: 33
#> $ marketId <chr> "98519821", "98519821", "98519821", "98519821", "9…
#> $ marketName <chr> "24/25 EPL Winner", "24/25 EPL Winner", "24/25 EPL…
#> $ shortName <chr> "Eng 24/25 EPL Winner", "Eng 24/25 EPL Winner", "E…
#> $ betOption <chr> "Winner", "Winner", "Winner", "Winner", "Winner", …
#> $ betOptionSpectrumId <chr> "52", "52", "52", "52", "52", "52", "52", "52", "5…
#> $ betOptionPriority <chr> "330", "330", "330", "330", "330", "330", "330", "…
#> $ marketUniqueId <chr> "98519821", "98519821", "98519821", "98519821", "9…
#> $ closeTime <chr> "2024-08-16T19:00:00.000Z", "2024-08-16T19:00:00.0…
#> $ marketBettingStatus <chr> "Open", "Open", "Open", "Open", "Open", "Open", "O…
#> $ isFuture <chr> "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "T…
#> $ onlineBetting <chr> "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "T…
#> $ phoneBettingOnly <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ inPlay <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ goingInPlay <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ allowWin <chr> "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "T…
#> $ marketAllowPlace <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ allowEachWay <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ allowMulti <chr> "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "T…
#> $ allowMultiWin <chr> "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "T…
#> $ allowMultiPlace <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ allowMultiEachWay <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ numberOfPlaces <chr> "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", …
#> $ cashOutEligibility <chr> "Enabled", "Enabled", "Enabled", "Enabled", "Enabl…
#> $ allowBundle <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ id <chr> "494060", "494061", "494062", "494064", "494065", …
#> $ name <chr> "Man City", "Arsenal", "Liverpool", "Chelsea", "To…
#> $ returnWin <chr> "2.1", "2.85", "9", "21", "21", "23", "26", "51", …
#> $ returnPlace <chr> "0", "0", "0", "0", "0", "0", "0", "0", "0", "0", …
#> $ bettingStatus <chr> "Open", "Open", "Open", "Open", "Open", "Open", "O…
#> $ allowPlace <chr> "FALSE", "FALSE", "FALSE", "FALSE", "FALSE", "FALS…
#> $ number <chr> "494060", "494061", "494062", "494064", "494065", …
#> $ isOpen <chr> "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "T…
#> $ sortOrder <chr> "1", "2", "3", "5", "6", "4", "8", "7", "9", "11",…
We can then inspect to see which betting markets are available:
epl_futures %>%
select(betOption) %>% distinct()
#> betOption
#> 1 Winner
#> 2 Top Goal Scorer
#> 3 Exacta
#> 4 Winner/Top Goalscorer Double
#> 5 Top 2
#> 6 Top 4
#> 7 Top 6
#> 8 Relegation
#> 9 To Finish Top Half
#> 10 To Finish Bottom Half
#> 11 Team to Finish Last
#> 12 Favourite Out
#> 13 Winner without Big 6
#> 14 Most Assists
#> 15 Top North West Club
#> 16 Top London Team
#> 17 Top Midlands Team
#> 18 Top Promoted Team
and we can then explore some key markets… say we want to see the odds of relegation in the EPL:
epl_futures %>%
filter(betOption == "Relegation") %>%
select(marketName, id, name, returnWin)
#> marketName id name returnWin
#> 1 Eng 24/25 EPL Relegation 200081 Leicester 1.4
#> 2 Eng 24/25 EPL Relegation 200084 Ipswich 1.72
#> 3 Eng 24/25 EPL Relegation 200275 Southampton 2.35
#> 4 Eng 24/25 EPL Relegation 200278 Everton 2.6
#> 5 Eng 24/25 EPL Relegation 200279 Nottingham Forest 3
#> 6 Eng 24/25 EPL Relegation 200283 Wolverhampton 4.5
#> 7 Eng 24/25 EPL Relegation 200293 Brentford 6
#> 8 Eng 24/25 EPL Relegation 200298 Fulham 6.5
#> 9 Eng 24/25 EPL Relegation 200313 Bournemouth 10
#> 10 Eng 24/25 EPL Relegation 200316 Crystal Palace 10
#> 11 Eng 24/25 EPL Relegation 200325 Man City 17
#> 12 Eng 24/25 EPL Relegation 200332 West Ham 21
#> 13 Eng 24/25 EPL Relegation 200354 Brighton 21
#> 14 Eng 24/25 EPL Relegation 200360 Chelsea 21
#> 15 Eng 24/25 EPL Relegation 200379 Aston Villa 101
#> 16 Eng 24/25 EPL Relegation 200386 Man Utd 151
#> 17 Eng 24/25 EPL Relegation 200390 Newcastle 151
#> 18 Eng 24/25 EPL Relegation 200397 Tottenham 251
#> 19 Eng 24/25 EPL Relegation 200404 Arsenal 2001
#> 20 Eng 24/25 EPL Relegation 200419 Liverpool 2001
Live (In-Play) Sports
To get live and in-play sports markets as they’re being played, the
get_live_sports()
function is to be used. This will return
a data frame of all sports that have an in-play market.
The user can then filter the outputted data frame on any of the
variables in the data set (name
, displayName
,
competitions.id
, competitions.matches.name
,
etc).
live_sports <- get_live_sports()
dplyr::glimpse(live_sports)
Racing Data
This sections will outline the functions available to extract Horse, Harness and Greyhound racing.
Past Racing Data
The following section will outline how to use functions do get racing data (horses, harness racing and greyhounds) for past races run.
This section does not include functions to get current day or future racing data.
Race Meet Meta Data
To get meta data for race meets for a selected date(s), use the
get_race_meet_meta()
function.
The output of this function will return where and when the meet(s) were, the weather and track condition, and high level data about each of the races at the meets.
dates <- seq(from = as.Date("2022-05-01"), to=as.Date("2022-05-03"), by=1)
race_meets <- get_race_meet_meta(race_dates=dates)
glimpse(race_meets)
#> Rows: 134
#> Columns: 11
#> $ meetingName <chr> "BENDIGO", "SWAN HILL", "SANDOWN PARK", "HEALESVILLE"…
#> $ location <chr> "VIC", "VIC", "VIC", "VIC", "VIC", "VIC", "NSW", "NSW…
#> $ venueMnemonic <chr> "M", "M", "M", "E", "P", "P", "C", "C", "S", "S", "S"…
#> $ raceType <chr> "R", "H", "G", "G", "R", "G", "R", "G", "R", "H", "G"…
#> $ meetingDate <chr> "2022-05-01", "2022-05-01", "2022-05-01", "2022-05-01…
#> $ weatherCondition <chr> "FINE", "FINE", "OCAST", "OCAST", "FINE", "OCAST", "S…
#> $ trackCondition <chr> "HVY8", "GOOD", "GOOD", "GOOD", "SOFT6", "GOOD", "HVY…
#> $ exoticPools <list> [<data.frame[13 x 6]>], [<data.frame[12 x 6]>], [<da…
#> $ X_links <df[,1]> <data.frame[26 x 1]>
#> $ races <list> [<data.frame[8 x 10]>], [<data.frame[7 x 10]>], [<…
#> $ sellCode <df[,2]> <data.frame[26 x 2]>
Loading Data
Rather than scraping this information, you can instead use the
load_race_meet_meta()
function. This will allow you to get
a whole year’s worth of race meet meta data extremely quickly. This data
updates daily, so should only very rarely not have the previous day’s
race meet data:
loaded_race_meets <- load_race_meet_meta(cal_year=2022)
glimpse(loaded_race_meets)
#> Rows: 16,807
#> Columns: 11
#> $ meetingName <chr> "FLEMINGTON", "ECHUCA", "THE MEADOWS", "MORNINGTON", …
#> $ location <chr> "VIC", "VIC", "VIC", "VIC", "VIC", "VIC", "NSW", "NSW…
#> $ venueMnemonic <chr> "M", "M", "M", "C", "E", "P", "L", "C", "C", "T", "S"…
#> $ raceType <chr> "R", "H", "G", "R", "R", "G", "G", "R", "G", "G", "R"…
#> $ meetingDate <chr> "2022-01-01", "2022-01-01", "2022-01-01", "2022-01-01…
#> $ weatherCondition <chr> "FINE", "FINE", "FINE", "FINE", "FINE", "OCAST", "FIN…
#> $ trackCondition <chr> "GOOD4", "GOOD", "GOOD", "GOOD3", "FIRM2", "GOOD", "G…
#> $ exoticPools <list> [<data.frame[14 x 6]>], [<data.frame[15 x 6]>], [<da…
#> $ X_links <df[,1]> <data.frame[26 x 1]>
#> $ races <list> [<data.frame[9 x 10]>], [<data.frame[10 x 10]>], […
#> $ sellCode <df[,2]> <data.frame[26 x 2]>
Each Race Data
To get detailed data on runners, markets, pools and dividends, the following set of functions will be used.
Past Race data output
The get_past_races()
function will allow you to pass in
values for four parameters to have a list of races outputted. The result
of this function should be stored for use with the parsing functions to
be explained shortly.
The race_num
value can be left off the function call to
default to returning all races on a card’s meet.
The race_type
value needs to be either ‘R’, ‘H’ or
‘G’.
The rest of the parameters can have multiple arguments passed to them.
race_list_output <- get_past_races(meet_date = c('2022-09-03', '2022-09-10'), venue_mnem = 'M', race_type = 'R', race_num = c(8:9))
Alternatively, you can get the same data as above using the TAB race API URL. To get this URL, follow the below steps:
# first load in meet data
meets <- load_race_meet_meta(cal_year = 2022)
# then filter to the race(s) meets wanted
meet_dates_df <- meets %>%
filter(venueMnemonic == "M",
raceType == "R",
meetingDate == "2022-01-01")
# then get the URLs
meet_url <- meet_dates_df$races[[1]]
meet_url <- meet_url$`_links`$self[1]
# finally, running the function:
race_list_output <- get_past_race_content(urls = meet_url)
Parsing Race Data
So to easily get the data you want, parsing functions have been
created to operate on the stored outputs of either the
get_past_races()
or
get_past_race_content()
.
Parse runners and betting market
To get a race’s runners and betting odds, use the
parse_runners()
function:
runners <- parse_runners(race_list = race_list_output)
glimpse(runners)
#> Rows: 61
#> Columns: 34
#> $ meetingDate <chr> "2022-09-03", "2022-09-03", "2022-09-03", …
#> $ location <chr> "VIC", "VIC", "VIC", "VIC", "VIC", "VIC", …
#> $ meetingName <chr> "MOONEE VALLEY", "MOONEE VALLEY", "MOONEE …
#> $ raceType <chr> "R", "R", "R", "R", "R", "R", "R", "R", "R…
#> $ venueMnemonic <chr> "M", "M", "M", "M", "M", "M", "M", "M", "M…
#> $ raceNumber <chr> "8", "8", "8", "8", "8", "8", "8", "8", "8…
#> $ raceName <chr> "DRUMMOND GOLF HANDICAP", "DRUMMOND GOLF H…
#> $ raceStartTime <chr> "2022-09-03T06:50:00+00:00", "2022-09-03T0…
#> $ raceStatus <chr> "Paying", "Paying", "Paying", "Paying", "P…
#> $ raceDistance <chr> "1600", "1600", "1600", "1600", "1600", "1…
#> $ resultedTime <chr> "2022-09-03T06:53:24+00:00", "2022-09-03T0…
#> $ substitute <chr> "5", "5", "5", "5", "5", "5", "5", "5", "5…
#> $ raceClassConditions <chr> "OPEN", "OPEN", "OPEN", "OPEN", "OPEN", "O…
#> $ winBook <chr> "http://internal-aviato-lb-212286492.ap-so…
#> $ scratchings <chr> "1", "1", "1", "1", "1", "1", "1", "1", "1…
#> $ skyRacingAudio <chr> "https://mediatabs.skyracing.com.au/Audio_…
#> $ runnerName <chr> "JOHNNY GET ANGRY", "DARK DREAM", "SO SI B…
#> $ runnerNumber <int> 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 2, 3…
#> $ finishingPosition <int> 0, 0, 3, 0, 2, 1, 0, 0, 4, 0, 0, 0, 0, 2, …
#> $ trainerName <chr> "DENIS PAGAN", "BEN & JD HAYES", "BEN & JD…
#> $ barrierNumber <int> 11, 2, 3, 6, 5, 7, 10, 4, 8, 1, 9, 9, 16, …
#> $ riderDriverName <chr> "LACHLAN KING", "LAURA LAFFERTY", "LUKE NO…
#> $ claimAmount <dbl> -1.0, 3.0, -1.0, 2.0, -1.0, -1.0, -1.0, -1…
#> $ fixedOdds.returnWin <dbl> 151.0, 31.0, 10.0, 23.0, 2.5, 3.4, 26.0, 2…
#> $ fixedOdds.returnWinOpen <dbl> 81.0, 13.0, 12.0, 17.0, 3.0, 3.7, 13.0, 21…
#> $ fixedOdds.returnPlace <dbl> 22.00, 6.00, 2.60, 4.60, 1.30, 1.45, 5.00,…
#> $ fixedOdds.bettingStatus <chr> "LateScratched", "Loser", "Placing", "Lose…
#> $ fixedOdds.winDeduction <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
#> $ fixedOdds.placeDeduction <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, …
#> $ fixedOdds.propositionNumber <int> 159951, 159952, 159953, 159954, 159955, 15…
#> $ fixedOdds.scratchedTime <chr> "2022-09-01T21:29:32+00:00", NA, NA, NA, N…
#> $ parimutuel.bettingStatus <chr> "Scratched", "Normal", "Normal", "Normal",…
#> $ parimutuel.returnWin <dbl> 0.0, 33.2, 10.1, 21.4, 2.5, 3.3, 21.2, 24.…
#> $ parimutuel.returnPlace <dbl> 0.0, 6.3, 2.2, 4.7, 1.3, 1.5, 4.4, 5.2, 2.…
Parse pools
To get a race’s betting pools, use the parse_pools()
function:
pools <- parse_pools(race_list = race_list_output)
glimpse(pools)
#> Rows: 108
#> Columns: 25
#> $ meetingDate <chr> "2022-09-03", "2022-09-03", "2022-09-03", "2022-09…
#> $ location <chr> "VIC", "VIC", "VIC", "VIC", "VIC", "VIC", "VIC", "…
#> $ meetingName <chr> "MOONEE VALLEY", "MOONEE VALLEY", "MOONEE VALLEY",…
#> $ raceType <chr> "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", …
#> $ venueMnemonic <chr> "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", …
#> $ raceNumber <chr> "8", "8", "8", "8", "8", "8", "8", "8", "8", "8", …
#> $ raceName <chr> "DRUMMOND GOLF HANDICAP", "DRUMMOND GOLF HANDICAP"…
#> $ raceStartTime <chr> "2022-09-03T06:50:00+00:00", "2022-09-03T06:50:00+…
#> $ raceStatus <chr> "Paying", "Paying", "Paying", "Paying", "Paying", …
#> $ raceDistance <chr> "1600", "1600", "1600", "1600", "1600", "1600", "1…
#> $ resultedTime <chr> "2022-09-03T06:53:24+00:00", "2022-09-03T06:53:24+…
#> $ substitute <chr> "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", …
#> $ raceClassConditions <chr> "OPEN", "OPEN", "OPEN", "OPEN", "OPEN", "OPEN", "O…
#> $ winBook <chr> "http://internal-aviato-lb-212286492.ap-southeast-…
#> $ scratchings <chr> "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", …
#> $ skyRacingAudio <chr> "https://mediatabs.skyracing.com.au/Audio_Replay/2…
#> $ poolStatusCode <chr> "Paying", "Paying", "Paying", "Paying", "Paying", …
#> $ wageringProduct <chr> "OddsAndEvens", "Win", "Place", "Quinella", "Exact…
#> $ poolTotal <dbl> 57.00, 199653.25, 72917.28, 41096.61, 14199.70, 39…
#> $ jackpot <int> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ poolCloseTime <chr> "2022-09-03T06:50:19+00:00", "2022-09-03T06:50:19+…
#> $ mergePool <list> "Quinella", <NULL>, <NULL>, "OddsAndEvens", <NULL…
#> $ mergePoolTotal <dbl> 41153.61, NA, NA, 41153.61, NA, NA, 93090.12, 9309…
#> $ legNumber <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 4, 1, 2, 1, 2,…
#> $ startTime <chr> "2022-09-03T06:50:00+00:00", "2022-09-03T06:50:00+…
Parse dividends
To get a race’s dividends, use the parse_dividends()
function:
dividends <- parse_dividends(race_list = race_list_output)
glimpse(dividends)
#> Rows: 66
#> Columns: 24
#> $ meetingDate <chr> "2022-09-03", "2022-09-03", "2022-09-03", "2022-09…
#> $ location <chr> "VIC", "VIC", "VIC", "VIC", "VIC", "VIC", "VIC", "…
#> $ meetingName <chr> "MOONEE VALLEY", "MOONEE VALLEY", "MOONEE VALLEY",…
#> $ raceType <chr> "R", "R", "R", "R", "R", "R", "R", "R", "R", "R", …
#> $ venueMnemonic <chr> "M", "M", "M", "M", "M", "M", "M", "M", "M", "M", …
#> $ raceNumber <chr> "8", "8", "8", "8", "8", "8", "8", "8", "8", "8", …
#> $ raceName <chr> "DRUMMOND GOLF HANDICAP", "DRUMMOND GOLF HANDICAP"…
#> $ raceStartTime <chr> "2022-09-03T06:50:00+00:00", "2022-09-03T06:50:00+…
#> $ raceStatus <chr> "Paying", "Paying", "Paying", "Paying", "Paying", …
#> $ raceDistance <chr> "1600", "1600", "1600", "1600", "1600", "1600", "1…
#> $ resultedTime <chr> "2022-09-03T06:53:24+00:00", "2022-09-03T06:53:24+…
#> $ substitute <chr> "5", "5", "5", "5", "5", "5", "5", "5", "5", "5", …
#> $ raceClassConditions <chr> "OPEN", "OPEN", "OPEN", "OPEN", "OPEN", "OPEN", "O…
#> $ winBook <chr> "http://internal-aviato-lb-212286492.ap-southeast-…
#> $ scratchings <chr> "1", "1", "1", "1", "1", "1", "1", "1", "1", "1", …
#> $ skyRacingAudio <chr> "https://mediatabs.skyracing.com.au/Audio_Replay/2…
#> $ poolStatusCode <chr> "Paying", "Paying", "Paying", "Paying", "Paying", …
#> $ wageringProduct <chr> "OddsAndEvens", "Win", "Place", "Place", "Place", …
#> $ jackpotCarriedOver <dbl> 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,…
#> $ poolCloseTime <chr> "2022-09-03T06:50:19+00:00", "2022-09-03T06:50:19+…
#> $ mergePool <list> "Quinella", <NULL>, <NULL>, <NULL>, <NULL>, "Odds…
#> $ mergePoolTotal <dbl> 41153.61, NA, NA, NA, NA, 41153.61, NA, NA, NA, NA…
#> $ selections <list> "SPLIT", 6, 6, 5, 3, <6, 5>, <6, 5>, <6, 5>, <6, …
#> $ amount <dbl> 1.5, 3.3, 1.5, 1.3, 2.2, 3.6, 6.8, 2.1, 5.0, 4.2, …