
Getting TAB betting data using bettRtab
Jason Zivkovic
2022-12-19
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: 289
#> Columns: 33
#> $ marketId <chr> "68519348", "68519348", "68519348", "68519348", "6…
#> $ marketName <chr> "EPL Winner 2022/23", "EPL Winner 2022/23", "EPL W…
#> $ shortName <chr> "EPL Winner 2022/23", "EPL Winner 2022/23", "EPL W…
#> $ betOption <chr> "Winner", "Winner", "Winner", "Winner", "Winner", …
#> $ betOptionSpectrumId <chr> "52", "52", "52", "52", "52", "52", "52", "52", "5…
#> $ betOptionPriority <chr> "220", "220", "220", "220", "220", "220", "220", "…
#> $ marketUniqueId <chr> "68519348", "68519348", "68519348", "68519348", "6…
#> $ closeTime <chr> "2022-12-26T12:30:00.000Z", "2022-12-26T12:30: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> "278385", "278390", "278386", "278391", "278388", …
#> $ name <chr> "Man City", "Arsenal", "Liverpool", "Newcastle", "…
#> $ returnWin <chr> "1.42", "3.25", "34", "34", "67", "67", "251", "50…
#> $ 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> "278385", "278390", "278386", "278391", "278388", …
#> $ isOpen <chr> "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "TRUE", "T…
#> $ sortOrder <chr> "1", "6", "2", "7", "4", "5", "3", "10", "8", "14"…
We can then inspect to see which betting markets are available:
epl_futures %>%
select(betOption) %>% distinct()
#> betOption
#> 1 Winner
#> 2 Top Goal Scorer
#> 3 Top Goal Scorer Without Haaland
#> 4 Top 4
#> 5 Relegation
#> 6 Top 2
#> 7 Top 6
#> 8 To Finish Top Half
#> 9 Team to Finish Last
#> 10 Season Points Handicap
#> 11 Winner without Liverpool & Man City
#> 12 Winner without Big 6
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 EPL Relegation 2022/23 228288 Nottingham Forest 1.55
#> 2 EPL Relegation 2022/23 228287 Bournemouth 1.65
#> 3 EPL Relegation 2022/23 228277 Wolverhampton 1.8
#> 4 EPL Relegation 2022/23 228278 Everton 2.6
#> 5 EPL Relegation 2022/23 228285 Southampton 2.6
#> 6 EPL Relegation 2022/23 228286 Leeds 5
#> 7 EPL Relegation 2022/23 228284 Fulham 6.5
#> 8 EPL Relegation 2022/23 228279 Leicester 9
#> 9 EPL Relegation 2022/23 228282 Brentford 9
#> 10 EPL Relegation 2022/23 228275 West Ham 11
#> 11 EPL Relegation 2022/23 228283 Aston Villa 15
#> 12 EPL Relegation 2022/23 228280 Crystal Palace 21
#> 13 EPL Relegation 2022/23 228281 Brighton 67
#> 14 EPL Relegation 2022/23 228271 Chelsea 351
#> 15 EPL Relegation 2022/23 228270 Liverpool 401
#> 16 EPL Relegation 2022/23 228272 Man Utd 401
#> 17 EPL Relegation 2022/23 228273 Tottenham 751
#> 18 EPL Relegation 2022/23 228276 Newcastle 751
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,282
#> 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: 120
#> 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, …