Skip to content
Open
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
8 changes: 6 additions & 2 deletions .github/workflows/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@ export PKG_CONFIG_PATH="$(brew --prefix libpq)/lib/pkgconfig:$PKG_CONFIG_PATH"
export PostgreSQL_ROOT="$(brew --prefix libpq)"

# 1. Generate build files (Passing your CXX flags directly to CMake instead of configure)
cmake .. -DCMAKE_CXX_STANDARD=20 -DCMAKE_BUILD_TYPE=Release
cmake .. \
-DCMAKE_CXX_STANDARD=20 \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_SYSROOT=$(xcrun --show-sdk-path) \
-DSKIP_BUILD_TEST=ON

# 2. Compile libpqxx
make
make -j$(sysctl -n hw.logicalcpu)
9 changes: 5 additions & 4 deletions .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:

build:
name: Build & Test
runs-on: macos-14 # Use macOS 14 (Sonoma) runner
runs-on: macos-26

steps:
# Step 1: Check out the repository code
Expand All @@ -35,12 +35,13 @@ jobs:

# Step 4: Build project external libraries
- name: Build C++ Libraries
run: bash ./.github/workflows/build.sh
run: bash ./scripts/build_dep.sh
# run: bash ./.github/workflows/build.sh

# Step 5: Configure Xcode version
- name: Select Xcode version
run: |
sudo xcode-select -switch /Applications/Xcode_15.2.app
sudo xcode-select -switch /Applications/Xcode_26.2.app
/usr/bin/xcodebuild -version
# Run XCode tests with specific configurations:
# - Builds and runs the test suite
Expand Down Expand Up @@ -92,7 +93,7 @@ jobs:
cmake --version
- name: Build C++ Libraries
run: >
bash ./.github/workflows/build.sh
sh ./scripts/build.sh
- name: Install Python 3.12 for gcovr
uses: actions/setup-python@v5
with:
Expand Down
12 changes: 12 additions & 0 deletions .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"configurations": [
{
"name": "Mac",
"compileCommands": "${workspaceFolder}/build/compile_commands.json",
"includePath": [
"${workspaceFolder}/external/libpqxx/include"
]
}
],
"version": 4
}
15 changes: 12 additions & 3 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ project(BacktestingEngine)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)


# Configure libpqxx build
set(PQXX_LIBRARIES_INSTALL ON)
set(SKIP_BUILD_TEST ON)
Expand Down Expand Up @@ -51,8 +50,18 @@ file(GLOB_RECURSE SOURCES "source/*.cpp")
# Create a library of your project's code
add_library(BacktestingEngineLib STATIC ${SOURCES})

# Link against pqxx
target_link_libraries(BacktestingEngineLib pqxx)
# Configure OpenMP. On Apple, provide Homebrew libomp hints before discovery.
if(APPLE)
set(OpenMP_C_FLAGS "-Xclang -fopenmp")
set(OpenMP_CXX_FLAGS "-Xclang -fopenmp")
set(OpenMP_C_LIB_NAMES "omp")
set(OpenMP_CXX_LIB_NAMES "omp")
set(OpenMP_omp_LIBRARY /opt/homebrew/opt/libomp/lib/libomp.dylib)
target_include_directories(BacktestingEngineLib PRIVATE /opt/homebrew/opt/libomp/include)
endif()

find_package(OpenMP REQUIRED)
target_link_libraries(BacktestingEngineLib PUBLIC pqxx OpenMP::OpenMP_CXX)

