Betfair bot programming: trailing stop loss

This is one of many articles dedicated to betfair bot programming utilizing the Bfexplorer BOT SDK. If you did not yet read the previous articles, here is the list:

In this betfair bot tutorial I will show you how to program a trading bot with the trailing stop loss feature. The bot will place two bets, the opening and the closing one. To close position we will use three parameters (in ticks):

  • Total reached profit (MinReturnOnInvestment)
  • Total reached loss (StopLossOnPercentageLiability)
  • Trailing stop loss (MinOddsDifference)

Only the last parameter is mandatory and it defines the odds difference in ticks to close position in profit if the odds trend moves against.

To monitor odds movements I will use the class: BetPriceTrend and the TraderUsingTrailingStopLossBot will extend the functionality of the PlaceBetBot adding just necessary code for odds movements monitoring and closing position. As it was mentioned already the bot will close its position (traded it out) when one of three parameters for closing position is reached, or at preset time before official event start:

  • ClosePositionAt

The bot source code in C#:

using System;
using BeloSoft.Betfair.Data;
using BeloSoft.Betfair.Data.Betting;
using BeloSoft.Betfair.Data.Calculation;
using BeloSoft.Betfair.Trading;

namespace Bfexplorer.Scripting
{
  public class TraderUsingTrailingStopLossBot : PlaceBetBot
  {
    // Data
    private BetPriceTrend betPriceTrend = new BetPriceTrend();
    private bool closingPosition;

    public TraderUsingTrailingStopLossBot(IBetfairService betfairService, 
           MonitoredMarket monitoredMarket, Runner runner)
      : base(betfairService, monitoredMarket, runner)
    {
      stopWhenBetPlaced = false;

      if (RunnerProperty.MinOddsDifference == 0)
      {
        AddMessage(
           "Please set the MinOddsDifference (TrailingStopLoss) if you want to run the bot."
        );

        Stop();
      }
    }

    public override void DoYourJob()
    {
      base.DoYourJob();

      if (betPlaced)
      {
        if (GetIsTimeToClosePosition())
        {
          AddMessage("There was reached time to stop this bot.");

          CloseMyPosition();
        }
        else if (betPriceTrend.SetBetPriceTrend(runner, betType))
        {
          AddMessage(
            string.Format("Bet price changed: {0}, {1}({2})[{3}], Min:{4}, Max:{5}",
                  betPriceTrend.Price,
                  betPriceTrend.TotalPriceDifference,
                  betPriceTrend.DeltaPriceDifference,
                  betPriceTrend.TotalDeltaPriceDifference,
                  betPriceTrend.PriceDifferenceFromMinPrice,
                  betPriceTrend.PriceDifferenceFromMaxPrice)
          );

          if (GetCanClosePosition())
          {
            CloseMyPosition();
          }
        }
      }
    }

    protected override void BotJobDone()
    {
      base.BotJobDone();

      if (closingPosition)
      {
        stopWhenBetPlaced = true;
      }
      else
      {
        betPriceTrend.SetBetPriceTrend(placedAtOdds);

        betType = betType == BetType.Back ? BetType.Lay : BetType.Back;
        closingPosition = true;
      }
    }

    private bool GetIsTimeToClosePosition()
    {
      return RunnerProperty.ClosePositionAt != DateTime.MinValue && 
          DateTime.Now.TimeOfDay > RunnerProperty.ClosePositionAt.TimeOfDay;
    }

    private bool GetCanClosePosition()
    {
      bool backingToClosePosition = betType == BetType.Back;
      bool isProfitPosition = (backingToClosePosition ? 
            betPriceTrend.TotalPriceDifference > 0 : betPriceTrend.TotalPriceDifference < 0) 
             && GetIsProfitPosition();
      int totalPriceDifference = Math.Abs(betPriceTrend.TotalPriceDifference);

      if (isProfitPosition)
      {
        if (RunnerProperty.MinReturnOnInvestment != 0)
        {
          // Stop at the profit
          if (totalPriceDifference >= RunnerProperty.MinReturnOnInvestment)
          {
            return true;
          }
        }

        int trailingStop = RunnerProperty.MinOddsDifference;

        return backingToClosePosition ?
            betPriceTrend.PriceDifferenceFromMaxPrice >= trailingStop :
            betPriceTrend.PriceDifferenceFromMinPrice >= trailingStop;
      }
      else if (RunnerProperty.StopLossOnPercentageLiability != 0)
      {
        // Stop at the loss
        return totalPriceDifference >= RunnerProperty.StopLossOnPercentageLiability;
      }

      return false;
    }

