Skip to content

Custom Fitness Functions for Optimisation

cBot optimiation is the process of determining the best possible parameter values for a cBot before deploying it for live trading. In cTrader, you can optimise cBots against a wide-range of built-in criteria such as maximising profits or minimising equity drawdown. However, you can also write your own fitness functions for optimisation which can be useful if you want to apply custom performance criteria to a cBot. In this article and its corresponding video, we discuss how custom fitness functions work.

Note that this 'how to' does not cover the process of cBot optimisation using built-in criteria. To learn more about this process, click here.

Defining Custom Fitness Functions

A custom fitness function is just an override of the GetFitness() method.

1
2
3
4
protected override double GetFitness(GetFitnessArgs args)
{

}

The override must accept a single argument of the GetFitness args type in order for it to count as a custom fitness function.

Writing a Custom Fitness Function

As the GetFitness() method must return a double, the body of the method should contain any sort of calculation that evaluates to a number of this type.

For example, we can write a simple custom fitness function that squares the total number of winning trades made by a cBot and then divides this value by the total of value of losing trades. That way, we allocate more 'weight' to winning trades and, in a sense, allow the optimised cBot to make some risky plays.

Here is how our fitness function would look like.

1
2
3
4
protected override double GetFitness(GetFitnessArgs args)
{
    return Math.Pow(args.WinningTrades, 2) / args.LosingTrades;
}

At this point, we can add the override to any cBot we want, and then save it and build it.

Using a Custom Fitness Function in Optimisation

After adding our function, all that is left is to add an instance of our chosen cBot and then switch to the 'Backtesting' tab. To conduct optimisation using our custom function, all we have to do is open the 'Optimisation Criteria' section and select the 'Custom' option. Afterward, we can run optimisation as usual.

During optimisation, higher fitness scores will be allocated to the passes during which the cBot has maximised the ratio between the number of winning trades squared and the number of losing trades.

Modifying the Custom Fitness Function

We can also go back to our custom function and modify it as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
protected override double GetFitness(GetFitnessArgs args)
{
    if (args.TotalTrades > 20)
    {
        return Math.Pow(args.WinningTrades, 2) / args.LosingTrades;
    }
    else
    {
        return double.MinValue;
    }
}

This function still follows the previous algorithm but it only applies the custom calculation if more than 20 trades have been placed by a cBot. In any other case, the function returns the lowest possible double. In theory, this should improve results by reducing the possibility of statistical bias.

If we rebuild the bot and run optimisation again, we should see several passes get a very low fitness score due to the low number of total trades placed by the cBot.

Last but not least, we also want to minimise equity drawdown while still encouraging active trading.

1
2
3
4
5
6
7
8
if (args.TotalTrades > 20 && args.MaxEquityDrawdownPercentages < 50)
{
    return Math.Pow(args.WinningTrades, 2) / args.LosingTrades;
}
else
{
    return double.MinValue;
}

If we run optimisation again, we should see the best possible passes given our trading strategy.

Summary

Custom fitness functions provide an excellent tool for determining the best possible parameter values suited to your unique approach to trading. We hope you found this tutorial helpful! Consider subscribing to our YouTube channel to be alerted every time we publish a new tutorial.

Subscribe to our YouTube channel