---
title: "Track Bias"
author: "Kyle King"
date: "November 2022"
output: html_notebook
---

# %% [markdown]
# # 1. Proving Track Bias
# 
# After analyzing the latitudinal/longitudinal data for all 14915 running lines at NYRA tracks in 2019, I have come to this conclusion:
# 
# > #### Track bias in horse racing can be proven mathematically
# 
# > #### Moreover, there exists a metric that tracks/players can use to determine the reliability and strength of a bias (if one exists at all). 
# 
# 
# Track bias is not a new topic.  Horsemen and horseplayers alike have known of track biases for several decades now.  However, usually track bias is talked about anecdotally based on observations of certain horses running better or worse than expected.  I have never seen a successful data-driven metric that measures the strength & validity of a bias (until now).  With this Bias Metric, unexpected running lines can now potentially be explained with much greater accuracy.
# 
# Some potential benefits of this Bias Metric:
# 
#   - Horsemen will be able to use this Bias Metric to get a more-accurate picture of how well the horse ran on any given day.
#   - Horseplayers will be able to use this Bias Metric to spot horses that ran with the bias for potential "bet-againsts" next time out or horses that ran against the bias for potential "bet-ons" next time out.
#   - If one or two races early on a racecard are returning with a strong Bias Metric, then trainers/jockeys/horseplayers will potentially be able to use this knowledge to their favor for the rest of the card.
# 
# 

# %% [markdown]
# # 2.  What is Track Bias?
# 
# The layout between a track & field oval (think Olympics) and a horse track is actually quite similar.  The biggest difference, of course, is the size.  Most track & field ovals in the United States are standardized to be 1/4 mile circumference, while the vast majority of North American horse tracks tend to be around one mile for turf courses and around 1 1/8 miles for dirt courses.  
# 
# However, for both humans and equines, these distances are the <i>minimum</i> distances around the track.  Basic geometry would suggest that the further out you travel from the inner-edge, the longer distance you must go.  This geometry is heavily regulated in track & field, as many disciplines have designated lanes in which the runners must stay.  To make up for the extra distances covered by the runners in the outsides lanes, you will see a staggered start, where the outside runners get a slight "head start".  
# 
# <img src="https://cdn.vox-cdn.com/thumbor/VeFKXKNMWADHEjGXB6k7aW9eDZY=/0x0:2178x1196/920x0/filters:focal(0x0:2178x1196):format(webp):no_upscale()/cdn.vox-cdn.com/uploads/chorus_asset/file/6942839/400start.0.png" alt="Staggered start in track & field" height="267" width="400">
# 
# 
# 
# In horse racing, the geometry of running lanes is not regulated.  Once horses leave the starting gate, they are free to run anywhere they wish (as long as that space is not currently occupied by another horse).  So, logic would imply that horses who secure that inner-most running lane have a slight advantage over their rivals.  Often, that is the case.  However, it is not <i>always</i> the case.
# 
# Sometimes, a race ends up where the horses running further out (covering greater distances) perform better than their rivals.  Consersely, sometimes a race is run where the horses closest to the inside-rail have an advantage much greater than the geometry would suggest.  These results, while not occurring all the time, do happen too often to be ignored, and they are the result of running the races outside on dirt and grass that is not 100% uniform across the entire running surface.  
# > #### When one running lane has an advantage over another running lane, a track bias exists.
# 
# These track biases are the topic of many conversations both on horseracing-Twitter and around tracks/barns.  However, without any sort of bias-metric, these conversations tend to be very subjective in-nature.  Additionally, very few participants in these conversations have access to this kind of data on a daily basis.  Sharing this data set allows the public to look at track bias objectively for the first time ever.          
# 
# 
# ![nyratwitter.JPG](attachment:0612fc73-24dd-462b-967a-bcb1c5a49865.JPG)
# 
# 

# %% [markdown]
# # 3.  The Coding - Organizing Data
# 
# This dataset of all 2000 races run at NYRA tracks in 2019 was very nicely organized for my purposes.  In my opinion, the biggest issue was figuring out which Trakus index was closest to the finish line for each invividual horse.  Borrowing from the ideas given by user [@shapital](https://www.kaggle.com/shapital) and his discussion post [here](https://www.kaggle.com/competitions/big-data-derby-2022/discussion/357130), I was able to come as close as possible to finding the latitude/longitude where each horse crossed the finish line.