    private bool GetIsProfitPosition()
    {
      BetPrice betPrice = betType == BetType.Back ? 
           runner.BestPriceToBack : runner.BestPriceToLay;

      if (betPrice != null)
      {
        double odds = betPrice.Price;
        double stake = betType == BetType.Back ? 
           betStakeCalculation.GetStakeAmountToBack(odds) : 
           betStakeCalculation.GetStakeAmountToLay(odds);

        return betStakeCalculation.GetProfit(betType, odds, stake) > 0;
      }

      return false;
    }

    private void CloseMyPosition()
    {
      BetPrice betPrice = betType == BetType.Back ? 
            runner.BestPriceToBack : runner.BestPriceToLay;

      if (betPrice != null)
      {
        betPlaced = false;

        double odds = betPrice.Price;
        double stake = betType == BetType.Back ? 
             betStakeCalculation.GetStakeAmountToBack(odds) : 
             betStakeCalculation.GetStakeAmountToLay(odds);

        PlaceBet(betType, odds, stake);
      }
    }
  }
}

Testing the bot TraderUsingTrailingStopLossBot

As you can see on the screenshot above I set the MinOddsDifference parameter to 3 and run the bot at the right moment when odds trend moved up opening the position laying 100@3.85

The bot closed its position at odds: 4.4, 9 ticks from minimal odds reached during the monitoring and after 3 ticks of odds drop from 4.7 to 4.4.

If you have got some ideas for the next bot tutorial or you want to use this bot just leave your comment below.

