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
2 changes: 1 addition & 1 deletion .github/workflows/brew.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,5 @@ check_and_install() {
}

# Install packages if they don't exist
check_and_install postgresql # Check and install PostgreSQL
check_and_install postgresql # Check and install PostgreSQL (which includes libpq)
check_and_install pkg-config # Check and install pkg-config
16 changes: 7 additions & 9 deletions .github/workflows/build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,13 @@ cd ./external/libpqxx
mkdir -p build
cd ./build

# Generate build system files using CMake
# '..' points to the libpqxx root directory containing CMakeLists.txt
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)"

# Configure the build with specific C++ compiler flags:
# -std=c++20: Use C++20 standard
# -O3: Enable maximum optimization
# --enable-silent-rules: Reduce build output verbosity
./configure CXXFLAGS="-std=c++20 -O3" --enable-silent-rules
# 1. Generate build files (Passing your CXX flags directly to CMake instead of configure)
cmake .. -DCMAKE_CXX_STANDARD=20 -DCMAKE_BUILD_TYPE=Release

# Compile libpqxx using generated build files
# 2. Compile libpqxx
make
2 changes: 1 addition & 1 deletion .github/workflows/sonarcloud.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ jobs:
-derivedDataPath "${RUNNER_TEMP}/Build/DerivedData"
HEADER_SEARCH_PATHS="./external/libpqxx/include/pqxx/internal ./external/libpqxx/include/ ./external/libpqxx/build/include/ ./external/"
LIBRARY_SEARCH_PATHS="./external/libpqxx/src/ ./external/libpqxx/build/src/"
OTHER_LDFLAGS="-L./external/libpqxx/build/src -lpqxx -lpq -L/opt/homebrew/Cellar/pkgconf/2.3.0_1/lib -L/opt/homebrew/Cellar/pkgconf/2.3.0_1/lib/pkgconfig -L/opt/homebrew/Cellar/postgresql@14/14.15/lib/postgresql@14 -L/opt/homebrew/Cellar/postgresql@14/14.15/lib/postgresql@14/pgxs -L/opt/homebrew/Cellar/postgresql@14/14.15/lib/postgresql@14/pkgconfig"
OTHER_LDFLAGS="-L./external/libpqxx/build/src -lpqxx -lpq -L$(brew --prefix pkgconf)/lib -L$(brew --prefix pkgconf)/lib/pkgconfig -L$(brew --prefix postgresql@18)/lib/postgresql -L$(brew --prefix postgresql@18)/lib/postgresql/pgxs -L$(brew --prefix postgresql@18)/lib/postgresql/pkgconfig"
clean build test
| xcpretty -r junit && exit ${PIPESTATUS[0]}
- name: Convert coverage report to sonarqube format
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,5 @@ TestResult
xcuserdata/
xcuserstate/
TestResult.xcresult/
sonarqube-generic-coverage.xml
sonarqube-generic-coverage.xml
external/libpqxx
10 changes: 5 additions & 5 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,11 @@ add_subdirectory(external/libpqxx EXCLUDE_FROM_ALL)

# Include directories
include_directories(
${CMAKE_SOURCE_DIR}/source/include
${CMAKE_SOURCE_DIR}/source/include/utilities
${CMAKE_SOURCE_DIR}/source/include/models
${CMAKE_SOURCE_DIR}/source/include/trading
${CMAKE_SOURCE_DIR}/source/include/trading_definitions
${CMAKE_SOURCE_DIR}/include
${CMAKE_SOURCE_DIR}/include/utilities
${CMAKE_SOURCE_DIR}/include/models
${CMAKE_SOURCE_DIR}/include/trading
${CMAKE_SOURCE_DIR}/include/trading_definitions
${CMAKE_SOURCE_DIR}/external
)