# %% [code] {"execution":{"iopub.status.busy":"2022-11-03T18:16:19.561930Z","iopub.execute_input":"2022-11-03T18:16:19.567785Z","iopub.status.idle":"2022-11-03T18:17:11.054898Z"}}
#uploading dataframe and naming columns
nyra_2019_complete <- read.csv("../input/big-data-derby-2022/nyra_2019_complete.csv", header=T)
names(nyra_2019_complete) <- c("track_id", "race_date", "race_number", "program_number", "trakus_index", "latitude", "longitude", "distance_id", "course_type", "track_condition", "run_up_distance", "race_type", "post_time", "purse", "weight_carried", "jockey", "odds", "position_at_finish")

# %% [code] {"execution":{"iopub.status.busy":"2022-11-03T18:17:16.318285Z","iopub.execute_input":"2022-11-03T18:17:16.320050Z","iopub.status.idle":"2022-11-03T18:18:37.555678Z"}}
#organizing df and adding some columns

nyra_2019_complete$IDrace <- NA
nyra_2019_complete$IDrace <- paste(nyra_2019_complete$race_date, nyra_2019_complete$race_number, collpase="", sep=",")

nyra_2019_complete$IDhorse <- NA
nyra_2019_complete$IDhorse <- paste(nyra_2019_complete$race_date, nyra_2019_complete$race_number, nyra_2019_complete$program_number, collpase="", sep=",")

nyra_2019_complete$last_trakus <- NA
nyra_2019_complete$last_trakus_2 <- NA



#reordering df
nyra_2019_complete <- nyra_2019_complete[order(nyra_2019_complete$trakus_index),]
nyra_2019_complete <- nyra_2019_complete[order(nyra_2019_complete$position_at_finish),]
nyra_2019_complete <- nyra_2019_complete[order(nyra_2019_complete$race_number),]
nyra_2019_complete <- nyra_2019_complete[order(nyra_2019_complete$race_date),]
nyra_2019_complete <- nyra_2019_complete[order(nyra_2019_complete$track_id),]

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2022-11-03T18:18:37.558804Z","iopub.execute_input":"2022-11-03T18:18:37.560234Z","iopub.status.idle":"2022-11-03T18:18:37.592851Z"}}
#uploading additional dataframe which estimated where each horse crossed finish line
df_name <- read.csv("../input/calculating-when-horses-finish-the-race-kk/df_name.csv", header=T)
df_name$race_index <- (df_name$race_index+1)

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2022-11-03T18:18:37.596088Z","iopub.execute_input":"2022-11-03T18:18:37.597675Z","iopub.status.idle":"2022-11-03T18:24:27.216699Z"}}
#finding trackus stopping points
for (i in 1:length(unique(nyra_2019_complete$IDrace)))  {
  A <- which(nyra_2019_complete$IDrace == unique(nyra_2019_complete$IDrace)[i])
  num_racers <- max(nyra_2019_complete$position_at_finish[A])
  for (j in 1:num_racers) {
    B <- which(nyra_2019_complete$position_at_finish[A] == j)
    nyra_2019_complete$last_trakus[A][B] <- df_name[i,(j+1)]
  }
}
rm(i,j,A,B,num_racers)

# %% [code] {"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2022-11-03T18:24:27.219092Z","iopub.execute_input":"2022-11-03T18:24:27.220487Z","iopub.status.idle":"2022-11-03T19:23:57.779941Z"}}
#subtracting from max trakus to get the trakus_index where horse crossed "finish line"
for (i in 1:length(unique(nyra_2019_complete$IDrace)))  {
  A <- which(nyra_2019_complete$IDrace == unique(nyra_2019_complete$IDrace)[i])
  nyra_2019_complete[A,]$last_trakus_2 <- max(nyra_2019_complete[A,]$trakus_index) - nyra_2019_complete[A,]$last_trakus  
}
rm(A,i)


