Skip to content

Commit f4680ce

Browse files
committed
Added scaffolding for a random trading strategy
1 parent 4ed667e commit f4680ce

5 files changed

Lines changed: 102 additions & 5 deletions

File tree

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ include_directories(
4141
${CMAKE_SOURCE_DIR}/include/models
4242
${CMAKE_SOURCE_DIR}/include/trading
4343
${CMAKE_SOURCE_DIR}/include/trading_definitions
44+
${CMAKE_SOURCE_DIR}/include/strategies
4445
${CMAKE_SOURCE_DIR}/external
4546
)
4647

backtesting-engine-cpp.xcodeproj/project.pbxproj

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@
3333
94724A842F8B92C10029B940 /* operations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94724A822F8B92C10029B940 /* operations.cpp */; };
3434
94CD8BA02D2E8CE500041BBA /* databaseConnection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94CD8B9F2D2E8CE500041BBA /* databaseConnection.cpp */; };
3535
94CD8BA12D2E8CE500041BBA /* databaseConnection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94CD8B9F2D2E8CE500041BBA /* databaseConnection.cpp */; };
36+
94D601102FA9CD700066F51A /* randomStrategy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94D6010E2FA9CD700066F51A /* randomStrategy.cpp */; };
37+
94D601112FA9CD700066F51A /* randomStrategy.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94D6010E2FA9CD700066F51A /* randomStrategy.cpp */; };
3638
/* End PBXBuildFile section */
3739

3840
/* Begin PBXCopyFilesBuildPhase section */
@@ -1254,6 +1256,8 @@
12541256
94CD8B9A2D2DCF6E00041BBA /* libpqxx-7.10.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = "libpqxx-7.10.a"; path = "build/external/libpqxx/src/libpqxx-7.10.a"; sourceTree = "<group>"; };
12551257
94CD8B9E2D2E8CE500041BBA /* databaseConnection.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = databaseConnection.hpp; sourceTree = "<group>"; };
12561258
94CD8B9F2D2E8CE500041BBA /* databaseConnection.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = databaseConnection.cpp; sourceTree = "<group>"; };
1259+
94D6010E2FA9CD700066F51A /* randomStrategy.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = randomStrategy.cpp; sourceTree = "<group>"; };
1260+
94D601122FA9CD890066F51A /* randomStrategy.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = randomStrategy.hpp; sourceTree = "<group>"; };
12571261
/* End PBXFileReference section */
12581262

