Skip to content

Commit d6b7003

Browse files
committed
feat: first working fix8 compilation
1 parent 99e485e commit d6b7003

17 files changed

Lines changed: 1900 additions & 42 deletions

CMakeLists.txt

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,18 @@ set(CMAKE_CXX_STANDARD_REQUIRED YES)
88
# === App Source Files ==========================
99

1010
# Collect source files
11-
file(GLOB_RECURSE ALL_SOURCES "src/*.cpp")
11+
file(GLOB_RECURSE APP_SOURCES "src/*.cpp")
1212
# Separate main.cpp from the rest of the sources
13-
list(FILTER ALL_SOURCES EXCLUDE REGEX ".*main\\.cpp$")
14-
set(SOURCES ${ALL_SOURCES})
13+
list(FILTER APP_SOURCES EXCLUDE REGEX ".*main\\.cpp$")
14+
15+
# Third-party sources
16+
set(THIRD_PARTY_SOURCES
17+
third_party/fix8/fix8_classes.cpp
18+
third_party/fix8/fix8_traits.cpp
19+
third_party/fix8/fix8_types.cpp
20+
)
21+
22+
set(SOURCES ${APP_SOURCES} ${THIRD_PARTY_SOURCES})
1523

1624

1725
# === Create core logic library =================
@@ -20,6 +28,18 @@ set(SOURCES ${ALL_SOURCES})
2028
# Link dependencies to the *library* (so both exe and tests get them)
2129
add_library(traderlib STATIC ${SOURCES})
2230

31+
# Treat third_party headers as SYSTEM to suppress header warnings
32+
target_include_directories(traderlib SYSTEM PUBLIC
33+
${CMAKE_CURRENT_SOURCE_DIR}/third_party/fix8
34+
)
35+
36+
# Silence all warnings and disable warnings-as-errors for third_party sources only
37+
set_source_files_properties(
38+
${THIRD_PARTY_SOURCES}
39+
PROPERTIES
40+
COMPILE_OPTIONS "-w;-Wno-error"
41+
)
42+
2343

2444
# === Conan Setup ===============================
2545

CMakePresets.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
"CMAKE_BUILD_TYPE": "Debug",
1616
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
1717
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/build/Debug/generators/conan_toolchain.cmake",
18-
"CMAKE_CXX_FLAGS": "-Wall -Wextra -Werror -DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG -g -O0 -fprofile-arcs -ftest-coverage -fprofile-update=atomic -fsanitize=address -fno-omit-frame-pointer",
18+
"CMAKE_CXX_FLAGS": "-Wall -Wextra -Werror -Wswitch-enum -DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_DEBUG -g -O0 -fprofile-arcs -ftest-coverage -fprofile-update=atomic -fsanitize=address -fno-omit-frame-pointer",
1919
"CMAKE_EXE_LINKER_FLAGS": "-fsanitize=address --coverage",
2020
"CMAKE_SHARED_LINKER_FLAGS": "-fsanitize=address --coverage"
2121
}
@@ -29,7 +29,7 @@
2929
"CMAKE_BUILD_TYPE": "Release",
3030
"CMAKE_EXPORT_COMPILE_COMMANDS": "ON",
3131
"CMAKE_TOOLCHAIN_FILE": "${sourceDir}/build/Release/generators/conan_toolchain.cmake",
32-
"CMAKE_CXX_FLAGS": "-Wall -Wextra -Werror -DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO"
32+
"CMAKE_CXX_FLAGS": "-Wall -Wextra -Werror -Wswitch-enum -DSPDLOG_ACTIVE_LEVEL=SPDLOG_LEVEL_INFO"
3333
}
3434
}
3535
],

README.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,16 +150,17 @@ NB: this app uses `make` as a recipe book, but it's not essential:
150150
- profile-guided optimization (pgo)
151151
- load test with mocked FIX server
152152
- set process priority
153-
- NIC affinity
154-
- QoS
155153
- sparse arrays & flat matrix
156154
- memory-mapped files
157-
- (analyse) find Binance's server location for a low-latency connection
158155
- (analyse) how to quantify latency?
159156
- FIX SSL connectivity, to avoid stunnel latency overhead
160157
- QuickFIX alternative (Fix8)
161158
- kernel space vs user space
162159
- RT OS
160+
- networking:
161+
- ✅ traffic QoS
162+
- (analyse) find Binance's server location for a low-latency connection
163+
- dedicated NIC queues
163164
- ✅ logging
164165
- ✅ fast
165166
- error handling

src/binance/fix_app.cpp

