Skip to content

BacktestOptimizer

Grid search optimizer for strategy parameter tuning.

Overview

BacktestOptimizer runs exhaustive parameter search by executing backtests for all combinations in a parameter grid and collecting results for analysis.

#include "flox/backtest/backtest_optimizer.h"

Template Parameters

template <typename ParamsT, typename GridT>
class BacktestOptimizer;
  • ParamsT - Parameter struct, must have toString() method
  • GridT - Grid type, must implement totalCombinations() and operator[]

Class Definition

template <typename ParamsT, typename GridT>
class BacktestOptimizer {
public:
  using BacktestFactory = MoveOnlyFunction<BacktestResult(const ParamsT&)>;
  using ProgressCallback = MoveOnlyFunction<void(size_t, size_t, const OptimizationResult<ParamsT>&)>;

  BacktestOptimizer() = default;

  void setParameterGrid(const GridT& grid);
  void setBacktestFactory(BacktestFactory factory);
  void setProgressCallback(ProgressCallback callback);

  size_t totalCombinations() const;

  std::vector<OptimizationResult<ParamsT>> runLocal(size_t numThreads = 0);
  OptimizationResult<ParamsT> runSingle(size_t index);

  static std::vector<OptimizationResult<ParamsT>> rankResults(
      std::vector<OptimizationResult<ParamsT>> results,
      RankMetric metric,
      bool ascending = false);

  template <typename Predicate>
  static std::vector<OptimizationResult<ParamsT>> filterResults(
      const std::vector<OptimizationResult<ParamsT>>& results,
      Predicate&& predicate);

  static bool exportToCSV(
      const std::vector<OptimizationResult<ParamsT>>& results,
      const std::filesystem::path& path);
};

Grid Requirements

struct MyGrid {
  size_t totalCombinations() const;        // Total param combinations
  MyParams operator[](size_t index) const; // Get params at index
};

Params Requirements

struct MyParams {
  std::string toString() const;  // For CSV export
};

Methods

setParameterGrid

void setParameterGrid(const GridT& grid);

Set the parameter grid to search.

setBacktestFactory

void setBacktestFactory(BacktestFactory factory);

Set function that creates and runs a backtest for given parameters.

setProgressCallback

void setProgressCallback(ProgressCallback callback);

Optional callback invoked periodically during optimization.

runLocal

std::vector<OptimizationResult<ParamsT>> runLocal(size_t numThreads = 0);

Run all backtests. Returns empty vector if no factory set or grid is empty.

runSingle

OptimizationResult<ParamsT> runSingle(size_t index);

Run single backtest at grid index.

rankResults (static)

static std::vector<OptimizationResult<ParamsT>> rankResults(
    std::vector<OptimizationResult<ParamsT>> results,
    RankMetric metric,
    bool ascending = false);

Sort results by metric. Descending by default (best first).

filterResults (static)

template <typename Predicate>
static std::vector<OptimizationResult<ParamsT>> filterResults(
    const std::vector<OptimizationResult<ParamsT>>& results,
    Predicate&& predicate);

Filter results matching predicate.

exportToCSV (static)

static bool exportToCSV(
    const std::vector<OptimizationResult<ParamsT>>& results,
    const std::filesystem::path& path);

Export results to CSV file. Returns false on error.

RankMetric

enum class RankMetric {
  SharpeRatio,
  SortinoRatio,
  CalmarRatio,
  TotalReturn,
  MaxDrawdown,
  WinRate,
  ProfitFactor
};

OptimizationResult

template <typename ParamsT>
struct OptimizationResult {
  ParamsT parameters;
  BacktestStats stats;

  double sharpeRatio() const;
  double sortinoRatio() const;
  double calmarRatio() const;
  double totalReturn() const;
  double maxDrawdown() const;
  double maxDrawdownPct() const;
  double winRate() const;
  double profitFactor() const;
  size_t totalTrades() const;

  void setFromStats(const BacktestStats& s);
};

Example

struct MAParams {
  int fast, slow;
  std::string toString() const {
    return std::to_string(fast) + "," + std::to_string(slow);
  }
};

struct MAGrid {
  std::vector<int> fasts = {5, 10, 15};
  std::vector<int> slows = {20, 30, 40};
  size_t totalCombinations() const { return fasts.size() * slows.size(); }
  MAParams operator[](size_t i) const {
    return {fasts[i / slows.size()], slows[i % slows.size()]};
  }
};

BacktestOptimizer<MAParams, MAGrid> opt;
opt.setParameterGrid(MAGrid{});
opt.setBacktestFactory([](const MAParams& p) {
  return runMyStrategy(p);
});

auto results = opt.runLocal();
auto ranked = decltype(opt)::rankResults(results, RankMetric::SharpeRatio);
decltype(opt)::exportToCSV(ranked, "results.csv");

See Also