Introduction
This guide explains how to use CppBacktester for backtesting trading strategies. CppBacktester is a high-performance backtesting framework with both C++ and Python interfaces, designed to work with machine learning models including ONNX, XGBoost, and HMM (Hidden Markov Models) for market regime detection.
Configuration
Using config.json
CppBacktester is primarily configured through the config.json file in the project root. This file controls
all aspects of the backtesting process including:
- Broker settings (commission, leverage, starting cash)
- Data source configuration (CSV or API)
- Strategy parameters
- Model paths and hyperparameters
Example of a minimal config.json:
{
"Broker": {
"COMMISSION_RATE": 0.06,
"LEVERAGE": 100.0,
"STARTING_CASH": 100000.0
},
"Data": {
"CSV_Close_Col": 1,
"CSV_Columns": [
{
"index": 0,
"name": "timestamp",
"type": "Timestamp"
},
{
"index": 1,
"name": "close",
"type": "Close"
}
],
"CSV_Delimiter": ",",
"CSV_Has_Header": true,
"CSV_Timestamp_Col": 0,
"CSV_Timestamp_Format": "%Y-%m-%d %H:%M:%S",
"INPUT_CSV_PATH": "path/to/data.csv",
"SourceType": "CSV"
},
"Strategy": {
"StopLossPips": 50.0,
"TakeProfitPips": 50.0,
"Type": "Random"
}
}
C++ Interface
Running the C++ Executable
After building the project, you can run the backtest executable directly:
cd build/output/bin
backtest_executable.exe
The executable will:
- Load settings from
config.json - Create a backtest engine with the specified configuration
- Load market data from the specified source
- Initialize the selected strategy based on the "Type" field in config
- Run the backtest
- Display results
Creating Custom Strategies
To implement a custom strategy, create a new class that inherits from the Strategy base class:
#include "Strategy.h"
#include "Order.h"
#include "Bar.h"
class MyCustomStrategy : public Strategy {
public:
MyCustomStrategy() : Strategy("MyCustom") {}
virtual void initialize() override {
// Initialize strategy parameters, indicators, etc.
}
virtual void processBar(const Bar& bar) override {
// Your strategy logic goes here
if (someCondition) {
// Create a buy order
Order buyOrder;
buyOrder.type = OrderType::BUY;
buyOrder.symbol = bar.symbol;
buyOrder.price = bar.close;
buyOrder.size = 1.0;
buyOrder.stopLoss = bar.close - stopLossPips_;
buyOrder.takeProfit = bar.close + takeProfitPips_;
// Submit the order to the broker
submitOrder(buyOrder);
}
}
virtual void finalize() override {
// Clean up resources
}
private:
double stopLossPips_ = 50.0;
double takeProfitPips_ = 100.0;
};
To use your custom strategy, you would need to modify main.cpp to include your strategy class and add a condition to create it based on your config setting.
Python Interface
Basic Usage
The Python module cppbacktester_py.pyd exposes the core functionality of the C++ library:
import cppbacktester_py as cb
import pandas as pd
# Create a config object (or load from a json file)
config = cb.Config()
config.set_broker_param("STARTING_CASH", 100000.0)
config.set_broker_param("LEVERAGE", 100.0)
config.set_broker_param("COMMISSION_RATE", 0.06)
# Create a backtester instance
engine = cb.BacktestEngine(config)
# Load data - either from DataFrame or CSV
data = pd.read_csv("data/market_data.csv")
engine.load_dataframe(data, {
'timestamp_col': 0,
'timestamp_format': '%Y-%m-%d %H:%M:%S',
'close_col': 1,
'has_header': True
})
# Create a strategy (built-in or custom)
strategy = cb.RandomStrategy() # Or ML, Benchmark, etc.
engine.set_strategy(strategy)
# Run the backtest
engine.run()
# Get results
metrics = engine.get_metrics()
print(f"Total Return: {metrics.total_return:.2f}%")
print(f"Sharpe Ratio: {metrics.sharpe_ratio:.2f}")
Creating Custom Python Strategies
You can create custom strategies in Python by subclassing from the Strategy base class:
import cppbacktester_py as cb
class MyPythonStrategy(cb.Strategy):
def __init__(self):
super().__init__("MyPythonStrategy")
self.stop_loss_pips = 50.0
self.take_profit_pips = 100.0
def initialize(self):
# Set up strategy parameters
pass
def process_bar(self, bar):
# Implement your strategy logic
if bar.close > self.last_close:
# Create buy order
order = cb.Order()
order.type = cb.OrderType.BUY
order.symbol = bar.symbol
order.price = bar.close
order.size = 1.0
order.stop_loss = bar.close - self.stop_loss_pips
order.take_profit = bar.close + self.take_profit_pips
self.submit_order(order)
# Store current close for next comparison
self.last_close = bar.close
def finalize(self):
# Clean up resources
pass
Working with Machine Learning Models
Supported Model Types
CppBacktester currently supports the following model types:
- ONNX models - Cross-platform ML model format
- XGBoost models - Gradient boosting library
- HMM models - Hidden Markov Models for regime detection
HMM Strategy Example
The HMMStrategy class demonstrates how to use a Hidden Markov Model to detect market regimes and then apply regime-specific models for trading signals:
# Using HMM strategy in Python
import cppbacktester_py as cb
# Create and configure the engine
config = cb.Config()
config.load_from_file("config.json")
# For HMMStrategy, ensure your config.json has:
# - RegimeDetection section with model_path
# - Strategy.RegimeModelOnnxPaths mapping regimes to models
engine = cb.BacktestEngine(config)
engine.load_data()
# Use HMM Strategy
strategy = cb.HMMStrategy()
engine.set_strategy(strategy)
# Run backtest
engine.run()
metrics = engine.get_metrics()
print(f"Sharpe: {metrics.sharpe_ratio:.2f}")
Data Format
CppBacktester expects input data in a specific format:
CSV Format
For CSV files, you need to define the column structure in the config:
"CSV_Columns": [
{
"index": 0,
"name": "timestamp",
"type": "Timestamp"
},
{
"index": 1,
"name": "open",
"type": "Open"
},
{
"index": 2,
"name": "high",
"type": "High"
},
{
"index": 3,
"name": "low",
"type": "Low"
},
{
"index": 4,
"name": "close",
"type": "Close"
},
{
"index": 5,
"name": "volume",
"type": "Volume"
}
]
At a minimum, you need timestamp and close columns. Other columns (open, high, low, volume) are optional but recommended for most strategies.
Additional Data Fields
You can also include additional custom columns by specifying them with a "Feature" type in the CSV_Columns configuration.
Performance Considerations
For optimal performance:
- Use the C++ API directly for maximum speed
- Preprocess data before loading into the backtester
- Use ONNX models for inference (faster than direct Python model calls)
- Configure the number of threads in the Data.Threads setting for parallel processing
- Consider using the PARTIAL_DATA_PERCENT setting during development to test with a subset of data