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
16 changes: 5 additions & 11 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
# Contributing

Thanks for taking a look! This is an active solo experiment, and I'm grateful for the interest.
Thanks for taking a look! This is an active experiment, and I'm grateful for the interest.

## I'm not accepting pull requests right now

This repository isn't currently accepting external contributions. I am actively experimenting with different approaches and want to avoid merge conflicts, so any PR opened from a non-owner account will be commented on and closed automatically by a GitHub bot.
This repository isn't currently accepting external contributions.

Please don't take it personally — the policy is the same for everyone, and it isn't a judgement on the change.
I am actively experimenting with different approaches and want to avoid merge conflicts, so any PR opened from a non-owner account will be commented on and closed automatically by a GitHub bot.

## Please fork freely

The project is [MIT-licensed](LICENSE.MD), so you're very welcome to fork it and take the code in your own direction. If your fork sparks a discussion or you want to share what you've built on top of it, feel free to open an issue (see below).
The project is [MIT-licensed](LICENSE.MD), so you're very welcome to fork it and take the code in your own direction.

## Use GitHub Issues for bugs, questions, and ideas

Bugs, questions, and ideas are all welcome on the [Issues tab](https://github.com/mccaffers/backtesting-engine-cpp/issues). That's the supported way to flag something — much more useful to me than a PR I'd have to close.

When filing an issue, a short description of what you saw, what you expected, and (for bugs) how to reproduce it goes a long way.

## A note on `@claude` mentions

This repo uses the [Claude Code GitHub Action](https://github.com/anthropics/claude-code-action) on `@claude` mentions, but it's restricted to the repository owner — `@claude` comments from other accounts won't trigger a run. This keeps paid API usage under control.
Bugs, questions, and ideas are all welcome on the [Issues tab](https://github.com/mccaffers/backtesting-engine-cpp/issues).
4 changes: 3 additions & 1 deletion include/trading/tradeManager.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@ class TradeManager {
boost::decimal::decimal64_t stopDistancePips = boost::decimal::decimal64_t{0},
boost::decimal::decimal64_t limitDistancePips = boost::decimal::decimal64_t{0});
size_t reviewAccount() const;
bool closeTrade(const std::string& tradeId, boost::decimal::decimal64_t closePrice);
bool closeTrade(const std::string& tradeId,
boost::decimal::decimal64_t closePrice,
const PriceData& tick);
const std::unordered_map<std::string, Trade>& getActiveTrades() const;
const std::vector<Trade>& getClosedTrades() const;
boost::decimal::decimal64_t calculatePnl() const;
Expand Down
2 changes: 1 addition & 1 deletion source/operations.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ void reviewStopAndLimit(TradeManager& tradeManager, const PriceData& tick) {
}
}
for (const auto& [id, exitPrice] : toClose) {
tradeManager.closeTrade(id, exitPrice);
tradeManager.closeTrade(id, exitPrice, tick);
}
}

Expand Down
25 changes: 23 additions & 2 deletions source/trading/tradeManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@

#include "tradeManager.hpp"
#include <atomic>
#include <chrono>
#include <ctime>
#include <iomanip>
#include <iostream>
#include <sstream>

namespace {
std::string nextTradeId() {
Expand Down Expand Up @@ -35,19 +40,35 @@ size_t TradeManager::reviewAccount() const {
return activeTrades.size();
}

bool TradeManager::closeTrade(const std::string& tradeId, boost::decimal::decimal64_t closePrice) {
bool TradeManager::closeTrade(const std::string& tradeId,
boost::decimal::decimal64_t closePrice,
const PriceData& tick) {
auto it = activeTrades.find(tradeId);
if (it != activeTrades.end()) {
Trade closed = it->second;
closed.closePrice = closePrice;
closed.closeTime = std::chrono::system_clock::now();
closed.closeTime = tick.timestamp;
auto diff = closePrice - closed.entryPrice;
if (closed.direction == Direction::SHORT) diff = -diff;
// scalingFactor stays an int — boost::decimal has overloads for builtin
// integer types, so no conversion is needed there.
closed.pnl = diff * closed.scalingFactor * closed.size;
closedTrades.push_back(closed);
activeTrades.erase(it);

auto t = std::chrono::system_clock::to_time_t(tick.timestamp);
std::tm utc{};
gmtime_r(&t, &utc);
std::ostringstream ts;
ts << std::put_time(&utc, "%Y-%m-%dT%H:%M:%SZ");

const char* side = (closed.direction == Direction::LONG) ? "BUY" : "SELL";
std::cout << ts.str()
<< ", Trade Closed, " << closed.symbol
<< ", " << side
<< ", " << std::showpos << std::fixed << std::setprecision(2) << closed.pnl
<< std::noshowpos
<< std::endl;
return true;
}
return false;
Expand Down
2 changes: 1 addition & 1 deletion tests/tradeManager.mm
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ - (void)testOpenTrade {
- (void)testCloseTrade {
PriceData tick("100.0"_dd, "99.0"_dd, std::chrono::system_clock::now(), "EURUSD");
std::string tradeId = self.manager->openTrade(tick, "1.0"_dd, Direction::LONG);
bool closed = self.manager->closeTrade(tradeId, "110.0"_dd);
bool closed = self.manager->closeTrade(tradeId, "110.0"_dd, tick);
XCTAssertTrue(closed, "Trade should be closed successfully");
XCTAssertEqual(self.manager->reviewAccount(), 0, "Should have 0 active trades");
}
Expand Down
Loading