Expand Down
32 changes: 18 additions & 14 deletions backtesting-engine-cpp.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
940A61142C92CE210083FEB8 /* configManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 940A61112C92CE210083FEB8 /* configManager.cpp */; };
940A61172C92CE960083FEB8 /* serviceA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 940A61152C92CE960083FEB8 /* serviceA.cpp */; };
940A61182C92CE960083FEB8 /* serviceA.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 940A61152C92CE960083FEB8 /* serviceA.cpp */; };
941408AE2D59F93F000ED1F9 /* sqlManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941408AD2D59F93F000ED1F9 /* sqlManager.cpp */; };
941408AF2D59F93F000ED1F9 /* sqlManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941408AD2D59F93F000ED1F9 /* sqlManager.cpp */; };
941B549A2D3BBADE00E3BF64 /* trading_definitions_json.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941B54992D3BBADD00E3BF64 /* trading_definitions_json.cpp */; };
941B549B2D3BBADE00E3BF64 /* trading_definitions_json.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941B54992D3BBADD00E3BF64 /* trading_definitions_json.cpp */; };
94280BA32D2FC00200F1CF56 /* base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94280BA22D2FC00200F1CF56 /* base64.cpp */; };
Expand Down Expand Up @@ -51,6 +53,8 @@
940A61122C92CE210083FEB8 /* configManager.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = configManager.hpp; sourceTree = "<group>"; };
940A61152C92CE960083FEB8 /* serviceA.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = serviceA.cpp; sourceTree = "<group>"; };
940A61162C92CE960083FEB8 /* serviceA.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = serviceA.hpp; sourceTree = "<group>"; };
941408AD2D59F93F000ED1F9 /* sqlManager.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = sqlManager.cpp; sourceTree = "<group>"; };
941408B02D59F954000ED1F9 /* sqlManager.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = sqlManager.hpp; sourceTree = "<group>"; };
941B54902D3BBA4900E3BF64 /* ohlc_variables.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ohlc_variables.hpp; sourceTree = "<group>"; };
941B54912D3BBA5600E3BF64 /* ohlc_rsi_variables.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ohlc_rsi_variables.hpp; sourceTree = "<group>"; };
941B54922D3BBA6500E3BF64 /* trading_variables.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = trading_variables.hpp; sourceTree = "<group>"; };
Expand Down Expand Up @@ -1317,6 +1321,7 @@
94CD849E2D2D22C900041BBA /* external */,
944D0DD22C8C3704004DD0FC /* images */,
944D0DCE2C8C3704004DD0FC /* scripts */,
94DE4F772C8C3E7C00FE48FF /* include */,
9470B5A22C8C5AD0007D9CC6 /* source */,
9470B5AD2C8C5B99007D9CC6 /* tests */,
948A9CEC2C906ADB00E23669 /* resources */,
Expand Down Expand Up @@ -1395,16 +1400,16 @@
9470B5A22C8C5AD0007D9CC6 /* source */ = {
isa = PBXGroup;
children = (
943398232D57E53400287A2D /* jsonParser.cpp */,
94674B8C2D533E7800973137 /* models */,
94674B862D533B4000973137 /* trading */,
94DE4F772C8C3E7C00FE48FF /* include */,
941B54982D3BBAD800E3BF64 /* trading_definitions */,
94280BA72D2FC29F00F1CF56 /* utilities */,
9470B5A32C8C5AD0007D9CC6 /* main.cpp */,
940A61112C92CE210083FEB8 /* configManager.cpp */,
940A61152C92CE960083FEB8 /* serviceA.cpp */,
94CD8B9F2D2E8CE500041BBA /* databaseConnection.cpp */,
943398232D57E53400287A2D /* jsonParser.cpp */,
941408AD2D59F93F000ED1F9 /* sqlManager.cpp */,
);
path = source;
sourceTree = "<group>";
Expand Down Expand Up @@ -3510,6 +3515,7 @@
94DE4F772C8C3E7C00FE48FF /* include */ = {
isa = PBXGroup;
children = (
941408B02D59F954000ED1F9 /* sqlManager.hpp */,
943398222D57E52900287A2D /* jsonParser.hpp */,
94674B842D533B2F00973137 /* trading */,
942966D72D48E84100532862 /* models */,
Expand Down Expand Up @@ -3616,6 +3622,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
941408AE2D59F93F000ED1F9 /* sqlManager.cpp in Sources */,
9470B5A42C8C5AD0007D9CC6 /* main.cpp in Sources */,
943398252D57E53400287A2D /* jsonParser.cpp in Sources */,
94280BA32D2FC00200F1CF56 /* base64.cpp in Sources */,
Expand All @@ -3633,6 +3640,7 @@
buildActionMask = 2147483647;
files = (
94CD8BA12D2E8CE500041BBA /* databaseConnection.cpp in Sources */,
941408AF2D59F93F000ED1F9 /* sqlManager.cpp in Sources */,
943398242D57E53400287A2D /* jsonParser.cpp in Sources */,
94280BA42D2FC00200F1CF56 /* base64.cpp in Sources */,
94674B8D2D533E7800973137 /* trade.cpp in Sources */,
Expand Down Expand Up @@ -3777,9 +3785,8 @@
);
INCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES = "";
LIBRARY_SEARCH_PATHS = (
"\"$(SRCROOT)/external/libpqxx/src\"",
"$(SRCROOT)/build/external/libpqxx/src",
"/opt/homebrew/Cellar/postgresql@14/14.15/lib/postgresql@14",
"\"$(SRCROOT)/external/libpqxx/build/src\"",
"/opt/homebrew/Cellar/postgresql@18/18.3/lib/postgresql",
);
OTHER_LDFLAGS = "";
OTHER_LIBTOOLFLAGS = "";
Expand All @@ -3800,9 +3807,8 @@
);
INCLUDED_RECURSIVE_SEARCH_PATH_SUBDIRECTORIES = "";
LIBRARY_SEARCH_PATHS = (
"\"$(SRCROOT)/external/libpqxx/src\"",
"$(SRCROOT)/build/external/libpqxx/src",
"/opt/homebrew/Cellar/postgresql@14/14.15/lib/postgresql@14",
"\"$(SRCROOT)/external/libpqxx/build/src\"",
"/opt/homebrew/Cellar/postgresql@18/18.3/lib/postgresql",
);
OTHER_LDFLAGS = "";
OTHER_LIBTOOLFLAGS = "";
Expand All @@ -3823,9 +3829,8 @@
"\"$(SRCROOT)/external/",
);
LIBRARY_SEARCH_PATHS = (
"\"$(SRCROOT)/external/libpqxx/src\"",
"$(PROJECT_DIR)/build/external/libpqxx/src",
"/opt/homebrew/Cellar/postgresql@14/14.15/lib/postgresql@14",
"\"$(SRCROOT)/external/libpqxx/build/src\"",
"/opt/homebrew/Cellar/postgresql@18/18.3/lib/postgresql",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.mccaffers.tests;
Expand All @@ -3846,9 +3851,8 @@
"\"$(SRCROOT)/external/",
);
LIBRARY_SEARCH_PATHS = (
"\"$(SRCROOT)/external/libpqxx/src\"",
"$(PROJECT_DIR)/build/external/libpqxx/src",
"/opt/homebrew/Cellar/postgresql@14/14.15/lib/postgresql@14",
"\"$(SRCROOT)/external/libpqxx/build/src\"",
"/opt/homebrew/Cellar/postgresql@18/18.3/lib/postgresql",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.mccaffers.tests;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@
</BuildableProductRunnable>
<CommandLineArguments>
<CommandLineArgument
argument = "100.85.44.114"
argument = "localhost"
isEnabled = "YES">
</CommandLineArgument>
<CommandLineArgument
Expand Down
File renamed without changes.
File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
// This code is licensed under MIT license (see LICENSE.txt for details)
// ---------------------------------------
#pragma once
#include <chrono>

struct PriceData {
double value1;
Expand Down
File renamed without changes.
File renamed without changes.
18 changes: 18 additions & 0 deletions include/sqlManager.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Backtesting Engine in C++
//
// (c) 2025 Ryan McCaffery | https://mccaffers.com
// This code is licensed under MIT license (see LICENSE.txt for details)
// ---------------------------------------
#pragma once
#include <string>
#include <vector>
#include "models/priceData.hpp"
#include "databaseConnection.hpp"

class SqlManager {
public:
static std::vector<PriceData> getInitialPriceData(const DatabaseConnection& db);
static std::string getBaseQuery();
private:
static constexpr int DEFAULT_LIMIT = 1000;
};
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion scripts/run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
current_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"

# Build the source code
source $current_dir/environment.sh
# source $current_dir/environment.sh - no longer necessary
source $current_dir/clean.sh
source $current_dir/build.sh

Expand Down
11 changes: 7 additions & 4 deletions scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@
xcodebuild \
-project backtesting-engine-cpp.xcodeproj \
-scheme tests \
HEADER_SEARCH_PATHS="./external/libpqxx/include/pqxx/internal ./external/libpqxx/include/ ./external/libpqxx/build/include/ ./external/" \
LIBRARY_SEARCH_PATHS="./external/libpqxx/src/ ./external/libpqxx/build/src/" \
OTHER_LDFLAGS="-L./external/libpqxx/build/src -lpqxx -lpq -L/opt/homebrew/Cellar/pkgconf/2.3.0_1/lib -L/opt/homebrew/Cellar/pkgconf/2.3.0_1/lib/pkgconfig -L/opt/homebrew/Cellar/postgresql@14/14.15/lib/postgresql@14 -L/opt/homebrew/Cellar/postgresql@14/14.15/lib/postgresql@14/pgxs -L/opt/homebrew/Cellar/postgresql@14/14.15/lib/postgresql@14/pkgconfig" \
clean build test
clean build test
# HEADER_SEARCH_PATHS="./external/libpqxx/include/pqxx/internal ./external/libpqxx/include/ ./external/libpqxx/build/include/ ./external/" \
# LIBRARY_SEARCH_PATHS="./external/libpqxx/src/ ./external/libpqxx/build/src/" \
# OTHER_LDFLAGS="-L./external/libpqxx/build/src -lpqxx -lpq -L$(brew --prefix pkgconf)/lib -L$(brew --prefix pkgconf)/lib/pkgconfig -L$(brew --prefix postgresql@18)/lib/postgresql -L$(brew --prefix postgresql@18)/lib/postgresql/pgxs -L$(brew --prefix postgresql@18)/lib/postgresql/pkgconfig" \



6 changes: 6 additions & 0 deletions source/jsonParser.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
// Backtesting Engine in C++
//
// (c) 2025 Ryan McCaffery | https://mccaffers.com
// This code is licensed under MIT license (see LICENSE.txt for details)
// ---------------------------------------

#include "jsonParser.hpp"
#include "base64.hpp"
#include <iostream>
Expand Down
6 changes: 2 additions & 4 deletions source/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
#include "trading_definitions.hpp" // For everything
#include "tradeManager.hpp"
#include "jsonParser.hpp"
#include "sqlManager.hpp"

using json = nlohmann::json;

Expand All @@ -33,10 +34,7 @@ int main(int argc, const char * argv[]) {
// Load strategy from Base64 argv[2]
JsonParser::parseConfigurationFromBase64(argv[2]);

// Example query - replace with your actual query
std::string query = "SELECT * FROM EURUSD LIMIT 5;";

std::vector<PriceData> priceData = db.executeQuery(query);
std::vector<PriceData> priceData = SqlManager::getInitialPriceData(db);

// Convert timestamp to readable format for debugging
auto timeT = std::chrono::system_clock::to_time_t(priceData[0].timestamp);
Expand Down
14 changes: 14 additions & 0 deletions source/sqlManager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
// Backtesting Engine in C++
//
// (c) 2025 Ryan McCaffery | https://mccaffers.com
// This code is licensed under MIT license (see LICENSE.txt for details)
// ---------------------------------------
#include "sqlManager.hpp"

std::string SqlManager::getBaseQuery() {
return "SELECT * FROM EURUSD LIMIT " + std::to_string(DEFAULT_LIMIT) + ";";

Check warning on line 9 in source/sqlManager.cpp

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Use std::format instead of concatenating pieces manually.

See more on https://sonarcloud.io/project/issues?id=mccaffers_backtesting-engine-cpp&issues=AZ1ihCBybB_ux2Y8WXdR&open=AZ1ihCBybB_ux2Y8WXdR&pullRequest=7
Comment on lines +7 to +9
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

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

getBaseQuery() hardcodes the table/symbol (EURUSD) and the row limit, even though configuration includes a SYMBOLS field. To make SqlManager reusable (and avoid having to change code for different symbols/limits), consider taking symbol and limit as parameters (or reading them from the parsed configuration).

Suggested change
std::string SqlManager::getBaseQuery() {
return "SELECT * FROM EURUSD LIMIT " + std::to_string(DEFAULT_LIMIT) + ";";
#include <cstdlib>
#include <string>
namespace {
std::string getConfiguredSymbol() {
const char* symbols = std::getenv("SYMBOLS");
if (symbols == nullptr || *symbols == '\0') {
return "EURUSD";
}
std::string configuredSymbols(symbols);
std::size_t separator = configuredSymbols.find(',');
std::string symbol = configuredSymbols.substr(0, separator);
std::size_t first = symbol.find_first_not_of(" \t");
if (first == std::string::npos) {
return "EURUSD";
}
std::size_t last = symbol.find_last_not_of(" \t");
return symbol.substr(first, last - first + 1);
}
int getConfiguredLimit() {
const char* configuredLimit = std::getenv("SQL_LIMIT");
if (configuredLimit == nullptr || *configuredLimit == '\0') {
return DEFAULT_LIMIT;
}
char* end = nullptr;
long parsedLimit = std::strtol(configuredLimit, &end, 10);
if (end == configuredLimit || *end != '\0' || parsedLimit <= 0) {
return DEFAULT_LIMIT;
}
return static_cast<int>(parsedLimit);
}
} // namespace
std::string SqlManager::getBaseQuery() {
return "SELECT * FROM " + getConfiguredSymbol() + " LIMIT " + std::to_string(getConfiguredLimit()) + ";";

Copilot uses AI. Check for mistakes.
}

std::vector<PriceData> SqlManager::getInitialPriceData(const DatabaseConnection& db) {
Comment on lines +8 to +12
Copy link

Copilot AI Apr 5, 2026

Choose a reason for hiding this comment

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

There are XCTest-based tests for other components, but nothing covers the new SqlManager behavior (e.g., the generated base query / default limit). Adding a small unit test would help catch regressions as the query logic evolves.

Copilot uses AI. Check for mistakes.
return db.executeQuery(getBaseQuery());
}
Loading