Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -195,9 +195,10 @@ find_package(OpenMP REQUIRED)
string(APPEND CMAKE_CXX_FLAGS " ${OpenMP_CXX_FLAGS}")

# Boost (Homebrew) — Boost.Redis is header-only but its implementation is
# compiled into one dedicated TU via <boost/redis/src.hpp> (source/
# boostRedisImpl.cpp), which isolates its macro/SSL pollution from the rest of
# the build. It pulls in Boost.Asio's SSL layer, which needs OpenSSL.
# compiled into one dedicated TU via <boost/redis/src.hpp>
# (source/shared/redis/connection/boostRedisImpl.cpp), which isolates its
# macro/SSL pollution from the rest of the build. It pulls in Boost.Asio's SSL
# layer, which needs OpenSSL.
find_package(Boost REQUIRED CONFIG)
find_package(OpenSSL REQUIRED)
find_package(Threads REQUIRED)
Expand Down
38 changes: 5 additions & 33 deletions source/load/loadCommand.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -14,50 +14,22 @@ module;
#include "shared/utilities/env.hpp"
#include "shared/utilities/parameterSweep.hpp"
#include "shared/utilities/queueKeys.hpp"
#include "shared/redis/redisLoader.hpp"
#include "shared/tradingDefinitions/runConfiguration.hpp" // RunConfiguration (json conversion)
#include "shared/tradingDefinitions/strategy.hpp"
#include "shared/redis/producer/redisLoader.hpp"
#include "shared/tradingDefinitions/config/runConfiguration.hpp" // RunConfiguration -> json
#include "shared/tradingDefinitions/strategy.hpp" // Strategy -> json (makeStrategy result)

export module loadCommand;

import std; // replaces <print>, <ranges>, <string>, <vector>
import randomStrategySweep; // buildRandomStrategySweep
import runConfigurationBuilder; // makeRunConfiguration
import makeStrategy; // sweep::makeStrategy

export class LoadCommand {
public:
static int run();
};

using tradingDefinitions::Strategy;

Strategy makeStrategy(const sweep::Combination& combo) {
using namespace tradingDefinitions;

return Strategy{
// Each parameter combination gets its own UUID so a single backtest
// result is uniquely identifiable and traceable back to its inputs.
.UUID = boost::uuids::to_string(boost::uuids::random_generator()()),
.TRADING_VARIABLES = TradingVariables{
.STRATEGY = "RandomStrategy",
.STOP_DISTANCE_IN_PIPS = combo.getInt("STOP_DISTANCE_IN_PIPS"),
.LIMIT_DISTANCE_IN_PIPS = combo.getInt("LIMIT_DISTANCE_IN_PIPS"),
.TRADING_SIZE = 1,
},
.OHLC_VARIABLES = {
OHLCVariables{
// Only read OHLC params when the sweep actually registers them;
// they default to 0 otherwise (see buildRandomStrategySweep).
.OHLC_COUNT = combo.has("OHLC_COUNT") ? combo.getInt("OHLC_COUNT") : 0,
.OHLC_MINUTES = combo.has("OHLC_MINUTES") ? combo.getInt("OHLC_MINUTES") : 0,
},
},
.STRATEGY_VARIABLES = StrategyVariables{
.OHLC_RSI_VARIABLES = OHLCRSIVariables{.RSI_LONG = 60, .RSI_SHORT = 40},
},
};
}