12591263
/* Begin PBXFrameworksBuildPhase section */
@@ -1388,6 +1392,7 @@
13881392
9470B5A22C8C5AD0007D9CC6 /* source */ = {
13891393
isa = PBXGroup;
13901394
children = (
1395+
94D6010F2FA9CD700066F51A /* strategies */,
13911396
94674B8C2D533E7800973137 /* models */,
13921397
94674B862D533B4000973137 /* trading */,
13931398
941B54982D3BBAD800E3BF64 /* trading_definitions */,
@@ -3494,9 +3499,26 @@
34943499
path = "../../../../../opt/homebrew/Cellar/postgresql@14/14.15/lib/postgresql@14/pgxs";
34953500
sourceTree = "<group>";
34963501
};
3502+
94D6010F2FA9CD700066F51A /* strategies */ = {
3503+
isa = PBXGroup;
3504+
children = (
3505+
94D6010E2FA9CD700066F51A /* randomStrategy.cpp */,
3506+
);
3507+
path = strategies;
3508+
sourceTree = "<group>";
3509+
};
3510+
94D601132FA9CD890066F51A /* strategies */ = {
3511+
isa = PBXGroup;
3512+
children = (
3513+
94D601122FA9CD890066F51A /* randomStrategy.hpp */,
3514+
);
3515+
path = strategies;
3516+
sourceTree = "<group>";
3517+
};
34973518
94DE4F772C8C3E7C00FE48FF /* include */ = {
34983519
isa = PBXGroup;
34993520
children = (
3521+
94D601132FA9CD890066F51A /* strategies */,
35003522
94674B842D533B2F00973137 /* trading */,
35013523
942966D72D48E84100532862 /* models */,
35023524
94B8C7932D3D770800E17EB6 /* utilities */,
@@ -3616,6 +3638,7 @@
36163638
940A61132C92CE210083FEB8 /* configManager.cpp in Sources */,
36173639
94724A842F8B92C10029B940 /* operations.cpp in Sources */,
36183640
940A61172C92CE960083FEB8 /* serviceA.cpp in Sources */,
3641+
94D601112FA9CD700066F51A /* randomStrategy.cpp in Sources */,
36193642
);
36203643
runOnlyForDeploymentPostprocessing = 0;
36213644
};
@@ -3630,6 +3653,7 @@
36303653
94280BA42D2FC00200F1CF56 /* base64.cpp in Sources */,
36313654
94674B8D2D533E7800973137 /* trade.cpp in Sources */,
36323655
941B549A2D3BBADE00E3BF64 /* trading_definitions_json.cpp in Sources */,
3656+
94D601102FA9CD700066F51A /* randomStrategy.cpp in Sources */,
36333657
94674B8A2D533BDA00973137 /* tradeManager.mm in Sources */,
36343658
94724A832F8B92C10029B940 /* operations.cpp in Sources */,
36353659
940A61182C92CE960083FEB8 /* serviceA.cpp in Sources */,
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
// Backtesting Engine in C++
2+
//
3+
// (c) 2026 Ryan McCaffery | https://mccaffers.com
4+
// This code is licensed under MIT license (see LICENSE.txt for details)
5+
// ---------------------------------------
6+
7+
#pragma once
8+
#include <optional>
9+
#include <random>
10+
#include "models/priceData.hpp"
11+
#include "models/trade.hpp" // for Direction enum
12+
#include "trading_definitions/strategy.hpp"
13+
14+
// A trivial strategy: on every tick it flips a fair coin and returns
15+
// LONG or SHORT. Intended as scaffolding for the strategy interface,
16+
// not as a real trading approach.
17+
//
18+
// C# parallels for readers from a C# background:
19+
// - `class` here is a value/owning type managed via stack or
20+
// std::unique_ptr — there is no GC. Lifetime is explicit.
21+
// - `explicit` on a single-arg constructor disables implicit
22+
// conversion (C# constructors are always explicit, so this is
23+
// just C++ catching up to the default C# behaviour).
24+
// - `std::mt19937` is the modern C++ RNG engine; it replaces the
25+
// globally-shared `std::rand()` used elsewhere in this codebase
26+
// and gives us the option of seeding for reproducible backtests.
27+
class RandomStrategy {
28+
public:
29+
explicit RandomStrategy(const trading_definitions::Strategy& strategyConfig);
30+
31+
// Returns `std::nullopt` to mean "no signal — don't trade". The
32+
// random strategy always returns a direction, but the interface
33+
// matches future strategies that only fire on certain conditions.
34+
// `std::optional<T>` is roughly C#'s `Nullable<T>` / `T?` — a
35+
// value type that may or may not hold a T, with no heap allocation.
36+
//
37+
// Not const because the RNG engine mutates its internal state on
38+
// each call. The `tick` parameter is unused today but keeps the
39+
// interface stable for strategies that will look at price.
40+
std::optional<Direction> decide(const PriceData& tick);
41+
42+
private:
43+
trading_definitions::Strategy config;
44+
std::mt19937 rng;
45+
std::bernoulli_distribution coin;
46+
};

source/operations.cpp

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,21 +15,26 @@
1515
#include <ctime>
1616
#include <boost/decimal.hpp>
1717
#include "tradeManager.hpp"
18+
#include "strategies/randomStrategy.hpp"
1819

1920
void Operations::run(const std::vector<PriceData>& ticks,
2021
const trading_definitions::Configuration& config) {
2122

2223
// Create
2324
auto tradeManager = new TradeManager();
24-
25+
RandomStrategy strategy(config.STRATEGY);
26+
2527
for (const auto& tick : ticks) {
2628

2729
size_t openTrades = tradeManager->reviewAccount();
28-
29-
// this would be strategy invoke point
30+
31+
// strategy invoke point: ask the strategy for a signal. If it
32+
// returns std::nullopt the strategy is saying "no trade".
3033
if (openTrades == 0) {
31-
std::string tradeId = tradeManager->openTrade(tick, config.STRATEGY.TRADING_VARIABLES.TRADING_SIZE, Direction::LONG);
32-
std::cout << "Opened trade: " << tradeId << std::endl;
34+
if (auto signal = strategy.decide(tick)) {
35+
std::string tradeId = tradeManager->openTrade(tick, config.STRATEGY.TRADING_VARIABLES.TRADING_SIZE, *signal);
36+
std::cout << "Opened trade: " << tradeId << std::endl;
37+
}
3338
}
3439

3540
// this would be a position manager review point
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// Backtesting Engine in C++
2+
//
3+
// (c) 2026 Ryan McCaffery | https://mccaffers.com
4+
// This code is licensed under MIT license (see LICENSE.txt for details)
5+
// ---------------------------------------
6+
7+
#include "strategies/randomStrategy.hpp"
8+
9+
// Member initialiser list (the bit after the `:`) runs in declaration
10+
// order, not the order written here — same caveat called out in
11+
// trade.hpp. The C# equivalent would be field initialisers plus a
12+
// constructor body, but C++ prefers the initialiser list because it
13+
// constructs members directly rather than default-then-assign.
14+
RandomStrategy::RandomStrategy(const trading_definitions::Strategy& strategyConfig)
15+
: config(strategyConfig),
16+
rng(std::random_device{}()), // seed once from the OS entropy source
17+
coin(0.5) {}
18+
19+
std::optional<Direction> RandomStrategy::decide(const PriceData& /*tick*/) {
20+
return coin(rng) ? Direction::LONG : Direction::SHORT;
21+
}

0 commit comments

Comments
 (0)