How to use a horse form in betfair bot script
Posted in Tutorials by StefanBelo Tags: bot programming
The bfexplorer shows for UK and Irish horse racing and data about each selection including:
- Silks description
- Trainer name
- Age and Weight
- Form
- Days since last run
- Jockey claim
- Wearing text
- Saddle cloth
- Stall draw
Any of this information can be used by a bot script, in this article I will show you how to show the horse form and sort horses by the form and how to use the horse form information in a simple betting strategy on which I will show you how to build LookUp bot script running a trading bot.

You can see on the picture that moving the cursor over the horse name a tooltip is displayed with all that information but it would be useful to show all horses sorted by the horse form in the output window too. If a market supports additional information about each selection the SelectionData property of the selection/runner is set to the object inherited form ISelectionData interface. In the case of the horse racing market this object is represented by the class: HorseRacingSelectionData
using BeloSoft.Betfair.Data;
using BeloSoft.Betfair.Trading;
using System.Collections.Generic;
/*
* Used parameters:
*
* PlaceBetOnRunners - Show the form from last x races, if 0 sorted by all race results
*/
namespace Bfexplorer.Scripting
{
class ShowHorsesForm : Bot
{
private class HorseSortedCollection : List
{
// Data
private byte sortByLastRaces;
public HorseSortedCollection(MonitoredMarket monitoredMarket)
{
AddRange(monitoredMarket.MarketDetails.Runners);
}
public void SortByForm()
{
Sort(SortByFormValue);
}
public void SortByFormFromLastRaces(byte lastRaces)
{
sortByLastRaces = lastRaces;
Sort(SortByFormValueOfLastRaces);
}
private static int SortByFormValue(Runner x, Runner y)
{
float xData = GetFormValue(((HorseRacingSelectionData)x.SelectionData).HorseFormFigures);
float yData = GetFormValue(((HorseRacingSelectionData)y.SelectionData).HorseFormFigures);
return xData > yData ? -1 : (xData < yData ? 1 : 0);
}
private int SortByFormValueOfLastRaces(Runner x, Runner y)
{
float xData = GetFormValue(GetLastRaces(((HorseRacingSelectionData)x.SelectionData).HorseFormFigures, sortByLastRaces));
float yData = GetFormValue(GetLastRaces(((HorseRacingSelectionData)y.SelectionData).HorseFormFigures, sortByLastRaces));
return xData > yData ? -1 : (xData < yData ? 1 : 0);
}
private static string GetLastRaces(string formText, byte lastRaces)
{
if (!string.IsNullOrEmpty(formText))
{
if (formText.Length > lastRaces)
{
formText = formText.Substring(formText.Length - lastRaces, lastRaces);
}
}
return formText;
}
private static float GetFormValue(string formText)
{
float formValue = 0.0f;
if (!string.IsNullOrEmpty(formText))
{
foreach (char result in formText.ToCharArray())
{
if (char.IsDigit(result))
{
int place = (int)(result - '0');
if (place > 0)
{
formValue += 9.0f / place;
}
}
}
}
return formValue;
}
}
// Const
private const int HorseRacingEventId = 7;
public ShowHorsesForm(IBetfairService betfairService, MonitoredMarket monitoredMarket, Runner runner)
: base(betfairService, monitoredMarket, runner)
{
}
public override void DoYourJob()
{
base.DoYourJob();
if (GetHaveHorseRacingSelectionData())
{
HorseSortedCollection horses = new HorseSortedCollection(monitoredMarket);
byte formForLastRaces = RunnerProperty.PlaceBetOnRunners;
if (formForLastRaces > 0)
{
horses.SortByFormFromLastRaces(formForLastRaces);
}
else
{
horses.SortByForm();
}
horses.Reverse();
int place = horses.Count;
foreach (Runner horse in horses)
{
HorseRacingSelectionData horseRacingSelectionData = horse.SelectionData as HorseRacingSelectionData;
AddMessage(string.Format("{0}.{1}: {2}", place--, horse.Name, horseRacingSelectionData.HorseFormFigures));
}
}
else
{
AddMessage("No form inforamtion!");
}
Stop();
}
private bool GetHaveHorseRacingSelectionData()
{
MarketDetails marketDetails = monitoredMarket.MarketDetails;
return marketDetails.EventTypeId == HorseRacingEventId && marketDetails.Runners[0].SelectionData is HorseRacingSelectionData;
}
}
}
If you run this bot script on a horse racing market the bot will show you sorted list of horses by the last 3 races form.