int LoadCommand::run() {

// One RUN_ID identifies the whole sweep; each combination becomes its own
Expand All @@ -79,7 +51,7 @@ int LoadCommand::run() {
const auto strategyPayloads = combinations
| std::views::transform([](const sweep::Combination& combo) {
// Pin to JSON explicitly to trigger implicit Strategy conversion
const nlohmann::json j = makeStrategy(combo);
const nlohmann::json j = sweep::makeStrategy(combo);
return j.dump();
})
| std::ranges::to<std::vector>();
Expand Down
49 changes: 49 additions & 0 deletions source/load/makeStrategy.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Backtesting Engine in C++
//
// (c) 2026 Ryan McCaffery | https://mccaffers.com
// This code is licensed under MIT license (see LICENSE.txt for details)
// ---------------------------------------

module;

#include <boost/uuid/random_generator.hpp>
#include <boost/uuid/uuid_io.hpp>

#include "shared/utilities/parameterSweep.hpp" // sweep::Combination
#include "shared/tradingDefinitions/strategy.hpp"

export module makeStrategy;

import std;

export namespace sweep {

// Map one swept parameter combination onto a concrete Strategy. Each
// combination gets its own UUID so a single backtest result is uniquely
// identifiable and traceable back to its inputs.
tradingDefinitions::Strategy makeStrategy(const sweep::Combination& combo) {
using namespace tradingDefinitions;

return Strategy{
.UUID = boost::uuids::to_string(boost::uuids::random_generator()()),
.TRADING_VARIABLES = TradingVariables{
.STRATEGY = "RandomStrategy",
.STOP_DISTANCE_IN_PIPS = combo.getInt("STOP_DISTANCE_IN_PIPS"),
.LIMIT_DISTANCE_IN_PIPS = combo.getInt("LIMIT_DISTANCE_IN_PIPS"),
.TRADING_SIZE = 1,
},
.OHLC_VARIABLES = {
OHLCVariables{
// Only read OHLC params when the sweep actually registers them;
// they default to 0 otherwise (see buildRandomStrategySweep).
.OHLC_COUNT = combo.has("OHLC_COUNT") ? combo.getInt("OHLC_COUNT") : 0,
.OHLC_MINUTES = combo.has("OHLC_MINUTES") ? combo.getInt("OHLC_MINUTES") : 0,
},
},
.STRATEGY_VARIABLES = StrategyVariables{
.OHLC_RSI_VARIABLES = OHLCRSIVariables{.RSI_LONG = 60, .RSI_SHORT = 40},
},
};
}

} // namespace sweep
2 changes: 1 addition & 1 deletion source/load/sweep/runConfigurationBuilder.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ module;
#include <boost/decimal.hpp>
#include <boost/decimal/literals.hpp>

#include "shared/tradingDefinitions/runConfiguration.hpp"
#include "shared/tradingDefinitions/config/runConfiguration.hpp"

export module runConfigurationBuilder;

Expand Down
27 changes: 11 additions & 16 deletions source/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,29 +4,24 @@
// This code is licensed under MIT license (see LICENSE.txt for details)
// ---------------------------------------

import std; // replaces <iostream>, <string_view>
import std;

// backtesting engine headers
import loadCommand;
import runCommand;

// Entry point
int main(const int argc, const char* argv[]) {

if (argc < 2) {
std::cerr << "BacktestingEngine: missing a subcommand. See README.md for usage"
<< std::endl;
return 1;
}
if (argc < 2) {
std::println(std::cerr, "Error: missing subcommand");
return 1;
}

const std::string_view subcommand = argv[1];
const std::string_view subcommand = argv[1];

// Two paths, load or run
if (subcommand == "load") return LoadCommand::run();
if (subcommand == "run") return RunCommand::run(argc, argv);
if (subcommand == "load") return LoadCommand::run();
if (subcommand == "run") return RunCommand::run(argc, argv);

std::cerr << "BacktestingEngine: unknown subcommand. See README.md for usage."
<< std::endl;

return 1;
}
std::println(std::cerr, "Error: unknown subcommand '{}'.", subcommand);
return 1;
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
module;

#include "shared/utilities/env.hpp"
#include "shared/tradingDefinitions/configuration.hpp"
#include "shared/tradingDefinitions/config/configuration.hpp"

export module backtestRunner;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
// This code is licensed under MIT license (see LICENSE.txt for details)
// ---------------------------------------

#include "shared/redis/runnerBridge.hpp"
#include "run/execution/runnerBridge.hpp"

import std;
import priceData; // PriceData
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
#pragma once
#include <memory>
#include <string>
#include "shared/tradingDefinitions/configuration.hpp"
#include "shared/tradingDefinitions/config/configuration.hpp"

// Opaque handle to a run's loaded tick data, defined in runnerBridge.cpp.
//
Expand Down
8 changes: 4 additions & 4 deletions source/run/operations.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
module;

#include "shared/utilities/backtestLog.hpp"
#include "shared/tradingDefinitions/configuration.hpp"
#include "shared/tradingDefinitions/config/configuration.hpp"
#include "run/reporting/tradingResults.hpp"

export module operations;
Expand Down Expand Up @@ -76,7 +76,7 @@ void Operations::run(const std::vector<PriceData>& ticks,
trading::runTicks(tradeManager, *strategy, ticks,
config.STRATEGY.TRADING_VARIABLES, riskLimits);

ResultsSummary::summarise(tradeManager);
ResultsSummary::summarise(tradeManager, config);

// Elapsed backtest time for this run, measured from the top of run(). The
// Elasticsearch PUT below is deliberately excluded so the duration reflects
Expand Down Expand Up @@ -128,7 +128,7 @@ void Operations::run(const std::vector<PriceData>& ticks,
durationSeconds,
reason.str(),
config,
ResultsSummary::collect(tradeManager),
ResultsSummary::collect(tradeManager, config),
};
ElasticClient::putTradingFailure(failure);
} else {
Expand All @@ -137,7 +137,7 @@ void Operations::run(const std::vector<PriceData>& ticks,
TradingResults::nowIsoUtc(),
durationSeconds,
config,
ResultsSummary::collect(tradeManager),
ResultsSummary::collect(tradeManager, config),
};
ElasticClient::putTradingResults(results);
}
Expand Down
Loading
Loading