# Main executable
add_executable(BacktestingEngine source/main.cpp)
Expand Down
18 changes: 15 additions & 3 deletions backtesting-engine-cpp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
94674B8E2D533E7800973137 /* trade.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94674B8B2D533E7800973137 /* trade.cpp */; };
9470B5A42C8C5AD0007D9CC6 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9470B5A32C8C5AD0007D9CC6 /* main.cpp */; };
9470B5B62C8C5BFD007D9CC6 /* main.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 9470B5A32C8C5AD0007D9CC6 /* main.cpp */; };
94724A832F8B92C10029B940 /* operations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94724A822F8B92C10029B940 /* operations.cpp */; };
94724A842F8B92C10029B940 /* operations.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94724A822F8B92C10029B940 /* operations.cpp */; };
94CD8B992D2DCDD800041BBA /* libpqxx-7.10.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 94CD8B982D2DCDD800041BBA /* libpqxx-7.10.a */; };
94CD8B9C2D2DD02A00041BBA /* libpqxx-7.10.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 94CD8B9A2D2DCF6E00041BBA /* libpqxx-7.10.a */; };
94CD8BA02D2E8CE500041BBA /* databaseConnection.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94CD8B9F2D2E8CE500041BBA /* databaseConnection.cpp */; };
Expand Down Expand Up @@ -89,6 +91,8 @@
9470B5A12C8C5AD0007D9CC6 /* source */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = source; sourceTree = BUILT_PRODUCTS_DIR; };
9470B5A32C8C5AD0007D9CC6 /* main.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = main.cpp; sourceTree = "<group>"; };
9470B5AC2C8C5B99007D9CC6 /* tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
94724A822F8B92C10029B940 /* operations.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = operations.cpp; sourceTree = "<group>"; };
94724A852F8B92E30029B940 /* operations.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = operations.hpp; sourceTree = "<group>"; };
948A9CCD2C906A5600E23669 /* CONVENTIONS.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = CONVENTIONS.md; sourceTree = "<group>"; };
948A9CED2C906AFE00E23669 /* 2020.csv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = 2020.csv; sourceTree = "<group>"; };
94BBA4512D2EA2640010E04D /* build.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = build.sh; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1302,6 +1306,7 @@
isa = PBXGroup;
children = (
94280BA22D2FC00200F1CF56 /* base64.cpp */,
943398232D57E53400287A2D /* jsonParser.cpp */,
);
path = utilities;
sourceTree = "<group>";
Expand Down Expand Up @@ -1408,8 +1413,8 @@
940A61112C92CE210083FEB8 /* configManager.cpp */,
940A61152C92CE960083FEB8 /* serviceA.cpp */,
94CD8B9F2D2E8CE500041BBA /* databaseConnection.cpp */,
943398232D57E53400287A2D /* jsonParser.cpp */,
941408AD2D59F93F000ED1F9 /* sqlManager.cpp */,
94724A822F8B92C10029B940 /* operations.cpp */,
);
path = source;
sourceTree = "<group>";
Expand Down Expand Up @@ -3515,15 +3520,16 @@
94DE4F772C8C3E7C00FE48FF /* include */ = {
isa = PBXGroup;
children = (
941408B02D59F954000ED1F9 /* sqlManager.hpp */,
943398222D57E52900287A2D /* jsonParser.hpp */,
94674B842D533B2F00973137 /* trading */,
942966D72D48E84100532862 /* models */,
94B8C7932D3D770800E17EB6 /* utilities */,
941B548F2D3BBA3B00E3BF64 /* trading_definitions */,
941B549C2D3BBFB900E3BF64 /* trading_definitions.hpp */,
940A61162C92CE960083FEB8 /* serviceA.hpp */,
943398222D57E52900287A2D /* jsonParser.hpp */,
940A61122C92CE210083FEB8 /* configManager.hpp */,
941408B02D59F954000ED1F9 /* sqlManager.hpp */,
94724A852F8B92E30029B940 /* operations.hpp */,
94CD8B9E2D2E8CE500041BBA /* databaseConnection.hpp */,
);
path = include;
Expand Down Expand Up @@ -3631,6 +3637,7 @@
94674B872D533B4000973137 /* tradeManager.cpp in Sources */,
94CD8BA02D2E8CE500041BBA /* databaseConnection.cpp in Sources */,
940A61132C92CE210083FEB8 /* configManager.cpp in Sources */,
94724A842F8B92C10029B940 /* operations.cpp in Sources */,
940A61172C92CE960083FEB8 /* serviceA.cpp in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -3646,6 +3653,7 @@
94674B8D2D533E7800973137 /* trade.cpp in Sources */,
941B549A2D3BBADE00E3BF64 /* trading_definitions_json.cpp in Sources */,
94674B8A2D533BDA00973137 /* tradeManager.mm in Sources */,
94724A832F8B92C10029B940 /* operations.cpp in Sources */,
940A61182C92CE960083FEB8 /* serviceA.cpp in Sources */,
94674B882D533B4000973137 /* tradeManager.cpp in Sources */,
943398272D57E54000287A2D /* jsonParser.mm in Sources */,
Expand Down Expand Up @@ -3788,6 +3796,7 @@
"\"$(SRCROOT)/external/libpqxx/build/src\"",
"/opt/homebrew/Cellar/postgresql@18/18.3/lib/postgresql",
);
MACOSX_DEPLOYMENT_TARGET = 26.0;
OTHER_LDFLAGS = "";
OTHER_LIBTOOLFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
Expand All @@ -3810,6 +3819,7 @@
"\"$(SRCROOT)/external/libpqxx/build/src\"",
"/opt/homebrew/Cellar/postgresql@18/18.3/lib/postgresql",
);
MACOSX_DEPLOYMENT_TARGET = 26.0;
OTHER_LDFLAGS = "";
OTHER_LIBTOOLFLAGS = "";
PRODUCT_NAME = "$(TARGET_NAME)";
Expand All @@ -3832,6 +3842,7 @@
"\"$(SRCROOT)/external/libpqxx/build/src\"",
"/opt/homebrew/Cellar/postgresql@18/18.3/lib/postgresql",
);
MACOSX_DEPLOYMENT_TARGET = 26.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.mccaffers.tests;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand All @@ -3854,6 +3865,7 @@
"\"$(SRCROOT)/external/libpqxx/build/src\"",
"/opt/homebrew/Cellar/postgresql@18/18.3/lib/postgresql",
);
MACOSX_DEPLOYMENT_TARGET = 26.0;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.mccaffers.tests;
PRODUCT_NAME = "$(TARGET_NAME)";
Expand Down
5 changes: 5 additions & 0 deletions documents/questdb.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
Start QuestDB (on macOS)