#modifying the lat/lon for trakus indices greater than last_trakus_2...basically "freezes" the horse at that location
for (i in 1:length(unique(nyra_2019_complete$IDhorse)))  {
  A <- which(nyra_2019_complete$IDhorse == unique(nyra_2019_complete$IDhorse)[i])
  B <- which(nyra_2019_complete$trakus_index[A] == max(nyra_2019_complete$last_trakus_2[A]) )
  C <- which(nyra_2019_complete$trakus_index[A] > max(nyra_2019_complete$last_trakus_2[A]) )
 
  nyra_2019_complete$latitude[A][C] <- nyra_2019_complete$latitude[A][B]
  nyra_2019_complete$longitude[A][C] <- nyra_2019_complete$longitude[A][B]
}
rm(i,A,B,C)

# %% [markdown]
# # 4.  The Coding - Finding True Distances per Horse
# 
# Now that I have an improved estimate for the latitude/longitude coordinates of each horse during each race, I'm ready to compute the total distance run by each horse.  To do this, I find the distance between any two successive lat/lon coordinates, then add those all together.  In the end, I convert everything to feet.

# %% [code] {"execution":{"iopub.status.busy":"2022-11-03T19:23:57.782258Z","iopub.execute_input":"2022-11-03T19:23:57.783649Z","iopub.status.idle":"2022-11-03T20:23:21.945659Z"}}
install.packages("geosphere")
library(geosphere)

nyra_2019_complete$race_runningdistance <- NA

#finding total distance run by each horse (in feet)
temp_distance <- unname( sapply( unique(nyra_2019_complete$IDhorse), function(x)  {
  A <- which(nyra_2019_complete$IDhorse == x)
  a <- nyra_2019_complete$longitude[A][order(nyra_2019_complete$trakus_index[A])]
  b <- nyra_2019_complete$latitude[A][order(nyra_2019_complete$trakus_index[A])]
  c <- rep(0, length(a))
  for (i in 1:(length(a)-1)) {c[i] <- distm(c(a[i], b[i]), c(a[(i+1)], b[(i+1)]), fun = distHaversine) }
  sum(c) * 3.28084
  }))


for (i in 1:length(temp_distance))  {
  A <- which(nyra_2019_complete$IDhorse == unique(nyra_2019_complete$IDhorse)[i])
  nyra_2019_complete$race_runningdistance[A] <- rep(temp_distance[i], length(A))
}

# %% [code]
#now creating matrix of just one row per horse
temp_horses <- unname( sapply( unique(nyra_2019_complete$IDhorse), function(x)  {
  max( which(nyra_2019_complete$IDhorse == x) ) }))

nyra_horses <- nyra_2019_complete[temp_horses,]
rm(nyra_2019_complete); gc()


#finding the max trakus index for each race
max_trakus_race <- unname( sapply( unique(nyra_horses$IDrace), function(x)  {
  A <- which(nyra_horses$IDrace == x)
  max(nyra_horses$trakus_index[A])
  }))
raceIDs <- unique(nyra_horses$IDrace)

#finding the horses that did not finish each race
not_finish <- sapply( 1:nrow(nyra_horses), function(x)  {
  A <- which(raceIDs == nyra_horses$IDrace[x])
  if (nyra_horses$trakus_index[x] < max_trakus_race[A]) {1}
  else {0}
})

#removing the horses that did not finish from the data set
A <- which(not_finish > 0)
nyra_horses <- nyra_horses[-A,]
rm(A,max_trakus_race,not_finish,raceIDs)


#reorganizing df...
nyra_horses <- nyra_horses[,c(1,2,3,4,8,9,18,19,20,23)]

nyra_horses <- nyra_horses[order(nyra_horses$program_number),]
nyra_horses <- nyra_horses[order(nyra_horses$race_number),]
nyra_horses <- nyra_horses[order(nyra_horses$race_date),]
nyra_horses <- nyra_horses[order(nyra_horses$track_id),]

# %% [markdown]
# # 5.  The Coding - Calculating the Bias Metric
# 
# So now I have calculated how many feet each horse covered during his/her respective races.  The next step is organizing this data to analyze one race at a time.  Once that is done, we calculate the Bias Metric, which is a variation of the correlation coefficient (r) calculated between each horse's finishing position and distance covered for each race. 