So now we can use the horse form in a simple LookUp bot script which will lay the horse with the best form from last 3 races and trade it out with 2% profit or 20% loss. This is just sample showing how to use the horse form.
using System;
using BeloSoft.Betfair.Data;
using BeloSoft.Betfair.Data.Betting;
using BeloSoft.Betfair.Data.Statistics;
using BeloSoft.Betfair.Trading;
using System.Collections.Generic;
namespace Bfexplorer.Scripting
{
public class HorseFormSampleLookUpBot : LookUpBot
{
private class HorseSortedCollection : List
{
// Data
private byte sortByLastRaces;
public HorseSortedCollection(MonitoredMarket monitoredMarket)
{
AddRange(monitoredMarket.MarketDetails.Runners);
}
public void SortByForm()
{
Sort(SortByFormValue);
}
public void SortByFormFromLastRaces(byte lastRaces)
{
sortByLastRaces = lastRaces;
Sort(SortByFormValueOfLastRaces);
}
private static int SortByFormValue(Runner x, Runner y)
{
float xData = GetFormValue(((HorseRacingSelectionData)x.SelectionData).HorseFormFigures);
float yData = GetFormValue(((HorseRacingSelectionData)y.SelectionData).HorseFormFigures);
return xData > yData ? -1 : (xData < yData ? 1 : 0);
}
private int SortByFormValueOfLastRaces(Runner x, Runner y)
{
float xData = GetFormValue(GetLastRaces(((HorseRacingSelectionData)x.SelectionData).HorseFormFigures, sortByLastRaces));
float yData = GetFormValue(GetLastRaces(((HorseRacingSelectionData)y.SelectionData).HorseFormFigures, sortByLastRaces));
return xData > yData ? -1 : (xData < yData ? 1 : 0);
}
private static string GetLastRaces(string formText, byte lastRaces)
{
if (!string.IsNullOrEmpty(formText))
{
if (formText.Length > lastRaces)
{
formText = formText.Substring(formText.Length - lastRaces, lastRaces);
}
}
return formText;
}
private static float GetFormValue(string formText)
{
float formValue = 0.0f;
if (!string.IsNullOrEmpty(formText))
{
foreach (char result in formText.ToCharArray())
{
if (char.IsDigit(result))
{
int place = (int)(result - '0');
if (place > 0)
{
formValue += 9.0f / place;
}
}
}
}
return formValue;
}
}
// Const
private const double Stake = 100;
private const float Profit = 2.0f;
private const float Loss = 20.0f;
private const int PlaceBetBeforeInMinutes = 5;
private const byte FormForLastRaces = 3;
public HorseFormSampleLookUpBot(IBetfairService betfairService)
: base(betfairService)
{
}
public override bool DoYourJob(MonitoredMarket monitoredMarket)
{
if (GetIsMyMarket(monitoredMarket))
{
if (GetIsTheRaceXMinutesBeforeStart(monitoredMarket))
{
bool startMonitoring = true;
MonitoredMarket myMonitoredMarket = betfairService.GetMonitoredMarket(monitoredMarket.MarketId);
if (myMonitoredMarket == null)
{
myMonitoredMarket = monitoredMarket;
}
else
{
startMonitoring = !myMonitoredMarket.Enabled;
}
Runner runner = GetRunnerToBet(myMonitoredMarket);
if (runner != null)
{
SetupMyBot(myMonitoredMarket, runner);
if (startMonitoring)
{
betfairService.StartMonitorThisMarket(myMonitoredMarket);
}
betfairService.StartBot(myMonitoredMarket, runner.Id);
}
return false;
}
return true;
}
return false;
}
private bool GetIsMyMarket(MonitoredMarket monitoredMarket)
{
return monitoredMarket.MarketDetails.Runners[0].SelectionData is HorseRacingSelectionData;
}
private bool GetIsTheRaceXMinutesBeforeStart(MonitoredMarket monitoredMarket)
{
return DateTime.Now >= monitoredMarket.MarketDetails.MarketTime.AddMinutes(-PlaceBetBeforeInMinutes);
}
private Runner GetRunnerToBet(MonitoredMarket monitoredMarket)
{
HorseSortedCollection horses = new HorseSortedCollection(monitoredMarket);
horses.SortByFormFromLastRaces(FormForLastRaces);
return horses[0];
}
private void SetupMyBot(MonitoredMarket monitoredMarket, Runner runner)
{
RunnerProperty runnerProperty = runner.RunnerProperty;
runnerProperty.BotType = BotType.PlaceBetClosePosition;
runnerProperty.BetType = BetType.Lay;
runnerProperty.Stake = Stake;
//runnerProperty.StakeIsMyLiability = true;
runnerProperty.MinimalOdds = OddsRange.MinOdds;
runnerProperty.MaximalOdds = OddsRange.MaxOdds;
runnerProperty.AllowPlacingBetInPlay = true;
runnerProperty.PlaceBetAtBetterOdds = true;
runnerProperty.MinReturnOnInvestment = Profit;
runnerProperty.StopLossOnPercentageLiability = Loss;
}
}
}
How to run the bot
You can run this bot only with the Trade Opportunity Lookup Service. Your bot full class name is:
- Bfexplorer.Scripting.HorseFormSampleLookUpBot
When writing your own bot script you need to follow these rules:
- Your bot script must be in the MyBots directory
- The file name of the bot script must be the same as the full class name adding the .cs extension if the bot script is written in C# or .vb for Visual Basic
You can download the source code for both bot scripts here.
Testing the bot







Sunday, December 07, 2008
"Horse form in bots" Posted by Eric
I am truly amazed !...after a short question to Stefan ("Can the form figures for a race be shown and used in a bot" ?) I get an E mail saying "Yes" it can be done......and overnight an article appears in the blog explaining what can be done,how it could be done plus a fully coded bot to run and show the information,..and...a further fully coded bot giving a working example of how to use the detail for trading the Horse Racing Markets. Needless to say the code has been downloaded and installed on my computer and is ready to go !
Tuesday, December 09, 2008
Testing
Eric, just today I run some tests and they are quite surprising at least for today. You know that this bot is just a sample showing how to use the horse form in a bot script. This idea has maybe some profitability potential but needs to be tested couple weeks and qualifying parameters should be narrowed.
Sunday, December 21, 2008
Testing and feedbck
Have been running these bots both in practice and live mode for the past week.I have been pleasantly surprised by the results,Have run bots with small stakes yesterday and today.
Yesterday in live mode with small stakes bet in 13 races with profit in each race and today bet in 11 races with profit in each race.