```
JAVA_HOME="/opt/homebrew/opt/openjdk@17" sh $HOME/dev/questdb/questdb.sh start -d $HOME/dev/questdb/data
```
2 changes: 1 addition & 1 deletion include/databaseConnection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class DatabaseConnection {
const std::string& password = "");

void printResults(const std::vector<PriceData>& results) const;
std::vector<PriceData> executeQuery(const std::string& query) const;
std::vector<PriceData> streamQuery(const std::string& query) const;

const std::string& getConnectionString() const {
return connection_string;
Expand Down
14 changes: 9 additions & 5 deletions include/models/priceData.hpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,21 @@
// Backtesting Engine in C++
//
// (c) 2025 Ryan McCaffery | https://mccaffers.com
// (c) 2026 Ryan McCaffery | https://mccaffers.com
// This code is licensed under MIT license (see LICENSE.txt for details)
// ---------------------------------------
#pragma once
#include <chrono>
#include <string>

struct PriceData {
double value1;
double value2;
double ask;
double bid;
std::chrono::system_clock::time_point timestamp;
std::string symbol;

// Constructor for easy creation
PriceData(double v1, double v2, const std::chrono::system_clock::time_point& ts)
: value1(v1), value2(v2), timestamp(ts) {}
PriceData(double ask, double bid, const std::chrono::system_clock::time_point& ts, const std::string& symbol)
: ask(ask), bid(bid), timestamp(ts), symbol(symbol) {}

PriceData() : ask(0.0), bid(0.0), timestamp{}, symbol("") {}
};
15 changes: 15 additions & 0 deletions include/operations.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
// Backtesting Engine in C++
//
// (c) 2026 Ryan McCaffery | https://mccaffers.com
// This code is licensed under MIT license (see LICENSE.txt for details)
// ---------------------------------------

#pragma once
#include <vector>
#include "models/priceData.hpp"

class Operations {

public:
static void run(const std::vector<PriceData>& priceData);
};
9 changes: 4 additions & 5 deletions include/sqlManager.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
// Backtesting Engine in C++
//
// (c) 2025 Ryan McCaffery | https://mccaffers.com
// (c) 2026 Ryan McCaffery | https://mccaffers.com
// This code is licensed under MIT license (see LICENSE.txt for details)
// ---------------------------------------
#pragma once
Expand All @@ -11,8 +11,7 @@

class SqlManager {
public:
static std::vector<PriceData> getInitialPriceData(const DatabaseConnection& db);
static std::string getBaseQuery();
private:
static constexpr int DEFAULT_LIMIT = 1000;
static std::vector<PriceData> streamPriceData(const DatabaseConnection& db, const std::vector<std::string>& symbols, int LAST_MONTHS = 1);
static std::string getBaseQuery(const std::vector<std::string>& symbols, int LAST_MONTHS = 1);

};
13 changes: 11 additions & 2 deletions scripts/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,17 @@ fi
# Step 2: Navigate to the build directory
cd "$BUILD_DIR" || exit

# Step 3: Run CMake to configure the project
cmake ..
# Expose paths so CMake finds libpq
export PATH="$(brew --prefix libpq)/bin:$PATH"
export PKG_CONFIG_PATH="$(brew --prefix libpq)/lib/pkgconfig:$PKG_CONFIG_PATH"
export PostgreSQL_ROOT="$(brew --prefix libpq)"

# 1. Generate build files (Passing your CXX flags directly to CMake instead of configure)
cmake .. \
-DCMAKE_CXX_STANDARD=20 \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_SYSROOT=$(xcrun --show-sdk-path) \
-DSKIP_BUILD_TEST=ON

# Step 4: Compile the project
cmake --build .
Expand Down
17 changes: 17 additions & 0 deletions scripts/build_dep.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
cd ./external/libpqxx

mkdir -p build
Comment on lines +1 to +3
Copy link

Copilot AI Apr 12, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This script is missing a shebang (e.g., #!/bin/bash or #!/usr/bin/env bash). As-is, running it directly (instead of sh scripts/build_dep.sh) may fail depending on permissions/default shell. Add a shebang and consider set -euo pipefail so dependency builds fail fast.

Copilot uses AI. Check for mistakes.
cd ./build

# Expose paths so CMake finds libpq
export PATH="$(brew --prefix libpq)/bin:$PATH"
export PKG_CONFIG_PATH="$(brew --prefix libpq)/lib/pkgconfig:$PKG_CONFIG_PATH"
export PostgreSQL_ROOT="$(brew --prefix libpq)"

# 1. Generate build files (Passing your CXX flags directly to CMake instead of configure)
cmake .. \
-DCMAKE_CXX_STANDARD=20 \
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_OSX_SYSROOT=$(xcrun --show-sdk-path) \
-DSKIP_BUILD_TEST=ON
make
11 changes: 9 additions & 2 deletions scripts/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@ current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

# Build the source code
# source $current_dir/environment.sh - no longer necessary
source $current_dir/clean.sh
source $current_dir/build.sh
source "$current_dir/clean.sh"
if ! source "$current_dir/build.sh"; then
echo "Error: Build failed. Aborting."
exit 1
fi

# Debug: Check if the executable exists
if [ -f "$BUILD_DIR/$EXECUTABLE_NAME" ]; then
Expand Down Expand Up @@ -49,5 +52,9 @@ output=$(echo "$json" | base64)

# Step 6: Run the tests for now (/executable) from the root directory
# Passing two arguements, the destination of the QuestDB and the Strategy JSON (in base64)
start_time=$(date +%s%N)
./"$BUILD_DIR/$EXECUTABLE_NAME" localhost "$output"
end_time=$(date +%s%N)
elapsed=$(( (end_time - start_time) / 1000000 ))
echo "Execution time: ${elapsed}ms"

Loading
Loading