diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 59cec5f2..975267e4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,18 +7,34 @@ on: branches: [ main ] jobs: - moving-average-accumulator-test: - runs-on: ubuntu-latest - + itch_streamgen: + runs-on: ubuntu-24.04 steps: - - name: Checkout code - uses: actions/checkout@v3 + - uses: actions/checkout@v3 + + - name: Download Dependencies + run: | + sudo apt-get install ninja-build + sudo apt-get install valgrind + + - name: Configure Build Directory + run: | + cd itch_streamgen + mkdir -p build + cmake -G Ninja -S . -B build + + - name: Build + run: | + cd itch_streamgen + ninja -C build - - name: Install Dependencies + - name: Run Unit Tests run: | - sudo apt-get install -y verilator make + cd itch_streamgen + ninja runtests -C build + ninja memcheck -C build - - name: Run Simulation + - name: Run Valgrind run: | - cd mov_avg_acc - make test + cd itch_streamgen + ninja valgrind -C build diff --git a/.gitignore b/.gitignore index d3503922..f4087919 100644 --- a/.gitignore +++ b/.gitignore @@ -36,6 +36,7 @@ vivado/project/ *.str *.wdb *.backup +.cache/ # Clash generated files */clash/generated/ diff --git a/flake.nix b/flake.nix index 5ac37500..8b137891 100644 --- a/flake.nix +++ b/flake.nix @@ -1,35 +1 @@ -{ - description = "A dev/build env for punt-engine."; - inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11"; - - outputs = { - self, - nixpkgs, - }: let - supportedSystems = ["x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin"]; - forEachSupportedSystem = f: - nixpkgs.lib.genAttrs supportedSystems (system: - f { - pkgs = import nixpkgs {inherit system;}; - }); - in { - devShells = forEachSupportedSystem ({pkgs}: { - default = - pkgs.mkShell.override {stdenv = pkgs.llvmPackages_19.libcxxStdenv;} - { - packages = with pkgs; [ - llvmPackages_19.libcxxClang - llvmPackages_19.clang-tools - gdb - cmake - gtest - openssl - valgrind - ninja - verilator - ]; - }; - }); - }; -} diff --git a/include/constant.hpp b/include/constant.hpp deleted file mode 100644 index 0519ecba..00000000 --- a/include/constant.hpp +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/itch_streamgen/.DS_Store b/itch_streamgen/.DS_Store deleted file mode 100644 index a5f525dd..00000000 Binary files a/itch_streamgen/.DS_Store and /dev/null differ diff --git a/itch_streamgen/CMakeLists.txt b/itch_streamgen/CMakeLists.txt new file mode 100644 index 00000000..6a0008ea --- /dev/null +++ b/itch_streamgen/CMakeLists.txt @@ -0,0 +1,67 @@ +cmake_minimum_required(VERSION 3.28) +project(itch_streamgen) +set(CMAKE_CXX_STANDARD 23) +set(CMAKE_CXX_STANDARD_REQUIRED ON) +set(CMAKE_EXPORT_COMPILE_COMMAND ON) +set(CMAKE_COLOR_DIAGNOSTICS ON) + +set(CMAKE_EXPORT_COMPILE_COMMANDS + ON + CACHE INTERNAL "") + +if(CMAKE_EXPORT_COMPILE_COMMANDS) + set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES + ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) +endif() + +set(CMAKE_CXX_FLAGS "-std=c++23") + +add_compile_options(-Wall -Wextra -pedantic) + +include_directories(include) +add_definitions(-DSOME_DEFINITION) +add_subdirectory(src) + +enable_testing() +add_subdirectory(tests) + +include(FetchContent) + +FetchContent_Declare( + googletest + URL https://github.com/google/googletest/archive/refs/tags/release-1.12.1.zip) + +# For Windows: Prevent overriding the parent project's compiler/linker settings +set(gtest_force_shared_crt + ON + CACHE BOOL "" FORCE) + +FetchContent_MakeAvailable(googletest) + +include(CTest) + +add_custom_target( + memcheck + COMMAND ${CMAKE_CTEST_COMMAND} --force-new-ctest-process --test-action + memcheck + COMMAND ${CMAKE_COMMAND} -E cat + "${CMAKE_BINARY_DIR}/Testing/Temporary/MemoryChecker.*.log" + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") + +add_custom_target( + runtests + COMMAND ${CMAKE_CTEST_COMMAND} --output-on-failure --schedule-random + WORKING_DIRECTORY "${CMAKE_BINARY_DIR}") + +add_custom_target( + valgrind + COMMAND valgrind --leak-check=full ./src/itch_streamgen + WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) + +function(analyze target) + get_target_property(SRCs ${target} SOURCES) + add_library(${target}_analyze OBJECT EXCLUDE_FROM_ALL ${SRCs}) + set_target_properties( + ${target}_analyze PROPERTIES COMPILE_OPTIONS "--analyze" + EXCLUDE_FROM_DEFAULT_BUILD true) +endfunction() diff --git a/itch_streamgen/Makefile b/itch_streamgen/Makefile deleted file mode 100644 index 5b975a33..00000000 --- a/itch_streamgen/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -# Top-level module name -TOP_MODULE = moving_average_accumulator - -# Verilog source files -VERILOG_SOURCES = $(TOP_MODULE).sv - -# C++ testbench file -TESTBENCH = tb.cpp - -# Output directory -OBJ_DIR = obj_dir - -# Simulation executable -SIM_EXE = $(OBJ_DIR)/V$(TOP_MODULE) - -# Default target -.PHONY: all -all: run_simulation - -# Compile Verilog and C++ sources -$(SIM_EXE): $(VERILOG_SOURCES) $(TESTBENCH) - verilator --cc $(VERILOG_SOURCES) --exe $(TESTBENCH) --trace - make -j -C $(OBJ_DIR) -f V$(TOP_MODULE).mk V$(TOP_MODULE) - -# Run the simulation -.PHONY: run_simulation -run_simulation: $(SIM_EXE) - ./$(SIM_EXE) - -# Clean up generated files -.PHONY: clean -clean: - rm -rf $(OBJ_DIR) waveform.vcd - -# Phony target for CI testing (exits with non-zero status on failure) -.PHONY: test -test: run_simulation diff --git a/itch_streamgen/README.md b/itch_streamgen/README.md new file mode 100644 index 00000000..1c06c70f --- /dev/null +++ b/itch_streamgen/README.md @@ -0,0 +1,27 @@ +## Development + +Before anything, build and enter the development environment with `nix develop`. + +### Run CMake +```bash +cmake -G Ninja -S . -B build +``` + +### Build +```bash +ninja -C build +``` + +### Run Tests +```bash +ninja runtests -C build +ninja memcheck -C build +ninja valgrind -C build +``` + +### Clangd Setup +I'm using the Clangd C++ LSP. Run the command below to make it aware of your linked libraries. + +```bash +cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug -DCMAKE_EXPORT_COMPILE_COMMANDS=1 +``` diff --git a/itch_streamgen/flake.lock b/itch_streamgen/flake.lock new file mode 100644 index 00000000..b38790c2 --- /dev/null +++ b/itch_streamgen/flake.lock @@ -0,0 +1,27 @@ +{ + "nodes": { + "nixpkgs": { + "locked": { + "lastModified": 1742751704, + "narHash": "sha256-rBfc+H1dDBUQ2mgVITMGBPI1PGuCznf9rcWX/XIULyE=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "f0946fa5f1fb876a9dc2e1850d9d3a4e3f914092", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-24.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/itch_streamgen/flake.nix b/itch_streamgen/flake.nix new file mode 100644 index 00000000..55713f06 --- /dev/null +++ b/itch_streamgen/flake.nix @@ -0,0 +1,33 @@ +{ + description = "A dev/build env for itch_streamgen."; + + inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.11"; + + outputs = { + self, + nixpkgs, + }: let + supportedSystems = ["x86_64-linux" "aarch64-linux" "x86_64-darwin" "aarch64-darwin"]; + forEachSupportedSystem = f: + nixpkgs.lib.genAttrs supportedSystems (system: + f { + pkgs = import nixpkgs {inherit system;}; + }); + in { + devShells = forEachSupportedSystem ({pkgs}: { + default = + pkgs.mkShell.override {stdenv = pkgs.llvmPackages_19.libcxxStdenv;} + { + packages = with pkgs; [ + llvmPackages_19.libcxxClang + llvmPackages_19.clang-tools + gdb + cmake + gtest + valgrind + ninja + ]; + }; + }); + }; +} diff --git a/itch_streamgen/include/constant.hpp b/itch_streamgen/include/constant.hpp new file mode 100644 index 00000000..5d181b2b --- /dev/null +++ b/itch_streamgen/include/constant.hpp @@ -0,0 +1,123 @@ +#pragma once +#include +#include + +enum class MessageType : uint8_t { + SystemEvent = 'S', + StockDirectory = 'R', + StockTradingAction = 'H', + RegSHORestriction = 'Y', + MarketParticipantPosition = 'L', + MWCBDeclineLevel = 'V', + MWCBStatus = 'W', + IPOQuotingPeriodUpdate = 'K', + LULDAuctionCollar = 'J', + OperationHalt = 'h', + AddOrder = 'A', + AddOrderWithMPID = 'F', + OrderExecuted = 'E', + OrderExecutedWithPrice = 'C', + OrderCancel = 'X', + OrderDelete = 'D', + OrderReplace = 'U', + Trade = 'P', + CrossTrade = 'Q', + BrokenTrade = 'B', + NOII = 'I', + RPII = 'N', + DRWCRPD = 'O', +}; + +enum class SystemEventCode : uint8_t { + StartOfMessages = + 'O', // Outside of time stamp messages, the start of day message is the + // first message sent in any trading day. + StartOfSystem = 'S', // This message indicates that NASDAQ is open and ready + // to start accepting orders. + StartOfMarket = 'Q', // This message is intended to indicate that Market Hours + // orders are available for execution. + EndOfMarket = 'M', // This message is intended to indicate that Market Hours + // orders are no longer available for execution. + EndOfSystem = 'E', // It indicates that Nasdaq is now closed and will not + // accept any new orders today. + EndOfMessages = + 'C', // This is always the last message sent in any trading day. +}; + +enum class TradingStateCode : uint8_t { + Halted = 'H', + Paused = 'P', + Quotation = 'Q', + Trading = 'T', +}; + +using Price4 = uint32_t; + +enum class Side : uint8_t { + Buy = 'B', + Sell = 'S', +}; + +inline std::string toString(MessageType type) { + switch (type) { + case MessageType::SystemEvent: + return "System Event"; + case MessageType::StockDirectory: + return "Stock Directory"; + case MessageType::StockTradingAction: + return "Stock Trading Action"; + case MessageType::RegSHORestriction: + return "RegSHO Restriction"; + case MessageType::MarketParticipantPosition: + return "Market Participant Position"; + case MessageType::MWCBDeclineLevel: + return "MWCB Decline Level"; + case MessageType::MWCBStatus: + return "MWCB Status"; + case MessageType::IPOQuotingPeriodUpdate: + return "IPO Quoting Period Update"; + case MessageType::LULDAuctionCollar: + return "LULD Auction Collar"; + case MessageType::OperationHalt: + return "Operation Halt"; + case MessageType::AddOrder: + return "Add Order"; + case MessageType::AddOrderWithMPID: + return "Add Order with MPID"; + case MessageType::OrderExecuted: + return "Order Executed"; + case MessageType::OrderExecutedWithPrice: + return "Order Executed with Price"; + case MessageType::OrderCancel: + return "Order Cancel"; + case MessageType::OrderDelete: + return "Order Delete"; + case MessageType::OrderReplace: + return "Order Replace"; + case MessageType::Trade: + return "Trade"; + case MessageType::CrossTrade: + return "Cross Trade"; + case MessageType::BrokenTrade: + return "Broken Trade"; + case MessageType::NOII: + return "Net Order Imbalance Indicator"; + case MessageType::RPII: + return "Retail Price Improvement Indicator"; + case MessageType::DRWCRPD: + return "Direct Listing With Capital Raise Price Discovery"; + default: + return "Unknown"; + } +} + +struct Timestamp { + uint64_t ns_since_midnight; + + static Timestamp now() { return Timestamp{0}; } + + void advanceBy(uint64_t ns) { + ns_since_midnight += ns; + ns_since_midnight &= 0xFFFFFFFFFFFF; // Mask to 48 bits + } +}; diff --git a/itch_streamgen/include/message.hpp b/itch_streamgen/include/message.hpp new file mode 100644 index 00000000..11e118b5 --- /dev/null +++ b/itch_streamgen/include/message.hpp @@ -0,0 +1,303 @@ +#pragma once +#include +#include + +using Price4 = uint32_t; + +#pragma pack(push, 1) + +struct SystemEventMessage { + char message_type; // 1 byte - 'S' + uint16_t stock_locate; // 2 bytes - Always 0 + uint16_t tracking_number; // 2 bytes - Nasdaq internal tracking number + std::array timestamp; // 6 bytes - Nanoseconds since midnight + char event_code; // 1 byte - 'O', 'S', 'Q', 'M', 'E', 'C' +}; +struct StockDirectoryMessage { + char message_type; // 1 byte - 'R' + uint16_t stock_locate; // 2 bytes - Locate Code uniquely assinged to the + // security symbol + uint16_t tracking_number; // 2 bytes - Nasdaq internal tracking number + std::array timestamp; // 6 bytes - Nanoseconds since midnight + std::array stock; // 8 bytes - stock symbol right padded with spaces + char market_category; // 1 byte - NASDAQ-Listed'G': Global Market, 'Q': + // Global Select Market, 'S': Capital Markets + // NON-NASDAQ-Listed 'N': New York Stock Exchange, 'A': + // NYSE American, 'P': NYSE Arca, 'Z': BATS Z Exchange, + // 'V': Investors Exchange LLC, + char financial_status_indicator; // 1 byte - 'D': Deficient, 'E': Delinquent, + // 'Q': Bankrupt, 'S': Suspended, 'G': + // Deficient and Bankrupt, 'H': Deficient and + // Delinquent, 'J': Delinquent and Bankrupt, + // 'K': Deficient, Delinquent and Bankrupt, + // 'C': Creations/Redemptions Suspended for + // ETP, 'N': Normal (not Deficient, + // Delinquent, or Bankrupt), ' ' (space): Not + // available (non-Nasdaq-listed) + uint32_t round_lot_size; // 4 bytes - number of shares that represents a round + // lot for the issue + char round_lots_only; // 1 byte - 'Y': Round lots only, 'N': All sizes + char issue_classification; // 1 byte - identifes the security class + std::array + issue_subtype; // 2 bytes - identifies the security subtype + char authenticity; // 1 byte - 'P' - live/production, 'T' - test + char short_sale_threshold_indicator; // 1 byte - Indicates if a security is + // mandatory close out of short sales Y - + // restricted, N - not restricted + char ipo_flag; // 1 byte- set for ipo or not (Y or N) + char + LULDReference_price_tier; // 1 byte - indicates which limit up/ limit down + // price band calculation parameter is used + char etp_flag; // 1 byte - indicates if security is exchange traded product (Y + // or N) + uint32_t etp_leverage_factor; // 4 bytes - indicates the levergae factor (e.g. + // 3 indicates ETF will increase/decrease by 3) + char inverse_indicator; // 1 byte - Y or N for inverse ETP +}; +struct StockTradingActionMessage { + char message_type; // 1 byte - 'H' + uint16_t stock_locate; // 2 bytes - Locate code identifying the security + uint16_t tracking_number; // 2 bytes - Nasdaq internal tracking number + std::array timestamp; // 6 bytes - Nanoseconds since midnight + std::array stock; // 8 bytes - stock symbol right padded with spaces + char trading_state; // 1 byte - "H":Halted, "P":Paused, "Q":Quotation, + // "T":Trading on NASDAQ + char reserved; // 1 byte - Reserved + std::array reason; // 4 bytes - trading action reason +}; +struct RegSHORestrictionMessage { + char message_type; // 1 byte - 'Y' + uint16_t stock_locate; // 2 bytes - Locate code + uint16_t tracking_number; // 2 bytes - Nasdaq tracking number + std::array timestamp; // 6 bytes - Nanoseconds since midnight + std::array stock; // 8 bytes - stock symbol right padded with spaces + char reg_sho_action; // 1 byte - '0', '1', or '2' +}; +struct MarketParticipantPositionMessage { + char message_type; // 1 byte - 'L' + uint16_t stock_locate; // 2 bytes + uint16_t tracking_number; // 2 bytes + std::array timestamp; // 6 bytes - Nanoseconds since midnight + std::array mpid; // 4 bytes + std::array stock; // 8 bytes + char primary_market_maker; // 1 byte - 'Y' or 'N' + char market_maker_mode; // 1 byte - 'N', 'P', 'S', 'R', 'L' + char market_participant_state; // 1 byte - 'A', 'E', 'W', 'S', 'D' +}; +struct MWCBStatusMessage { + char message_type; // 1 byte - 'W' + uint16_t stock_locate; // 2 bytes - Always 0 + uint16_t tracking_number; // 2 bytes - Nasdaq tracking number + std::array timestamp; // 6 bytes - Nanoseconds since midnight + char breached_level; // 1 byte - '1', '2', or '3' +}; +struct IPOQuotingPeriodUpdateMessage { + char message_type; // 1 byte - 'K' + uint16_t stock_locate; // 2 bytes - Always 0 + uint16_t tracking_number; // 2 bytes - Nasdaq tracking number + std::array timestamp; // 6 bytes - Nanoseconds since midnight + std::array stock; // 8 bytes - stock symbol right padded with spaces + uint32_t ipo_quotation_release_time; // 4 bytes - Seconds since midnight + char ipo_quotation_release_qualifier; // 1 byte - 'A': Anticipated Quotation + // Release Time or 'C': Cancelled + uint32_t ipo_price; // 4 bytes - Price (4 decimal digits implied) +}; +struct LULDAuctionCollarMessage { + char message_type; // 1 byte - 'J' + uint16_t stock_locate; // 2 bytes + uint16_t tracking_number; // 2 bytes + std::array timestamp; // 6 bytes - Nanoseconds since midnight + std::array stock; // 8 bytes - stock symbol right padded with spaces + uint32_t auction_collar_ref_price; // 4 bytes - Reference price used to set + // auction collars + uint32_t upper_auction_collar_price; // 4 bytes - Upper collar price + uint32_t lower_auction_collar_price; // 4 bytes - Lower collar price + uint32_t auction_collar_extension; // 4 bytes - Number of extensions to the + // Reopening Auction +}; +struct OperationalHaltMessage { + char message_type; // 1 byte - 'h' + uint16_t stock_locate; // 2 bytes + uint16_t tracking_number; // 2 bytes + std::array timestamp; // 6 bytes - Nanoseconds since midnight + std::array stock; // 8 bytes - stock symbol right padded with spaces + char market_code; // 1 byte - 'Q':NASDAQ, 'B':BX, or 'X':PSX + char operational_halt_action; // 1 byte - 'H' (halted), 'T' (trading resumed) +}; +struct AddOrderMessage { + char message_type; // 1 byte - 'A' + uint16_t stock_locate; // 2 bytes + uint16_t tracking_number; // 2 bytes + std::array timestamp; // 6 bytes - Nanoseconds since midnight + uint64_t order_reference_number; // 8 bytes - Unique order ID + char side; // 1 byte - 'B' or 'S' + uint32_t shares; // 4 bytes - Total shares in the order + std::array stock; // 8 bytes - stock symbol right padded with spaces + uint32_t price; // 4 bytes - Price (in 4-digit fixed decimal format) +}; +struct AddOrderWithMPIDMessage { + char message_type; // 1 byte - 'F' + uint16_t stock_locate; // 2 bytes + uint16_t tracking_number; // 2 bytes + std::array timestamp; // 6 bytes - Nanoseconds since midnight + uint64_t order_reference_number; // 8 bytes - Unique order ID + char side; // 1 byte - 'B' or 'S' + uint32_t shares; // 4 bytes + std::array stock; // 8 bytes - Stock symbol, right-padded + uint32_t price; // 4 bytes - Fixed-point price + std::array attribution; // 4 bytes - MPID (e.g., "GSCO", "JPMX") +}; +struct OrderExecutedMessage { + char message_type; // 1 byte - 'E' + uint16_t stock_locate; // 2 bytes + uint16_t tracking_number; // 2 bytes + std::array timestamp; // 6 bytes - Nanoseconds since midnight + uint64_t order_reference_number; // 8 bytes - Unique order ID + uint32_t executed_shares; // 4 bytes - Number of shares executed + uint64_t match_number; // 8 bytes - Unique match ID for the trade +}; +struct OrderExecutedWithPriceMessage { + char message_type; // 1 byte - 'C' + uint16_t stock_locate; // 2 bytes + uint16_t tracking_number; // 2 bytes + std::array timestamp; // 6 bytes - Nanoseconds since midnight + uint64_t order_reference_number; // 8 bytes + uint32_t executed_shares; // 4 bytes + uint64_t match_number; // 8 bytes - Execution ID + char printable; // 1 byte - 'Y' or 'N' + uint32_t execution_price; // 4 bytes - Price (e.g., 101250 = $10.1250) +}; +struct OrderCancelMessage { + char message_type; // 1 byte - 'X' + uint16_t stock_locate; // 2 bytes + uint16_t tracking_number; // 2 bytes + std::array timestamp; // 6 bytes - Nanoseconds since midnight + uint64_t order_reference_number; // 8 bytes + uint32_t cancelled_shares; // 4 bytes +}; +struct OrderDeleteMessage { + char message_type; // 1 byte - 'D' + uint16_t stock_locate; // 2 bytes + uint16_t tracking_number; // 2 bytes + std::array timestamp; // 6 bytes - Nanoseconds since midnight + uint64_t order_reference_number; // 8 bytes +}; +struct OrderReplaceMessage { + char message_type; // 1 byte - 'U' + uint16_t stock_locate; // 2 bytes + uint16_t tracking_number; // 2 bytes + std::array timestamp; // 6 bytes - Nanoseconds since midnight + uint64_t original_order_ref; // 8 bytes - Reference to the original order + uint64_t new_order_ref; // 8 bytes - New reference number for the replacement + uint32_t shares; // 4 bytes - New total displayed quantity + uint32_t price; // 4 bytes - New price (in fixed-point format) +}; +struct TradeMessage { + char message_type; // 1 byte - 'P' + uint16_t stock_locate; // 2 bytes + uint16_t tracking_number; // 2 bytes + std::array timestamp; // 6 bytes - Nanoseconds since midnight + uint64_t order_reference_number; // 8 bytes - May be zero + char side; // 1 byte - 'B' or 'S' (typically 'B' after 2014) + uint32_t shares; // 4 bytes + std::array stock; // 8 bytes - Stock symbol, right-padded + uint32_t price; // 4 bytes - Price (fixed-point, 4 decimals) + uint64_t match_number; // 8 bytes - Unique match ID +}; +struct CrossTradeMessage { + char message_type; // 1 byte - 'Q' + uint16_t stock_locate; // 2 bytes + uint16_t tracking_number; // 2 bytes + std::array timestamp; // 6 bytes - Nanoseconds since midnight + uint64_t shares; // 8 bytes - Shares matched in cross + std::array stock; // 8 bytes - Stock symbol, right-padded + uint32_t cross_price; // 4 bytes - Fixed-point price + uint64_t match_number; // 8 bytes - Unique match ID + char cross_type; // 1 byte - 'O', 'C', or 'H' +}; +struct BrokenTradeMessage { + char message_type; // 1 byte - 'B' + uint16_t stock_locate; // 2 bytes + uint16_t tracking_number; // 2 bytes + std::array timestamp; // 6 bytes - Nanoseconds since midnight + uint64_t match_number; // 8 bytes - Match ID of broken trade +}; +struct NOIIMessage { + char message_type; // 1 byte - 'I' + uint16_t stock_locate; // 2 bytes + uint16_t tracking_number; // 2 bytes + std::array timestamp; // 6 bytes - Nanoseconds since midnight + uint64_t paired_shares; // 8 bytes + uint64_t imbalance_shares; // 8 bytes + char imbalance_direction; // 1 byte - 'B', 'S', 'N', 'O', 'P' + std::array stock; // 8 bytes - Stock symbol, right-padded + uint32_t far_price; // 4 bytes - For cross orders only + uint32_t near_price; // 4 bytes - For continuous + cross orders + uint32_t current_reference_price; // 4 bytes - Price used for NOII calculation + char cross_type; // 1 byte - 'O', 'C', 'H', 'A' + char price_variation_indicator; // 1 byte - 'L', '1'–'9', 'A', 'B', 'C', or ' + // ' (space) +}; +struct RetailPriceImprovementIndicator { + char message_type; // 1 byte - 'N' + uint16_t stock_locate; // 2 bytes + uint16_t tracking_number; // 2 bytes + std::array timestamp; // 6 bytes - Nanoseconds since midnight + std::array stock; // 8 bytes - Stock symbol, right-padded + char interest_flag; // 1 byte - 'B', 'S', 'A', or 'N' +}; +struct DRWCRPDMessage { + char message_type; // 1 byte - 'O' + uint16_t stock_locate; // 2 bytes + uint16_t tracking_number; // 2 bytes + std::array timestamp; // 6 bytes - Nanoseconds since midnight + std::array stock; // 8 bytes - Stock symbol, right-padded + char open_eligibility_status; // 1 byte - 'Y' or 'N' + uint32_t min_allowable_price; // 4 bytes + uint32_t max_allowable_price; // 4 bytes + uint32_t near_execution_price; // 4 bytes + uint64_t near_execution_time; // 8 bytes + uint32_t lower_price_collar; // 4 bytes + uint32_t upper_price_collar; // 4 bytes +}; +#pragma pack(pop) + +static_assert(sizeof(SystemEventMessage) == 12, + "SystemEventMessage size is incorrect"); +static_assert(sizeof(StockDirectoryMessage) == 39, + "StockDirectoryMessage size is incorrect"); +static_assert(sizeof(StockTradingActionMessage) == 25, + "StockTradingActionMessage size is incorrect"); +static_assert(sizeof(RegSHORestrictionMessage) == 20, + "RegSHORestrictionMessage size is incorrect"); +static_assert(sizeof(MarketParticipantPositionMessage) == 26, + "MarketParticipantPositionMessage size is incorrect"); +static_assert(sizeof(MWCBStatusMessage) == 12, + "MWCBStatusMessage size is incorrect"); +static_assert(sizeof(IPOQuotingPeriodUpdateMessage) == 28, + "IPOQuotingPeriodUpdateMessage size is incorrect"); +static_assert(sizeof(LULDAuctionCollarMessage) == 35, + "LULDAuctionCollarMessage size is incorrect"); +static_assert(sizeof(AddOrderMessage) == 36, + "AddOrderNoMPIDMessage size is incorrect"); +static_assert(sizeof(AddOrderWithMPIDMessage) == 40, + "AddOrderWithMPIDMessage size is incorrect"); +static_assert(sizeof(OrderExecutedMessage) == 31, + "OrderExecutedMessage size is incorrect"); +static_assert(sizeof(OrderExecutedWithPriceMessage) == 36, + "OrderExecutedWithPriceMessage size is incorrect"); +static_assert(sizeof(OrderCancelMessage) == 23, + "OrderCancelMessage size is incorrect"); +static_assert(sizeof(OrderDeleteMessage) == 19, + "OrderDeleteMessage size is incorrect"); +static_assert(sizeof(OrderReplaceMessage) == 35, + "OrderReplaceMessage size is incorrect"); +static_assert(sizeof(TradeMessage) == 44, "TradeMessage size is incorrect"); +static_assert(sizeof(CrossTradeMessage) == 40, + "CrossTradeMessage size is incorrect"); +static_assert(sizeof(BrokenTradeMessage) == 19, + "BrokenTradeMessage size is incorrect"); +static_assert(sizeof(NOIIMessage) == 50, "NOIIMessage size is incorrect"); +static_assert(sizeof(RetailPriceImprovementIndicator) == 20, + "RetailPriceImprovementIndicator size is incorrect"); +static_assert(sizeof(DRWCRPDMessage) == 48, "DRWCRPDMessage size is incorrect"); diff --git a/itch_streamgen/src/.DS_Store b/itch_streamgen/src/.DS_Store deleted file mode 100644 index 9c053302..00000000 Binary files a/itch_streamgen/src/.DS_Store and /dev/null differ diff --git a/itch_streamgen/src/CMakeLists.txt b/itch_streamgen/src/CMakeLists.txt new file mode 100644 index 00000000..e170cabf --- /dev/null +++ b/itch_streamgen/src/CMakeLists.txt @@ -0,0 +1,7 @@ +set(SOURCE_FILES generator.cpp) + +add_library(itch_streamgen_lib ${SOURCE_FILES}) + +add_executable(itch_streamgen main.cpp) + +target_link_libraries(itch_streamgen itch_streamgen_lib) diff --git a/itch_streamgen/src/generator.cpp b/itch_streamgen/src/generator.cpp new file mode 100644 index 00000000..4ba561e4 --- /dev/null +++ b/itch_streamgen/src/generator.cpp @@ -0,0 +1,504 @@ +#include // For std::min, std::copy_n +#include +#include +#include // For memcpy, memset +#include +#include +#include +#include +#include + +#include "constant.hpp" // ITCH constants +#include "message.hpp" // ITCH protocol message struct + +// Helper function: packs a 64-bit timestamp (with only the lower 48 bits valid) +// into a std::array in big-endian order +void packTimestamp(std::array &dest, uint64_t timestamp) { + for (int i = 0; i < 6; ++i) { + dest[i] = static_cast((timestamp >> (8 * (5 - i))) & 0xFF); + } +} + +// Helper template to pack a string into a fixed-size std::array +// Padding with spaces if needed. +template +void packString(std::array &dest, const std::string &s) { + std::fill(dest.begin(), dest.end(), ' '); + size_t len = std::min(s.size(), static_cast(N)); + std::copy_n(s.begin(), len, dest.begin()); +} + +// SystemEventMessage +std::vector generateSystemEventMessage(uint16_t tracking_number, + uint64_t timestamp, + char event_code) { + SystemEventMessage msg{}; + msg.message_type = static_cast(MessageType::SystemEvent); + msg.stock_locate = 0; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + msg.event_code = event_code; + + std::vector message(sizeof(SystemEventMessage)); + std::memcpy(message.data(), &msg, sizeof(SystemEventMessage)); + return message; +} + +// StockDirectoryMessage +std::vector generateStockDirectoryMessage( + uint16_t stock_locate, uint16_t tracking_number, uint64_t timestamp, + const std::string &stock, char market_category, + char financial_status_indicator, uint32_t round_lot_size, + char round_lots_only, char issue_classification, + const std::string &issue_subtype, char authenticity, + char short_sale_threshold_indicator, char ipo_flag, + char LULDReference_price_tier, char etp_flag, uint32_t etp_leverage_factor, + char inverse_indicator) { + StockDirectoryMessage msg{}; + msg.message_type = static_cast(MessageType::StockDirectory); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + packString(msg.stock, stock); + msg.market_category = market_category; + msg.financial_status_indicator = financial_status_indicator; + msg.round_lot_size = round_lot_size; + msg.round_lots_only = round_lots_only; + msg.issue_classification = issue_classification; + std::fill(msg.issue_subtype.begin(), msg.issue_subtype.end(), ' '); + size_t len = std::min(issue_subtype.size(), msg.issue_subtype.size()); + std::copy_n(issue_subtype.begin(), len, msg.issue_subtype.begin()); + msg.authenticity = authenticity; + msg.short_sale_threshold_indicator = short_sale_threshold_indicator; + msg.ipo_flag = ipo_flag; + msg.LULDReference_price_tier = LULDReference_price_tier; + msg.etp_flag = etp_flag; + msg.etp_leverage_factor = etp_leverage_factor; + msg.inverse_indicator = inverse_indicator; + + std::vector message(sizeof(StockDirectoryMessage)); + std::memcpy(message.data(), &msg, sizeof(StockDirectoryMessage)); + return message; +} + +// StockTradingActionMessage +std::vector generateStockTradingActionMessage( + uint16_t stock_locate, uint16_t tracking_number, uint64_t timestamp, + const std::string &stock, char trading_state, char reserved, + const std::string &action_reason) { + StockTradingActionMessage msg{}; + msg.message_type = static_cast(MessageType::StockTradingAction); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + packString(msg.stock, stock); + msg.trading_state = trading_state; + msg.reserved = reserved; + + std::fill(msg.reason.begin(), msg.reason.end(), ' '); + size_t len = std::min(action_reason.size(), msg.reason.size()); + std::copy_n(action_reason.begin(), len, msg.reason.begin()); + + std::vector message(sizeof(StockTradingActionMessage)); + std::memcpy(message.data(), &msg, sizeof(StockTradingActionMessage)); + return message; +} + +// Reg SHO Restriction Message +std::vector generateRegSHORestrictionMessage(uint16_t stock_locate, + uint16_t tracking_number, + uint64_t timestamp, + const std::string &stock, + char reg_sho_action) { + RegSHORestrictionMessage msg{}; + msg.message_type = static_cast(MessageType::RegSHORestriction); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + packString(msg.stock, stock); + msg.reg_sho_action = reg_sho_action; + + std::vector message(sizeof(RegSHORestrictionMessage)); + std::memcpy(message.data(), &msg, sizeof(RegSHORestrictionMessage)); + return message; +} + +// Market Participant Position Message +std::vector generateMarketParticipantPositionMessage( + uint16_t stock_locate, uint16_t tracking_number, uint64_t timestamp, + const std::string &mpid, const std::string &stock, + char primary_market_maker, char market_maker_mode, + char market_participant_state) { + MarketParticipantPositionMessage msg{}; + msg.message_type = static_cast(MessageType::MarketParticipantPosition); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + std::fill(msg.mpid.begin(), msg.mpid.end(), ' '); + size_t len_mpid = std::min(mpid.size(), msg.mpid.size()); + std::copy_n(mpid.begin(), len_mpid, msg.mpid.begin()); + packString(msg.stock, stock); + msg.primary_market_maker = primary_market_maker; + msg.market_maker_mode = market_maker_mode; + msg.market_participant_state = market_participant_state; + + std::vector message(sizeof(MarketParticipantPositionMessage)); + std::memcpy(message.data(), &msg, sizeof(MarketParticipantPositionMessage)); + return message; +} + +// MWCB Status Message +std::vector generateMWCBStatusMessage(uint16_t tracking_number, + uint64_t timestamp, + char breached_level) { + MWCBStatusMessage msg{}; + msg.message_type = static_cast(MessageType::MWCBStatus); + msg.stock_locate = 0; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + msg.breached_level = breached_level; + + std::vector message(sizeof(MWCBStatusMessage)); + std::memcpy(message.data(), &msg, sizeof(MWCBStatusMessage)); + return message; +} + +// IPO Quoting Period Update Message +std::vector generateIPOQuotingPeriodUpdateMessage( + uint16_t tracking_number, uint64_t timestamp, const std::string &stock, + uint32_t ipo_quotation_release_time, char ipo_quotation_release_qualifier, + uint32_t ipo_price) { + IPOQuotingPeriodUpdateMessage msg{}; + msg.message_type = static_cast(MessageType::IPOQuotingPeriodUpdate); + msg.stock_locate = 0; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + packString(msg.stock, stock); + msg.ipo_quotation_release_time = ipo_quotation_release_time; + msg.ipo_quotation_release_qualifier = ipo_quotation_release_qualifier; + msg.ipo_price = ipo_price; + + std::vector message(sizeof(IPOQuotingPeriodUpdateMessage)); + std::memcpy(message.data(), &msg, sizeof(IPOQuotingPeriodUpdateMessage)); + return message; +} + +// LULD Auction Collar Message +std::vector generateLULDAuctionCollarMessage( + uint16_t stock_locate, uint16_t tracking_number, uint64_t timestamp, + const std::string &stock, uint32_t auction_collar_ref_price, + uint32_t upper_auction_collar_price, uint32_t lower_auction_collar_price, + uint32_t auction_collar_extension) { + LULDAuctionCollarMessage msg{}; + msg.message_type = static_cast(MessageType::LULDAuctionCollar); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + packString(msg.stock, stock); + msg.auction_collar_ref_price = auction_collar_ref_price; + msg.upper_auction_collar_price = upper_auction_collar_price; + msg.lower_auction_collar_price = lower_auction_collar_price; + msg.auction_collar_extension = auction_collar_extension; + + std::vector message(sizeof(LULDAuctionCollarMessage)); + std::memcpy(message.data(), &msg, sizeof(LULDAuctionCollarMessage)); + return message; +} + +// OperationalHaltMessage + +std::vector +generateOperationalHaltMessage(uint16_t stock_locate, uint16_t tracking_number, + uint64_t timestamp, const std::string &stock, + char market_code, char operational_halt_action) { + OperationalHaltMessage msg{}; + msg.message_type = static_cast(MessageType::OperationHalt); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + packString(msg.stock, stock); + msg.market_code = market_code; + msg.operational_halt_action = operational_halt_action; + + std::vector message(sizeof(OperationalHaltMessage)); + std::memcpy(message.data(), &msg, sizeof(OperationalHaltMessage)); + return message; +} + +std::vector generateAddOrderMessage(uint64_t timestamp, + uint64_t orderRef, uint8_t side, + uint32_t shares, + const std::string &stock, + uint32_t price) { + AddOrderMessage msg{}; + msg.message_type = static_cast(MessageType::AddOrder); + msg.stock_locate = 0; + msg.tracking_number = 0; + packTimestamp(msg.timestamp, timestamp); + msg.order_reference_number = orderRef; + msg.side = static_cast(side); + msg.shares = shares; + packString(msg.stock, stock); + msg.price = price; + + std::vector message(sizeof(AddOrderMessage)); + std::memcpy(message.data(), &msg, sizeof(AddOrderMessage)); + return message; +} + +// Add Order With MPID Message +std::vector generateAddOrderWithMPIDMessage( + uint16_t stock_locate, uint16_t tracking_number, uint64_t timestamp, + uint64_t orderRef, uint8_t side, uint32_t shares, const std::string &stock, + uint32_t price, const std::string &attribution) { + AddOrderWithMPIDMessage msg{}; + msg.message_type = static_cast(MessageType::AddOrderWithMPID); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + msg.order_reference_number = orderRef; + msg.side = static_cast(side); + msg.shares = shares; + packString(msg.stock, stock); + msg.price = price; + std::fill(msg.attribution.begin(), msg.attribution.end(), ' '); + size_t len = std::min(attribution.size(), msg.attribution.size()); + std::copy_n(attribution.begin(), len, msg.attribution.begin()); + + std::vector message(sizeof(AddOrderWithMPIDMessage)); + std::memcpy(message.data(), &msg, sizeof(AddOrderWithMPIDMessage)); + return message; +} + +// Order Executed Message +std::vector +generateOrderExecutedMessage(uint16_t stock_locate, uint16_t tracking_number, + uint64_t timestamp, uint64_t orderRef, + uint32_t executed_shares, uint64_t match_number) { + OrderExecutedMessage msg{}; + msg.message_type = static_cast(MessageType::OrderExecuted); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + msg.order_reference_number = orderRef; + msg.executed_shares = executed_shares; + msg.match_number = match_number; + + std::vector message(sizeof(OrderExecutedMessage)); + std::memcpy(message.data(), &msg, sizeof(OrderExecutedMessage)); + return message; +} + +// Order Executed With Price Message +std::vector generateOrderExecutedWithPriceMessage( + uint16_t stock_locate, uint16_t tracking_number, uint64_t timestamp, + uint64_t orderRef, uint32_t executed_shares, uint64_t match_number, + char printable, uint32_t execution_price) { + OrderExecutedWithPriceMessage msg{}; + msg.message_type = static_cast(MessageType::OrderExecutedWithPrice); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + msg.order_reference_number = orderRef; + msg.executed_shares = executed_shares; + msg.match_number = match_number; + msg.printable = printable; + msg.execution_price = execution_price; + + std::vector message(sizeof(OrderExecutedWithPriceMessage)); + std::memcpy(message.data(), &msg, sizeof(OrderExecutedWithPriceMessage)); + return message; +} + +// OrderCancelMessage + +std::vector generateOrderCancelMessage(uint16_t stock_locate, + uint16_t tracking_number, + uint64_t timestamp, + uint64_t orderRef, + uint32_t cancelled_shares) { + OrderCancelMessage msg{}; + msg.message_type = static_cast(MessageType::OrderCancel); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + msg.order_reference_number = orderRef; + msg.cancelled_shares = cancelled_shares; + + std::vector message(sizeof(OrderCancelMessage)); + std::memcpy(message.data(), &msg, sizeof(OrderCancelMessage)); + return message; +} + +// OrderDeleteMessage +std::vector generateOrderDeleteMessage(uint16_t stock_locate, + uint16_t tracking_number, + uint64_t timestamp, + uint64_t orderRef) { + OrderDeleteMessage msg{}; + msg.message_type = static_cast(MessageType::OrderDelete); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + msg.order_reference_number = orderRef; + + std::vector message(sizeof(OrderDeleteMessage)); + std::memcpy(message.data(), &msg, sizeof(OrderDeleteMessage)); + return message; +} + +// OrderReplaceMessage + +std::vector +generateOrderReplaceMessage(uint16_t stock_locate, uint16_t tracking_number, + uint64_t timestamp, uint64_t original_order_ref, + uint64_t new_order_ref, uint32_t shares, + uint32_t price) { + OrderReplaceMessage msg{}; + msg.message_type = static_cast(MessageType::OrderReplace); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + msg.original_order_ref = original_order_ref; + msg.new_order_ref = new_order_ref; + msg.shares = shares; + msg.price = price; + + std::vector message(sizeof(OrderReplaceMessage)); + std::memcpy(message.data(), &msg, sizeof(OrderReplaceMessage)); + return message; +} + +// TradeMessage +std::vector +generateTradeMessage(uint16_t stock_locate, uint16_t tracking_number, + uint64_t timestamp, uint64_t orderRef, uint8_t side, + uint32_t shares, const std::string &stock, uint32_t price, + uint64_t match_number) { + TradeMessage msg{}; + msg.message_type = static_cast(MessageType::Trade); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + msg.order_reference_number = orderRef; + msg.side = static_cast(side); + msg.shares = shares; + packString(msg.stock, stock); + msg.price = price; + msg.match_number = match_number; + + std::vector message(sizeof(TradeMessage)); + std::memcpy(message.data(), &msg, sizeof(TradeMessage)); + return message; +} + +// CrossTradeMessage +std::vector +generateCrossTradeMessage(uint16_t stock_locate, uint16_t tracking_number, + uint64_t timestamp, uint64_t shares, + const std::string &stock, uint32_t cross_price, + uint64_t match_number, char cross_type) { + CrossTradeMessage msg{}; + msg.message_type = static_cast(MessageType::CrossTrade); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + msg.shares = shares; + packString(msg.stock, stock); + msg.cross_price = cross_price; + msg.match_number = match_number; + msg.cross_type = cross_type; + + std::vector message(sizeof(CrossTradeMessage)); + std::memcpy(message.data(), &msg, sizeof(CrossTradeMessage)); + return message; +} + +// BrokenTradeMessage +std::vector generateBrokenTradeMessage(uint16_t stock_locate, + uint16_t tracking_number, + uint64_t timestamp, + uint64_t match_number) { + BrokenTradeMessage msg{}; + msg.message_type = static_cast(MessageType::BrokenTrade); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + msg.match_number = match_number; + + std::vector message(sizeof(BrokenTradeMessage)); + std::memcpy(message.data(), &msg, sizeof(BrokenTradeMessage)); + return message; +} + +// NOIIMessage +std::vector +generateNOIIMessage(uint16_t stock_locate, uint16_t tracking_number, + uint64_t timestamp, uint64_t paired_shares, + uint64_t imbalance_shares, char imbalance_direction, + const std::string &stock, uint32_t far_price, + uint32_t near_price, uint32_t current_reference_price, + char cross_type, char price_variation_indicator) { + NOIIMessage msg{}; + msg.message_type = static_cast(MessageType::NOII); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + msg.paired_shares = paired_shares; + msg.imbalance_shares = imbalance_shares; + msg.imbalance_direction = imbalance_direction; + packString(msg.stock, stock); + msg.far_price = far_price; + msg.near_price = near_price; + msg.current_reference_price = current_reference_price; + msg.cross_type = cross_type; + msg.price_variation_indicator = price_variation_indicator; + + std::vector message(sizeof(NOIIMessage)); + std::memcpy(message.data(), &msg, sizeof(NOIIMessage)); + return message; +} + +// RetailPriceImprovementIndicatorMessage +std::vector generateRetailPriceImprovementIndicatorMessage( + uint16_t stock_locate, uint16_t tracking_number, uint64_t timestamp, + const std::string &stock, char interest_flag) { + RetailPriceImprovementIndicator msg{}; + msg.message_type = static_cast(MessageType::RPII); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + packString(msg.stock, stock); + msg.interest_flag = interest_flag; + + std::vector message(sizeof(RetailPriceImprovementIndicator)); + std::memcpy(message.data(), &msg, sizeof(RetailPriceImprovementIndicator)); + return message; +} + +// DRWCRPDMessage + +std::vector generateDRWCRPDMessage( + uint16_t stock_locate, uint16_t tracking_number, uint64_t timestamp, + const std::string &stock, char open_eligibility_status, + uint32_t min_allowable_price, uint32_t max_allowable_price, + uint32_t near_execution_price, uint64_t near_execution_time, + uint32_t lower_price_collar, uint32_t upper_price_collar) { + DRWCRPDMessage msg{}; + msg.message_type = static_cast(MessageType::DRWCRPD); + msg.stock_locate = stock_locate; + msg.tracking_number = tracking_number; + packTimestamp(msg.timestamp, timestamp); + packString(msg.stock, stock); + msg.open_eligibility_status = open_eligibility_status; + msg.min_allowable_price = min_allowable_price; + msg.max_allowable_price = max_allowable_price; + msg.near_execution_price = near_execution_price; + msg.near_execution_time = near_execution_time; + msg.lower_price_collar = lower_price_collar; + msg.upper_price_collar = upper_price_collar; + + std::vector message(sizeof(DRWCRPDMessage)); + std::memcpy(message.data(), &msg, sizeof(DRWCRPDMessage)); + return message; +} diff --git a/itch_streamgen/src/itch/hardware/top.sv b/itch_streamgen/src/itch/hardware/top.sv deleted file mode 100644 index e69de29b..00000000 diff --git a/itch_streamgen/src/itch/software/constant.hpp b/itch_streamgen/src/itch/software/constant.hpp deleted file mode 100644 index b45abb6d..00000000 --- a/itch_streamgen/src/itch/software/constant.hpp +++ /dev/null @@ -1,100 +0,0 @@ -#pragma once -#include -#include -#include - - -enum class MessageType : uint8_t { - SystemEvent = 'S', - StockDirectory = 'R', - StockTradingAction = 'H', - RegSHORestriction = 'Y', - MarketParticipantPosition = 'L', - MWCBDeclineLevel = 'V', - MWCBStatus = 'W', - IPOQuotingPeriodUpdate = 'K', - LULDAuctionCollar = 'J', - OperationHalt = 'h', - AddOrder = 'A', - AddOrderWithMPID = 'F', - OrderExecuted = 'E', - OrderExecutedWithPrice = 'C', - OrderCancel = 'X', - OrderDelete = 'D', - OrderReplace = 'U', - Trade = 'P', - CrossTrade = 'Q', - BrokenTrade = 'B', - NOII = 'I', - RPII = 'N', - DRWCRPD = 'O', -}; - -enum class SystemEventCode : uint8_t { - StartOfMessages = 'O', // Outside of time stamp messages, the start of day message is the first message sent in any trading day. - StartOfSystem = 'S', // This message indicates that NASDAQ is open and ready to start accepting orders. - StartOfMarket = 'Q', // This message is intended to indicate that Market Hours orders are available for execution. - EndOfMarket = 'M', // This message is intended to indicate that Market Hours orders are no longer available for execution. - EndOfSystem = 'E', // It indicates that Nasdaq is now closed and will not accept any new orders today. - EndOfMessages = 'C', // This is always the last message sent in any trading day. -}; - -enum class TradingStateCode : uint8_t { - Halted = 'H', - Paused = 'P', - Quotation = 'Q', - Trading = 'T', -}; - - - - -using Price4 = uint32_t; - -enum class Side : uint8_t { - Buy = 'B', - Sell = 'S', -}; - - -inline std::string toString(MessageType type) { - switch (type) { - case MessageType::SystemEvent: return "System Event"; - case MessageType::StockDirectory: return "Stock Directory"; - case MessageType::StockTradingAction: return "Stock Trading Action"; - case MessageType::RegSHORestriction: return "RegSHO Restriction"; - case MessageType::MarketParticipantPosition: return "Market Participant Position"; - case MessageType::MWCBDeclineLevel: return "MWCB Decline Level"; - case MessageType::MWCBStatus: return "MWCB Status"; - case MessageType::IPOQuotingPeriodUpdate: return "IPO Quoting Period Update"; - case MessageType::LULDAuctionCollar: return "LULD Auction Collar"; - case MessageType::OperationHalt: return "Operation Halt"; - case MessageType::AddOrder: return "Add Order"; - case MessageType::AddOrderWithMPID: return "Add Order with MPID"; - case MessageType::OrderExecuted: return "Order Executed"; - case MessageType::OrderExecutedWithPrice: return "Order Executed with Price"; - case MessageType::OrderCancel: return "Order Cancel"; - case MessageType::OrderDelete: return "Order Delete"; - case MessageType::OrderReplace: return "Order Replace"; - case MessageType::Trade: return "Trade"; - case MessageType::CrossTrade: return "Cross Trade"; - case MessageType::BrokenTrade: return "Broken Trade"; - case MessageType::NOII: return "Net Order Imbalance Indicator"; - case MessageType::RPII: return "Retail Price Improvement Indicator"; - case MessageType::DRWCRPD: return "Direct Listing With Capital Raise Price Discovery"; - default: return "Unknown"; - } -} - -struct Timestamp { - uint64_t ns_since_midnight; - - static Timestamp now() { - return Timestamp{0}; - } - - void advanceBy(uint64_t ns) { - ns_since_midnight += ns; - ns_since_midnight &= 0xFFFFFFFFFFFF; // Mask to 48 bits - } -}; \ No newline at end of file diff --git a/itch_streamgen/src/itch/software/generator.cpp b/itch_streamgen/src/itch/software/generator.cpp deleted file mode 100644 index eb5f9cfc..00000000 --- a/itch_streamgen/src/itch/software/generator.cpp +++ /dev/null @@ -1,606 +0,0 @@ -#include -#include -#include -#include // For memcpy, memset -#include // For std::min, std::copy_n -#include -#include -#include -#include - -#include "message.hpp" // ITCH protocol message struct -#include "constant.hpp" // ITCH constants - -// Helper function: packs a 64-bit timestamp (with only the lower 48 bits valid) -// into a std::array in big-endian order -void packTimestamp(std::array& dest, uint64_t timestamp) { - for (int i = 0; i < 6; ++i) { - dest[i] = static_cast((timestamp >> (8 * (5 - i))) & 0xFF); - } -} - -// Helper template to pack a string into a fixed-size std::array -// Padding with spaces if needed. -template -void packString(std::array& dest, const std::string &s) { - std::fill(dest.begin(), dest.end(), ' '); - size_t len = std::min(s.size(), static_cast(N)); - std::copy_n(s.begin(), len, dest.begin()); -} - - -// SystemEventMessage -std::vector generateSystemEventMessage( - uint16_t tracking_number, - uint64_t timestamp, - char event_code -){ - SystemEventMessage msg{}; - msg.message_type = static_cast(MessageType::SystemEvent); - msg.stock_locate = 0; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - msg.event_code = event_code; - - std::vector message(sizeof(SystemEventMessage)); - std::memcpy(message.data(), &msg, sizeof(SystemEventMessage)); - return message; -} - -// StockDirectoryMessage -std::vector generateStockDirectoryMessage( - uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - const std::string &stock, - char market_category, - char financial_status_indicator, - uint32_t round_lot_size, - char round_lots_only, - char issue_classification, - const std::string &issue_subtype, - char authenticity, - char short_sale_threshold_indicator, - char ipo_flag, - char LULDReference_price_tier, - char etp_flag, - uint32_t etp_leverage_factor, - char inverse_indicator) -{ - StockDirectoryMessage msg{}; - msg.message_type = static_cast(MessageType::StockDirectory); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - packString(msg.stock, stock); - msg.market_category = market_category; - msg.financial_status_indicator = financial_status_indicator; - msg.round_lot_size = round_lot_size; - msg.round_lots_only = round_lots_only; - msg.issue_classification = issue_classification; - std::fill(msg.issue_subtype.begin(), msg.issue_subtype.end(), ' '); - size_t len = std::min(issue_subtype.size(), msg.issue_subtype.size()); - std::copy_n(issue_subtype.begin(), len, msg.issue_subtype.begin()); - msg.authenticity = authenticity; - msg.short_sale_threshold_indicator = short_sale_threshold_indicator; - msg.ipo_flag = ipo_flag; - msg.LULDReference_price_tier = LULDReference_price_tier; - msg.etp_flag = etp_flag; - msg.etp_leverage_factor = etp_leverage_factor; - msg.inverse_indicator = inverse_indicator; - - std::vector message(sizeof(StockDirectoryMessage)); - std::memcpy(message.data(), &msg, sizeof(StockDirectoryMessage)); - return message; -} - -// StockTradingActionMessage -std::vector generateStockTradingActionMessage( - uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - const std::string &stock, - char trading_state, - char reserved, - const std::string &action_reason -){ - StockTradingActionMessage msg{}; - msg.message_type = static_cast(MessageType::StockTradingAction); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - packString(msg.stock, stock); - msg.trading_state = trading_state; - msg.reserved = reserved; - - std::fill(msg.reason.begin(), msg.reason.end(), ' '); - size_t len = std::min(action_reason.size(), msg.reason.size()); - std::copy_n(action_reason.begin(), len, msg.reason.begin()); - - std::vector message(sizeof(StockTradingActionMessage)); - std::memcpy(message.data(), &msg, sizeof(StockTradingActionMessage)); - return message; -} - -// Reg SHO Restriction Message -std::vector generateRegSHORestrictionMessage( - uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - const std::string &stock, - char reg_sho_action -){ - RegSHORestrictionMessage msg{}; - msg.message_type = static_cast(MessageType::RegSHORestriction); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - packString(msg.stock, stock); - msg.reg_sho_action = reg_sho_action; - - std::vector message(sizeof(RegSHORestrictionMessage)); - std::memcpy(message.data(), &msg, sizeof(RegSHORestrictionMessage)); - return message; -} - -// Market Participant Position Message -std::vector generateMarketParticipantPositionMessage(uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - const std::string &mpid, - const std::string &stock, - char primary_market_maker, - char market_maker_mode, - char market_participant_state) -{ - MarketParticipantPositionMessage msg{}; - msg.message_type = static_cast(MessageType::MarketParticipantPosition); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - std::fill(msg.mpid.begin(), msg.mpid.end(), ' '); - size_t len_mpid = std::min(mpid.size(), msg.mpid.size()); - std::copy_n(mpid.begin(), len_mpid, msg.mpid.begin()); - packString(msg.stock, stock); - msg.primary_market_maker = primary_market_maker; - msg.market_maker_mode = market_maker_mode; - msg.market_participant_state = market_participant_state; - - std::vector message(sizeof(MarketParticipantPositionMessage)); - std::memcpy(message.data(), &msg, sizeof(MarketParticipantPositionMessage)); - return message; -} - - // MWCB Status Message - std::vector generateMWCBStatusMessage( - uint16_t tracking_number, - uint64_t timestamp, - char breached_level) -{ - MWCBStatusMessage msg{}; - msg.message_type = static_cast(MessageType::MWCBStatus); - msg.stock_locate = 0; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - msg.breached_level = breached_level; - - std::vector message(sizeof(MWCBStatusMessage)); - std::memcpy(message.data(), &msg, sizeof(MWCBStatusMessage)); - return message; -} - -// IPO Quoting Period Update Message -std::vector generateIPOQuotingPeriodUpdateMessage(uint16_t tracking_number, - uint64_t timestamp, - const std::string &stock, - uint32_t ipo_quotation_release_time, - char ipo_quotation_release_qualifier, - uint32_t ipo_price -){ - IPOQuotingPeriodUpdateMessage msg{}; - msg.message_type = static_cast(MessageType::IPOQuotingPeriodUpdate); - msg.stock_locate = 0; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - packString(msg.stock, stock); - msg.ipo_quotation_release_time = ipo_quotation_release_time; - msg.ipo_quotation_release_qualifier = ipo_quotation_release_qualifier; - msg.ipo_price = ipo_price; - - std::vector message(sizeof(IPOQuotingPeriodUpdateMessage)); - std::memcpy(message.data(), &msg, sizeof(IPOQuotingPeriodUpdateMessage)); - return message; -} - -// LULD Auction Collar Message -std::vector generateLULDAuctionCollarMessage(uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - const std::string &stock, - uint32_t auction_collar_ref_price, - uint32_t upper_auction_collar_price, - uint32_t lower_auction_collar_price, - uint32_t auction_collar_extension -){ - LULDAuctionCollarMessage msg{}; - msg.message_type = static_cast(MessageType::LULDAuctionCollar); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - packString(msg.stock, stock); - msg.auction_collar_ref_price = auction_collar_ref_price; - msg.upper_auction_collar_price = upper_auction_collar_price; - msg.lower_auction_collar_price = lower_auction_collar_price; - msg.auction_collar_extension = auction_collar_extension; - - std::vector message(sizeof(LULDAuctionCollarMessage)); - std::memcpy(message.data(), &msg, sizeof(LULDAuctionCollarMessage)); - return message; -} - - -// OperationalHaltMessage - -std::vector generateOperationalHaltMessage( - uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - const std::string &stock, - char market_code, - char operational_halt_action -){ - OperationalHaltMessage msg{}; - msg.message_type = static_cast(MessageType::OperationHalt); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - packString(msg.stock, stock); - msg.market_code = market_code; - msg.operational_halt_action = operational_halt_action; - - std::vector message(sizeof(OperationalHaltMessage)); - std::memcpy(message.data(), &msg, sizeof(OperationalHaltMessage)); - return message; -} - - - - -std::vector generateAddOrderMessage( - uint64_t timestamp, - uint64_t orderRef, - uint8_t side, - uint32_t shares, - const std::string &stock, - uint32_t price -){ - AddOrderMessage msg{}; - msg.message_type = static_cast(MessageType::AddOrder); - msg.stock_locate = 0; - msg.tracking_number = 0; - packTimestamp(msg.timestamp, timestamp); - msg.order_reference_number = orderRef; - msg.side = static_cast(side); - msg.shares = shares; - packString(msg.stock, stock); - msg.price = price; - - std::vector message(sizeof(AddOrderMessage)); - std::memcpy(message.data(), &msg, sizeof(AddOrderMessage)); - return message; -} - -// Add Order With MPID Message -std::vector generateAddOrderWithMPIDMessage(uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - uint64_t orderRef, - uint8_t side, - uint32_t shares, - const std::string &stock, - uint32_t price, - const std::string &attribution -){ - AddOrderWithMPIDMessage msg{}; - msg.message_type = static_cast(MessageType::AddOrderWithMPID); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - msg.order_reference_number = orderRef; - msg.side = static_cast(side); - msg.shares = shares; - packString(msg.stock, stock); - msg.price = price; - std::fill(msg.attribution.begin(), msg.attribution.end(), ' '); - size_t len = std::min(attribution.size(), msg.attribution.size()); - std::copy_n(attribution.begin(), len, msg.attribution.begin()); - - std::vector message(sizeof(AddOrderWithMPIDMessage)); - std::memcpy(message.data(), &msg, sizeof(AddOrderWithMPIDMessage)); - return message; -} - -// Order Executed Message -std::vector generateOrderExecutedMessage(uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - uint64_t orderRef, - uint32_t executed_shares, - uint64_t match_number -){ - OrderExecutedMessage msg{}; - msg.message_type = static_cast(MessageType::OrderExecuted); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - msg.order_reference_number = orderRef; - msg.executed_shares = executed_shares; - msg.match_number = match_number; - - std::vector message(sizeof(OrderExecutedMessage)); - std::memcpy(message.data(), &msg, sizeof(OrderExecutedMessage)); - return message; -} - -// Order Executed With Price Message -std::vector generateOrderExecutedWithPriceMessage(uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - uint64_t orderRef, - uint32_t executed_shares, - uint64_t match_number, - char printable, - uint32_t execution_price -){ - OrderExecutedWithPriceMessage msg{}; - msg.message_type = static_cast(MessageType::OrderExecutedWithPrice); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - msg.order_reference_number = orderRef; - msg.executed_shares = executed_shares; - msg.match_number = match_number; - msg.printable = printable; - msg.execution_price = execution_price; - - std::vector message(sizeof(OrderExecutedWithPriceMessage)); - std::memcpy(message.data(), &msg, sizeof(OrderExecutedWithPriceMessage)); - return message; -} - - - -// OrderCancelMessage - -std::vector generateOrderCancelMessage(uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - uint64_t orderRef, - uint32_t cancelled_shares -){ - OrderCancelMessage msg{}; - msg.message_type = static_cast(MessageType::OrderCancel); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - msg.order_reference_number = orderRef; - msg.cancelled_shares = cancelled_shares; - - std::vector message(sizeof(OrderCancelMessage)); - std::memcpy(message.data(), &msg, sizeof(OrderCancelMessage)); - return message; -} - -// OrderDeleteMessage -std::vector generateOrderDeleteMessage(uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - uint64_t orderRef -){ - OrderDeleteMessage msg{}; - msg.message_type = static_cast(MessageType::OrderDelete); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - msg.order_reference_number = orderRef; - - std::vector message(sizeof(OrderDeleteMessage)); - std::memcpy(message.data(), &msg, sizeof(OrderDeleteMessage)); - return message; -} - - -// OrderReplaceMessage - -std::vector generateOrderReplaceMessage(uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - uint64_t original_order_ref, - uint64_t new_order_ref, - uint32_t shares, - uint32_t price -){ - OrderReplaceMessage msg{}; - msg.message_type = static_cast(MessageType::OrderReplace); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - msg.original_order_ref = original_order_ref; - msg.new_order_ref = new_order_ref; - msg.shares = shares; - msg.price = price; - - std::vector message(sizeof(OrderReplaceMessage)); - std::memcpy(message.data(), &msg, sizeof(OrderReplaceMessage)); - return message; -} - -// TradeMessage -std::vector generateTradeMessage(uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - uint64_t orderRef, - uint8_t side, - uint32_t shares, - const std::string &stock, - uint32_t price, - uint64_t match_number -){ - TradeMessage msg{}; - msg.message_type = static_cast(MessageType::Trade); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - msg.order_reference_number = orderRef; - msg.side = static_cast(side); - msg.shares = shares; - packString(msg.stock, stock); - msg.price = price; - msg.match_number = match_number; - - std::vector message(sizeof(TradeMessage)); - std::memcpy(message.data(), &msg, sizeof(TradeMessage)); - return message; -} - - -// CrossTradeMessage -std::vector generateCrossTradeMessage(uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - uint64_t shares, - const std::string &stock, - uint32_t cross_price, - uint64_t match_number, - char cross_type) -{ - CrossTradeMessage msg{}; - msg.message_type = static_cast(MessageType::CrossTrade); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - msg.shares = shares; - packString(msg.stock, stock); - msg.cross_price = cross_price; - msg.match_number = match_number; - msg.cross_type = cross_type; - - std::vector message(sizeof(CrossTradeMessage)); - std::memcpy(message.data(), &msg, sizeof(CrossTradeMessage)); - return message; -} - - -// BrokenTradeMessage -std::vector generateBrokenTradeMessage(uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - uint64_t match_number) -{ - BrokenTradeMessage msg{}; - msg.message_type = static_cast(MessageType::BrokenTrade); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - msg.match_number = match_number; - - std::vector message(sizeof(BrokenTradeMessage)); - std::memcpy(message.data(), &msg, sizeof(BrokenTradeMessage)); - return message; -} - - -// NOIIMessage -std::vector generateNOIIMessage(uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - uint64_t paired_shares, - uint64_t imbalance_shares, - char imbalance_direction, - const std::string &stock, - uint32_t far_price, - uint32_t near_price, - uint32_t current_reference_price, - char cross_type, - char price_variation_indicator -){ - NOIIMessage msg{}; - msg.message_type = static_cast(MessageType::NOII); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - msg.paired_shares = paired_shares; - msg.imbalance_shares = imbalance_shares; - msg.imbalance_direction = imbalance_direction; - packString(msg.stock, stock); - msg.far_price = far_price; - msg.near_price = near_price; - msg.current_reference_price = current_reference_price; - msg.cross_type = cross_type; - msg.price_variation_indicator = price_variation_indicator; - - std::vector message(sizeof(NOIIMessage)); - std::memcpy(message.data(), &msg, sizeof(NOIIMessage)); - return message; -} - - -// RetailPriceImprovementIndicatorMessage -std::vector generateRetailPriceImprovementIndicatorMessage( - uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - const std::string &stock, - char interest_flag -){ - RetailPriceImprovementIndicator msg{}; - msg.message_type = static_cast(MessageType::RPII); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - packString(msg.stock, stock); - msg.interest_flag = interest_flag; - - std::vector message(sizeof(RetailPriceImprovementIndicator)); - std::memcpy(message.data(), &msg, sizeof(RetailPriceImprovementIndicator)); - return message; -} - - -// DRWCRPDMessage - -std::vector generateDRWCRPDMessage( - uint16_t stock_locate, - uint16_t tracking_number, - uint64_t timestamp, - const std::string &stock, - char open_eligibility_status, - uint32_t min_allowable_price, - uint32_t max_allowable_price, - uint32_t near_execution_price, - uint64_t near_execution_time, - uint32_t lower_price_collar, - uint32_t upper_price_collar -){ - DRWCRPDMessage msg{}; - msg.message_type = static_cast(MessageType::DRWCRPD); - msg.stock_locate = stock_locate; - msg.tracking_number = tracking_number; - packTimestamp(msg.timestamp, timestamp); - packString(msg.stock, stock); - msg.open_eligibility_status = open_eligibility_status; - msg.min_allowable_price = min_allowable_price; - msg.max_allowable_price = max_allowable_price; - msg.near_execution_price = near_execution_price; - msg.near_execution_time = near_execution_time; - msg.lower_price_collar = lower_price_collar; - msg.upper_price_collar = upper_price_collar; - - std::vector message(sizeof(DRWCRPDMessage)); - std::memcpy(message.data(), &msg, sizeof(DRWCRPDMessage)); - return message; -} - - - - diff --git a/itch_streamgen/src/itch/software/message.hpp b/itch_streamgen/src/itch/software/message.hpp deleted file mode 100644 index 8ba2d06d..00000000 --- a/itch_streamgen/src/itch/software/message.hpp +++ /dev/null @@ -1,267 +0,0 @@ -#pragma once -#include -#include - -using Price4 = uint32_t; - -#pragma pack(push, 1) - -struct SystemEventMessage { - char message_type; // 1 byte - 'S' - uint16_t stock_locate; // 2 bytes - Always 0 - uint16_t tracking_number; // 2 bytes - Nasdaq internal tracking number - std::array timestamp; // 6 bytes - Nanoseconds since midnight - char event_code; // 1 byte - 'O', 'S', 'Q', 'M', 'E', 'C' -}; -struct StockDirectoryMessage { - char message_type; // 1 byte - 'R' - uint16_t stock_locate; // 2 bytes - Locate Code uniquely assinged to the security symbol - uint16_t tracking_number; // 2 bytes - Nasdaq internal tracking number - std::array timestamp; // 6 bytes - Nanoseconds since midnight - std::array stock; // 8 bytes - stock symbol right padded with spaces - char market_category; // 1 byte - NASDAQ-Listed'G': Global Market, 'Q': Global Select Market, 'S': Capital Markets - // NON-NASDAQ-Listed 'N': New York Stock Exchange, 'A': NYSE American, 'P': NYSE Arca, 'Z': BATS Z Exchange, 'V': Investors Exchange LLC, - char financial_status_indicator; // 1 byte - 'D': Deficient, 'E': Delinquent, 'Q': Bankrupt, - // 'S': Suspended, 'G': Deficient and Bankrupt, - // 'H': Deficient and Delinquent, 'J': Delinquent and Bankrupt, - // 'K': Deficient, Delinquent and Bankrupt, - // 'C': Creations/Redemptions Suspended for ETP, - // 'N': Normal (not Deficient, Delinquent, or Bankrupt), - // ' ' (space): Not available (non-Nasdaq-listed) - uint32_t round_lot_size; // 4 bytes - number of shares that represents a round lot for the issue - char round_lots_only; // 1 byte - 'Y': Round lots only, 'N': All sizes - char issue_classification; // 1 byte - identifes the security class - std::array issue_subtype; // 2 bytes - identifies the security subtype - char authenticity; // 1 byte - 'P' - live/production, 'T' - test - char short_sale_threshold_indicator; // 1 byte - Indicates if a security is mandatory close out of short sales - // Y - restricted, N - not restricted - char ipo_flag; // 1 byte- set for ipo or not (Y or N) - char LULDReference_price_tier; // 1 byte - indicates which limit up/ limit down price band calculation parameter is used - char etp_flag; // 1 byte - indicates if security is exchange traded product (Y or N) - uint32_t etp_leverage_factor; // 4 bytes - indicates the levergae factor (e.g. 3 indicates ETF will increase/decrease by 3) - char inverse_indicator; // 1 byte - Y or N for inverse ETP -}; -struct StockTradingActionMessage { - char message_type; // 1 byte - 'H' - uint16_t stock_locate; // 2 bytes - Locate code identifying the security - uint16_t tracking_number; // 2 bytes - Nasdaq internal tracking number - std::array timestamp; // 6 bytes - Nanoseconds since midnight - std::array stock; // 8 bytes - stock symbol right padded with spaces - char trading_state; // 1 byte - "H":Halted, "P":Paused, "Q":Quotation, "T":Trading on NASDAQ - char reserved; // 1 byte - Reserved - std::array reason; // 4 bytes - trading action reason -}; -struct RegSHORestrictionMessage { - char message_type; // 1 byte - 'Y' - uint16_t stock_locate; // 2 bytes - Locate code - uint16_t tracking_number; // 2 bytes - Nasdaq tracking number - std::array timestamp; // 6 bytes - Nanoseconds since midnight - std::array stock; // 8 bytes - stock symbol right padded with spaces - char reg_sho_action; // 1 byte - '0', '1', or '2' -}; -struct MarketParticipantPositionMessage { - char message_type; // 1 byte - 'L' - uint16_t stock_locate; // 2 bytes - uint16_t tracking_number; // 2 bytes - std::array timestamp; // 6 bytes - Nanoseconds since midnight - std::array mpid; // 4 bytes - std::array stock; // 8 bytes - char primary_market_maker; // 1 byte - 'Y' or 'N' - char market_maker_mode; // 1 byte - 'N', 'P', 'S', 'R', 'L' - char market_participant_state; // 1 byte - 'A', 'E', 'W', 'S', 'D' -}; -struct MWCBStatusMessage { - char message_type; // 1 byte - 'W' - uint16_t stock_locate; // 2 bytes - Always 0 - uint16_t tracking_number; // 2 bytes - Nasdaq tracking number - std::array timestamp; // 6 bytes - Nanoseconds since midnight - char breached_level; // 1 byte - '1', '2', or '3' -}; -struct IPOQuotingPeriodUpdateMessage { - char message_type; // 1 byte - 'K' - uint16_t stock_locate; // 2 bytes - Always 0 - uint16_t tracking_number; // 2 bytes - Nasdaq tracking number - std::array timestamp; // 6 bytes - Nanoseconds since midnight - std::array stock; // 8 bytes - stock symbol right padded with spaces - uint32_t ipo_quotation_release_time; // 4 bytes - Seconds since midnight - char ipo_quotation_release_qualifier; // 1 byte - 'A': Anticipated Quotation Release Time or 'C': Cancelled - uint32_t ipo_price; // 4 bytes - Price (4 decimal digits implied) -}; -struct LULDAuctionCollarMessage { - char message_type; // 1 byte - 'J' - uint16_t stock_locate; // 2 bytes - uint16_t tracking_number; // 2 bytes - std::array timestamp; // 6 bytes - Nanoseconds since midnight - std::array stock; // 8 bytes - stock symbol right padded with spaces - uint32_t auction_collar_ref_price; // 4 bytes - Reference price used to set auction collars - uint32_t upper_auction_collar_price; // 4 bytes - Upper collar price - uint32_t lower_auction_collar_price; // 4 bytes - Lower collar price - uint32_t auction_collar_extension; // 4 bytes - Number of extensions to the Reopening Auction -}; -struct OperationalHaltMessage { - char message_type; // 1 byte - 'h' - uint16_t stock_locate; // 2 bytes - uint16_t tracking_number; // 2 bytes - std::array timestamp; // 6 bytes - Nanoseconds since midnight - std::array stock; // 8 bytes - stock symbol right padded with spaces - char market_code; // 1 byte - 'Q':NASDAQ, 'B':BX, or 'X':PSX - char operational_halt_action; // 1 byte - 'H' (halted), 'T' (trading resumed) -}; -struct AddOrderMessage { - char message_type; // 1 byte - 'A' - uint16_t stock_locate; // 2 bytes - uint16_t tracking_number; // 2 bytes - std::array timestamp; // 6 bytes - Nanoseconds since midnight - uint64_t order_reference_number; // 8 bytes - Unique order ID - char side; // 1 byte - 'B' or 'S' - uint32_t shares; // 4 bytes - Total shares in the order - std::array stock; // 8 bytes - stock symbol right padded with spaces - uint32_t price; // 4 bytes - Price (in 4-digit fixed decimal format) -}; -struct AddOrderWithMPIDMessage { - char message_type; // 1 byte - 'F' - uint16_t stock_locate; // 2 bytes - uint16_t tracking_number; // 2 bytes - std::array timestamp; // 6 bytes - Nanoseconds since midnight - uint64_t order_reference_number; // 8 bytes - Unique order ID - char side; // 1 byte - 'B' or 'S' - uint32_t shares; // 4 bytes - std::array stock; // 8 bytes - Stock symbol, right-padded - uint32_t price; // 4 bytes - Fixed-point price - std::array attribution; // 4 bytes - MPID (e.g., "GSCO", "JPMX") -}; -struct OrderExecutedMessage { - char message_type; // 1 byte - 'E' - uint16_t stock_locate; // 2 bytes - uint16_t tracking_number; // 2 bytes - std::array timestamp; // 6 bytes - Nanoseconds since midnight - uint64_t order_reference_number;// 8 bytes - Unique order ID - uint32_t executed_shares; // 4 bytes - Number of shares executed - uint64_t match_number; // 8 bytes - Unique match ID for the trade -}; -struct OrderExecutedWithPriceMessage { - char message_type; // 1 byte - 'C' - uint16_t stock_locate; // 2 bytes - uint16_t tracking_number; // 2 bytes - std::array timestamp; // 6 bytes - Nanoseconds since midnight - uint64_t order_reference_number;// 8 bytes - uint32_t executed_shares; // 4 bytes - uint64_t match_number; // 8 bytes - Execution ID - char printable; // 1 byte - 'Y' or 'N' - uint32_t execution_price; // 4 bytes - Price (e.g., 101250 = $10.1250) -}; -struct OrderCancelMessage { - char message_type; // 1 byte - 'X' - uint16_t stock_locate; // 2 bytes - uint16_t tracking_number; // 2 bytes - std::array timestamp; // 6 bytes - Nanoseconds since midnight - uint64_t order_reference_number;// 8 bytes - uint32_t cancelled_shares; // 4 bytes -}; -struct OrderDeleteMessage { - char message_type; // 1 byte - 'D' - uint16_t stock_locate; // 2 bytes - uint16_t tracking_number; // 2 bytes - std::array timestamp; // 6 bytes - Nanoseconds since midnight - uint64_t order_reference_number;// 8 bytes -}; -struct OrderReplaceMessage { - char message_type; // 1 byte - 'U' - uint16_t stock_locate; // 2 bytes - uint16_t tracking_number; // 2 bytes - std::array timestamp; // 6 bytes - Nanoseconds since midnight - uint64_t original_order_ref; // 8 bytes - Reference to the original order - uint64_t new_order_ref; // 8 bytes - New reference number for the replacement - uint32_t shares; // 4 bytes - New total displayed quantity - uint32_t price; // 4 bytes - New price (in fixed-point format) -}; -struct TradeMessage { - char message_type; // 1 byte - 'P' - uint16_t stock_locate; // 2 bytes - uint16_t tracking_number; // 2 bytes - std::array timestamp; // 6 bytes - Nanoseconds since midnight - uint64_t order_reference_number;// 8 bytes - May be zero - char side; // 1 byte - 'B' or 'S' (typically 'B' after 2014) - uint32_t shares; // 4 bytes - std::array stock; // 8 bytes - Stock symbol, right-padded - uint32_t price; // 4 bytes - Price (fixed-point, 4 decimals) - uint64_t match_number; // 8 bytes - Unique match ID -}; -struct CrossTradeMessage { - char message_type; // 1 byte - 'Q' - uint16_t stock_locate; // 2 bytes - uint16_t tracking_number; // 2 bytes - std::array timestamp; // 6 bytes - Nanoseconds since midnight - uint64_t shares; // 8 bytes - Shares matched in cross - std::array stock; // 8 bytes - Stock symbol, right-padded - uint32_t cross_price; // 4 bytes - Fixed-point price - uint64_t match_number; // 8 bytes - Unique match ID - char cross_type; // 1 byte - 'O', 'C', or 'H' -}; -struct BrokenTradeMessage { - char message_type; // 1 byte - 'B' - uint16_t stock_locate; // 2 bytes - uint16_t tracking_number; // 2 bytes - std::array timestamp; // 6 bytes - Nanoseconds since midnight - uint64_t match_number; // 8 bytes - Match ID of broken trade -}; -struct NOIIMessage { - char message_type; // 1 byte - 'I' - uint16_t stock_locate; // 2 bytes - uint16_t tracking_number; // 2 bytes - std::array timestamp; // 6 bytes - Nanoseconds since midnight - uint64_t paired_shares; // 8 bytes - uint64_t imbalance_shares; // 8 bytes - char imbalance_direction; // 1 byte - 'B', 'S', 'N', 'O', 'P' - std::array stock; // 8 bytes - Stock symbol, right-padded - uint32_t far_price; // 4 bytes - For cross orders only - uint32_t near_price; // 4 bytes - For continuous + cross orders - uint32_t current_reference_price;// 4 bytes - Price used for NOII calculation - char cross_type; // 1 byte - 'O', 'C', 'H', 'A' - char price_variation_indicator; // 1 byte - 'L', '1'–'9', 'A', 'B', 'C', or ' ' (space) -}; -struct RetailPriceImprovementIndicator { - char message_type; // 1 byte - 'N' - uint16_t stock_locate; // 2 bytes - uint16_t tracking_number; // 2 bytes - std::array timestamp; // 6 bytes - Nanoseconds since midnight - std::array stock; // 8 bytes - Stock symbol, right-padded - char interest_flag; // 1 byte - 'B', 'S', 'A', or 'N' -}; -struct DRWCRPDMessage { - char message_type; // 1 byte - 'O' - uint16_t stock_locate; // 2 bytes - uint16_t tracking_number; // 2 bytes - std::array timestamp; // 6 bytes - Nanoseconds since midnight - std::array stock; // 8 bytes - Stock symbol, right-padded - char open_eligibility_status; // 1 byte - 'Y' or 'N' - uint32_t min_allowable_price; // 4 bytes - uint32_t max_allowable_price; // 4 bytes - uint32_t near_execution_price; // 4 bytes - uint64_t near_execution_time; // 8 bytes - uint32_t lower_price_collar; // 4 bytes - uint32_t upper_price_collar; // 4 bytes -}; -#pragma pack(pop) - -static_assert(sizeof(SystemEventMessage) == 12, "SystemEventMessage size is incorrect"); -static_assert(sizeof(StockDirectoryMessage) == 39, "StockDirectoryMessage size is incorrect"); -static_assert(sizeof(StockTradingActionMessage) == 25, "StockTradingActionMessage size is incorrect"); -static_assert(sizeof(RegSHORestrictionMessage) == 20, "RegSHORestrictionMessage size is incorrect"); -static_assert(sizeof(MarketParticipantPositionMessage) == 26, "MarketParticipantPositionMessage size is incorrect"); -static_assert(sizeof(MWCBStatusMessage) == 12, "MWCBStatusMessage size is incorrect"); -static_assert(sizeof(IPOQuotingPeriodUpdateMessage) == 28, "IPOQuotingPeriodUpdateMessage size is incorrect"); -static_assert(sizeof(LULDAuctionCollarMessage) == 35, "LULDAuctionCollarMessage size is incorrect"); -static_assert(sizeof(AddOrderMessage) == 36, "AddOrderNoMPIDMessage size is incorrect"); -static_assert(sizeof(AddOrderWithMPIDMessage) == 40, "AddOrderWithMPIDMessage size is incorrect"); -static_assert(sizeof(OrderExecutedMessage) == 31, "OrderExecutedMessage size is incorrect"); -static_assert(sizeof(OrderExecutedWithPriceMessage) == 36, "OrderExecutedWithPriceMessage size is incorrect"); -static_assert(sizeof(OrderCancelMessage) == 23, "OrderCancelMessage size is incorrect"); -static_assert(sizeof(OrderDeleteMessage) == 19, "OrderDeleteMessage size is incorrect"); -static_assert(sizeof(OrderReplaceMessage) == 35, "OrderReplaceMessage size is incorrect"); -static_assert(sizeof(TradeMessage) == 44, "TradeMessage size is incorrect"); -static_assert(sizeof(CrossTradeMessage) == 40, "CrossTradeMessage size is incorrect"); -static_assert(sizeof(BrokenTradeMessage) == 19, "BrokenTradeMessage size is incorrect"); -static_assert(sizeof(NOIIMessage) == 50, "NOIIMessage size is incorrect"); -static_assert(sizeof(RetailPriceImprovementIndicator) == 20, "RetailPriceImprovementIndicator size is incorrect"); -static_assert(sizeof(DRWCRPDMessage) == 48, "DRWCRPDMessage size is incorrect"); diff --git a/itch_streamgen/src/itch/tb.cpp b/itch_streamgen/src/itch/tb.cpp deleted file mode 100644 index e69de29b..00000000 diff --git a/itch_streamgen/src/main.cpp b/itch_streamgen/src/main.cpp new file mode 100644 index 00000000..78f2de10 --- /dev/null +++ b/itch_streamgen/src/main.cpp @@ -0,0 +1 @@ +int main(void) { return 0; } diff --git a/itch_streamgen/tests/CMakeLists.txt b/itch_streamgen/tests/CMakeLists.txt new file mode 100644 index 00000000..95e0666c --- /dev/null +++ b/itch_streamgen/tests/CMakeLists.txt @@ -0,0 +1,11 @@ +set(TEST_SOURCE_FILES tautology_test.cpp) + +add_executable(itch_streamgen_test ${TEST_SOURCE_FILES}) + +target_link_libraries(itch_streamgen_test gtest gtest_main gmock + itch_streamgen_lib) + +add_test(NAME test COMMAND itch_streamgen_test) + +include(GoogleTest) +gtest_discover_tests(itch_streamgen_test) diff --git a/itch_streamgen/tests/tautology_test.cpp b/itch_streamgen/tests/tautology_test.cpp new file mode 100644 index 00000000..43106f90 --- /dev/null +++ b/itch_streamgen/tests/tautology_test.cpp @@ -0,0 +1,6 @@ +#include + +/** + * @begin Test tests. + */ +TEST(Tautology, Addition) { EXPECT_EQ(1 + 1, 2); } diff --git a/madlib/.DS_Store b/madlib/.DS_Store deleted file mode 100644 index 93ef97d6..00000000 Binary files a/madlib/.DS_Store and /dev/null differ diff --git a/mov_avg_acc/Makefile b/mov_avg_acc/Makefile deleted file mode 100644 index 5b975a33..00000000 --- a/mov_avg_acc/Makefile +++ /dev/null @@ -1,37 +0,0 @@ -# Top-level module name -TOP_MODULE = moving_average_accumulator - -# Verilog source files -VERILOG_SOURCES = $(TOP_MODULE).sv - -# C++ testbench file -TESTBENCH = tb.cpp - -# Output directory -OBJ_DIR = obj_dir - -# Simulation executable -SIM_EXE = $(OBJ_DIR)/V$(TOP_MODULE) - -# Default target -.PHONY: all -all: run_simulation - -# Compile Verilog and C++ sources -$(SIM_EXE): $(VERILOG_SOURCES) $(TESTBENCH) - verilator --cc $(VERILOG_SOURCES) --exe $(TESTBENCH) --trace - make -j -C $(OBJ_DIR) -f V$(TOP_MODULE).mk V$(TOP_MODULE) - -# Run the simulation -.PHONY: run_simulation -run_simulation: $(SIM_EXE) - ./$(SIM_EXE) - -# Clean up generated files -.PHONY: clean -clean: - rm -rf $(OBJ_DIR) waveform.vcd - -# Phony target for CI testing (exits with non-zero status on failure) -.PHONY: test -test: run_simulation diff --git a/mov_avg_acc/moving_average_accumulator.sv b/mov_avg_acc/moving_average_accumulator.sv deleted file mode 100644 index 57b63f3f..00000000 --- a/mov_avg_acc/moving_average_accumulator.sv +++ /dev/null @@ -1,57 +0,0 @@ -module moving_average_accumulator #( - parameter integer Exponent /*verilator public*/ = 3, // N = 2^k - parameter integer DataWidth /*verilator public*/ = 16 -) ( - input wire clk, - input wire reset, - input wire unsigned [DataWidth-1:0] d_in, - output reg unsigned [DataWidth-1:0] d_out -); - - localparam integer N = 1 << Exponent; - localparam integer AccWidth = DataWidth + Exponent; - - // holds sum of last `N` samples - reg unsigned [AccWidth-1:0] acc; - - // circular buffer storing last 'N' samples - reg unsigned [DataWidth-1:0] sample_buffer[N]; - - // index of oldest element in `sample_buffer` - integer oldest_index; - - // loop - integer i; - - always_ff @(posedge clk or posedge reset) begin - if (reset) begin - acc <= '0; - d_out <= '0; - - // reset sample buffer - for (i = 0; i < N; i++) begin - sample_buffer[i] <= '0; - end - - oldest_index <= 0; - end else begin - // subtract oldest, add newest - acc <= acc - - { - {(AccWidth - DataWidth){sample_buffer[oldest_index][DataWidth-1]}}, - sample_buffer[oldest_index] - } - + {{(AccWidth - DataWidth){d_in[DataWidth-1]}}, d_in}; - - // overwrite oldest sample - sample_buffer[oldest_index] <= d_in; - - // inc oldest_index - oldest_index <= (oldest_index + 1) % N; - - // compute moving average by dividing by 2^n - d_out <= acc[AccWidth-1:Exponent]; // effectively a right shift - end - end - -endmodule diff --git a/mov_avg_acc/tb.cpp b/mov_avg_acc/tb.cpp deleted file mode 100644 index 13fa5338..00000000 --- a/mov_avg_acc/tb.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "Vmoving_average_accumulator.h" -#include "verilated.h" -#include "verilated_vcd_c.h" -#include -#include -#include - -#define SIM_TIME 20 // Simulation time in clock cycles - -int main(int argc, char **argv) { - Verilated::commandArgs(argc, argv); - Vmoving_average_accumulator *top = new Vmoving_average_accumulator; - VerilatedVcdC *tfp = nullptr; - vluint64_t sim_time = 0; - - top->clk = 0; - top->reset = 1; - top->d_in = 0; - - const int Exponent = 3; - const int N = 1 << Exponent; - const int DataWidth = 16; - - uint16_t one_ago = 0; - uint16_t two_ago = 0; - uint16_t expected_out = 0; - - std::vector sample_buffer(N, 0); - uint32_t acc = 0; // using 32 bits to prevent overflow - - // current index pointing to the oldest sample - int oldest_index = 0; - - while (sim_time < SIM_TIME) { - top->clk = !top->clk; - - if (sim_time > 4) - top->reset = 0; - - if (top->clk) { - uint16_t input_data = (3 * sim_time + 2) % 8031; - top->d_in = input_data; - if (!top->reset) { - acc -= sample_buffer[oldest_index]; - acc += input_data; - sample_buffer[oldest_index] = input_data; - - oldest_index = (oldest_index + 1) % N; - - two_ago = one_ago; - one_ago = expected_out; - expected_out = acc >> Exponent; - - // compare and err if bad - if (top->d_out != two_ago) { - std::cerr << "Mismatch on simtime" << sim_time << ": expected " - << two_ago << ", got " << top->d_out << std::endl; - return EXIT_FAILURE; - } - } - } - - top->eval(); - - - sim_time++; - } - - top->final(); - - // cleanup - delete top; - - std::cout << "Simulation completed successfully." << std::endl; - return EXIT_SUCCESS; -} - diff --git a/itch_streamgen/src/itch/hardware/itch_parser.sv b/pipebomb/rtl/orderbook_pkg.sv similarity index 100% rename from itch_streamgen/src/itch/hardware/itch_parser.sv rename to pipebomb/rtl/orderbook_pkg.sv