# %% [code] {"execution":{"iopub.status.busy":"2022-11-03T21:55:54.526325Z","iopub.execute_input":"2022-11-03T21:55:54.527801Z","iopub.status.idle":"2022-11-03T21:55:57.422295Z"}}
#finding correlation of each race
correlations <- sapply( unique(nyra_horses$IDrace), function(x)  {
  A <- which(nyra_horses$IDrace == x )
  a <- nyra_horses$position_at_finish[A]
  b <- nyra_horses$race_runningdistance[A]
  cor(a,b)
})



#finding pval from t-test for beta for each race
sig_correlations <- sapply( unique(nyra_horses$IDrace), function(x)  {
  A <- which(nyra_horses$IDrace == x )
  a <- nyra_horses$position_at_finish[A]
  b <- nyra_horses$race_runningdistance[A]
  c <- lm(b~a)
  coef(summary(c))[2,4]
})


#computing signed pval and capping it at +/- 0.05
#positives indicate golden rail, negative indicate bad rail
signed_sig_correlation <- sign(correlations)*sig_correlations

race_metric_simple <- sapply( signed_sig_correlation, function(x)  {
  if (x < 0)  {max(1/x, -20)}
  else {min(1/x, 20)}
})


#now create a matrix one row from each race
temp_races <- unname( sapply( unique(nyra_horses$IDrace), function(x)  {
  min( which(nyra_horses$IDrace == x) ) }))

nyra_races <- nyra_horses[temp_races,c(1,2,3,5,6,8)]
nyra_races$Race_Metric_Simple <- race_metric_simple

rm(correlations,race_metric_simple,sig_correlations,signed_sig_correlation,temp_races)


# %% [code] {"execution":{"iopub.status.busy":"2022-11-03T21:57:42.468834Z","iopub.execute_input":"2022-11-03T21:57:42.470724Z","iopub.status.idle":"2022-11-03T21:57:44.794054Z"}}
#putting races into groups (basically racing Weeks) by consecutive racing days
nyra_races <- nyra_races[order(nyra_races$race_date),]
nyra_races$WeekNumber <- NA  
a <- sapply( nyra_races$race_date, function(x)  as.POSIXlt(x)$yday )
A <- sort(unique(a))
B <- cumsum(c(1, abs(A[-length(A)] - A[-1]) > 1))
for (i in 1:nrow(nyra_races)) {
  c <- which(unique(nyra_races$race_date) == nyra_races$race_date[i])
  nyra_races$WeekNumber[i] <- B[c]
}
rm(a,A,B,c,i)
            


#now finding Race_Metric_Day
nyra_races$Race_Metric_Day <- sapply( 1:nrow(nyra_races), function(x)  {
  A <- which(nyra_races$race_date == nyra_races$race_date[x])
  B <- which(nyra_races$course_type == nyra_races$course_type[x])
  C <- intersect(A,B)
  temp_df <- nyra_races[C,]
  temp_df$num <- 1:nrow(temp_df)
  temp_df$working.1 <- NA
  
  D <- temp_df$num[which(temp_df$IDrace == nyra_races$IDrace[x])]
  E <- unique(  abs(temp_df$num - D)  )[which(unique(  abs(temp_df$num - D)  )>0)]

  for (i in E)  {
    F <- which(abs(temp_df$num - D) == i)
    temp_df$working.1[F] <- 0.9^i
  }
  
  G <- mean(temp_df$working.1, na.rm=T)  
  temp_df$working.1 <- temp_df$working.1/G
  temp_df$working.2 <- temp_df$Race_Metric_Simple * temp_df$working.1
  
  #makes it 0 if different sign (pos/neg) than original
  if ( length(C)>1 & sign(mean(temp_df$working.2, na.rm=T))!=sign(temp_df$Race_Metric_Simple[which(is.na(temp_df$working.2))]) ) {temp_df$working.2 <- 0}
  
  #slight penalty if only 2 races on a given day on given surface
  if ( length(C)==2 ) {temp_df$working.2 <- 0.5*temp_df$working.2}
  
  #mean(temp_df$working.2, na.rm=T)
  if (is.na(mean(temp_df$working.2, na.rm=T))) {0
  } else {mean(temp_df$working.2, na.rm=T)}
  
})