Lines changed: 35 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -118,47 +118,52 @@ static std::string replace_soh(const std::string& input) {
118118
return output;
119119
}
120120

121-
void FixApp::onCreate(const FIX::SessionID& sessionId) {
121+
void FixApp::onCreate(const FIX::SessionID& session_id) {
122122
spdlog::info("session created. qualifier [{}], id [{}]",
123-
sessionId.getSessionQualifier(), sessionId.toString());
123+
session_id.getSessionQualifier(), session_id.toString());
124124
};
125-
void FixApp::onLogon(const FIX::SessionID& sessionId) {
126-
spdlog::info("Session logon, qualifier [{}], id [{}]", sessionId.getSessionQualifier(),
127-
sessionId.toString());
125+
void FixApp::onLogon(const FIX::SessionID& session_id) {
126+
spdlog::info("Session logon, qualifier [{}], id [{}]", session_id.getSessionQualifier(),
127+
session_id.toString());
128128
// logon successful, nullify access keys
129129
// TODO: now need to wait for all sessions to be logged on before nullifying keys
130-
// auth_->clear_keys();
130+
auth_->clear_keys();
131131

132-
std::string thread_name = THREAD_NAME_ + "_" + sessionId.getSessionQualifier();
132+
// sessions run on their own threads when using @ref FIX::ThreadedSocketInitiator .
133+
// set thread names
134+
std::string thread_name = THREAD_NAME_ + "_" + session_id.getSessionQualifier();
133135
utils::Threading::set_thread_name(thread_name);
134136
spdlog::info("naming FIX session thread, name [{}], id [{}]", thread_name,
135137
utils::Threading::get_os_thread_id());
136138

137-
if (sessionId.getSessionQualifier() == PX_SESSION_QUALIFIER_) {
139+
// FIX::Session* session = FIX::Session::lookupSession(session_id);
140+
141+
//
142+
if (session_id.getSessionQualifier() == PX_SESSION_QUALIFIER_) {
138143
utils::Threading::set_current_thread_affinity(PX_SESSION_CPU_AFFINITY_);
139-
subscribe_to_prices(sessionId);
140-
} else if (sessionId.getSessionQualifier() == TX_SESSION_QUALIFIER_) {
144+
subscribe_to_prices(session_id);
145+
} else if (session_id.getSessionQualifier() == TX_SESSION_QUALIFIER_) {
141146
utils::Threading::set_current_thread_affinity(TX_SESSION_CPU_AFFINITY_);
142-
subscribe_to_trades(sessionId);
143-
} else if (sessionId.getSessionQualifier() == OX_SESSION_QUALIFIER_) {
147+
subscribe_to_trades(session_id);
148+
} else if (session_id.getSessionQualifier() == OX_SESSION_QUALIFIER_) {
144149
// do nothing for order session
145150
} else {
146151
spdlog::error("unknown session, qualifier [{}], id [{}]",
147-
sessionId.getSessionQualifier(), sessionId.toString());
152+
session_id.getSessionQualifier(), session_id.toString());
148153
}
149154
};
150-
void FixApp::onLogout(const FIX::SessionID& sessionId) {
151-
spdlog::info("session logout. qualifier [{}], id [{}]", sessionId.getSessionQualifier(),
152-
sessionId.toString());
155+
void FixApp::onLogout(const FIX::SessionID& session_id) {
156+
spdlog::info("session logout. qualifier [{}], id [{}]",
157+
session_id.getSessionQualifier(), session_id.toString());
153158
};
154159

155-
void FixApp::toAdmin(FIX::Message& msg, const FIX::SessionID& sessionId) {
160+
void FixApp::toAdmin(FIX::Message& msg, const FIX::SessionID& session_id) {
156161
const FIX::Header& header = msg.getHeader();
157162
FIX::MsgType msg_type;
158163
header.getField(msg_type);
159164
if (msg_type.getString() == static_cast<const char*>(FIX::MsgType_Logon)) {
160165
spdlog::info("authenticating. session qualifier [{}], session id [{}]",
161-
sessionId.getSessionQualifier(), sessionId.toString());
166+
session_id.getSessionQualifier(), session_id.toString());
162167

163168
// collect required fields
164169
const std::string sender = header.getField(FIX::FIELD::SenderCompID);
@@ -184,48 +189,48 @@ void FixApp::toAdmin(FIX::Message& msg, const FIX::SessionID& sessionId) {
184189
} else {
185190
spdlog::info(
186191
"toAdmin. session qualifier [{}], session id [{}], type [{}], message [{}]",
187-
sessionId.getSessionQualifier(), sessionId.toString(), msg_type.getString(),
192+
session_id.getSessionQualifier(), session_id.toString(), msg_type.getString(),
188193
replace_soh(msg.toString()));
189194
}
190195
};
191-
void FixApp::toApp(FIX::Message& msg, const FIX::SessionID& sessionId) noexcept(false) {
196+
void FixApp::toApp(FIX::Message& msg, const FIX::SessionID& session_id) noexcept(false) {
192197
const FIX::Header& header = msg.getHeader();
193198
FIX::MsgType msg_type;
194199
header.getField(msg_type);
195200
spdlog::info("toApp. session qualifier [{}], session id [{}], type [{}], message [{}]",
196-
sessionId.getSessionQualifier(), sessionId.toString(),
201+
session_id.getSessionQualifier(), session_id.toString(),
197202
msg_type.getString(), replace_soh(msg.toString()));
198203
};
199204

200205
void FixApp::fromAdmin(const FIX::Message& msg,
201-
const FIX::SessionID& sessionId) noexcept(false) {
206+
const FIX::SessionID& session_id) noexcept(false) {
202207
const FIX::Header& header = msg.getHeader();
203208
FIX::MsgType msg_type;
204209
header.getField(msg_type);
205210
spdlog::info(
206211
"fromAdmin. session qualifier [{}], session id [{}], type [{}], message [{}]",
207-
sessionId.getSessionQualifier(), sessionId.toString(), msg_type.getString(),
212+
session_id.getSessionQualifier(), session_id.toString(), msg_type.getString(),
208213
replace_soh(msg.toString()));
209214
};
210215
void FixApp::fromApp(const FIX::Message& msg,
211-
const FIX::SessionID& sessionId) noexcept(false) {
212-
FIX44::MessageCracker::crack(msg, sessionId);
216+
const FIX::SessionID& session_id) noexcept(false) {
217+
FIX44::MessageCracker::crack(msg, session_id);
213218
}
214219

215220
void FixApp::onMessage(const FIX44::MarketDataSnapshotFullRefresh& m,
216-
[[maybe_unused]] const FIX::SessionID& sessionID) {
221+
[[maybe_unused]] const FIX::SessionID& session_id) {
217222
order_queue_.enqueue(std::make_shared<const FIX44::MarketDataSnapshotFullRefresh>(m));
218223
}
219224
void FixApp::onMessage(const FIX44::MarketDataIncrementalRefresh& m,
220-
const FIX::SessionID& sessionID) {
221-
if (sessionID.getSessionQualifier() == PX_SESSION_QUALIFIER_) {
225+
const FIX::SessionID& session_id) {
226+
if (session_id.getSessionQualifier() == PX_SESSION_QUALIFIER_) {
222227
order_queue_.enqueue(std::make_shared<const FIX44::MarketDataIncrementalRefresh>(m));
223-
} else if (sessionID.getSessionQualifier() == TX_SESSION_QUALIFIER_) {
228+
} else if (session_id.getSessionQualifier() == TX_SESSION_QUALIFIER_) {
224229
trade_queue_.enqueue(std::make_shared<const FIX44::MarketDataIncrementalRefresh>(m));
225230
} else {
226231
spdlog::error(
227232
"ivalid session for market data incremental refresh, qualifier [{}], id [{}]",
228-
sessionID.getSessionQualifier(), sessionID.toString());
233+
session_id.getSessionQualifier(), session_id.toString());
229234
}
230235
}
231236
void FixApp::onMessage([[maybe_unused]] const FIX44::ExecutionReport& message,

src/f8/includes.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#pragma once
2+
3+
// fix8 includes need fixed ordering (headers only)
4+
// clang-format off
5+
#include <cstring>
6+
#include <fix8/f8includes.hpp>
7+
#include "fix8_router.hpp"
8+
#include "fix8_types.hpp"
9+
#include "fix8_classes.hpp"
10+
#include "fix8_session.hpp"
11+
// clang-format on

src/f8/router.h

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
#pragma once
2+
3+
#include <fix8_router.hpp>
4+
#include <functional>
5+
6+
#include "includes.h"
7+
#include "session.h"
8+
9+
namespace f8 {
10+
11+
class Session;
12+
13+
class Router : public FIX8::binance::fix8_Router {
14+
[[maybe_unused]] Session& _session;
15+
16+
public:
17+
explicit Router(Session& session) : _session(session) {}
18+
~Router() override = default;
19+
20+
// Override these methods to receive specific message callbacks.
21+
// bool operator() (const FIX8::binance::Heartbeat *msg) const;
22+
// bool operator() (const FIX8::binance::TestRequest *msg) const;
23+
// bool operator() (const FIX8::binance::Reject *msg) const;
24+
// bool operator() (const FIX8::binance::Logout *msg) const;
25+
// bool operator() (const FIX8::binance::Logon *msg) const;
26+
// bool operator() (const FIX8::binance::News *msg) const;
27+
// bool operator() (const FIX8::binance::MarketDataRequest *msg) const;
28+
// bool operator() (const FIX8::binance::MarketDataSnapshot *msg) const;
29+
// bool operator() (const FIX8::binance::MarketDataIncrementalRefresh *msg) const;
30+
// bool operator() (const FIX8::binance::LimitQuery *msg) const;
31+
// bool operator() (const FIX8::binance::LimitResponse *msg) const;
32+
// bool operator() (const FIX8::binance::MarketDataRequestReject *msg) const;
33+
// bool operator() (const FIX8::binance::InstrumentListRequest *msg) const;
34+
// bool operator() (const FIX8::binance::InstrumentList *msg) const;
35+
};
36+
37+
} // namespace f8

src/f8/session.cpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#include "session.h"
2+
3+
#include <functional>
4+
#include <map>
5+
6+
namespace f8 {
7+
8+
bool Session::handle_application(const unsigned /*seqnum*/,
9+
const FIX8::Message*& /*msg*/) {
10+
// return enforce(seqnum, msg) || msg->process(_router);
11+
return true;
12+
}
13+
14+
} // namespace f8

src/f8/session.h

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#pragma once
2+
3+
#include <functional>
4+
#include <map>
5+
6+
#include "includes.h"
7+
#include "router.h"
8+
9+
namespace f8 {
10+
11+
class Session : public FIX8::Session {
12+
Router _router;
13+
14+
public:
15+
Session(const FIX8::F8MetaCntx& ctx,
16+
const FIX8::SessionID& sid,
17+
FIX8::Persister* persist = nullptr,
18+
FIX8::Logger* logger = nullptr,
19+
FIX8::Logger* plogger = nullptr)
20+
: FIX8::Session(ctx, sid, persist, logger, plogger), _router(*this) {}
21+
22+
// Override these methods if required but remember to call the base class method first.
23+
// bool handle_logon(const unsigned seqnum, const FIX8::Message *msg);
24+
// Message *generate_logon(const unsigned heartbeat_interval, const f8String
25+
// davi=f8String()); bool handle_logout(const unsigned seqnum, const FIX8::Message
26+
// *msg); Message *generate_logout(); bool handle_heartbeat(const unsigned seqnum, const
27+
// FIX8::Message *msg); Message *generate_heartbeat(const f8String& testReqID); bool
28+
// handle_resend_request(const unsigned seqnum, const FIX8::Message *msg); Message
29+
// *generate_resend_request(const unsigned begin, const unsigned end=0); bool
30+
// handle_sequence_reset(const unsigned seqnum, const FIX8::Message *msg); Message
31+
// *generate_sequence_reset(const unsigned newseqnum, const bool gapfillflag=false);
32+
// bool handle_test_request(const unsigned seqnum, const FIX8::Message *msg);
33+
// Message *generate_test_request(const f8String& testReqID);
34+
// bool handle_reject(const unsigned seqnum, const FIX8::Message *msg);
35+
// Message *generate_reject(const unsigned seqnum, const char *what);
36+
// bool handle_admin(const unsigned seqnum, const FIX8::Message *msg);
37+
// void modify_outbound(FIX8::Message *msg);
38+
// bool authenticate(SessionID& id, const FIX8::Message *msg);
39+
40+
// Override these methods to intercept admin and application methods.
41+
// bool handle_admin(const unsigned seqnum, const FIX8::Message *msg);
42+
43+
bool handle_application(const unsigned seqnum, const FIX8::Message*& msg) override;
44+
};
45+
46+
} // namespace f8

src/main.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
#include <csignal>
22
#include <cstdlib>
33
#include <exception>
4-
#include <iostream>
5-
#include <new>
64
#include <string>
75

86
#include "binance/config.h"

src/ui/log_box/log_file_watcher.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,9 @@ class LogFileWatcher : public ILogWatcher, public efsw::FileWatchListener {
6767
}
6868
cb_(std::move(lines));
6969
} break;
70-
// case efsw::Actions::Delete: {
71-
// } break;
70+
case efsw::Actions::Add:
71+
case efsw::Actions::Delete:
72+
case efsw::Actions::Moved:
7273
default:
7374
break;
7475
}

0 commit comments

Comments
 (0)