public void Optimise(DateTime fromDate, DateTime toDate)
        {
            _stakingService.Evaluate(fromDate, toDate);

            var history = _marketDataCache.TakeUntil(toDate).ToArray();

            // Build 2d array where each cell is sized to has equal data points and probability
            var xData = history.Select(_xSelector).ToArray();
            var yData = history.Select(_ySelector).ToArray();
            var grid  = new Grid <List <decimal> >(xData, yData, _parameters.Partitions);

            // Place each data point in appropriate cell
            var marketDataValues = _ratingService.CalculateMarketDataValues(history);

            foreach (var data in history)
            {
                var x    = _xSelector(data);
                var y    = _ySelector(data);
                var cell = grid[x, y];
                marketDataValues.TryGetValue(data.Date, out var value);
                cell.Add(value);
            }

            // Determine the average value of each cell
            var averages = grid
                           .Where(x => x.Any())
                           .Select(GetAverage)
                           .Distinct()
                           .ToArray();
            var minValue = Convert.ToInt32(Math.Round(averages.Min(), MidpointRounding.ToNegativeInfinity));
            var maxValue = Convert.ToInt32(Math.Round(averages.Max(), MidpointRounding.ToPositiveInfinity));
            var range    = maxValue - minValue;

            // Search for optimal buy threshold
            var potentials = Enumerable.Range(minValue, range)
                             .SelectMany(t => Enumerable.Range(2, 8)
                                         .Select(p => new ClusteringParameters
            {
                Partitions = p,
                Threshold  = -t,
                Grid       = grid
            }));

            var optimal = _searcher.Maximum(potentials, fromDate, toDate);

            _parameters = (ClusteringParameters)optimal;
        }