#now finding Race_Metric_Week
nyra_races$Race_Metric_Week <- sapply( 1:nrow(nyra_races), function(x)  {
  #x=5
  A <- which(nyra_races$WeekNumber == nyra_races$WeekNumber[x])
  D <- which(nyra_races$race_date == nyra_races$race_date[x])
  A <- setdiff(A,D)
  B <- which(nyra_races$course_type == nyra_races$course_type[x])
  C <- intersect(A,B)

  temp_df <- nyra_races[C,]
  
  E <- mean(temp_df$Race_Metric_Simple)
  
  #makes it 0 if different sign (pos/neg) than original
  if ( length(C)>1 & sign(E)!=sign(nyra_races$Race_Metric_Simple[x]) ) {E <- 0}

  if (is.na(E)) {0
  } else {E}
  
})


nyra_races$Bias_Metric <- nyra_races$Race_Metric_Simple + nyra_races$Race_Metric_Day + 0.5*nyra_races$Race_Metric_Week


download.csv <- function(dataframe) {
    csv.vector <- capture.output(write.csv(dataframe))
    csv.string <- paste(csv.vector, collapse="\n")
    csv.base64 <- as.character(RCurl::base64(csv.string))
    csv.html <- paste("<a download=\"dataframe.csv\" href=\"data:text/csv;base64,", csv.base64, "\" target=\"_blank\">Download nyra_races</a>", sep="")
    IRdisplay::display_html(csv.html)
}
download.csv(nyra_races)