Comments (11)

  1. pashulik Says:
    Sunday, February 01, 2009

    Adding TrailingStopLoss to working bot

    Stefan, I use my LookUpBot script and want to use TrailingStopLoss with it. I add to MyBots folder TrailingStopLoss Bot source code and make link to it in my script. My question is: how can I set MinOddsDifference if there is not this parameter in my script? Thank you.

  2. StefanBelo Says:
    Monday, February 02, 2009

    There are two types of bots: the action bot and the lookup bot.

    The action bot is used to execute a bet placing on a selection or trading. You can find such action bots in all bot wizards or in the Bot Executor.

    The lookup bot is used by Trade Opportunity lookup service to find a trade opportunity, basically to setup the action bot for particular selection/s. Each bot is represented by its algorithm, its betting/trading behavior and by its set of parameters which such action bot can use. Some bots use just couple parameters like byte type and stake, but some use even 20 different parameters, those parameters are entered into the action bot by RunnerProperty each selection has.

    So even if your lookup bot script has no such variable in its source code, the selection RunnerProperty has such parameter and as your action bot uses this parameter: MinOddsDifference, you can set it in the code of your lookup bot:

    private void SetupMyBot(MonitoredMarket monitoredMarket, Runner runner)
    {
        RunnerProperty runnerProperty = runner.RunnerProperty;
    
        runnerProperty.BotType = BotType.MyBot;
        runnerProperty.MyBotClassName = "Bfexplorer.Scripting.TraderUsingTrailingStopLossBot";
        runnerProperty.BetType = BetType.Lay;
        runnerProperty.Stake = Stake;
        runnerProperty.MinimalOdds = MinOdds;
        runnerProperty.MaximalOdds = MaxOdds;
        runnerProperty.AllowPlacingBetInPlay = true;
        runnerProperty.PlaceBetAtBetterOdds = true;
        runnerProperty.MinOddsDifference = 3;
        runnerProperty.MinReturnOnInvestment = Profit;
        runnerProperty.StopLossOnPercentageLiability = Loss;
    }
    
  3. pashulik Says:
    Monday, February 02, 2009

    Thank you, Stefan but I can't understand how can I set the parameter which I do not see in my lookup bot. May I ask you to show me how to set say MinOddsDifference = 3 ticks in my lookup bot? Thank you again.

  4. StefanBelo Says:
    Tuesday, February 03, 2009

    You are right, I forgot to add: runnerProperty.MinOddsDifference = 3;

  5. pashulik Says:
    Thursday, February 05, 2009

    The moment TrailingBot starts to do its job

    Stefan, what moment does the trailing bot start to work from? I tested the bot today and saw it went up (to profit) for 3 ticks from the start then go down (to lost) and close position on StopLossOnPercentageLiability though I set MinOddsDifference = 3 ticks. My StopLossOnPercentageLiability = 7. So it has closed my position (at lost) after it went down for 10 ticks. I thought it starts immediately after it goes to profit didn't I? Thank you.

  6. StefanBelo Says:
    Friday, February 06, 2009

    The bot closes your position when StopLossOnPercentageLiability [in ticks] is reached, so your position is 7 ticks in loss from the opening bet.

    The bot closes your position in profit, if from the last position there is movement MinOddsDifference (trailing stop loss) [in ticks] against your profit.

    The bot closes your position as well, if you set MinReturnOnInvestment [in ticks] without wait to close position otherwise by trailing stop loss.

  7. pashulik Says:
    Saturday, February 07, 2009

    Trailing bot behavior

    Stefan, there was interesting test with my trailing bot today. After the market (Kempton 13:10) started there was very high jump (up and down) of charts of my horse and the bot placed the Back bet on 34,0 (to close position after 3,40 laying), but it left unmatched. Then odds go down till 1,8 but the bot did not try to close position on StopLossOnPercentageLiability because it just  did its job yet I suppose. In my case odds go up again (the horse lost) and I got big profit but it’d be very dangerous in the case my horse wins. So is it possible to add one more parameter in this bot for canceling such unmatched bets of great odds difference within preset time? I’m sorry if this idea is known and unreal or it has other dangerous I did not think of. Thank you.

  8. StefanBelo Says:
    Saturday, February 07, 2009

    Pavel, as you maybe noticed this bot extends the functionality of the PlaceBetBot and really implements just trailing stop feature and I meant to use it for pre-race trading not for running it in the in-play market.

    Such feature as you suggested can be added into the bot as well it was added to PlaceBetClosePosition bot, but I do not want to complicate the source code as it was also meant for education purposes, so I want to keep it simple.

    The TraderUsingTrailingStopLossBot bot has the parameter: ClosePositionAt to close position at preset time before market is turned at in-play.

  9. pashulik Says:
    Friday, February 13, 2009

    Stefan, these days I test HorseForm Bot (Bfexplorer.Scripting.HorseFormSampleLookUpBot5) with trailing stop loss bot.

    I want to add to my lookup bot one more parameter - Total reached loss (StopLossOnPercentageLiability). Does it mean that I change the code by the following way:

     

    I add runnerProperty.StopLossOnPercentageLiability = Loss;

    to SetupTradingBot and to SetupLayingBot

     

     then I add new parameter to Const (10 ticks for instance)

     

          

    Am I correct?

     

    Thank you.

  10. StefanBelo Says:
    Friday, February 13, 2009

    Yes Pavel you are right. If you have a look at this betfair bot source code, in the header you can find list of all parameters this bot uses:

    /*
     * Used parameters:
     * 
     * BetType
     * MinimalOdds
     * MaximalOdds
     * PlaceBetAtBetterOdds
     * Odds
     * PlaceBetAtOdds
     * MarketMaker
     * Stake
     * StakeIsMyLiability
     * AllowPlacingBetInPlay
     * AtInPlayKeepBet
     * StartBetPlaceFrom
     * 
     * ClosePositionAt
     * MinOddsDifference
     * MinReturnOnInvestment
     * StopLossOnPercentageLiability
     * 
     */
    

    One of such parameter is StopLossOnPercentageLiability. In this case the bot takes it as number of ticks at which the position should be closed in loss. Check this part of code:

    private bool GetCanClosePosition()
    {
      bool backingToClosePosition = betType == BetType.Back;
      bool isProfitPosition = (backingToClosePosition ? betPriceTrend.TotalPriceDifference > 0 : 
               betPriceTrend.TotalPriceDifference < 0) && GetIsProfitPosition();
      int totalPriceDifference = Math.Abs(betPriceTrend.TotalPriceDifference);
    
      if (isProfitPosition)
      {
        if (RunnerProperty.MinReturnOnInvestment != 0)
        {
          // Stop at the profit
          if (totalPriceDifference >= RunnerProperty.MinReturnOnInvestment)
          {
            return true;
          }
        }
    
        int trailingStop = RunnerProperty.MinOddsDifference;
    
        return backingToClosePosition ?
                 betPriceTrend.PriceDifferenceFromMaxPrice >= trailingStop :
                 betPriceTrend.PriceDifferenceFromMinPrice >= trailingStop;
      }
      else if (RunnerProperty.StopLossOnPercentageLiability != 0)
      {
        // Stop at the loss
        return totalPriceDifference >= RunnerProperty.StopLossOnPercentageLiability;
      }
    
      return false;
    }
    

Do you want to comment this article? Sign up here or login.