diff --git a/.github/workflows/cmake-single-platform.yml b/.github/workflows/cmake-single-platform.yml new file mode 100644 index 0000000..fc7a51c --- /dev/null +++ b/.github/workflows/cmake-single-platform.yml @@ -0,0 +1,34 @@ +name: CMake on a single platform + +on: + push: + branches: [ "master" ] + pull_request: + branches: [ "master" ] + +env: + # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) + BUILD_TYPE: Release + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Install dependencies + run: sudo apt-get update && sudo apt-get install -y libboost-all-dev libapr1-dev libaprutil1-dev libtinyxml2-dev libsqlite3-dev libasio-dev + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} + + - name: Build + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + + - name: Test + working-directory: ${{github.workspace}}/build + run: ctest -C ${{env.BUILD_TYPE}} + + - name: ✅ Build Success + run: echo "🎉 Build completed successfully!" diff --git a/CMakeLists.txt b/CMakeLists.txt index f7923b9..f8e46e5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,57 +1,48 @@ -cmake_minimum_required(VERSION 3.12.4) - -if(NOT CMAKE_VERSION VERSION_LESS 3.0) - cmake_policy(SET CMP0048 NEW) -endif() +cmake_minimum_required(VERSION 3.28.3) project(DistributedATS) -#set(CMAKE_SUPPRESS_REGENERATION true) - -# Find requirements -if(NOT fastcdr_FOUND) - find_package(fastcdr REQUIRED) -endif() -if(NOT fastdds_FOUND) - find_package(fastdds 3 REQUIRED) -endif() +set(CMAKE_PREFIX_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake) set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) set(CMAKE_CXX_EXTENSIONS OFF) -FIND_PACKAGE( Boost 1.40 COMPONENTS program_options REQUIRED ) -INCLUDE_DIRECTORIES( ${Boost_INCLUDE_DIR} ) -link_directories(${Boost_LIBRARY_DIRS}) - -link_directories(${QUICKFIX_INSTALL_PREFIX}/lib) - -include_directories(Common) -add_subdirectory(GenTools/idl) - -include_directories(GenTools/idl) -add_subdirectory(FIXGateway/src) -add_subdirectory(DataService/src) -add_subdirectory(MatchingEngine/src) -add_subdirectory(LatencyTest) - -set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "-o linker-signed") - -install(DIRECTORY MiscATS DESTINATION ${CMAKE_INSTALL_PREFIX}) -install(CODE " - file(GLOB_RECURSE SCRIPT_FILES - \"\${CMAKE_INSTALL_PREFIX}/MiscATS/*.sh\") - foreach(script \${SCRIPT_FILES}) - execute_process(COMMAND chmod +x \${script}) - endforeach() -") -install(FILES FIXGateway/scripts/fixgateway.sh - DataService/scripts/dataservice.sh - MatchingEngine/scripts/matchingengine.sh +find_package( Boost ) +find_package( DDS REQUIRED ) +find_package( log4cxx REQUIRED ) +find_package( quickfix REQUIRED ) +find_package( liquibook REQUIRED ) + +set(DDS_INCLUDE_DIR "${DDS_INSTALL_PREFIX}/include") +set(DDS_LIBRARY_DIR "${DDS_INSTALL_PREFIX}/lib") +set(QUICKFIX_INCLUDE_DIR "${QUICKFIX_INSTALL_PREFIX}/include") +set(QUICKFIX_LIBRARY_DIR "${QUICKFIX_INSTALL_PREFIX}/lib") +set(LOG4CXX_INCLUDE_DIR "${LOG4CXX_INSTALL_PREFIX}/include") +set(LOG4CXX_LIBRARY_DIR "${LOG4CXX_INSTALL_PREFIX}/lib") + +set(LIQUIBOOK_INCLUDE_DIR "${LIQUIBOOK_INSTALL_PREFIX}/include") + +include_directories( Common ) +add_subdirectory( GenTools/idl ) + +include_directories( GenTools/idl ) +add_subdirectory( FIXGateway/src ) +add_subdirectory( DataService/src ) +add_subdirectory( MatchingEngine/src ) +add_subdirectory( LatencyTest ) + +install(DIRECTORY ${CMAKE_SOURCE_DIR}/MiscATS/ + DESTINATION MiscATS + FILES_MATCHING PATTERN "*.db" PATTERN "*.py" PATTERN "*.ini" PATTERN "*.cfg" PATTERN "*.json") + +install(FILES FIXGateway/scripts/fix_gateway_manager.py + DataService/scripts/data_service_manager.py + MatchingEngine/scripts/matching_engine_manager.py DESTINATION scripts PERMISSIONS OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE WORLD_READ WORLD_EXECUTE) -install(FILES config/log4cxx.xml DESTINATION config) -install(FILES FIXGateway/spec/FIX44.xml DESTINATION spec) +install( FILES config/log4cxx.xml DESTINATION config ) +install( FILES FIXGateway/spec/FIX44.xml DESTINATION spec ) diff --git a/DataService/scripts/data_service_manager.py b/DataService/scripts/data_service_manager.py new file mode 100644 index 0000000..3ea0565 --- /dev/null +++ b/DataService/scripts/data_service_manager.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 + +import os +import sys +import time +import subprocess +from pathlib import Path +import psutil + +PROGNAME = "DataService" + + +def get_config_file(): + if len(sys.argv) < 3: + print(f"Usage: {sys.argv[0]} [start|stop|check] ") + sys.exit(1) + return sys.argv[2] + + +def get_paths(config_file): + basedir = os.environ.get("BASEDIR_ATS", ".") + dats_home = os.environ.get("DATS_HOME") + if not dats_home: + print("DATS_HOME is not set.") + sys.exit(1) + + config_path = Path(basedir) / "config" / config_file + log_path = Path(basedir) / "logs" / f"{PROGNAME}.{config_file}.console.log" + binary_path = Path(dats_home) / "bin" / PROGNAME + return binary_path, config_path, log_path, basedir + + +def source_dats_env(): + dats_home = os.environ.get("DATS_HOME") + env_script = Path(dats_home) / "dats_env.sh" + if env_script.exists(): + subprocess.call(f"source {env_script}", shell=True, executable="/bin/bash") + + +def check_process(config_path): + for proc in psutil.process_iter(['pid', 'cmdline']): + try: + cmdline = proc.info['cmdline'] + if cmdline and PROGNAME in cmdline[0] and str(config_path) in ' '.join(cmdline): + print(f"{PROGNAME} [{config_path.name}] is running - {proc.pid}") + return proc.pid + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): + continue + print(f"{PROGNAME} [{config_path.name}] is not running") + return None + + +def start_process(binary_path, config_path, log_path): + print(f"Starting: {PROGNAME} -c {config_path}") + for key in sorted(os.environ): + print(f"{key}={os.environ[key]}") + if check_process(config_path) is None: + with open(log_path, "a") as f: + subprocess.Popen([str(binary_path), "-c", str(config_path)], + stdout=f, stderr=subprocess.STDOUT) + time.sleep(1) + check_process(config_path) + + +def stop_process(config_path): + cmd_str = f"{PROGNAME} -c {config_path}" + print(f"Stopping: {cmd_str}") + pid = check_process(config_path) + if pid: + os.system(f"pkill -SIGTERM -f \"{cmd_str}\"") + time.sleep(1) + + for _ in range(10): + if check_process(config_path) is None: + break + time.sleep(1) + else: + os.system(f"pkill -KILL -U {os.getuid()} -f \"{cmd_str}\"") + + +def main(): + if len(sys.argv) < 3: + print(f"Usage: {sys.argv[0]} [start|stop|check] ") + sys.exit(1) + + command = sys.argv[1].lower() + config_file = get_config_file() + + binary_path, config_path, log_path, basedir = get_paths(config_file) + source_dats_env() + + if command == "start": + start_process(binary_path, config_path, log_path) + elif command == "stop": + stop_process(config_path) + elif command == "check": + check_process(config_path) + else: + print("Unknown command") + sys.exit(1) + + with open(log_path, "a") as f: + f.write(f"{time.strftime('%Y%m%d.%H%M%S')} run-done : pid,{os.getpid()}\n") + + +if __name__ == "__main__": + main() diff --git a/DataService/src/CMakeLists.txt b/DataService/src/CMakeLists.txt index a6e8953..bc730d0 100644 --- a/DataService/src/CMakeLists.txt +++ b/DataService/src/CMakeLists.txt @@ -1,36 +1,45 @@ cmake_minimum_required(VERSION 3.12.4) -if(NOT CMAKE_VERSION VERSION_LESS 3.0) - cmake_policy(SET CMP0048 NEW) -endif() +message(STATUS "Current source dir: ${CMAKE_CURRENT_SOURCE_DIR}") +message(DDS_INCLUDE_DIR="${DDS_INCLUDE_DIR}") +message(DDS_LIBRARY_DIR="${DDS_LIBRARY_DIR}") -project(DataService) +message(QUICKFIX_INCLUDE_DIR="${QUICKFIX_INCLUDE_DIR}") +message(QUICKFIX_LIBRARY_DIR="${QUICKFIX_LIBRARY_DIR}") -# Find requirements -if(NOT fastcdr_FOUND) - find_package(fastcdr REQUIRED) -endif() +include_directories(${DDS_INCLUDE_DIR}) +link_directories(${DDS_LIBRARY_DIR}) -if(NOT fastdds_FOUND) - find_package(fastdds 3 REQUIRED) -endif() +include_directories(${QUICKFIX_INCLUDE_DIR}) +link_directories(${QUICKFIX_LIBRARY_DIR}) -find_package(log4cxx REQUIRED) +include_directories(${LOG4CXX_INCLUDE_DIR}) +link_directories(${LOG4CXX_LIBRARY_DIR}) -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) +include_directories(${Boost_INCLUDE_DIR}) +link_directories(${Boost_LIBRARY_DIRS}) -link_directories(${CMAKE_INSTALL_PREFIX}/lib) +file(GLOB DISTRIBUTED_ATS_DATASERVICE_SRC *.cpp) -file(GLOB DISTRIBUTED_ATS_DATASERVICE_SRC *) add_executable(DataService ${DISTRIBUTED_ATS_DATASERVICE_SRC}) -link_directories(DistributedATSLib quickfix log4cxx) -target_link_libraries(DataService DistributedATSLib quickfix log4cxx boost_program_options sqlite3) + +find_package(Boost REQUIRED COMPONENTS program_options) +include_directories(${Boost_INCLUDE_DIRS}) + +target_link_libraries(DataService + PRIVATE + DistributedATSLib + quickfix + log4cxx + Boost::program_options + fastcdr + fastdds + sqlite3 +) set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "-o linker-signed") -install(TARGETS DataService +install(TARGETS DataService PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dats ) diff --git a/DataService/src/MarketDataService.cpp b/DataService/src/MarketDataService.cpp index 6e19962..6a7f381 100644 --- a/DataService/src/MarketDataService.cpp +++ b/DataService/src/MarketDataService.cpp @@ -209,6 +209,8 @@ int MarketDataService::service (void) std::this_thread::sleep_for(std::chrono::duration(1000)); } }; + + return 0; } bool MarketDataService::populateMarketDataSnapshotFullRefresh( const Instrument& instrument, diff --git a/DataService/src/RefDataService.cpp b/DataService/src/RefDataService.cpp index e85fc3b..02fecf1 100644 --- a/DataService/src/RefDataService.cpp +++ b/DataService/src/RefDataService.cpp @@ -94,7 +94,8 @@ void RefDataService::populateUserGroupInstrumentMap() " m.market_name=im_map.market_name and " \ " im_map.market_name=ugm_map.market_name"); - LOG4CXX_INFO(logger, "Populating security list"); + LOG4CXX_INFO(logger, "Populating security list : "); + LOG4CXX_INFO(logger, "Query : " + sqliteQuery.getQuery()); m_sqliteConnection->execute(sqliteQuery); diff --git a/DataService/src/SQLiteConnection.hpp b/DataService/src/SQLiteConnection.hpp index 8359dbf..32a1ef1 100644 --- a/DataService/src/SQLiteConnection.hpp +++ b/DataService/src/SQLiteConnection.hpp @@ -42,8 +42,6 @@ class SQLiteQuery public: SQLiteQuery( const std::string& query ) : m_stmt(0), m_query( query ) { - - std::cout << query << std::endl; } ~SQLiteQuery() @@ -95,6 +93,11 @@ class SQLiteQuery { return m_rows[row][column]; } + + std::string getQuery() + { + return m_query; + } private: sqlite3_stmt* m_stmt; @@ -132,7 +135,7 @@ class SQLiteConnection private: void connect() { - int rc = sqlite3_open(m_pDatabase.getDatabase().c_str(), &m_pConnection); + int rc = sqlite3_open_v2(m_pDatabase.getDatabase().c_str(), &m_pConnection, SQLITE_OPEN_READONLY, nullptr); if (rc != SQLITE_OK) { diff --git a/FIXGateway/scripts/fix_gateway_manager.py b/FIXGateway/scripts/fix_gateway_manager.py new file mode 100644 index 0000000..a137a7a --- /dev/null +++ b/FIXGateway/scripts/fix_gateway_manager.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python3 + +import os +import sys +import time +import subprocess +from pathlib import Path +import psutil + +PROGNAME = "FIXGateway" + + +def usage(): + print(f"Usage: {sys.argv[0]} [start|stop|check] ") + sys.exit(1) + + +def get_paths(config_file): + basedir = os.environ.get("BASEDIR_ATS", ".") + dats_home = os.environ.get("DATS_HOME") + if not dats_home: + print("DATS_HOME is not set.") + sys.exit(1) + + config_path = Path(basedir) / "config" / config_file + log_path = Path(basedir) / "logs" / f"{PROGNAME}.{config_file}.console.log" + binary_path = Path(dats_home) / "bin" / PROGNAME + return binary_path, config_path, log_path + + +def check_process(config_path): + for proc in psutil.process_iter(['pid', 'cmdline']): + try: + cmdline = proc.info['cmdline'] + if cmdline and PROGNAME in cmdline[0] and str(config_path) in ' '.join(cmdline): + print(f"{PROGNAME} [{config_path.name}] is running - {proc.pid}") + return proc.pid + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): + continue + print(f"{PROGNAME} [{config_path.name}] is not running") + return None + + +def start_process(binary_path, config_path, log_path): + process_str = f"{PROGNAME} -c {config_path}" + print(f"Starting: {process_str}") + for key in sorted(os.environ): + print(f"{key}={os.environ[key]}") + if check_process(config_path) is None: + with open(log_path, "a") as f: + subprocess.Popen([str(binary_path), "-c", str(config_path)], + stdout=f, stderr=subprocess.STDOUT) + time.sleep(1) + check_process(config_path) + + +def stop_process(config_path): + process_str = f"{PROGNAME} -c {config_path}" + print(f"Stopping: {process_str}") + pid = check_process(config_path) + if pid: + os.system(f"pkill -SIGTERM -f \"{process_str}\"") + time.sleep(1) + + # Try up to 10 seconds to let it exit + for _ in range(10): + if check_process(config_path) is None: + break + time.sleep(1) + else: + os.system(f"pkill -KILL -U {os.getuid()} -f \"{process_str}\"") + + +def main(): + if len(sys.argv) < 3: + usage() + + command = sys.argv[1].lower() + config_file = sys.argv[2] + + binary_path, config_path, log_path = get_paths(config_file) + + if command == "start": + start_process(binary_path, config_path, log_path) + elif command == "stop": + stop_process(config_path) + elif command == "check": + check_process(config_path) + else: + usage() + + # Final run log + with open(log_path, "a") as f: + f.write(f"{time.strftime('%Y%m%d.%H%M%S')} run-done : pid,{os.getpid()}\n") + + +if __name__ == "__main__": + main() diff --git a/FIXGateway/src/CMakeLists.txt b/FIXGateway/src/CMakeLists.txt index 7691bef..d49ee7f 100644 --- a/FIXGateway/src/CMakeLists.txt +++ b/FIXGateway/src/CMakeLists.txt @@ -1,42 +1,41 @@ cmake_minimum_required(VERSION 3.12.4) -if(NOT CMAKE_VERSION VERSION_LESS 3.0) - cmake_policy(SET CMP0048 NEW) -endif() - project(FIXGateway) -# Find requirements -if(NOT fastcdr_FOUND) - find_package(fastcdr REQUIRED) -endif() - -if(NOT fastdds_FOUND) - find_package(fastdds 3 REQUIRED) -endif() - -find_package(log4cxx REQUIRED) - -# Set C++11 -include(CheckCXXCompilerFlag) -if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_COMPILER_IS_CLANG OR - CMAKE_CXX_COMPILER_ID MATCHES "Clang") - check_cxx_compiler_flag(-std=c++11 SUPPORTS_CXX11 -g) - if(SUPPORTS_CXX11) - add_compile_options(-std=c++11 -g) - else() - message(FATAL_ERROR "Compiler doesn't support C++11") - endif() -endif() - -link_directories(${CMAKE_INSTALL_PREFIX}/lib) -link_directories(${CMAKE_QUICKFIX_PREFIX}/lib) -#link_directories(${Boost_LIBRARY_DIRS}) - -file(GLOB DISTRIBUTED_ATS_FIX_GATEWAY_SRC *) +message(STATUS "Current source dir: ${CMAKE_CURRENT_SOURCE_DIR}") +message(DDS_INCLUDE_DIR="${DDS_INCLUDE_DIR}") +message(DDS_LIBRARY_DIR="${DDS_LIBRARY_DIR}") + +message(QUICKFIX_INCLUDE_DIR="${QUICKFIX_INCLUDE_DIR}") +message(QUICKFIX_LIBRARY_DIR="${QUICKFIX_LIBRARY_DIR}") + +include_directories(${DDS_INCLUDE_DIR}) +link_directories(${DDS_LIBRARY_DIR}) + +include_directories(${QUICKFIX_INCLUDE_DIR}) +link_directories(${QUICKFIX_LIBRARY_DIR}) + +include_directories(${LOG4CXX_INCLUDE_DIR}) +link_directories(${LOG4CXX_LIBRARY_DIR}) + +include_directories(${Boost_INCLUDE_DIR}) +link_directories(${Boost_LIBRARY_DIRS}) + +file(GLOB DISTRIBUTED_ATS_FIX_GATEWAY_SRC *.cpp) add_executable(FIXGateway ${DISTRIBUTED_ATS_FIX_GATEWAY_SRC}) -link_directories(DistributedATSLib quickfix log4cxx) -target_link_libraries(FIXGateway DistributedATSLib quickfix log4cxx boost_program_options) + +find_package(Boost REQUIRED COMPONENTS program_options) +include_directories(${Boost_INCLUDE_DIRS}) + +target_link_libraries(FIXGateway + PRIVATE + DistributedATSLib + quickfix + log4cxx + Boost::program_options + fastcdr + fastdds +) set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "-o linker-signed") diff --git a/FIXGateway/src/FileLog.h b/FIXGateway/src/FileLog.h index 496ef63..b6eead6 100644 --- a/FIXGateway/src/FileLog.h +++ b/FIXGateway/src/FileLog.h @@ -85,11 +85,11 @@ class FileLog : public FIX::Log { void backup(); void onIncoming(const std::string &value) { - m_messages << FIX::UtcTimeStampConvertor::convert(FIX::UtcTimeStamp(), 9) + m_messages << FIX::UtcTimeStampConvertor::convert(FIX::UtcTimeStamp::now(), 9) << " : " << value << std::endl; } void onOutgoing(const std::string &value) { - m_messages << FIX::UtcTimeStampConvertor::convert(FIX::UtcTimeStamp(), 9) + m_messages << FIX::UtcTimeStampConvertor::convert(FIX::UtcTimeStamp::now(), 9) << " : " << value << std::endl; } void onEvent(const std::string &value) { diff --git a/FIXGateway/src/SocketAcceptor.cpp b/FIXGateway/src/SocketAcceptor.cpp index 90c4e42..a34b004 100644 --- a/FIXGateway/src/SocketAcceptor.cpp +++ b/FIXGateway/src/SocketAcceptor.cpp @@ -129,7 +129,7 @@ void SocketAcceptor::onStart() { m_pServer = 0; } -bool SocketAcceptor::onPoll(double timeout) { +bool SocketAcceptor::onPoll() { if (!m_pServer) return false; @@ -150,7 +150,7 @@ bool SocketAcceptor::onPoll(double timeout) { } std::cout << "Polling" << std::endl; - m_pServer->block(*this, true, timeout); + m_pServer->block(*this, true); return true; } diff --git a/FIXGateway/src/SocketAcceptor.h b/FIXGateway/src/SocketAcceptor.h index 6b56912..6cc0a09 100644 --- a/FIXGateway/src/SocketAcceptor.h +++ b/FIXGateway/src/SocketAcceptor.h @@ -62,15 +62,13 @@ class SocketAcceptor : public FIX::Acceptor, FIX::SocketServer::Strategy { typedef std::set Sessions; typedef std::map PortToSessions; typedef std::map SocketConnections; - // typedef std::map - // PendingLogonSocketConnection; - void onConfigure(const FIX::SessionSettings &) throw(FIX::ConfigError); - void onInitialize(const FIX::SessionSettings &) throw(FIX::RuntimeError); + void onConfigure(const FIX::SessionSettings &) EXCEPT(ConfigError); + void onInitialize(const FIX::SessionSettings &) EXCEPT(RuntimeError); - void onStart(); - bool onPoll(double timeout); - void onStop(); + void onStart() override; + bool onPoll() override; + void onStop() override; void onConnect(FIX::SocketServer &, int, int); void onWrite(FIX::SocketServer &, int); diff --git a/FIXGateway/src/SocketConnection.cpp b/FIXGateway/src/SocketConnection.cpp index 1ee3014..f032b53 100644 --- a/FIXGateway/src/SocketConnection.cpp +++ b/FIXGateway/src/SocketConnection.cpp @@ -242,6 +242,6 @@ void SocketConnection::readMessages(SocketMonitor &s) { void SocketConnection::onTimeout() { if (m_pSession) - m_pSession->next(); + m_pSession->next(UtcTimeStamp()); } } // namespace DistributedATS diff --git a/GenTools/CMakeLists.txt b/GenTools/CMakeLists.txt index f8b5d94..a579ffa 100644 --- a/GenTools/CMakeLists.txt +++ b/GenTools/CMakeLists.txt @@ -1,29 +1,19 @@ -cmake_minimum_required(VERSION 3.12.4) - -if(NOT CMAKE_VERSION VERSION_LESS 3.0) - cmake_policy(SET CMP0048 NEW) -endif() - project(DistributedATSLib) -# Find requirements -if(NOT fastcdr_FOUND) - find_package(fastcdr REQUIRED) -endif() +message(STATUS "Current source dir: ${CMAKE_CURRENT_SOURCE_DIR}") +message(DDS_INCLUDE_DIR="${DDS_INCLUDE_DIR}") +message(DDS_LIBRARY_DIR="${DDS_LIBRARY_DIR}") +message(QUICKFIX_INCLUDE_DIR="${QUICKFIX_INCLUDE_DIR}") +message(QUICKFIX_LIBRARY_DIR="${QUICKFIX_LIBRARY_DIR}") -if(NOT fastdds_FOUND) - find_package(fastdds 3 REQUIRED) -endif() +include_directories(${DDS_INCLUDE_DIR}) +include_directories(${QUICKFIX_INCLUDE_DIR}) +link_directories(${DDS_LIBRARY_DIR}) +link_directories(${QUICKFIX_LIBRARY_DIR}) -find_package(log4cxx REQUIRED) +file(GLOB DISTRIBUTED_ATS_LIB *) -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) +include_directories(${DDS_INCLUDE_DIR}) -link_directories(${CMAKE_INSTALL_PREFIX}/lib) -link_directories(${QUICKFIX_INSTALL_PREFIX}/lib) -file(GLOB DISTRIBUTED_ATS_LIB *) -include_directories(${CMAKE_INSTALL_PREFIX}/include) add_library(DistributedATSLib SHARED ${DISTRIBUTED_ATS_LIB}) target_link_libraries(DistributedATSLib quickfix fastcdr fastdds log4cxx) diff --git a/GenTools/idl/CMakeLists.txt b/GenTools/idl/CMakeLists.txt index 3cb6d31..beebf49 100644 --- a/GenTools/idl/CMakeLists.txt +++ b/GenTools/idl/CMakeLists.txt @@ -1,32 +1,30 @@ cmake_minimum_required(VERSION 3.12.4) -if(NOT CMAKE_VERSION VERSION_LESS 3.0) - cmake_policy(SET CMP0048 NEW) -endif() - project(DistributedATSLib) -# Find requirements -if(NOT fastcdr_FOUND) - find_package(fastcdr REQUIRED) -endif() -if(NOT fastdds_FOUND) - find_package(fastdds 3 REQUIRED) -endif() +message(STATUS "Current source dir: ${CMAKE_CURRENT_SOURCE_DIR}") +message(DDS_INCLUDE_DIR="${DDS_INCLUDE_DIR}") +message(DDS_LIBRARY_DIR="${DDS_LIBRARY_DIR}") + +message(QUICKFIX_INCLUDE_DIR="${QUICKFIX_INCLUDE_DIR}") +message(QUICKFIX_LIBRARY_DIR="${QUICKFIX_LIBRARY_DIR}") + -find_package(log4cxx REQUIRED) +include_directories(${DDS_INCLUDE_DIR}) +link_directories(${DDS_LIBRARY_DIR}) -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) +include_directories(${QUICKFIX_INCLUDE_DIR}) +link_directories(${QUICKFIX_LIBRARY_DIR}) -link_directories(${CMAKE_INSTALL_PREFIX}/lib) -link_directories(${QUICKFIX_INSTALL_PREFIX}/lib) +include_directories(${LOG4CXX_INCLUDE_DIR}) +link_directories(${LOG4CXX_LIBRARY_DIR}) file(GLOB DISTRIBUTED_ATS_LIB *) -include_directories(${CMAKE_INSTALL_PREFIX}/include) add_library(DistributedATSLib SHARED ${DISTRIBUTED_ATS_LIB}) +add_dependencies(DistributedATSLib FastDDS) +add_dependencies(DistributedATSLib QuickFIX) +add_dependencies(DistributedATSLib Log4cxx) target_link_libraries(DistributedATSLib quickfix fastcdr fastdds log4cxx) install(TARGETS DistributedATSLib diff --git a/LatencyTest/CMakeLists.txt b/LatencyTest/CMakeLists.txt index 438e6d8..9a6b370 100644 --- a/LatencyTest/CMakeLists.txt +++ b/LatencyTest/CMakeLists.txt @@ -1,29 +1,38 @@ cmake_minimum_required(VERSION 3.12.4) -if(NOT CMAKE_VERSION VERSION_LESS 3.0) - cmake_policy(SET CMP0048 NEW) -endif() - project(LatencyTest) -# Find requirements -if(NOT fastcdr_FOUND) - find_package(fastcdr REQUIRED) -endif() +message(STATUS "Current source dir: ${CMAKE_CURRENT_SOURCE_DIR}") +message(DDS_INCLUDE_DIR="${DDS_INCLUDE_DIR}") +message(DDS_LIBRARY_DIR="${DDS_LIBRARY_DIR}") + +message(QUICKFIX_INCLUDE_DIR="${QUICKFIX_INCLUDE_DIR}") +message(QUICKFIX_LIBRARY_DIR="${QUICKFIX_LIBRARY_DIR}") -if(NOT fastdds_FOUND) - find_package(fastdds 3 REQUIRED) -endif() +include_directories(${DDS_INCLUDE_DIR}) +link_directories(${DDS_LIBRARY_DIR}) -find_package(log4cxx REQUIRED) +include_directories(${QUICKFIX_INCLUDE_DIR}) +link_directories(${QUICKFIX_LIBRARY_DIR}) -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) +include_directories(${LOG4CXX_INCLUDE_DIR}) +link_directories(${LOG4CXX_LIBRARY_DIR}) -link_directories(${CMAKE_INSTALL_PREFIX}/lib) +include_directories(${Boost_INCLUDE_DIR}) +link_directories(${Boost_LIBRARY_DIRS}) -file(GLOB DISTRIBUTED_ATS_LATENCY_TEST_SRC *) +file(GLOB DISTRIBUTED_ATS_LATENCY_TEST_SRC *.cpp) add_executable(LatencyTest ${DISTRIBUTED_ATS_LATENCY_TEST_SRC}) -link_directories(DistributedATSLib quickfix log4cxx) -target_link_libraries(LatencyTest DistributedATSLib quickfix log4cxx boost_program_options) + +find_package(Boost REQUIRED COMPONENTS program_options) +include_directories(${Boost_INCLUDE_DIRS}) + +target_link_libraries(LatencyTest + PRIVATE + DistributedATSLib + quickfix + log4cxx + Boost::program_options + fastcdr + fastdds +) diff --git a/MatchingEngine/scripts/matching_engine_manager.py b/MatchingEngine/scripts/matching_engine_manager.py new file mode 100644 index 0000000..e8e2237 --- /dev/null +++ b/MatchingEngine/scripts/matching_engine_manager.py @@ -0,0 +1,107 @@ +#!/usr/bin/env python3 + +import os +import sys +import time +import subprocess +from pathlib import Path +import psutil + +PROGNAME = "MatchingEngine" + +def get_config_file(): + if len(sys.argv) < 3: + print(f"Usage: {sys.argv[0]} [start|stop|check] ") + sys.exit(1) + return sys.argv[2] + + +def get_paths(config_file): + basedir = os.environ.get("BASEDIR_ATS", ".") + dats_home = os.environ.get("DATS_HOME") + if not dats_home: + print("DATS_HOME is not set.") + sys.exit(1) + + config_path = Path(basedir) / "config" / config_file + log_path = Path(basedir) / "logs" / f"{PROGNAME}.{config_file}.console.log" + binary_path = Path(dats_home) / "bin" / PROGNAME + return binary_path, config_path, log_path, basedir + + +def source_dats_env(): + dats_home = os.environ.get("DATS_HOME") + env_script = Path(dats_home) / "dats_env.sh" + if env_script.exists(): + subprocess.call(f"source {env_script}", shell=True, executable="/bin/bash") + + +def check_process(config_path): + for proc in psutil.process_iter(['pid', 'cmdline']): + try: + cmdline = proc.info['cmdline'] + if cmdline and PROGNAME in cmdline[0] and str(config_path) in ' '.join(cmdline): + print(f"{PROGNAME} [{config_path.name}] is running - {proc.pid}") + return proc.pid + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): + continue + print(f"{PROGNAME} [{config_path.name}] is not running") + return None + + +def start_process(binary_path, config_path, log_path): + print(f"Starting: {PROGNAME} -c {config_path}") + for key in sorted(os.environ): + print(f"{key}={os.environ[key]}") + if check_process(config_path) is None: + with open(log_path, "a") as f: + subprocess.Popen([str(binary_path), "-c", str(config_path)], + stdout=f, stderr=subprocess.STDOUT) + time.sleep(1) + check_process(config_path) + + +def stop_process(config_path): + cmd_str = f"{PROGNAME} -c {config_path}" + print(f"Stopping: {cmd_str}") + pid = check_process(config_path) + if pid: + os.system(f"pkill -SIGTERM -f \"{cmd_str}\"") + time.sleep(1) + + for _ in range(10): + if check_process(config_path) is None: + break + time.sleep(1) + else: + os.system(f"pkill -KILL -U {os.getuid()} -f \"{cmd_str}\"") + + +def main(): + if len(sys.argv) < 3: + print(f"Usage: {sys.argv[0]} [start|stop|check] ") + sys.exit(1) + + command = sys.argv[1].lower() + config_file = get_config_file() + + binary_path, config_path, log_path, basedir = get_paths(config_file) + + source_dats_env() + + if command == "start": + start_process(binary_path, config_path, log_path) + elif command == "stop": + stop_process(config_path) + elif command == "check": + check_process(config_path) + else: + print("Unknown command") + sys.exit(1) + + with open(log_path, "a") as f: + f.write(f"{time.strftime('%Y%m%d.%H%M%S')} run-done : pid,{os.getpid()}\n") + + +if __name__ == "__main__": + main() diff --git a/MatchingEngine/src/CMakeLists.txt b/MatchingEngine/src/CMakeLists.txt index c836803..d7cb7b7 100644 --- a/MatchingEngine/src/CMakeLists.txt +++ b/MatchingEngine/src/CMakeLists.txt @@ -1,39 +1,49 @@ cmake_minimum_required(VERSION 3.12.4) -if(NOT CMAKE_VERSION VERSION_LESS 3.0) - cmake_policy(SET CMP0048 NEW) -endif() +message(STATUS "Current source dir: ${CMAKE_CURRENT_SOURCE_DIR}") +message(DDS_INCLUDE_DIR="${DDS_INCLUDE_DIR}") +message(DDS_LIBRARY_DIR="${DDS_LIBRARY_DIR}") -project(MatchingEngine) +message(QUICKFIX_INCLUDE_DIR="${QUICKFIX_INCLUDE_DIR}") +message(QUICKFIX_LIBRARY_DIR="${QUICKFIX_LIBRARY_DIR}") -# Find requirements -if(NOT fastcdr_FOUND) - find_package(fastcdr REQUIRED) -endif() +message(LIQUIBOOK_LIBRARY_DIR="${LIQUIBOOK_LIBRARY_DIR}") -if(NOT fastdds_FOUND) - find_package(fastdds 3 REQUIRED) -endif() +include_directories(${DDS_INCLUDE_DIR}) +link_directories(${DDS_LIBRARY_DIR}) -find_package(log4cxx REQUIRED) +include_directories(${QUICKFIX_INCLUDE_DIR}) +link_directories(${QUICKFIX_LIBRARY_DIR}) -# Set C++11 -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) +include_directories(${LOG4CXX_INCLUDE_DIR}) +link_directories(${LOG4CXX_LIBRARY_DIR}) -include_directories(${LIQUIBOOK_HOME}) -#include_directories(${QUICKFIX_INSTALL_PREFIX}/include) +include_directories(${Boost_INCLUDE_DIR}) +link_directories(${Boost_LIBRARY_DIRS}) -link_directories(${CMAKE_INSTALL_PREFIX}/lib) -link_directories(${QUICKFIX_INSTALL_PREFIX}/lib) +include_directories(${LIQUIBOOK_INCLUDE_DIR}) + +file(GLOB DISTRIBUTED_ATS_MATCHING_ENGINE_SRC *.cpp) -file(GLOB DISTRIBUTED_ATS_MATCHING_ENGINE_SRC *) add_executable(MatchingEngine ${DISTRIBUTED_ATS_MATCHING_ENGINE_SRC}) -link_directories(DistributedATSLib quickfix log4cxx boost_program_options) -target_link_libraries(MatchingEngine DistributedATSLib quickfix log4cxx boost_program_options) -install(TARGETS MatchingEngine +find_package(Boost REQUIRED COMPONENTS program_options) +include_directories(${Boost_INCLUDE_DIRS}) + +target_link_libraries(MatchingEngine + PRIVATE + DistributedATSLib + quickfix + log4cxx + Boost::program_options + fastcdr + fastdds +) + +set(CMAKE_XCODE_ATTRIBUTE_OTHER_CODE_SIGN_FLAGS "-o linker-signed") + +install(TARGETS MatchingEngine PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dats ) + diff --git a/MatchingEngine/src/Market.cpp b/MatchingEngine/src/Market.cpp index 6d826f5..c64d1d5 100644 --- a/MatchingEngine/src/Market.cpp +++ b/MatchingEngine/src/Market.cpp @@ -54,7 +54,7 @@ Market::Market(DataWriterContainerPtr dataWriterContainerPtr, PriceDepthPublisherQueuePtr& price_depth_publisher_queue_ptr) : dataWriterContainerPtr_(dataWriterContainerPtr), _marketName(marketName), _dataServiceName(dataServiceName), - _price_depth_publisher_queue_ptr( price_depth_publisher_queue_ptr ) +_price_depth_publisher_queue_ptr( price_depth_publisher_queue_ptr ), _ready_to_trade(false) { stats_ptr_ = std::make_shared(); } @@ -389,7 +389,7 @@ void Market::on_fill(const OrderPtr &order, const OrderPtr &matched_order, void Market::on_cancel(const OrderPtr &order) { order->onCancelled(); DistributedATS_ExecutionReport::ExecutionReport executionReport; - order->populateExecutionReport(executionReport, FIX::ExecType_CANCELLED); + order->populateExecutionReport(executionReport, FIX::ExecType_CANCELED); publishExecutionReport(executionReport); // out() << "\tCanceled: " << *order<< std::endl; @@ -400,13 +400,13 @@ void Market::on_cancel_reject(const OrderPtr &order, const char *reason) { // out() << "\tCancel Reject: " <<*order<< " : " << reason << std::endl; } -void Market::on_replace(const OrderPtr &order, const int32_t &size_delta, +void Market::on_replace(const OrderPtr &order, const int64_t &size_delta, liquibook::book::Price new_price) { // out() << "\tCancel Reject: " <<*order<< " : " << size_delta << " : " << // new_price << std::endl; order->onReplaced(size_delta, new_price); DistributedATS_ExecutionReport::ExecutionReport executionReport; - order->populateExecutionReport(executionReport, FIX::ExecType_REPLACE); + order->populateExecutionReport(executionReport, FIX::ExecType_REPLACED); publishExecutionReport(executionReport); } @@ -442,9 +442,8 @@ void Market::on_depth_change(const DepthOrderBook *book, std::stringstream ss; MarketDataIncrementalRefreshLogger::log(ss, md_update->priceDepth); LOG4CXX_INFO(logger, "MarketDataIncrementalRefresh : [" << ss.str() << "]"); - std::cout << "Update : " << ss.str() << std::endl; - int market_data_index = MARKET_DATA_PRICE_DEPTH * 2; // bids and asks + int market_data_index = MARKET_DATA_PRICE_DEPTH * 2; // bids and asks auto current_stats = stats_ptr_->find(book->symbol()); diff --git a/MatchingEngine/src/Market.h b/MatchingEngine/src/Market.h index e39f81a..24ddf99 100644 --- a/MatchingEngine/src/Market.h +++ b/MatchingEngine/src/Market.h @@ -142,33 +142,34 @@ class Market : public liquibook::book::OrderListener, // Implementation of LiquiBook interfaces // // OrderListener interface - virtual void on_accept(const OrderPtr &order); - virtual void on_reject(const OrderPtr &order, const char *reason); + virtual void on_accept(const OrderPtr &order) override; + virtual void on_reject(const OrderPtr &order, const char *reason) override; virtual void on_fill(const OrderPtr &order, const OrderPtr &matched_order, liquibook::book::Quantity fill_qty, - liquibook::book::Cost fill_cost); - virtual void on_cancel(const OrderPtr &order); - virtual void on_cancel_reject(const OrderPtr &order, const char *reason); - virtual void on_replace(const OrderPtr &order, const int32_t &size_delta, - liquibook::book::Price new_price); - virtual void on_replace_reject(const OrderPtr &order, const char *reason); + liquibook::book::Cost fill_cost) override; + virtual void on_cancel(const OrderPtr &order) override; + virtual void on_cancel_reject(const OrderPtr &order, const char *reason) override; + virtual void on_replace(const OrderPtr &order, const int64_t &size_delta, + liquibook::book::Price new_price) override; + virtual void on_replace_reject(const OrderPtr &order, const char *reason) override; // TradeListener interface virtual void on_trade(const OrderBook *book, liquibook::book::Quantity qty, - liquibook::book::Cost cost); + liquibook::book::Cost cost) override; // OrderBookListener interface - virtual void on_order_book_change(const OrderBook *book); + virtual void on_order_book_change(const OrderBook *book) override; // BboListener interface - void on_bbo_change(const DepthOrderBook *book, const BookDepth *depth); + void on_bbo_change(const DepthOrderBook *book, const BookDepth *depth) override; // Implement DepthListener interface - void on_depth_change(const DepthOrderBook *book, const BookDepth *depth); + void on_depth_change(const DepthOrderBook *book, const BookDepth *depth) override; OrderBookPtr addBook(const std::string &symbol, bool useDepthBook); - bool is_ready_to_trade() { return (books_.size() > 0); }; + bool get_ready_to_trade() { return _ready_to_trade.load(); }; + void set_ready_to_trade( bool ready_to_trade ) { _ready_to_trade.store(ready_to_trade); }; public: @@ -216,6 +217,8 @@ class Market : public liquibook::book::OrderListener, std::string _marketName; // Quickfix field 207 - Security Exchange std::string _dataServiceName; + std::atomic _ready_to_trade; + }; typedef std::shared_ptr market_ptr; diff --git a/MatchingEngine/src/Order.cpp b/MatchingEngine/src/Order.cpp index 9adcc6d..dddb87d 100644 --- a/MatchingEngine/src/Order.cpp +++ b/MatchingEngine/src/Order.cpp @@ -105,7 +105,7 @@ void Order::onRejected(const char *reason) {} void Order::onFilled(liquibook::book::Quantity fill_qty, liquibook::book::Cost fill_cost) { quantityOnMarket_ -= fill_qty; - fillCost_ += fill_cost; + fillCost_ += (fill_cost * fill_qty); quantityFilled_ += fill_qty; std::stringstream msg; @@ -243,10 +243,6 @@ void Order::populateExecutionReport( executionReport.TimeInForce(FIX::TimeInForce_DAY); } - - // std::cout << "Transact Time : " << ACE_OS::gettimeofday().msec() << ":" << - // executionReport.TransactTime << std::endl; - if (quantityFilled() > 0) executionReport.AvgPx(std::nearbyint(fillCost() / quantityFilled())); // round to the nearest tick else diff --git a/MatchingEngine/src/PriceDepthPublisherService.cpp b/MatchingEngine/src/PriceDepthPublisherService.cpp index 562aef5..db74c2a 100644 --- a/MatchingEngine/src/PriceDepthPublisherService.cpp +++ b/MatchingEngine/src/PriceDepthPublisherService.cpp @@ -73,30 +73,21 @@ int PriceDepthPublisherService::service() // // Iterate through the queue and get the latest update // - - /*std::cout << "Queue Size : " - << _price_depth_publisher_queue_ptr->size() - << std::endl;*/ - - std::shared_ptr market_data_update; + std::shared_ptr market_data_update; - std::map - latestMarketDataUpdates; + std::map + latestMarketDataUpdates; - - while (_price_depth_publisher_queue_ptr->pop(market_data_update)) - { - latestMarketDataUpdates[market_data_update->symbol] = market_data_update->priceDepth; - - std::stringstream ss; - MarketDataIncrementalRefreshLogger::log(ss, latestMarketDataUpdates[market_data_update->symbol]); - LOG4CXX_INFO(logger, "MarketDataIncrementalRefresh : [" << ss.str() << "]"); - std::cout << "Update : " << ss.str() << std::endl; + while (_price_depth_publisher_queue_ptr->pop(market_data_update)) + { + latestMarketDataUpdates[market_data_update->symbol] = market_data_update->priceDepth; - } + std::stringstream ss; + MarketDataIncrementalRefreshLogger::log(ss, latestMarketDataUpdates[market_data_update->symbol]); + LOG4CXX_INFO(logger, "MarketDataIncrementalRefresh : [" << ss.str() << "]"); + } - DistributedATS_MarketDataIncrementalRefresh::MarketDataIncrementalRefresh chunkedIncrementalMarketDataRefresh; @@ -132,8 +123,6 @@ int PriceDepthPublisherService::service() chunkedIncrementalMarketDataRefresh, "MarketDataIncrementalRefresh"); - std::cout << "Publishing chunk of " << chunkedIncrementalMarketDataRefresh.c_NoMDEntries().size() << " updates" << std::endl; - std::stringstream ss; MarketDataIncrementalRefreshLogger::log( ss, chunkedIncrementalMarketDataRefresh); @@ -144,7 +133,6 @@ int PriceDepthPublisherService::service() if (ret != eprosima::fastdds::dds::RETCODE_OK) { LOG4CXX_ERROR(logger, "MarketDataIncrementalRefresh :" << ret); } - } } diff --git a/MatchingEngine/src/SecurityListDataReaderListenerImpl.cpp b/MatchingEngine/src/SecurityListDataReaderListenerImpl.cpp index 3dd30ca..95352fb 100644 --- a/MatchingEngine/src/SecurityListDataReaderListenerImpl.cpp +++ b/MatchingEngine/src/SecurityListDataReaderListenerImpl.cpp @@ -51,7 +51,7 @@ void SecurityListDataReaderListenerImpl::on_data_available( DistributedATS_SecurityList::SecurityList security_list; eprosima::fastdds::dds::SampleInfo info; - if (_marketPtr->is_ready_to_trade()) + if (_marketPtr->get_ready_to_trade() == true) return; if (reader->take_next_sample(&security_list, &info) == eprosima::fastdds::dds::RETCODE_OK) @@ -59,12 +59,14 @@ void SecurityListDataReaderListenerImpl::on_data_available( std::stringstream ss; SecurityListLogger::log(ss, security_list); LOG4CXX_INFO(logger, "SecurityList : [" << ss.str() << "]"); - + for (uint32_t sec_index = 0; sec_index < security_list.c_NoRelatedSym().size(); sec_index++) { std::string instrument = security_list.c_NoRelatedSym()[sec_index].Symbol(); _marketPtr->addBook(instrument, true); } + + _marketPtr->set_ready_to_trade(true); // request to recieve opening price _marketPtr->publishMarketDataRequest(); diff --git a/MatchingEngine/src/SecurityListRequestDataWriterListener.h b/MatchingEngine/src/SecurityListRequestDataWriterListener.h index de154b5..73d577e 100644 --- a/MatchingEngine/src/SecurityListRequestDataWriterListener.h +++ b/MatchingEngine/src/SecurityListRequestDataWriterListener.h @@ -1,3 +1,9 @@ +#include +#include +#include +#include +#include + #pragma once namespace DistributedATS @@ -5,44 +11,48 @@ namespace DistributedATS class SecurityListRequestDataWriterListener : public eprosima::fastdds::dds::DataWriterListener { - - public: +public: - SecurityListRequestDataWriterListener( const market_ptr& marketPtr ) : matched_(0), _market_ptr(marketPtr) + SecurityListRequestDataWriterListener(const market_ptr& marketPtr) + : matched_(0), _market_ptr(marketPtr), thread_started_(false) { } - ~SecurityListRequestDataWriterListener() override - { - } + ~SecurityListRequestDataWriterListener() override = default; void on_publication_matched( - eprosima::fastdds::dds::DataWriter* dwr, - const eprosima::fastdds::dds::PublicationMatchedStatus& info) override + eprosima::fastdds::dds::DataWriter* dwr, + const eprosima::fastdds::dds::PublicationMatchedStatus& info) override { - if (info.current_count_change >= 1) + if (info.current_count_change > 0) { matched_ = info.total_count; - LOG4CXX_INFO(logger, "SecurityListRequestDataWriterListener Publisher Matched:" << matched_); - if ( !_market_ptr->is_ready_to_trade() ) - _market_ptr->publishSecurityListRequest(dwr); + + // Start thread only once + bool expected = false; + if (thread_started_.compare_exchange_strong(expected, true)) + { + std::thread([this, dwr]() { + while (!_market_ptr->get_ready_to_trade()) + { + _market_ptr->publishSecurityListRequest(dwr); + std::this_thread::sleep_for(std::chrono::seconds(1)); + } + }).detach(); + } } else if (info.current_count_change == -1) { matched_ = info.total_count; - LOG4CXX_INFO(logger, "SecurityListRequestDataWriterListener Publisher UnMatched:" << matched_); - } - else - { - LOG4CXX_INFO(logger, "Is not a valid value for PublicationMatchedStatus current count change." << info.current_count_change); } } - private: - market_ptr _market_ptr; - std::atomic_int matched_; - }; +private: + market_ptr _market_ptr; + std::atomic_int matched_; + std::atomic thread_started_; // <--- Ensures thread is only started once +}; using security_list_request_data_writer_listener_ptr = std::unique_ptr; -}; +}; // namespace DistributedATS diff --git a/MiscATS/CryptoCLOB/README.md b/MiscATS/CryptoCLOB/README.md index 409895f..b7f35b2 100644 --- a/MiscATS/CryptoCLOB/README.md +++ b/MiscATS/CryptoCLOB/README.md @@ -23,8 +23,7 @@ The first matching engine consumes and processes orders from the instrument grou ### Starting ATS ``` - cd $DATS_HOME/MiscATS/CryptoCLOB/scipts - ./start_ats +BASEDIR_ATS=`pwd`/CryptoCLOB python3 start_ats.py --ats CryptoCLOB/crypto_ats.json ``` ## Test client diff --git a/MiscATS/CryptoCLOB/crypto_ats.json b/MiscATS/CryptoCLOB/crypto_ats.json new file mode 100644 index 0000000..3aa71ae --- /dev/null +++ b/MiscATS/CryptoCLOB/crypto_ats.json @@ -0,0 +1,10 @@ +[ + ["data_service_manager.py", "data_service_a.ini"], + ["data_service_manager.py", "data_service_b.ini"], + ["matching_engine_manager.py", "matching_engine_MARKET_BTC.ini"], + ["matching_engine_manager.py", "matching_engine_MARKET_ETH.ini"], + ["matching_engine_manager.py", "matching_engine_MARKET_OTHER_COIN.ini"], + ["fix_gateway_manager.py", "fix_gwy_1.cfg"], + ["fix_gateway_manager.py", "fix_gwy_2.cfg"], + ["fix_gateway_manager.py", "fix_gwy_3.cfg"] +] \ No newline at end of file diff --git a/MiscATS/CryptoCLOB/data/distributed_ats.db b/MiscATS/CryptoCLOB/data/distributed_ats.db index 440cc49..366d814 100644 Binary files a/MiscATS/CryptoCLOB/data/distributed_ats.db and b/MiscATS/CryptoCLOB/data/distributed_ats.db differ diff --git a/MiscATS/CryptoCLOB/scripts/start_ats.sh b/MiscATS/CryptoCLOB/scripts/start_ats.sh deleted file mode 100755 index aae85ee..0000000 --- a/MiscATS/CryptoCLOB/scripts/start_ats.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -trap cleanup 1 2 3 6 - -cleanup() -{ - echo "Caught Signal ... cleaning up..." - - $BASEDIR_ATS/scripts/stop_ats.sh - - echo "Done." - - exit 1; -} - -sleep 1 -$DATS_HOME/scripts/dataservice.sh start data_service_a.ini - -sleep 1 -$DATS_HOME/scripts/dataservice.sh start data_service_b.ini - -sleep 1 -$DATS_HOME/scripts/matchingengine.sh start matching_engine_MARKET_BTC.ini - -sleep 1 -$DATS_HOME/scripts/matchingengine.sh start matching_engine_MARKET_ETH.ini - -sleep 1 -$DATS_HOME/scripts/matchingengine.sh start matching_engine_MARKET_OTHER_COIN.ini - -sleep 1 -$DATS_HOME/scripts/fixgateway.sh start fix_gwy_1.cfg - -sleep 1 -$DATS_HOME/scripts/fixgateway.sh start fix_gwy_2.cfg - -sleep 1 -$DATS_HOME/scripts/fixgateway.sh start fix_gwy_3.cfg - -while true; do - $DATS_HOME/scripts/dataservice.sh check data_service_a.ini - $DATS_HOME/scripts/dataservice.sh check data_service_b.ini - $DATS_HOME/scripts/matchingengine.sh check matching_engine_MARKET_BTC.ini - $DATS_HOME/scripts/matchingengine.sh check matching_engine_MARKET_ETH.ini - $DATS_HOME/scripts/matchingengine.sh check matching_engine_MARKET_OTHER_COIN.ini - $DATS_HOME/scripts/fixgateway.sh check fix_gwy_1.cfg - $DATS_HOME/scripts/fixgateway.sh check fix_gwy_2.cfg - $DATS_HOME/scripts/fixgateway.sh check fix_gwy_3.cfg - - sleep 2; -done diff --git a/MiscATS/CryptoCLOB/scripts/stop_ats.sh b/MiscATS/CryptoCLOB/scripts/stop_ats.sh deleted file mode 100755 index a439ae6..0000000 --- a/MiscATS/CryptoCLOB/scripts/stop_ats.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/sh - -sleep 1 -$DATS_HOME/scripts/dataservice.sh stop data_service_a.ini - -sleep 1 -$DATS_HOME/scripts/dataservice.sh stop data_service_b.ini - -sleep 1 -$DATS_HOME/scripts/matchingengine.sh stop matching_engine_MARKET_BTC.ini - -sleep 1 -$DATS_HOME/scripts/matchingengine.sh stop matching_engine_MARKET_ETH.ini - -sleep 1 -$DATS_HOME/scripts/matchingengine.sh stop matching_engine_MARKET_OTHER_COIN.ini - -sleep 1 -$DATS_HOME/scripts/fixgateway.sh stop fix_gwy_1.cfg - -sleep 1 -$DATS_HOME/scripts/fixgateway.sh stop fix_gwy_2.cfg - -sleep 1 -$DATS_HOME/scripts/fixgateway.sh stop fix_gwy_3.cfg - diff --git a/MiscATS/CryptoCLOB/sql/populate_db.sh b/MiscATS/CryptoCLOB/sql/populate_db.sh index c60e78f..4c3b259 100755 --- a/MiscATS/CryptoCLOB/sql/populate_db.sh +++ b/MiscATS/CryptoCLOB/sql/populate_db.sh @@ -1,6 +1,7 @@ #!/bin/sh -$SQLITE_HOME/bin/sqlite3 $DATS_HOME/DataService/sql/sqlite/distributed_ats.db < + bash -c "LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/fastdds discovery -q 51000" + ports: + - "51000:51000" + + distributed_ats: + container_name: distributed_ats + image: ghcr.io/mkipnis/dats_crypto_clob:latest + depends_on: + - fast_dds_discovery + command: > + bash -c "cd /usr/local && source ./dats_env.sh && + cd MiscATS && + BASEDIR_ATS=`pwd`/CryptoCLOB python3 start_ats.py --ats CryptoCLOB/crypto_ats.json" + volumes: + - ./logs_ats:/usr/local/MiscATS/CryptoCLOB/logs + ports: + # FIX Gateways + - "15001:15001" + - "16001:16001" + - "17001:17001" + + distributed_ats_webtrader: + container_name: distributed_ats_webtrader + image: ghcr.io/mkipnis/distributed_ats_webtrader:latest + depends_on: + - distributed_ats + volumes: + - ./webtrader_logs:/usr/local/tomcat/logs + ports: + - "8080:8080" +``` + + +### US Treasuries CLOB/ATS – one matching engine that handles hundreds of instruments. +* Users: UST_TRADER_1, UST_TRADER_2, UST_TRADER_3, UST_TRADER_4 : Password: TEST +* http://localhost:8080/ +``` +services: + fast_dds_discovery: + container_name: discovery_service + image: ghcr.io/mkipnis/distributed_ats:latest + command: > + bash -c "LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/fastdds discovery -q 51000" + ports: + - "51000:51000" + restart: unless-stopped + + distributed_ats: + container_name: distributed_ats + image: ghcr.io/mkipnis/dats_ust_clob:latest + depends_on: + - fast_dds_discovery + command: > + bash -c "cd /usr/local && source ./dats_env.sh && + cd MiscATS && BASEDIR_ATS=`pwd`/USTreasuryCLOB python3 start_ats.py --ats USTreasuryCLOB/ust_ats.json" + volumes: + - ./logs_ats:/usr/local/MiscATS/USTreasuryCLOB/logs + ports: + - "15001:15001" + - "16001:16001" + restart: unless-stopped + + # WebTrader Front-End + distributed_ats_webtrader: + container_name: distributed_ats_webtrader + image: ghcr.io/mkipnis/distributed_ats_webtrader:latest + depends_on: + - distributed_ats + volumes: + - ./webtrader_logs:/usr/local/tomcat/logs + ports: + - "8080:8080" + restart: unless-stopped +``` + +### Multi Matching Engine ATS, with each component running in a separate container +* [Docker Compose - Multi Matching Engine ATS](docker/docker-compose-multi-matching-engine.yml) +* [Docker Compose - WebTrader](docker/docker-compose-webtrader.yml) + ### Dependencies @@ -71,54 +162,3 @@ DistributedATS is a [**FIX Protocol-based**](https://www.fixtrading.org) alterna ### Autogeneration of IDL from QuickFIX XML [GenTools](https://github.com/mkipnis/DistributedATS/tree/master/GenTools) is a utility that generates DDS IDL, FIX to IDL, and IDL to FIX adapters and IDL logger helper classes from QuickFIX's XML data dictionary. -### Building Distributed ATS and its dependencies - -To download and build all necessary dependencies, use the provided script: - -[download_deps_and_build_all.sh](https://github.com/mkipnis/DistributedATS/blob/master/download_deps_and_build_all.sh) - -To build the base Docker image: - -[Docker.Build_Distributed_ATS](https://github.com/mkipnis/DistributedATS/blob/master/docker/Docker.Build_Distributed_ATS) - - -## Basic ATS Examples -### Crypto Central Limit Order Book -- Docker Image: [Docker.Crypto_CLOB](https://github.com/mkipnis/DistributedATS/blob/master/docker/Docker.Crypto_CLOB) -- Docker Compose: [docker-compose-crypto.yml](https://github.com/mkipnis/DistributedATS/blob/master/docker/docker-compose-crypto.yml) -``` -sudo docker-compose -f docker-compose-crypto.yml up -d -``` -Upon starting the dockerized instance, open in the browser: -* http://localhost:8080 - -##### -Users: -- CRYPTO_TRADER_1 -- CRYPTO_TRADER_2 -- CRYPTO_TRADER_3 -- CRYPTO_TRADER_4 - -Password: -- TEST -##### - -### US Treasuries Central Limit Order Book - -- Docker Image: [Docker.UST_CLOB](https://github.com/mkipnis/DistributedATS/blob/master/docker/Docker.UST_CLOB) -- Docker Compose: [docker-compose-ust.yml](https://github.com/mkipnis/DistributedATS/blob/master/docker/docker-compose-ust.yml) -``` -sudo docker-compose -f docker-compose-ust.yml up -d -``` -Upon starting the dockerized instance, open in the browser: -* http://localhost:8080 -##### -Users: -- UST_TRADER_1 -- UST_TRADER_2 -- UST_TRADER_3 -- UST_TRADER_4 - -Password: -- TEST -###### diff --git a/build_with_cmake.sh b/build_with_cmake.sh new file mode 100755 index 0000000..724ff5f --- /dev/null +++ b/build_with_cmake.sh @@ -0,0 +1,58 @@ +#!/usr/bin/env bash + +set -ex # Print each command and exit on error + +# Detect OS for library path +OS="$(uname)" +if [[ "$OS" == "Darwin" ]]; then + LIB_PATH_VAR="DYLD_LIBRARY_PATH" +# CMAKE_FLAGS="-G Xcode" +else + LIB_PATH_VAR="LD_LIBRARY_PATH" +fi + +# Create and enter build directory +mkdir -p build +cd build + +# Get absolute path to current directory (fallback for macOS without realpath) +get_abs_path() { + if command -v realpath >/dev/null 2>&1; then + realpath "$1" + else + # Fallback using Python for macOS + python3 -c "import os; print(os.path.abspath('$1'))" + fi +} + +ROOT_DIR="$(get_abs_path ..)" + +# Set default paths +if [ -z "$1" ]; then + DDS_HOME="$ROOT_DIR/dds" + QUICKFIX_HOME="$ROOT_DIR/quickfix" + LOG4CXX_HOME="$ROOT_DIR/log4cxx" + INSTALL_PREFIX="$ROOT_DIR/DistributedATS" +else + INSTALL_PREFIX="$(get_abs_path "$1")" +fi + +# Run cmake and build +cmake ${CMAKE_FLAGS:-} .. -DCMAKE_INSTALL_PREFIX="$INSTALL_PREFIX" +cmake --build . --target install --config Debug -v + +# Write the environment setup script +cat < "$INSTALL_PREFIX/dats_env.sh" +#!/usr/bin/env bash + +export DATS_HOME="$INSTALL_PREFIX" +export DDS_HOME="$DDS_HOME" +export QUICKFIX_HOME="$QUICKFIX_HOME" +export LOG4CXX_HOME="$LOG4CXX_HOME" + +export $LIB_PATH_VAR="\$DATS_HOME/lib:\$DDS_HOME/lib:\$QUICKFIX_HOME/lib:\$LOG4CXX_HOME/lib:\$$LIB_PATH_VAR" +export LOG4CXX_CONFIGURATION="\$DATS_HOME/config/log4cxx.xml" + +EOM + +chmod +x "$INSTALL_PREFIX/dats_env.sh" diff --git a/cmake/DDSConfig.cmake b/cmake/DDSConfig.cmake new file mode 100644 index 0000000..0764dda --- /dev/null +++ b/cmake/DDSConfig.cmake @@ -0,0 +1,62 @@ +set(DDS_INCLUDE_DIRS "${DDS_ROOT_DIR}/include") +set(DDS_LIBRARY_DIRS "${DDS_ROOT_DIR}/lib") + +include(ExternalProject) + +if(NOT EXISTS "${DDS_INCLUDE_DIRS}/fastdds/config.hpp") + message(STATUS "FastDDS not found, building from source...") + + set(DDS_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/dds) + + include(ExternalProject) + + + ExternalProject_Add(ASIO + GIT_REPOSITORY https://github.com/chriskohlhoff/asio.git + GIT_TAG asio-1-28-0 + UPDATE_DISCONNECTED TRUE + SOURCE_SUBDIR asio + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + INSTALL_COMMAND ${CMAKE_COMMAND} -E copy_directory /asio/include ${DDS_INSTALL_PREFIX}/include + ) + + ExternalProject_Add(FoonathanMemory + GIT_REPOSITORY https://github.com/foonathan/memory.git + GIT_TAG main + UPDATE_DISCONNECTED TRUE + INSTALL_DIR ${DDS_INSTALL_PREFIX} + CMAKE_ARGS + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX=${DDS_INSTALL_PREFIX} + -DBUILD_SHARED_LIBS=ON + ) + + ExternalProject_Add(FastCDR + GIT_REPOSITORY https://github.com/eProsima/Fast-CDR.git + GIT_TAG master + UPDATE_DISCONNECTED TRUE + DEPENDS FoonathanMemory ASIO + INSTALL_DIR ${DDS_INSTALL_PREFIX} + CMAKE_ARGS + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX=${DDS_INSTALL_PREFIX} + -DBUILD_SHARED_LIBS=ON + ) + + ExternalProject_Add(FastDDS + GIT_REPOSITORY https://github.com/eProsima/Fast-DDS.git + GIT_TAG master + UPDATE_DISCONNECTED TRUE + DEPENDS FastCDR + INSTALL_DIR ${DDS_INSTALL_PREFIX} + CMAKE_ARGS + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX=${DDS_INSTALL_PREFIX} + -DBUILD_SHARED_LIBS=ON + ) + + +endif() + +set(DDS_FOUND TRUE) diff --git a/cmake/liquibookConfig.cmake b/cmake/liquibookConfig.cmake new file mode 100644 index 0000000..3ca1d44 --- /dev/null +++ b/cmake/liquibookConfig.cmake @@ -0,0 +1,26 @@ +set(LIQUIBOOK_INCLUDE_DIRS "${LIQUIBOOK_ROOT_DIR}/include") +set(LIQUIBOOK_LIBRARY_DIRS "${LIQUIBOOK_ROOT_DIR}/lib") + +include(ExternalProject) + +if(NOT EXISTS "${LIQUIBOOK_INCLUDE_DIRS}/liquibook_export.h") + message(STATUS "Liquibook not found, building from source...") + + set(LIQUIBOOK_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/liquibook) + + include(ExternalProject) + + ExternalProject_Add(LiquiBook + GIT_REPOSITORY https://github.com/enewhuis/liquibook.git + GIT_TAG master + UPDATE_DISCONNECTED TRUE + INSTALL_DIR "" + CONFIGURE_COMMAND "" + BUILD_COMMAND "" + SOURCE_SUBDIR src + INSTALL_COMMAND mkdir -p ${CMAKE_BINARY_DIR}/liquibook && cp -r / ${LIQUIBOOK_INSTALL_PREFIX}/include + ) + +endif() + +set(liquibook_FOUND TRUE) diff --git a/cmake/log4cxxConfig.cmake b/cmake/log4cxxConfig.cmake new file mode 100644 index 0000000..2858c55 --- /dev/null +++ b/cmake/log4cxxConfig.cmake @@ -0,0 +1,25 @@ +set(LOG4CXX_INCLUDE_DIRS "${LOG4CXX_ROOT_DIR}/include") +set(LOG4CXX_LIBRARY_DIRS "${LOG4CXX_ROOT_DIR}/lib") + +include(ExternalProject) + +if(NOT EXISTS "${LOG4CXX_INCLUDE_DIRS}/log4cxx/log4cxx.h") + message(STATUS "Log4cxx not found, building from source...") + + set(LOG4CXX_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/log4cxx) + + include(ExternalProject) + + ExternalProject_Add(Log4cxx + GIT_REPOSITORY https://github.com/apache/logging-log4cxx.git + GIT_TAG master + UPDATE_DISCONNECTED TRUE + CMAKE_ARGS + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX=${LOG4CXX_INSTALL_PREFIX} + -DBUILD_SHARED_LIBS=ON + ) + +endif() + +set(Log4cxx_FOUND TRUE) diff --git a/cmake/quickfixConfig.cmake b/cmake/quickfixConfig.cmake new file mode 100644 index 0000000..58060dc --- /dev/null +++ b/cmake/quickfixConfig.cmake @@ -0,0 +1,25 @@ +set(QUICKFIX_INCLUDE_DIR "${QUICKFIX_ROOT_DIR}/include") +set(QUICKFIX_LIBRARY_DIR "${QUICKFIX_ROOT_DIR}/lib") + +include(ExternalProject) + +if(NOT EXISTS "${QUICKFIX_INCLUDE_DIR}/quickfix/config-all.h") + message(STATUS "Quickfix not found, building from source...") + + set(QUICKFIX_INSTALL_PREFIX ${CMAKE_BINARY_DIR}/quickfix) + + include(ExternalProject) + + ExternalProject_Add(QuickFIX + GIT_REPOSITORY https://github.com/quickfix/quickfix.git + GIT_TAG master + UPDATE_DISCONNECTED TRUE + CMAKE_ARGS + -DCMAKE_BUILD_TYPE=${CMAKE_BUILD_TYPE} + -DCMAKE_INSTALL_PREFIX=${QUICKFIX_INSTALL_PREFIX} + -DBUILD_SHARED_LIBS=ON + ) + +endif() + +set(quickfix_FOUND TRUE) diff --git a/docker/Docker.Build_DATS_Deps b/docker/Docker.Build_DATS_Deps new file mode 100644 index 0000000..43939d9 --- /dev/null +++ b/docker/Docker.Build_DATS_Deps @@ -0,0 +1,15 @@ +FROM ubuntu:latest AS dats_dependency_builder + +RUN apt-get update +RUN apt install -y vim cmake curl build-essential libapr1-dev libaprutil1-dev git libboost-all-dev sqlite3 libsqlite3-dev libtool libasio-dev libtinyxml2-dev zip python3-pip +RUN pip install psutil --break-system-packages + +ADD build_dependences.sh / +RUN /build_dependences.sh + +FROM ubuntu:latest +RUN apt-get update && apt-get install --no-install-recommends -y \ + libboost-all-dev vim python3-pip libapr1-dev libaprutil1-dev libtinyxml2-dev \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +COPY --from=dats_dependency_builder /usr/local /usr/local diff --git a/docker/Docker.Build_Distributed_ATS b/docker/Docker.Build_Distributed_ATS index 2a47d64..42b878b 100644 --- a/docker/Docker.Build_Distributed_ATS +++ b/docker/Docker.Build_Distributed_ATS @@ -1,4 +1,4 @@ -FROM ubuntu:latest +FROM ghcr.io/mkipnis/distributed_ats_deps AS dats_builder RUN apt-get update RUN apt install -y vim cmake curl build-essential libapr1-dev libaprutil1-dev git libboost-all-dev sqlite3 libsqlite3-dev libtool libasio-dev libtinyxml2-dev zip python3-pip @@ -7,6 +7,9 @@ RUN pip install psutil --break-system-packages ADD build_distributed_ats.sh / RUN /build_distributed_ats.sh -EXPOSE 15001 -EXPOSE 16001 -EXPOSE 17001 +FROM ubuntu:latest +RUN apt-get update && apt-get install --no-install-recommends -y \ + libboost-all-dev vim python3-pip libapr1-dev libaprutil1-dev libtinyxml2-dev \ + && apt-get clean && rm -rf /var/lib/apt/lists/* + +COPY --from=dats_builder /usr/local /usr/local diff --git a/docker/Docker.Crypto_CLOB b/docker/Docker.Crypto_CLOB index 02bd1d6..aa99419 100644 --- a/docker/Docker.Crypto_CLOB +++ b/docker/Docker.Crypto_CLOB @@ -4,9 +4,6 @@ EXPOSE 15001 EXPOSE 16001 EXPOSE 17001 -WORKDIR /opt/DistributedATS - -COPY crypto_clob_env.sh dats_env.sh -COPY start_crypto_clob.sh /opt/DistributedATS/MiscATS/CryptoCLOB/scripts/ - -RUN chmod +x dats_env.sh +COPY dats_env.sh /usr/local/dats_env.sh +COPY FastDDS.xml /usr/local/ +RUN chmod +x /usr/local/dats_env.sh diff --git a/docker/Docker.UST_CLOB b/docker/Docker.UST_CLOB index 7770bd7..aa99419 100644 --- a/docker/Docker.UST_CLOB +++ b/docker/Docker.UST_CLOB @@ -4,9 +4,6 @@ EXPOSE 15001 EXPOSE 16001 EXPOSE 17001 -WORKDIR /opt/DistributedATS - -COPY ust_clob_env.sh dats_env.sh -COPY start_ust_clob.sh /opt/DistributedATS/MiscATS/USTreasuryCLOB/scripts/ - -RUN chmod +x dats_env.sh +COPY dats_env.sh /usr/local/dats_env.sh +COPY FastDDS.xml /usr/local/ +RUN chmod +x /usr/local/dats_env.sh diff --git a/docker/FastDDS.xml b/docker/FastDDS.xml new file mode 100644 index 0000000..3bc020f --- /dev/null +++ b/docker/FastDDS.xml @@ -0,0 +1,30 @@ +?xml version="1.0" encoding="utf-8" ?> + + + + + tcp_transport_client + TCPv4 + + + + + + + tcp_transport_client + + false + + + + +
127.0.0.1
+ 51000 +
+
+
+
+
+
+
+
diff --git a/docker/build_dependences.sh b/docker/build_dependences.sh new file mode 100755 index 0000000..fc112ca --- /dev/null +++ b/docker/build_dependences.sh @@ -0,0 +1,29 @@ +#!/bin/bash + +set -x + +# Default to system prefix if not specified by user +DEFAULT_INSTALL_DIR="/usr/local" +INSTALL_DIR="${1:-$DEFAULT_INSTALL_DIR}" + +build_and_install() { + local repo="$1" + local branch="$2" + + git clone -b "$branch" "$repo" + + cd "$(basename "$repo" .git)" || exit 1 + + mkdir -p build && cd build + cmake .. -DCMAKE_INSTALL_PREFIX="$INSTALL_DIR" + cmake --build . --target install + + cd ../.. || exit 1 +} + +build_and_install "https://github.com/foonathan/memory.git" "main" +build_and_install "https://github.com/eProsima/Fast-CDR.git" "master" +build_and_install "https://github.com/eProsima/Fast-DDS.git" "master" +build_and_install "https://github.com/apache/logging-log4cxx.git" "master" +build_and_install "https://github.com/quickfix/quickfix.git" "master" +build_and_install "https://github.com/mkipnis/liquibook.git" "cmake" diff --git a/docker/build_distributed_ats.sh b/docker/build_distributed_ats.sh index 9df12f1..7c87b35 100755 --- a/docker/build_distributed_ats.sh +++ b/docker/build_distributed_ats.sh @@ -1,6 +1,7 @@ #!/bin/bash -[[ ! -f /opt/distributed_ats_src ]] && git clone -b master https://github.com/mkipnis/DistributedATS /opt/distributed_ats_src +git clone -b enhancements_0424 https://github.com/mkipnis/DistributedATS /opt/distributed_ats_src cd /opt/distributed_ats_src -HOME=/opt ./download_deps_and_build_all.sh +cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr/local -DDDS_ROOT_DIR=/usr/local -DLOG4CXX_ROOT_DIR=/usr/local -DQUICKFIX_ROOT_DIR=/usr/local -DLIQUIBOOK_ROOT_DIR=/usr/local +cmake --build build --verbose --target install diff --git a/docker/crypto_clob_env.sh b/docker/crypto_clob_env.sh deleted file mode 100644 index fc6da81..0000000 --- a/docker/crypto_clob_env.sh +++ /dev/null @@ -1,22 +0,0 @@ -export DATS_HOME=/opt/DistributedATS -export DEPS_HOME=/opt/distributed_ats_deps_build -export LD_LIBRARY_PATH=$DEPS_HOME/lib:$DATS_HOME/lib:$LD_LIBRARY_PATH -export LOG4CXX_CONFIGURATION=/opt/DistributedATS/config/log4cxx.xml - -#echo Specific Specific - -#echo Crypto ATS -export BASEDIR_ATS=$DATS_HOME/MiscATS/CryptoCLOB -export DATS_LOG_HOME=$BASEDIR_ATS/logs -mkdir -p $BASEDIR_ATS/logs - -#echo US Treasuries -#export BASEDIR_ATS=$DATS_HOME/MiscATS/USTreasuryCLOB -#export DATS_LOG_HOME=$BASEDIR_ATS/logs -#mkdir -p $BASEDIR_ATS/logs - -#echo US Treasuries -#export BASEDIR_ATS=$DATS_HOME/MiscATS/MultiMatchingEngineATS -#export DATS_LOG_HOME=$BASEDIR_ATS/logs -#mkdir -p $BASEDIR_ATS/logs - diff --git a/docker/dats_env.sh b/docker/dats_env.sh new file mode 100644 index 0000000..ab359fc --- /dev/null +++ b/docker/dats_env.sh @@ -0,0 +1,6 @@ +# dependencies +export DATS_HOME=/usr/local +export DEPS_HOME=/usr/local +export LD_LIBRARY_PATH=$DEPS_HOME/lib:$LD_LIBRARY_PATH +export LOG4CXX_CONFIGURATION=$DATS_HOME/config/log4cxx.xml +export FASTDDS_DEFAULT_PROFILES_FILE=/usr/local/FastDDS.xml diff --git a/docker/docker-compose-crypto.yml b/docker/docker-compose-crypto.yml index c5ea1e0..8f58a97 100644 --- a/docker/docker-compose-crypto.yml +++ b/docker/docker-compose-crypto.yml @@ -1,24 +1,30 @@ -version: '2' +version: '3' services: - # Core Services: Matching Engines, FIX Gateways + fast_dds_discovery: + container_name: fast_dds_discovery + image: ghcr.io/mkipnis/distributed_ats:latest + command: > + bash -c "LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/fastdds discovery -q 51000" + ports: + - "51000:51000" + restart: unless-stopped + distributed_ats: container_name: distributed_ats image: ghcr.io/mkipnis/dats_crypto_clob:latest + depends_on: + - fast_dds_discovery command: > - bash -c "cd /opt/DistributedATS/ && - . ./dats_env.sh && - cd /opt/DistributedATS/MiscATS/CryptoCLOB/scripts && ./start_crypto_clob.sh" + bash -c "cd /usr/local && source ./dats_env.sh && cd MiscATS && BASEDIR_ATS=`pwd`/CryptoCLOB python3 start_ats.py --ats CryptoCLOB/crypto_ats.json" volumes: - - ./data_ats:/opt/DistributedATS/DataService/sql/sqlite - - ./logs_ats:/opt/DistributedATS/MiscATS/CryptoCLOB/logs + - ./logs_ats:/usr/local/MiscATS/CryptoCLOB/logs ports: - "15001:15001" - "16001:16001" - "17001:17001" restart: unless-stopped - # WebTrader Front-End distributed_ats_webtrader: container_name: distributed_ats_webtrader image: ghcr.io/mkipnis/distributed_ats_webtrader:latest diff --git a/docker/docker-compose-multi-matching-engine.yml b/docker/docker-compose-multi-matching-engine.yml new file mode 100644 index 0000000..f7c50fe --- /dev/null +++ b/docker/docker-compose-multi-matching-engine.yml @@ -0,0 +1,101 @@ +version: '3.8' + +services: + + # DDS Discovery + fast_dds_discovery: + container_name: fast_dds_discovery + image: ghcr.io/mkipnis/distributed_ats:latest + command: > + bash -c "LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/fastdds discovery -q 51000" + ports: + - "51000:51000" + restart: no + + + # FIX Gateways + fix_gateway_1: + container_name: distributed_ats + image: ghcr.io/mkipnis/multi_matching_engine_ats:latest + depends_on: + - fast_dds_discovery + command: > + bash -c "/usr/local/bin/FIXGateway -c /usr/local/MiscATS/MultiMatchingEngineATS/config/fix_gwy_1.cfg" + environment: + - LOG_FILE_NAME=fix_gateway_1.log + volumes: + - ./logs:/var/log + ports: + - "15001:15001" + restart: no + + fix_gateway_2: + container_name: fix_gateway_2 + image: ghcr.io/mkipnis/multi_matching_engine_ats:latest + depends_on: + - fast_dds_discovery + command: > + bash -c "/usr/local/bin/FIXGateway -c /usr/local/MiscATS/MultiMatchingEngineATS/config/fix_gwy_2.cfg" + environment: + - LOG_FILE_NAME=fix_gateway_2.log + volumes: + - ./logs:/var/log + ports: + - "16001:16001" + restart: no + + + # Data Services + data_service_a: + container_name: data_service_a + image: ghcr.io/mkipnis/multi_matching_engine_ats:latest + depends_on: + - fast_dds_discovery + command: > + bash -c "/usr/local/bin/DataService -c /usr/local/MiscATS/MultiMatchingEngineATS/config/data_service_a.ini" + environment: + - LOG_FILE_NAME=data_service_a.log + volumes: + - ./logs:/var/log + restart: no + + data_service_b: + container_name: data_service_b + image: ghcr.io/mkipnis/multi_matching_engine_ats:latest + depends_on: + - fast_dds_discovery + command: > + bash -c "/usr/local/bin/DataService -c /usr/local/MiscATS/MultiMatchingEngineATS/config/data_service_b.ini" + environment: + - LOG_FILE_NAME=data_service_b.log + volumes: + - ./logs:/var/log + restart: no + + + # Matching Engines + matching_engine_market_y: + container_name: market_y + image: ghcr.io/mkipnis/multi_matching_engine_ats:latest + depends_on: + - fast_dds_discovery + command: > + bash -c "/usr/local/bin/MatchingEngine -c /usr/local/MiscATS/MultiMatchingEngineATS/config/matching_engine_MARKET_Y.ini" + environment: + - LOG_FILE_NAME=matching_engine_MARKET_Y.ini + volumes: + - ./logs:/var/log + restart: no + + matching_engine_market_z: + container_name: market_z + image: ghcr.io/mkipnis/multi_matching_engine_ats:latest + depends_on: + - fast_dds_discovery + command: > + bash -c "/usr/local/bin/MatchingEngine -c /usr/local/MiscATS/MultiMatchingEngineATS/config/matching_engine_MARKET_Z.ini" + environment: + - LOG_FILE_NAME=matching_engine_MARKET_Z.ini + volumes: + - ./logs:/var/log + restart: no diff --git a/docker/docker-compose-ust.yml b/docker/docker-compose-ust.yml index 44104d7..3e3f49b 100644 --- a/docker/docker-compose-ust.yml +++ b/docker/docker-compose-ust.yml @@ -1,21 +1,27 @@ version: '2' services: - # Core Services: Matching Engines, FIX Gateways + fast_dds_discovery: + container_name: discovery_service + image: ghcr.io/mkipnis/distributed_ats:latest + command: > + bash -c "LD_LIBRARY_PATH=/usr/local/lib /usr/local/bin/fastdds discovery -q 51000" + ports: + - "51000:51000" + restart: unless-stopped + distributed_ats: container_name: distributed_ats - image: ghcr.io/mkipnis/dats_ust_clob:latest + image: ghcr.io/mkipnis/dats_ust_clob:latest + depends_on: + - fast_dds_discovery command: > - bash -c "cd /opt/DistributedATS/ && - . ./dats_env.sh && - cd /opt/DistributedATS/MiscATS/USTreasuryCLOB/scripts && ./start_ust_clob.sh" + bash -c "cd /usr/local && source ./dats_env.sh && cd MiscATS && BASEDIR_ATS=`pwd`/USTreasuryCLOB python3 start_ats.py --ats USTreasuryCLOB/ust_ats.json" volumes: - - ./data_ats:/opt/DistributedATS/DataService/sql/sqlite - - ./logs_ats:/opt/DistributedATS/MiscATS/USTreasuryCLOB/logs + - ./logs_ats:/usr/local/MiscATS/USTreasuryCLOB/logs ports: - "15001:15001" - "16001:16001" - - "17001:17001" restart: unless-stopped # WebTrader Front-End diff --git a/docker/docker-compose-webtrader.yml b/docker/docker-compose-webtrader.yml new file mode 100644 index 0000000..3b74624 --- /dev/null +++ b/docker/docker-compose-webtrader.yml @@ -0,0 +1,12 @@ +version: '2' + +services: + # WebTrader Front-End + distributed_ats_webtrader: + container_name: distributed_ats_webtrader + image: ghcr.io/mkipnis/distributed_ats_webtrader:latest + volumes: + - ./webtrader_logs:/usr/local/tomcat/logs + ports: + - "8080:8080" + restart: no diff --git a/docker/dockerize_dats.sh b/docker/dockerize_dats.sh index 0175662..539bba4 100755 --- a/docker/dockerize_dats.sh +++ b/docker/dockerize_dats.sh @@ -3,10 +3,15 @@ set -x # Core +docker build -t ghcr.io/mkipnis/distributed_ats_deps:latest -f Docker.Build_DATS_Deps . docker build -t ghcr.io/mkipnis/distributed_ats:latest -f Docker.Build_Distributed_ATS . + +# Exchanges docker build --no-cache -t ghcr.io/mkipnis/dats_crypto_clob:latest -f Docker.Crypto_CLOB . docker build --no-cache -t ghcr.io/mkipnis/dats_ust_clob:latest -f Docker.UST_CLOB . +docker build --no-cache -t ghcr.io/mkipnis/multi_matching_engine_ats:latest -f Docker.MultiMatchingEngineATS . +docker push ghcr.io/mkipnis/distributed_ats_deps:latest docker push ghcr.io/mkipnis/distributed_ats:latest docker push ghcr.io/mkipnis/dats_crypto_clob:latest docker push ghcr.io/mkipnis/dats_ust_clob:latest diff --git a/docker/fixclient_docker.cfg b/docker/fixclient_docker.cfg index c6e2cbf..73c3f0b 100644 --- a/docker/fixclient_docker.cfg +++ b/docker/fixclient_docker.cfg @@ -3,7 +3,6 @@ ConnectionType=initiator TargetCompID=FIX_GWY_1 SocketConnectHost=distributed_ats #SocketConnectHost=127.0.0.1 -#SocketConnectHost=165.22.179.176 StartTime=00:00:00 EndTime=24:00:00 HeartBtInt=30 diff --git a/docker/start_ust_clob.sh b/docker/start_ust_clob.sh deleted file mode 100755 index 8642b2a..0000000 --- a/docker/start_ust_clob.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -trap cleanup 1 2 3 6 - -cleanup() -{ - echo "Caught Signal ... cleaning up..." - - $BASEDIR_ATS/scripts/stop_ats.sh - - echo "Done." - - exit 1; -} - -sleep 1 -$DATS_HOME/scripts/matchingengine.sh start matching_engine_MARKET_UST.ini - -sleep 1 -$DATS_HOME/scripts/dataservice.sh start data_service_a.ini -$DATS_HOME/scripts/dataservice.sh start data_service_b.ini - -sleep 1 -$DATS_HOME/scripts/fixgateway.sh start fix_gwy_1.cfg -$DATS_HOME/scripts/fixgateway.sh start fix_gwy_2.cfg - -while true; do - $DATS_HOME/scripts/dataservice.sh check data_service_a.ini - $DATS_HOME/scripts/dataservice.sh check data_service_b.ini - $DATS_HOME/scripts/matchingengine.sh check matching_engine_MARKET_UST.ini - $DATS_HOME/scripts/fixgateway.sh check fix_gwy_1.cfg - $DATS_HOME/scripts/fixgateway.sh check fix_gwy_2.cfg - - sleep 10; -done diff --git a/docker/ust_clob_env.sh b/docker/ust_clob_env.sh deleted file mode 100644 index df7da7e..0000000 --- a/docker/ust_clob_env.sh +++ /dev/null @@ -1,21 +0,0 @@ -export DATS_HOME=/opt/DistributedATS -export DEPS_HOME=/opt/distributed_ats_deps_build -export LD_LIBRARY_PATH=$DEPS_HOME/lib:$DATS_HOME/lib:$LD_LIBRARY_PATH -export LOG4CXX_CONFIGURATION=/opt/DistributedATS/config/log4cxx.xml - -#echo Specific Specific - -#echo Crypto ATS -#export BASEDIR_ATS=$DATS_HOME/MiscATS/CryptoCLOB -#export DATS_LOG_HOME=$BASEDIR_ATS/logs -#mkdir -p $BASEDIR_ATS/logs - -#echo US Treasuries -export BASEDIR_ATS=$DATS_HOME/MiscATS/USTreasuryCLOB -export DATS_LOG_HOME=$BASEDIR_ATS/logs -mkdir -p $BASEDIR_ATS/logs - -#echo US Treasuries -#export BASEDIR_ATS=$DATS_HOME/MiscATS/MultiMatchingEngineATS -#export DATS_LOG_HOME=$BASEDIR_ATS/logs -#mkdir -p $BASEDIR_ATS/logs diff --git a/download_deps_and_build_all.sh b/download_deps_and_build_all.sh deleted file mode 100755 index 7b7ef8f..0000000 --- a/download_deps_and_build_all.sh +++ /dev/null @@ -1,130 +0,0 @@ -#!/bin/bash - -set -euo pipefail - -set -x - -# Define directories -DATS_SOURCE_DIR="$(pwd)" -DATS_HOME="$HOME/DistributedATS" -DEPS_BUILD_DIR="$HOME/distributed_ats_deps_build" -INSTALL_DIR="${1:-$DEPS_BUILD_DIR}" -LD_LIBRARY_PATH="LD_LIBRARY_PATH" - -mkdir -p "$DEPS_BUILD_DIR" -mkdir -p "$DATS_HOME" - -# Mac-specific flags -CMAKE_FLAGS="" -if [[ "$OSTYPE" == "darwin"* ]]; then - export CXXFLAGS="-DWAZOO_64_BIT -std=c++11 -stdlib=libc++" - CMAKE_FLAGS="-G Xcode" - LD_LIBRARY_PATH="DYLD_LIBRARY_PATH" -fi - -# Dependency versions -FOONATHAN_MEMORY_PKG=0.7-3 -FAST_CDR_PKG=2.3.0 -ASIO_PKG=1-28-0 -FAST_DDS_PKG=3.2.1 -LOG4CXX_PKG=1.2.0 -QUICKFIX_PKG=1.15.1 -LIQUIBOOK_PKG=2.0.0 - -# Download helper -download_if_missing() { - local url=$1 - local dest=$2 - if [[ ! -f "$dest" ]]; then - echo "Downloading $(basename "$dest")..." - curl -L "$url" -o "$dest" || { - echo " Failed to download $(basename "$dest")." - exit 1 - } - fi -} - -# Download dependencies -download_if_missing "https://github.com/foonathan/memory/archive/refs/tags/v$FOONATHAN_MEMORY_PKG.tar.gz" "$DEPS_BUILD_DIR/memory-v$FOONATHAN_MEMORY_PKG.tar.gz" -download_if_missing "https://github.com/eProsima/Fast-CDR/archive/refs/tags/v$FAST_CDR_PKG.tar.gz" "$DEPS_BUILD_DIR/Fast-CDR-v$FAST_CDR_PKG.tar.gz" -download_if_missing "https://github.com/eProsima/Fast-DDS/archive/refs/tags/v$FAST_DDS_PKG.tar.gz" "$DEPS_BUILD_DIR/Fast-DDS-v$FAST_DDS_PKG.tar.gz" -#download_if_missing "https://github.com/chriskohlhoff/asio/archive/refs/tags/asio-$ASIO_PKG.tar.gz" "$DEPS_BUILD_DIR/asio-$ASIO_PKG.tar.gz" -download_if_missing "https://github.com/apache/logging-log4cxx/archive/refs/tags/rel/v$LOG4CXX_PKG.tar.gz" "$DEPS_BUILD_DIR/log4cxx-$LOG4CXX_PKG.tar.gz" -download_if_missing "https://github.com/quickfix/quickfix/archive/refs/tags/v$QUICKFIX_PKG.tar.gz" "$DEPS_BUILD_DIR/quickfix-v$QUICKFIX_PKG.tar.gz" -download_if_missing "https://github.com/enewhuis/liquibook/archive/refs/tags/$LIQUIBOOK_PKG.tar.gz" "$DEPS_BUILD_DIR/liquibook-$LIQUIBOOK_PKG.tar.gz" - -export INSTALL_PREFIX="$INSTALL_DIR" - -build_and_install() { - - local tarball="$1" - local dir="$2" - local configure_cmd="${3:-./configure --prefix=$INSTALL_PREFIX --exec-prefix=$INSTALL_PREFIX}" - local build_cmd="${4:-make install -j $(nproc)}" - - cd "$DEPS_BUILD_DIR" - [[ ! -d "$dir" ]] && tar xvf "$tarball" - cd "$dir" - - if [[ -f CMakeLists.txt ]]; then - mkdir -p build && cd build - cmake .. -DCMAKE_INSTALL_PREFIX="$INSTALL_PREFIX" -DBUILD_SHARED_LIBS=ON "$configure_cmd" - cmake --build . --target install - else - eval "$configure_cmd" - eval "$build_cmd" - fi - -} - -[[ ! -f $INSTALL_DIR/include/log4cxx/log4cxx.h ]] && build_and_install "$DEPS_BUILD_DIR/log4cxx-$LOG4CXX_PKG.tar.gz" "$DEPS_BUILD_DIR/logging-log4cxx-rel-v$LOG4CXX_PKG" "-DBUILD_TESTING=false" -[[ ! -f $INSTALL_DIR/include/foonathan_memory/foonathan/memory/config.hpp ]] && build_and_install "$DEPS_BUILD_DIR/memory-v$FOONATHAN_MEMORY_PKG.tar.gz" "memory-$FOONATHAN_MEMORY_PKG" "" -[[ ! -f $INSTALL_DIR/include/fastcdr/config.h ]] && build_and_install "$DEPS_BUILD_DIR/Fast-CDR-v$FAST_CDR_PKG.tar.gz" "Fast-CDR-$FAST_CDR_PKG" "" -#[[ ! -f $INSTALL_DIR/include/asio.hpp ]] && build_and_install "$DEPS_BUILD_DIR/asio-$ASIO_PKG.tar.gz" "asio-asio-$ASIO_PKG" "cd asio && ./autogen.sh && ./configure --prefix=$INSTALL_PREFIX --exec-prefix=$INSTALL_PREFIX}" -[[ ! -f $INSTALL_DIR/include/fastdds/config.hpp ]] && build_and_install "$DEPS_BUILD_DIR/Fast-DDS-v$FAST_DDS_PKG.tar.gz" "Fast-DDS-$FAST_DDS_PKG" "" -[[ ! -f $INSTALL_DIR/include/quickfix/config-all.h ]] && build_and_install "$DEPS_BUILD_DIR/quickfix-v$QUICKFIX_PKG.tar.gz" "quickfix-$QUICKFIX_PKG" "-DCMAKE_CXX_STANDARD=11 -DCMAKE_CXX_EXTENSIONS=OFF" -[[ ! -f $INSTALL_DIR/liquibook-$LIQUIBOOK_PKG/src/liquibook_export.h ]] && build_and_install "$DEPS_BUILD_DIR/liquibook-$LIQUIBOOK_PKG.tar.gz" "liquibook-$LIQUIBOOK_PKG" ":" ":" - - -# Generate environment script -cat < "$HOME/DistributedATS/dats_env.sh" -export DATS_HOME=\$HOME/DistributedATS -export DEPS_HOME=$INSTALL_DIR -export $LD_LIBRARY_PATH=\$DEPS_HOME/lib:\$DATS_HOME/lib:\$LD_LIBRARY_PATH -export LOG4CXX_CONFIGURATION=$DATS_HOME/config/log4cxx.xml - -#echo Specific Specific - -#echo Crypto ATS -export BASEDIR_ATS=\$DATS_HOME/MiscATS/CryptoCLOB -export DATS_LOG_HOME=\$BASEDIR_ATS/logs -mkdir -p \$BASEDIR_ATS/logs - -#echo US Treasuries -#export BASEDIR_ATS=\$DATS_HOME/MiscATS/USTreasuryCLOB -#export DATS_LOG_HOME=\$BASEDIR_ATS/logs -#mkdir -p \$BASEDIR_ATS/logs - -#echo US Treasuries -#export BASEDIR_ATS=\$DATS_HOME/MiscATS/MultiMatchingEngineATS -#export DATS_LOG_HOME=\$BASEDIR_ATS/logs -#mkdir -p \$BASEDIR_ATS/logs - -EOM - -cd $DATS_SOURCE_DIR - -# Build DistributedATS -mkdir -p build -cd build - -cmake $CMAKE_FLAGS .. \ - -Dfastcdr_DIR=$INSTALL_DIR/lib/cmake/fastcdr/ \ - -Dfastdds_DIR=$INSTALL_DIR/share/fastdds/cmake/ \ - -Dfoonathan_memory_DIR=$INSTALL_DIR/lib/foonathan_memory/cmake/ \ - -Dlog4cxx_DIR=$INSTALL_DIR/lib/cmake/log4cxx \ - -DCMAKE_INSTALL_PREFIX="$HOME/DistributedATS" \ - -DLIQUIBOOK_HOME="$INSTALL_DIR/liquibook-$LIQUIBOOK_PKG/src" \ - -DQUICKFIX_INSTALL_PREFIX="$INSTALL_DIR" - -cmake --build . --target install --config Debug