# %% [markdown]
# # 6.  Discussion
# 
# The key to this analysis is the relationship between each horse's finishing position in a race and how far each horse ran in that race.  To begin this analysis, we can turn each horse into a bivariate (x,y) data point, where x represents finishing position and y represents distance covered.  
# 
# **Taking a closer look at Race 1 from Aqueduct on 1/1/19, we get this scatterplot:**
# ![010119r1.jpg](attachment:fb7fdd7e-d4f9-4bb1-ae60-57367310be43.jpg)
# 
# 
# **Adding in the line-of-best-fit gives us this scatterplot:**
# ![010119r1b.jpg](attachment:e66c811c-52b1-47bb-99c1-e0957a13b149.jpg)
# 
# 
# As observed in the scatterplot above (and by the low correlation coefficient), in this race, finishing position and distance do not have much of a linear relationship - looking at the extreme distances of the top-2 finishers confirms this.  While it's hard to draw conclusions from just one race, a scatterplot like this usually indicates no bias whatsoever.  As it turns out, that is the case for the vast majority of races in our 2000 race sample.  But that is not the case for every race...  
# 
# **Here is the scatterplot from Race 4 at Aqueduct on 12/15/19:**
# ![121519r4.jpg](attachment:1df4b4e0-83b3-4894-9b0d-e32e33346cd0.jpg)
# 
# In the race graphed above, the correlation coefficient is less than -0.8, indicating a strong, negative, linear relationship between the two variables.  In this race, it appears as though the horses that ran closest to the rail performed worse despite covering a shorter distance.  Most refer to this type of bias as a "dead rail" bias, because the running paths closest to the rail actually slowed horses down.  When horses that run against biases come back to run again, they often fare better.  Similarly, when horses that are aided by a bias come back to run, they often fare worse.  In the race shown above, **#4: Overbold** was most negatively-affected by this potential bias.  He came back to win his next start at Aqueduct on 1/30/20 against similar as the third betting choice.
# 
# ###  Calculating the Bias Metric
# 
# The issue with only using each race's correlation coefficient (r) is that r-values can be heavily influenced by races with small fields.  In fact, in a race with only 3 horses, there is almost a 50% chance that those horses will run in a linear pattern when one graphs <i>x = finishing position</i> versus <i>y = distance covered</i> on a scatterplot.  While a bias might exist in these races with small fields, there is also a lot of random noise that r-values fail to filter out.  Instead, I used a statistic that incorporated both aspects of the correlation coefficient along with field size.  
# 
# > #### I used the p-value from the linear regression t-test for slope.
# 
# This statistic basically computes the likelihood that one variable is dependent on the other variable.  Because they are probabilities, p-values are always going to be values between 0 and 1.  Races that follow a linear pattern (indicating a potential bias) would have very small p-values.  Conversely, races that do not follow a linear pattern have larger p-values.  I took the reciprocal of this p-value, capped it at 20, then multiplied it by -1 if the sign of the correlation coefficient was negative.  What results is a single-race statistic between -20 and 20.  A score of -20 would indicate a strong "dead-rail" bias, while a score of +20 would indicate a strong "golden-rail" bias.  As you can see, the majority of races scored close to 0, indicating that most races did not indicate a potential track bias.  I call this statistic the Race-Metric-Simple.
# 
# **Race-Metric-Simple from all 2000 races in sample:**
# ![biaschart.jpg](attachment:18f82208-5a28-4b34-91a6-9dd88e377889.jpg)
# 
# 
# 
# ###  Multiple Races
# 
# Looking at one race at a time provides some interesting results, such as Aqueduct's Race 4 on 12/15/19 as shown in one of the scatterplots above.  However, it can be hard to draw conclusions just from the results of one race.  Often, when a bias exists on a racing surface, multiple races run on that same surface around the same time will be impacted.
# 
# **Here is the scatterplot from multiple dirt races run at Saratoga on 7/26/19:**
# ![sar072619.jpg](attachment:c7fc6f50-c8b5-453f-9c3f-10e49a589342.jpg)
# 
# 
# Four of the eight dirt races (over a muddy/sealed surface) at Saratoga on July 26 exhibited an extreme "dead-rail" bias.  Looking at the trand of the scatterplot above, there was a clear negative trend present in those selected races.  In three of those races, the winner actually ran the longest distance (with the winner running the second-longest distance in the other race).  In fact, of all 2000 races in the sample, only 98 (5%) had a Race-Metric-Simple of -14 or less.  Four of those 98 races occured on this one particular race day.  Results like what happened on July 26 only have a 0.00035 probability of occurring randomly.  This essentially proves that track bias is more than something that is hypothetically bantered about on Twitter; it is a legitimate fact that occurs almost daily in racetracks across North America.  Further, the results from July 26 also demonstrate that this Race-Metric-Simple is a viable measurement tool that can be used to effectively measure track bias.  Notice the date of this Twitter post below:
# 
# ![july26twitter.jpg](attachment:8a402926-45b0-4e34-809f-3ab333a36879.jpg)
# 
# While the track dried and the dead-rail bias subsided on the next day, horseplayers (and jockeys, trainers, owners, etc...) that are able to notice a bias as it happens can certainly take advantage.
# 
# ###  Computing the Final Bias Metric
# 
# While the results from one race might suggest a track bias, it can be hard to draw conclusions from a sample of n=1.  So, the Race-Metric-Simple is actually one of three metrics that make up the final Bias Metric.  Here is how the Bias Metric is actually calculated:
# 
#   > #### 1. 40% is the Race-Metric-Simple, which is discussed in detail above and is calculated for each race individually.
#   > #### 2. 40% is the Race-Metric-Day, which is a weighted average of the Race-Metric-Simple from all *other* races run on the same date over the same surface.  This is a weighted average, so races (in post order) closest to the target race count the most, then the weights gradually decline from there. 
#   > #### 3. 20% is the Race-Metric-Week, which is an average Race-Metric-Simple of all races run during the same racing week (typically Wednesday/Thursday to Sunday) over the same surface.  This metric excludes races from the same day because those get factored into the Race-Metric-Day.
#   
#   
# The Race-Metric-Day and Race-Metric-Week increase the validity of the Bias Metric by removing any uncertainties that arise by looking at each race separately.  Combined, the final Bias Metric will be on a continuum from -50 to +50.  A maximum score of +50 for a race would indicate the the most confidence that a "golden rail" bias was present for that race, and a minimum score of -50 would indicate the most confidence that a "dead rail" bias was present for that race.  Any score near 0 would indicate no significant bias.
# 
# ![scale.JPG](attachment:0fb6f9d8-d5a4-4bc8-99b2-478f0de4841b.JPG)

