Skip to content

Commit 94ade91

Browse files
committed
Split off JSON parser with more unit tests
1 parent 86292e5 commit 94ade91

File tree

6 files changed

+119
-26
lines changed

6 files changed

+119
-26
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Feel free to explore, but this code base is usuable at the moment.
88

99
I'm developing a high-performance C++ backtesting engine designed to analyze financial data and evaluate multiple trading strategies at scale.
1010

11-
![alt](images/development_active.svg) [![Build](https://github.com/mccaffers/backtesting-engine-cpp/actions/workflows/sonarcloud.yml/badge.svg)](https://github.com/mccaffers/backtesting-engine-cpp/actions/workflows/sonarcloud.yml) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=mccaffers_backtesting-engine-cpp&metric=bugs)](https://sonarcloud.io/summary/new_code?id=mccaffers_backtesting-engine-cpp) [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=mccaffers_backtesting-engine-cpp&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=mccaffers_backtesting-engine-cpp) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=mccaffers_backtesting-engine-cpp&metric=coverage)](https://sonarcloud.io/summary/new_code?id=mccaffers_backtesting-engine-cpp)
11+
[![Build](https://github.com/mccaffers/backtesting-engine-cpp/actions/workflows/sonarcloud.yml/badge.svg)](https://github.com/mccaffers/backtesting-engine-cpp/actions/workflows/sonarcloud.yml) [![Bugs](https://sonarcloud.io/api/project_badges/measure?project=mccaffers_backtesting-engine-cpp&metric=bugs)](https://sonarcloud.io/summary/new_code?id=mccaffers_backtesting-engine-cpp) [![Code Smells](https://sonarcloud.io/api/project_badges/measure?project=mccaffers_backtesting-engine-cpp&metric=code_smells)](https://sonarcloud.io/summary/new_code?id=mccaffers_backtesting-engine-cpp) [![Coverage](https://sonarcloud.io/api/project_badges/measure?project=mccaffers_backtesting-engine-cpp&metric=coverage)](https://sonarcloud.io/summary/new_code?id=mccaffers_backtesting-engine-cpp)
1212

1313
I'm extracting results and creating various graphs for trend analyses using SciPy for calculations and Plotly for visualization.
1414

backtesting-engine-cpp.xcodeproj/project.pbxproj

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@
1515
941B549B2D3BBADE00E3BF64 /* trading_definitions_json.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 941B54992D3BBADD00E3BF64 /* trading_definitions_json.cpp */; };
1616
94280BA32D2FC00200F1CF56 /* base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94280BA22D2FC00200F1CF56 /* base64.cpp */; };
1717
94280BA42D2FC00200F1CF56 /* base64.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94280BA22D2FC00200F1CF56 /* base64.cpp */; };
18+
943398242D57E53400287A2D /* jsonParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 943398232D57E53400287A2D /* jsonParser.cpp */; };
19+
943398252D57E53400287A2D /* jsonParser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 943398232D57E53400287A2D /* jsonParser.cpp */; };
20+
943398272D57E54000287A2D /* jsonParser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 943398262D57E54000287A2D /* jsonParser.mm */; };
1821
94364CB62D416D8D00F35B55 /* db.mm in Sources */ = {isa = PBXBuildFile; fileRef = 94364CB52D416D8000F35B55 /* db.mm */; };
1922
944698852D3A545B0070E30F /* libpq.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 94CD8A972D2D34A100041BBA /* libpq.a */; };
2023
94674B872D533B4000973137 /* tradeManager.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 94674B852D533B4000973137 /* tradeManager.cpp */; };
@@ -59,6 +62,9 @@
5962
94280BA12D2FC00200F1CF56 /* base64.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = base64.hpp; sourceTree = "<group>"; };
6063
94280BA22D2FC00200F1CF56 /* base64.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = base64.cpp; sourceTree = "<group>"; };
6164
942966D82D48E84A00532862 /* priceData.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = priceData.hpp; sourceTree = "<group>"; };
65+
943398222D57E52900287A2D /* jsonParser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = jsonParser.hpp; sourceTree = "<group>"; };
66+
943398232D57E53400287A2D /* jsonParser.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = jsonParser.cpp; sourceTree = "<group>"; };
67+
943398262D57E54000287A2D /* jsonParser.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = jsonParser.mm; sourceTree = "<group>"; };
6268
94364CB52D416D8000F35B55 /* db.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = db.mm; sourceTree = "<group>"; };
6369
944D0DC82C8C3704004DD0FC /* LICENSE.MD */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = LICENSE.MD; sourceTree = "<group>"; };
6470
944D0DC92C8C3704004DD0FC /* build.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = build.sh; sourceTree = "<group>"; };
@@ -1389,6 +1395,7 @@
13891395
9470B5A22C8C5AD0007D9CC6 /* source */ = {
13901396
isa = PBXGroup;
13911397
children = (
1398+
943398232D57E53400287A2D /* jsonParser.cpp */,
13921399
94674B8C2D533E7800973137 /* models */,
13931400
94674B862D533B4000973137 /* trading */,
13941401
94DE4F772C8C3E7C00FE48FF /* include */,
@@ -1405,6 +1412,7 @@
14051412
9470B5AD2C8C5B99007D9CC6 /* tests */ = {
14061413
isa = PBXGroup;
14071414
children = (
1415+
943398262D57E54000287A2D /* jsonParser.mm */,
14081416
94674B892D533BDA00973137 /* tradeManager.mm */,
14091417
94364CB52D416D8000F35B55 /* db.mm */,
14101418
);
@@ -3502,6 +3510,7 @@
35023510
94DE4F772C8C3E7C00FE48FF /* include */ = {
35033511
isa = PBXGroup;
35043512
children = (
3513+
943398222D57E52900287A2D /* jsonParser.hpp */,
35053514
94674B842D533B2F00973137 /* trading */,
35063515
942966D72D48E84100532862 /* models */,
35073516
94B8C7932D3D770800E17EB6 /* utilities */,
@@ -3608,6 +3617,7 @@
36083617
buildActionMask = 2147483647;
36093618
files = (
36103619
9470B5A42C8C5AD0007D9CC6 /* main.cpp in Sources */,
3620+
943398252D57E53400287A2D /* jsonParser.cpp in Sources */,
36113621
94280BA32D2FC00200F1CF56 /* base64.cpp in Sources */,
36123622
94674B8E2D533E7800973137 /* trade.cpp in Sources */,
36133623
941B549B2D3BBADE00E3BF64 /* trading_definitions_json.cpp in Sources */,
@@ -3623,12 +3633,14 @@
36233633
buildActionMask = 2147483647;
36243634
files = (
36253635
94CD8BA12D2E8CE500041BBA /* databaseConnection.cpp in Sources */,
3636+
943398242D57E53400287A2D /* jsonParser.cpp in Sources */,
36263637
94280BA42D2FC00200F1CF56 /* base64.cpp in Sources */,
36273638
94674B8D2D533E7800973137 /* trade.cpp in Sources */,
36283639
941B549A2D3BBADE00E3BF64 /* trading_definitions_json.cpp in Sources */,
36293640
94674B8A2D533BDA00973137 /* tradeManager.mm in Sources */,
36303641
940A61182C92CE960083FEB8 /* serviceA.cpp in Sources */,
36313642
94674B882D533B4000973137 /* tradeManager.cpp in Sources */,
3643+
943398272D57E54000287A2D /* jsonParser.mm in Sources */,
36323644
9470B5B62C8C5BFD007D9CC6 /* main.cpp in Sources */,
36333645
94364CB62D416D8D00F35B55 /* db.mm in Sources */,
36343646
940A61142C92CE210083FEB8 /* configManager.cpp in Sources */,

source/include/jsonParser.hpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// Backtesting Engine in C++
2+
//
3+
// (c) 2025 Ryan McCaffery | https://mccaffers.com
4+
// This code is licensed under MIT license (see LICENSE.txt for details)
5+
// ---------------------------------------
6+
7+
#pragma once
8+
9+
#include <string>
10+
#include <nlohmann/json.hpp>
11+
#include "trading_definitions.hpp"
12+
13+
class JsonParser {
14+
public:
15+
static int parseConfigurationFromBase64(const std::string& input);
16+
};

source/jsonParser.cpp

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#include "jsonParser.hpp"
2+
#include "base64.hpp"
3+
#include <iostream>
4+
5+
using json = nlohmann::json;
6+
7+
int JsonParser::parseConfigurationFromBase64(const std::string& input) {
8+
// Ingest parameters
9+
std::string output = Base64::b64decode(input);
10+
11+
// Debug, console print out
12+
std::cout << output;
13+
14+
json j;
15+
try {
16+
j = json::parse(output);
17+
}
18+
catch (json::parse_error& ex) {
19+
std::cerr << "parse error at byte " << ex.byte << std::endl;
20+
}
21+
22+
auto config = j.get<trading_definitions::Configuration>();
23+
std::cout << config.RUN_ID << std::endl;
24+
25+
return 0;
26+
}

source/main.cpp

Lines changed: 2 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,40 +21,17 @@
2121
#include "base64.hpp"
2222
#include "trading_definitions.hpp" // For everything
2323
#include "tradeManager.hpp"
24+
#include "jsonParser.hpp"
2425

2526
using json = nlohmann::json;
2627

27-
int parseJson(const std::string& input) {
28-
29-
// Ingest parameters
30-
std::string output = Base64::b64decode(input);
31-
32-
// Debug, console print out
33-
std::cout << output;
34-
35-
json j;
36-
try
37-
{
38-
j = json::parse(output);
39-
}
40-
catch (json::parse_error& ex)
41-
{
42-
std::cerr << "parse error at byte " << ex.byte << std::endl;
43-
}
44-
45-
auto config = j.get<trading_definitions::Configuration>();
46-
std::cout << config.RUN_ID << std::endl;
47-
48-
return 0;
49-
}
50-
5128
int main(int argc, const char * argv[]) {
5229

5330
// Connect to QuestDb argv[1]
5431
DatabaseConnection db(argv[1], 8812, "qdb", "admin", "quest");
5532

5633
// Load strategy from Base64 argv[2]
57-
parseJson(argv[2]);
34+
JsonParser::parseConfigurationFromBase64(argv[2]);
5835

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

tests/jsonParser.mm

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// Backtesting Engine in C++
2+
//
3+
// (c) 2025 Ryan McCaffery | https://mccaffers.com
4+
// This code is licensed under MIT license (see LICENSE.txt for details)
5+
// ---------------------------------------
6+
7+
#import <XCTest/XCTest.h>
8+
#import "jsonParser.hpp"
9+
#include "base64.hpp"
10+
11+
@interface jsonParserTests : XCTestCase
12+
@end
13+
14+
@implementation jsonParserTests
15+
16+
- (void)setUp {
17+
// Put setup code here. This method is called before the invocation of each test method in the class.
18+
}
19+
20+
- (void)tearDown {
21+
// Put teardown code here. This method is called after the invocation of each test method in the class.
22+
}
23+
24+
- (void)testValidJsonParsing {
25+
// Create a sample configuration JSON with all required fields
26+
std::string validJson = R"({
27+
"RUN_ID": "UNIQUE_IDENTIFER",
28+
"SYMBOLS": "EURUSD",
29+
"LAST_MONTHS": 6,
30+
"STRATEGY": {
31+
"UUID": "",
32+
"TRADING_VARIABLES": {
33+
"STRATEGY": "OHLC_RSI",
34+
"STOP_DISTANCE_IN_PIPS": 1,
35+
"LIMIT_DISTANCE_IN_PIPS": 1,
36+
"TRADING_SIZE": 1
37+
},
38+
"OHLC_VARIABLES": [
39+
{
40+
"OHLC_COUNT": 60,
41+
"OHLC_MINUTES": 100
42+
}
43+
],
44+
"STRATEGY_VARIABLES" : {
45+
"OHLC_RSI_VARIABLES": {
46+
"RSI_LONG": 60,
47+
"RSI_SHORT": 40
48+
}
49+
}
50+
}
51+
})";
52+
53+
// Convert to base64
54+
std::string base64Input = Base64::b64encode(validJson);
55+
56+
// Test parsing
57+
int result = JsonParser::parseConfigurationFromBase64(base64Input);
58+
XCTAssertEqual(result, 0, "Parsing should succeed with valid JSON");
59+
}
60+
61+
62+
@end

0 commit comments

Comments
 (0)