Skip to content

Commit fcf1d9e

Browse files
committed
feat: logging, multiple symbols, max depth, readmes
1 parent 41e449f commit fcf1d9e

14 files changed

Lines changed: 115 additions & 63 deletions

File tree

.env.example

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1-
FIX_CONFIG_PATH="binance/fixconfig"
1+
LOG_LEVEL="info"
22
API_KEY=""
3+
FIX_CONFIG_PATH="binance/fixconfig"
34
PRIVATE_KEY_PATH="key.pem"
5+
SYMBOLS=BTCUSDT,

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ build/
66
binance/keys/
77
qf_files/
88
qf_logs/
9+
logs/
910

1011
.env
1112
.env.*

CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ find_package(ftxui REQUIRED)
2424
find_package(OpenSSL REQUIRED)
2525
find_package(quickfix REQUIRED CONFIG)
2626
find_package(libsodium REQUIRED)
27+
find_package(spdlog REQUIRED)
2728

2829
# Link them to the executable
2930
target_link_libraries(tradercpp PRIVATE
@@ -33,4 +34,5 @@ target_link_libraries(tradercpp PRIVATE
3334
OpenSSL::Crypto
3435
quickfix::quickfix
3536
libsodium::libsodium
37+
spdlog::spdlog
3638
)

conanfile.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ libsodium/1.0.20
44
openssl/3.5.2
55
ftxui/6.0.2
66
concurrentqueue/1.0.4
7+
spdlog/1.15.3
78

89
[generators]
910
CMakeToolchain

src/binance/Config.cpp

Lines changed: 30 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,46 @@
11
#include <format>
22
#include <iostream>
3+
#include <ranges>
34
#include <stdexcept>
45
#include <string>
6+
#include <vector>
57
#include "Config.h"
8+
#include "spdlog/spdlog.h"
69

710
namespace Binance {
811

9-
// static member function
10-
Config Config::fromEnv() {
11-
auto getEnvOrThrow = [](const char* key) -> std::string {
12-
if (const char* val = std::getenv(key)) {
13-
std::cout << std::format("fetched environment variable, key [{}], value [{}]", key, val) << std::endl;
14-
return std::string(val);
15-
}
16-
throw std::runtime_error(std::format("envvar not defined, key [{}]", key));
17-
};
12+
/// @brief load a variable from the environment, if it's not available, panic
13+
/// @param key the name of the environment variable
14+
/// @return env var string
15+
std::string getEnvOrThrow(const char* key) {
16+
if (const char* val = std::getenv(key)) {
17+
spdlog::info(std::format("fetched environment variable, key [{}], value [{}]", key, val));
18+
return std::string(val);
19+
}
20+
throw std::runtime_error(std::format("envvar not defined, key [{}]", key));
21+
};
1822

19-
std::string apiKey = getEnvOrThrow("API_KEY");
20-
std::string privateKey = getEnvOrThrow("PRIVATE_KEY_PATH");
21-
std::string fixConfig = getEnvOrThrow("FIX_CONFIG_PATH");
23+
/// @brief load Binance configuration from env
24+
/// (static member function)
25+
/// @return Config object
26+
Config Config::fromEnv() {
27+
const std::string apiKey = getEnvOrThrow("API_KEY");
28+
const std::string privateKey = getEnvOrThrow("PRIVATE_KEY_PATH");
29+
const std::string fixConfig = getEnvOrThrow("FIX_CONFIG_PATH");
30+
const std::string instStr = getEnvOrThrow("SYMBOLS");
31+
std::vector<std::string> symbols;
32+
for (auto inst : std::views::split(instStr, ',')) {
33+
if (inst.size() > 0)
34+
symbols.emplace_back(inst.begin(), inst.end());
35+
}
2236

37+
// copy
2338
return Config{
2439
apiKey,
2540
privateKey,
26-
fixConfig
41+
fixConfig,
42+
symbols
2743
};
2844
};
2945

30-
}
46+
}

src/binance/Config.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@
22
#define BINANCECONFIG_H
33

44
#include <string>
5+
#include <vector>
56

67
namespace Binance {
78

89
/// @brief Binance config parameters, fetched from env
910
struct Config{
1011
public:
1112
const std::string apiKey, privateKeyPath, fixConfigPath;
13+
const std::vector<std::string> symbols;
14+
//
1215
static Config fromEnv();
1316
};
1417

src/binance/FixApp.cpp

Lines changed: 28 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
#include <quickfix/fix44/MarketDataRequest.h>
1616
#include <quickfix/fix44/MarketDataSnapshotFullRefresh.h>
1717
#include <quickfix/fix44/MarketDataIncrementalRefresh.h>
18+
#include "spdlog/spdlog.h"
1819

1920
namespace Binance {
2021

@@ -45,24 +46,26 @@ std::string replaceSoh(const std::string& input) {
4546
}
4647

4748
void FixApp::onCreate(const FIX::SessionID& sessionId) {
48-
std::cout << std::format("Session created, id [{}]", sessionId.toString()) << std::endl;
49+
spdlog::info(std::format("Session created, id [{}]", sessionId.toString()));
4950
};
5051
void FixApp::onLogon(const FIX::SessionID& sessionId) {
51-
std::cout << std::format("Session logon, id [{}]", sessionId.toString()) << std::endl;
52+
spdlog::info(std::format("Session logon, id [{}]", sessionId.toString()));
53+
5254
subscribeToDepth(sessionId);
5355
};
5456
void FixApp::onLogout(const FIX::SessionID& sessionId) {
55-
std::cout << std::format("Session logout, id [{}]", sessionId.toString()) << std::endl;
57+
spdlog::info(std::format("Session logout, id [{}]", sessionId.toString()));
58+
5659
};
5760
void FixApp::toAdmin(FIX::Message& msg, const FIX::SessionID& sessionId) {
5861
const FIX::Header& header = msg.getHeader();
5962
FIX::MsgType msgType;
6063
header.getField(msgType);
61-
std::cout << std::format("toAdmin, session Id [{}], type [{}], message [{}]",
62-
sessionId.toString(), msgType.getString(), replaceSoh(msg.toString())) << std::endl;
64+
spdlog::info(std::format("toAdmin, session Id [{}], type [{}], message [{}]",
65+
sessionId.toString(), msgType.getString(), replaceSoh(msg.toString())));
6366

6467
if (msgType == FIX::MsgType_Logon) {
65-
std::cout << std::format("authenticating") << std::endl;
68+
spdlog::info(std::format("authenticating"));
6669

6770
// collect required fields
6871
const std::string sender = header.getField(FIX::FIELD::SenderCompID);
@@ -92,20 +95,20 @@ void FixApp::toApp(FIX::Message& msg, const FIX::SessionID& sessionId) noexcept(
9295
const FIX::Header& header = msg.getHeader();
9396
FIX::MsgType msgType;
9497
header.getField(msgType);
95-
// std::cout << std::format("toApp, session Id [{}], type [{}], message [{}]",
96-
// sessionId.toString(), msgType.getString(), replaceSoh(msg.toString())) << std::endl;
98+
spdlog::debug(std::format("toApp, session Id [{}], type [{}], message [{}]",
99+
sessionId.toString(), msgType.getString(), replaceSoh(msg.toString())));
97100
};
98101
void FixApp::fromAdmin(const FIX::Message& msg, const FIX::SessionID& sessionId) noexcept(false) {
99102
const FIX::Header& header = msg.getHeader();
100103
FIX::MsgType msgType;
101104
header.getField(msgType);
102-
// std::cout << std::format("fromAdmin, session Id [{}], type [{}], message [{}]",
103-
// sessionId.toString(), msgType.getString(), replaceSoh(msg.toString())) << std::endl;
105+
spdlog::debug(std::format("fromAdmin, session Id [{}], type [{}], message [{}]",
106+
sessionId.toString(), msgType.getString(), replaceSoh(msg.toString())));
104107
};
105108
void FixApp::fromApp(const FIX::Message& msg, const FIX::SessionID& sessionId) noexcept(false) {
106109
FIX::MessageCracker::crack(msg, sessionId);
107-
// std::cout << std::format("fromApp, session Id [{}], message [{}]",
108-
// sessionId.toString(), replaceSoh(msg.toString())) << std::endl;
110+
spdlog::trace(std::format("fromApp, session Id [{}], message [{}]",
111+
sessionId.toString(), replaceSoh(msg.toString())));
109112
}
110113

111114
void FixApp::onMessage(const FIX44::MarketDataSnapshotFullRefresh& m, const FIX::SessionID& sessionID) {
@@ -120,17 +123,16 @@ void FixApp::onMessage(const FIX44::MarketDataIncrementalRefresh& m, const FIX::
120123

121124
// PUBLIC
122125

123-
FixApp::FixApp(std::string apiKey, std::string privatePemPath) {
124-
apiKey_ = std::move(apiKey);
125-
privatePemPath_ = std::move(privatePemPath);
126-
126+
FixApp::FixApp(const std::string& apiKey, const std::string& privatePemPath, const std::vector<std::string>& instruments) :
127+
// TODO: should I use references here?
128+
apiKey_(apiKey), privatePemPath_(privatePemPath), symbols_(instruments)
129+
{
127130
if (sodium_init() < 0)
128131
throw std::runtime_error("libsodium failed to initialize");
129132
}
130133

131134
void FixApp::subscribeToDepth(const FIX::SessionID& sessionId) {
132-
std::cout << std::format("Subscribe to depth") << std::endl;
133-
135+
spdlog::debug(std::format("Subscribe to depth"));
134136
FIX44::MarketDataRequest marketDataRequest;
135137

136138
// Generate a unique request ID for this session's request
@@ -142,7 +144,8 @@ void FixApp::subscribeToDepth(const FIX::SessionID& sessionId) {
142144
FIX::SubscriptionRequestType_SNAPSHOT_PLUS_UPDATES));
143145

144146
// Set market depth
145-
marketDataRequest.set(FIX::MarketDepth(15)); // 1 = Top of book
147+
constexpr int BINANCE_MAX_DEPTH = 5000;
148+
marketDataRequest.set(FIX::MarketDepth(BINANCE_MAX_DEPTH));
146149

147150
// Create NoMDEntryTypes group for requesting BID and OFFER
148151
FIX44::MarketDataRequest::NoMDEntryTypes entryTypeGroup;
@@ -153,10 +156,12 @@ void FixApp::subscribeToDepth(const FIX::SessionID& sessionId) {
153156
entryTypeGroup.set(FIX::MDEntryType(FIX::MDEntryType_OFFER));
154157
marketDataRequest.addGroup(entryTypeGroup);
155158

156-
// Add symbol
157-
FIX44::MarketDataRequest::NoRelatedSym symbolGroup;
158-
symbolGroup.set(FIX::Symbol("BTCUSDT"));
159-
marketDataRequest.addGroup(symbolGroup);
159+
// Add symbols
160+
for (const auto& instrument : symbols_) {
161+
FIX44::MarketDataRequest::NoRelatedSym symbolGroup;
162+
symbolGroup.set(FIX::Symbol(instrument));
163+
marketDataRequest.addGroup(symbolGroup);
164+
}
160165

161166
// Send the request to the corresponding market data session
162167
FIX::Session::sendToTarget(marketDataRequest, sessionId);

src/binance/FixApp.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef BINANCEFIXAPP_H
22
#define BINANCEFIXAPP_H
33

4+
#include <string>
5+
#include <vector>
46
#include <quickfix/Application.h>
57
#include <quickfix/SessionID.h>
68
#include <quickfix/fix44/Message.h>
@@ -12,18 +14,20 @@ namespace Binance {
1214
/// @brief Binance FIX App - Manages FIX connectivity to Binance
1315
class FixApp final : public FIX::Application, public FIX::MessageCracker {
1416
public:
15-
FixApp(std::string apiKey, std::string privatePemPath);
17+
FixApp(const std::string& apiKey, const std::string& privatePemPath, const std::vector<std::string>& symbols);
1618
~FixApp() override = default;
1719
void subscribeToDepth(const FIX::SessionID& sessionId);
18-
20+
/// @brief queue of market messages from Binance
21+
moodycamel::ConcurrentQueue<std::shared_ptr<FIX44::Message>> queue;
1922
// TODO: performance implication of a polymorphic queue
2023
// TODO: perhaps better to run two queues, or something else
2124
// TODO: we will have one of these per instrument
22-
moodycamel::ConcurrentQueue<std::shared_ptr<FIX44::Message>> queue;
2325

2426
private:
2527
// TODO: no need to persist access tokens for lifetime of app
26-
std::string apiKey_, privatePemPath_;
28+
const std::string& apiKey_;
29+
const std::string& privatePemPath_;
30+
const std::vector<std::string>& symbols_;
2731

2832
void onCreate(const FIX::SessionID&) override;
2933
void onLogon(const FIX::SessionID&) override;

src/binance/Init.cpp

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "Init.h"
99
#include "FixApp.h"
1010
#include "Config.h"
11+
#include "spdlog/spdlog.h"
1112

1213
namespace Binance {
1314

@@ -23,7 +24,7 @@ Init::Init(std::unique_ptr<FixApp> fixApp,
2324

2425
// static member function
2526
Init Init::fromConf(Config& conf) {
26-
auto app = std::make_unique<FixApp>(conf.apiKey, conf.privateKeyPath);
27+
auto app = std::make_unique<FixApp>(conf.apiKey, conf.privateKeyPath, conf.symbols);
2728
auto settings = std::make_unique<FIX::SessionSettings>(conf.fixConfigPath);
2829
auto storeFactory = std::make_unique<FIX::FileStoreFactory>(*settings);
2930
auto logFactory = std::make_unique<FIX::FileLogFactory>(*settings);
@@ -39,20 +40,20 @@ Init Init::fromConf(Config& conf) {
3940

4041
void Init::start() {
4142
initiator_.start();
42-
std::cout << "started FIX session" << std::endl;
43+
spdlog::info("started FIX session");
4344
}
4445

4546
void Init::stop() {
4647
try {
4748
initiator_.stop(); // TODO: does this need a try/catch?
48-
std::cout << "stopped FIX session" << std::endl;
49+
spdlog::info("stopped FIX session");
4950
} catch (...) {
50-
std::cout << "error closing binance FIX session" << std::endl;
51+
// TODO: log error
52+
spdlog::error("error closing binance FIX session");
5153
}
5254
}
5355

54-
Init::~Init()
55-
{
56+
Init::~Init() {
5657
initiator_.stop();
5758
}
5859

src/binance/Init.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ class Init final {
1818
std::unique_ptr<FIX::SessionSettings> settings,
1919
std::unique_ptr<FIX::FileLogFactory> fileLogFactory);
2020
~Init();
21+
/// @brief generate concrete Binance instance from config
22+
/// @param conf binance configuration parameters
23+
/// @return
2124
static Init fromConf(Config& conf);
2225
void start();
2326
void stop();
2427
std::unique_ptr<FixApp> app;
2528

2629
private:
27-
// TODO: rename
2830
std::unique_ptr<FIX::FileStoreFactory> store_;
2931
std::unique_ptr<FIX::SessionSettings> settings_;
3032
std::unique_ptr<FIX::FileLogFactory> log_;

0 commit comments

Comments
 (0)