# %% [markdown]
# # 7.  Results
# 
# The full results can be downloaded as a .csv file at the conclusion of Section 5 above. The full distribution can be seen below.
# 
# **Bias Metric from all 2000 races in sample:**
# ![annotatedmetric.JPG](attachment:84f72732-50f2-4347-84df-bb596fad7ecb.JPG)
# 
# 
# 
# 
# ###  A bias was mathematically detected in roughly 20% of races in the sample.
# ![piechart.jpg](attachment:d823c7df-26c6-4df9-a60c-2f7369d8f252.jpg)
# 
# 
# ###  A Few Notable Races
# 
# Of all 2000 races in the sample, the race with the biggest Bias Metric (in either direction) was [race 9 from Aqueduct on November 9th](https://www.equibase.com/premium/eqbPDFChartPlus.cfm?RACE=9&BorP=P&TID=AQU&CTRY=USA&DT=11/09/2019&DAY=D&STYLE=EQB) ([video link](https://youtu.be/_RPUPYKGM9k?t=39)).  That race had a Bias Metric = 39.4.  As a matter of fact, virtually every turf race run at Aqueduct that entire week featured a pronounced "golden rail" bias, where the horses that secured running lanes closest to the inner rail had a significant advantage over their rivals.  In fact, according to my Bias Metric, the top 7 races in the sample all came on the turf at Aqueduct on that particular weekend (and all had "golden rail" biases).  As was the case with most other biases, astute horsemen, horseplayers, and jockeys were able to take advantage of this bias as it happened:
# 
# ![serlingnovem.JPG](attachment:5f49a28c-c2ab-4214-baec-45c10d35cbf9.JPG)
# 
# 
# If a horse runs a race against a track bias, then often, that running line in the past performances appears worse than it really was.  That is a prime opportunity for horseplayers to bet on that horse next time it runs.  In Race 9 from Aqueduct on November 9th, the seventh place horse **#6: Turned Aside** ran about 3.5 lengths further than the winners while finishing about 3.5 lengths behind the winners.  However, he did a lot of his running further out from the rail, which proved to be against the bias that day.  He went on to win his next start at Aqueduct on 11/27/19.  Similar things can be said for **#9: Axiomo**, who finished last in that same Race 9, but went on to win his next start at Churchill Downs on 11/23/19.  As for some of the runners who ran *with* the bias in that Race 9?  None of the top 6 finishers, including winner **#4: Jack and Noah** came back to win their next start, despite three of them going off as posttime favorite.
# 
# Interestingly, the two races with the most-reliable "dead-rail" Bias Metric came during the dirt races on the second-biggest NYRA racing day of the year: Travers Day.  One of those two races was actually the Grade 1 [H. Allen Jerkens Stakes](https://youtu.be/CUv3GbiA04M?t=63) (Bias Metric = -29.9), during which the heavy favorite Shancelot took the inside path and Mind Control went much wider to win at odds of 10/1.  While the [Travers](https://youtu.be/dNGWO3I6lbM?t=97) itself has basically a Bias Metric = ~0, the chart says Code of Honor went out to the 8-path to cirlce the field during that race, which the math confidently says was the fastest part of the track that day.  
# 
# **Bias Metric throughout the NYRA calendar:**
# ![calendar.jpg](attachment:e0d06c00-779b-4703-bfd2-a9df1eed2db7.jpg)
# 
# ###  Areas of Future Study
# 
#   - Using my Bias Metric: If I were an owner or trainer, I would want to know which jockeys tended to run with and against the track biases.  All else being equal, I would prefer a jockey who is aware of how the track is acting and can manuever horses to the correct part of the track.
#   - There are also biases created by the pace of certain races.  For example, if a race is run slowly, horses that tend to be closers are negatively affected.  Up to now, these "pace/flow" numbers and Sartin Methodology relied only on sectional times.  With this GPS tracking data, this methodology can/should be updated to allow for continuous calculations instead of the outdated discrete methods.  Similar pace-bias metrics can then be computed.
# 
# 