├── .cargo └── audit.toml ├── .dockerignore ├── .env ├── .github └── dependabot.yml ├── .gitignore ├── BUILD.md ├── Cargo.toml ├── LICENSE ├── README.md ├── data └── Kraken_Readme.md ├── doc ├── analyze_data.md ├── img │ ├── autometrics_client_login.png │ ├── autometrics_dashboard.png │ ├── query_result_1.png │ ├── resamples_bars.png │ ├── reverse_symbol_query.png │ ├── symbol_query.png │ ├── top_ten_btc_volume_days.png │ ├── top_ten_markets.png │ └── web_console.png ├── import_data.md ├── install.md ├── qd_protocol.md ├── quest_db_config │ └── server.conf └── symbol_mapping.md ├── flv_cli ├── csv_import │ ├── Cargo.toml │ └── src │ │ └── bin │ │ └── csv_import │ │ ├── main.rs │ │ ├── process_file.rs │ │ ├── query_gen.rs │ │ ├── query_utils.rs │ │ └── types.rs └── iggy_admin │ ├── Cargo.toml │ └── src │ └── main.rs ├── flv_clients ├── qd_client │ ├── Cargo.toml │ ├── src │ │ └── lib │ │ │ ├── getters.rs │ │ │ ├── mod.rs │ │ │ ├── send_login.rs │ │ │ ├── send_logout.rs │ │ │ ├── send_start_data.rs │ │ │ └── shared.rs │ └── tests │ │ └── mod.rs └── symdb_client │ ├── Cargo.toml │ ├── src │ ├── error.rs │ ├── lib.rs │ ├── lookup.rs │ └── utils_proto.rs │ └── tests │ ├── mod.rs │ └── symdb_client_tests.rs ├── flv_common ├── Cargo.toml ├── src │ ├── lib.rs │ ├── prelude.rs │ └── types │ │ ├── config_types │ │ ├── click_house_config.rs │ │ ├── client_channel.rs │ │ ├── db_config.rs │ │ ├── environment_types.rs │ │ ├── host_endpoint.rs │ │ ├── iggy_config.rs │ │ ├── iggy_user.rs │ │ ├── message_client_config.rs │ │ ├── metric_config.rs │ │ ├── mod.rs │ │ ├── service_config.rs │ │ └── service_id.rs │ │ ├── data_types │ │ ├── mod.rs │ │ ├── ohlcv_bar │ │ │ ├── default.rs │ │ │ ├── display.rs │ │ │ ├── getters.rs │ │ │ └── mod.rs │ │ ├── sampled_bars │ │ │ └── mod.rs │ │ ├── time_resolution.rs │ │ └── trade_bar │ │ │ ├── default.rs │ │ │ ├── display.rs │ │ │ ├── getters.rs │ │ │ └── mod.rs │ │ ├── error_types │ │ └── mod.rs │ │ ├── exchange_types │ │ ├── account_type.rs │ │ ├── exchange_id.rs │ │ ├── mod.rs │ │ └── security_type.rs │ │ ├── mod.rs │ │ ├── symbol_types │ │ ├── mod.rs │ │ └── symbol.rs │ │ └── time_types │ │ ├── mod.rs │ │ ├── month.rs │ │ └── time_scale.rs └── tests │ ├── errors │ ├── init_error_tests.rs │ ├── message_client_config_error_tests.rs │ ├── message_processing_error_tests.rs │ └── mod.rs │ ├── mod.rs │ └── types │ ├── config_types │ ├── click_house_config_tests.rs │ ├── client_channel_tests.rs │ ├── db_config_tests.rs │ ├── environment_types_tests.rs │ ├── message_client_config_tests.rs │ ├── metric_config_tests.rs │ ├── mod.rs │ ├── service_config_tests.rs │ └── service_id_tests.rs │ ├── data_types │ ├── mod.rs │ ├── ohlcv_bar_tests.rs │ ├── time_resolution_tests.rs │ └── trade_bar_tests.rs │ ├── exchange_types │ ├── account_type_tests.rs │ ├── exchange_id_tests.rs │ ├── mod.rs │ └── security_type_tests.rs │ ├── mod.rs │ ├── symbol_types │ ├── mod.rs │ └── symbol_tests.rs │ └── time_types │ ├── mod.rs │ ├── month_tests.rs │ └── time_scale_tests.rs ├── flv_components ├── config_manager │ ├── Cargo.toml │ ├── src │ │ ├── getters.rs │ │ ├── lib.rs │ │ └── utils.rs │ └── tests │ │ ├── config_manager_tests.rs │ │ └── mod.rs ├── db_query_manager │ ├── Cargo.toml │ ├── src │ │ ├── error.rs │ │ ├── lib.rs │ │ ├── query_gen.rs │ │ ├── query_ohlcv.rs │ │ ├── query_symbols.rs │ │ ├── query_trades.rs │ │ ├── query_utils.rs │ │ ├── stream_ohlcv.rs │ │ ├── stream_trades.rs │ │ └── types.rs │ └── tests │ │ ├── db_query_manager_tests.rs │ │ └── mod.rs └── symbol_manager │ ├── Cargo.toml │ ├── src │ ├── getters.rs │ ├── lib.rs │ ├── lookup_exchange_name.rs │ ├── lookup_symbol.rs │ └── lookup_symbol_table.rs │ └── tests │ ├── mod.rs │ └── symbol_manager_tests.rs ├── flv_examples ├── basic_data_stream │ ├── Cargo.toml │ └── src │ │ ├── handle_data.rs │ │ └── main.rs ├── causal_data_inference │ ├── Cargo.toml │ └── src │ │ ├── handle_data.rs │ │ └── main.rs ├── causal_model │ ├── Cargo.toml │ ├── src │ │ ├── context │ │ │ ├── build_context.rs │ │ │ └── mod.rs │ │ ├── extensions │ │ │ ├── mod.rs │ │ │ ├── range.rs │ │ │ └── time_index.rs │ │ ├── mod.rs │ │ ├── model │ │ │ ├── mod.rs │ │ │ └── model.rs │ │ ├── prelude.rs │ │ ├── types │ │ │ ├── alias.rs │ │ │ ├── bar_range.rs │ │ │ ├── mod.rs │ │ │ └── range_data.rs │ │ └── utils │ │ │ ├── context_utils.rs │ │ │ ├── mod.rs │ │ │ └── time_utils.rs │ └── tests │ │ ├── context │ │ ├── build_context_tests.rs │ │ └── mod.rs │ │ ├── extensions │ │ ├── mod.rs │ │ └── time_indexable_tests.rs │ │ ├── mod.rs │ │ ├── model │ │ ├── mod.rs │ │ └── model_builder_tests.rs │ │ ├── types │ │ ├── bar_range_tests.rs │ │ ├── mod.rs │ │ └── range_data_tests.rs │ │ └── utils │ │ ├── augment_data_tests.rs │ │ ├── counter_tests.rs │ │ ├── mod.rs │ │ └── time_scale_utils_tests.rs └── symbol_master │ ├── Cargo.toml │ └── src │ ├── handle_data.rs │ ├── main.rs │ └── utils.rs ├── flv_proto ├── Cargo.toml ├── build.rs ├── proto │ └── symdb.proto └── src │ └── lib.rs ├── flv_sbe ├── bindings │ ├── Cargo.toml │ └── src │ │ ├── client_error_codec.rs │ │ ├── client_error_type.rs │ │ ├── client_login_codec.rs │ │ ├── client_logout_codec.rs │ │ ├── data_bar_codec.rs │ │ ├── data_error_codec.rs │ │ ├── exchange_id.rs │ │ ├── first_data_bar_codec.rs │ │ ├── first_trade_bar_codec.rs │ │ ├── last_data_bar_codec.rs │ │ ├── last_trade_bar_codec.rs │ │ ├── lib.rs │ │ ├── message_header_codec.rs │ │ ├── message_type.rs │ │ ├── start_data_msg_codec.rs │ │ ├── stop_all_data_msg_codec.rs │ │ ├── stop_data_msg_codec.rs │ │ └── trade_bar_codec.rs ├── sbe_messages │ ├── Cargo.toml │ ├── src │ │ ├── errors │ │ │ └── mod.rs │ │ ├── lib.rs │ │ ├── messages │ │ │ ├── client_messages │ │ │ │ ├── client_login │ │ │ │ │ ├── display.rs │ │ │ │ │ ├── getters.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── sbe_decode.rs │ │ │ │ │ └── sbe_encode.rs │ │ │ │ ├── client_logout │ │ │ │ │ ├── display.rs │ │ │ │ │ ├── getters.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── sbe_decode.rs │ │ │ │ │ └── sbe_encode.rs │ │ │ │ └── mod.rs │ │ │ ├── data_messages │ │ │ │ ├── mod.rs │ │ │ │ ├── ohlcv_bar │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── sbe_decoder.rs │ │ │ │ │ └── sbe_encoder.rs │ │ │ │ ├── ohlcv_bar_first │ │ │ │ │ ├── display.rs │ │ │ │ │ ├── getters.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── sbe_decode.rs │ │ │ │ │ └── sbe_encode.rs │ │ │ │ ├── ohlcv_bar_last │ │ │ │ │ ├── display.rs │ │ │ │ │ ├── getters.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── sbe_decode.rs │ │ │ │ │ └── sbe_encode.rs │ │ │ │ ├── start_data │ │ │ │ │ ├── display.rs │ │ │ │ │ ├── getter.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── sbe_decode.rs │ │ │ │ │ └── sbe_encode.rs │ │ │ │ ├── stop_all_data │ │ │ │ │ ├── display.rs │ │ │ │ │ ├── getter.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── sbe_decode.rs │ │ │ │ │ └── sbe_encode.rs │ │ │ │ ├── stop_data │ │ │ │ │ ├── display.rs │ │ │ │ │ ├── getter.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── sbe_decode.rs │ │ │ │ │ └── sbe_encode.rs │ │ │ │ ├── trade_bar │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── sbe_decode.rs │ │ │ │ │ └── sbe_encode.rs │ │ │ │ ├── trade_bar_first │ │ │ │ │ ├── display.rs │ │ │ │ │ ├── getters.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── sbe_decode.rs │ │ │ │ │ └── sbe_encode.rs │ │ │ │ └── trade_bar_last │ │ │ │ │ ├── display.rs │ │ │ │ │ ├── getters.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── sbe_decode.rs │ │ │ │ │ └── sbe_encode.rs │ │ │ ├── error_messages │ │ │ │ ├── client_error_message │ │ │ │ │ ├── display.rs │ │ │ │ │ ├── getters.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── sbe_decode.rs │ │ │ │ │ └── sbe_encode.rs │ │ │ │ ├── data_error_message │ │ │ │ │ ├── display.rs │ │ │ │ │ ├── getters.rs │ │ │ │ │ ├── mod.rs │ │ │ │ │ ├── sbe_decode.rs │ │ │ │ │ └── sbe_encode.rs │ │ │ │ └── mod.rs │ │ │ └── mod.rs │ │ ├── prelude.rs │ │ └── types │ │ │ ├── client_error_types.rs │ │ │ ├── data_error_types.rs │ │ │ ├── data_type.rs │ │ │ ├── message_types.rs │ │ │ └── mod.rs │ └── tests │ │ ├── errors │ │ ├── errors_tests.rs │ │ └── mod.rs │ │ ├── messages │ │ ├── client_messages │ │ │ ├── client_login │ │ │ │ ├── client_login_message_tests.rs │ │ │ │ └── mod.rs │ │ │ ├── client_logout │ │ │ │ ├── client_logout_message_tests.rs │ │ │ │ └── mod.rs │ │ │ └── mod.rs │ │ ├── data_messages │ │ │ ├── mod.rs │ │ │ ├── ohlcv_bar │ │ │ │ ├── mod.rs │ │ │ │ └── ohlcv_bar_tests.rs │ │ │ ├── ohlcv_bar_first │ │ │ │ ├── mod.rs │ │ │ │ └── ohlcv_bar_first_tests.rs │ │ │ ├── ohlcv_bar_last │ │ │ │ ├── mod.rs │ │ │ │ └── ohlcv_bar_last_tests.rs │ │ │ ├── start_data │ │ │ │ ├── mod.rs │ │ │ │ └── start_data_message_tests.rs │ │ │ ├── stop_all_data │ │ │ │ ├── mod.rs │ │ │ │ └── stop_all_data_message_tests.rs │ │ │ ├── stop_data │ │ │ │ ├── mod.rs │ │ │ │ └── stop_data_message_tests.rs │ │ │ ├── trade_bar │ │ │ │ ├── mod.rs │ │ │ │ └── trade_bar_tests.rs │ │ │ ├── trade_bar_first │ │ │ │ ├── mod.rs │ │ │ │ └── trade_bar_first_tests.rs │ │ │ └── trade_bar_last │ │ │ │ ├── mod.rs │ │ │ │ └── trade_bar_last_tests.rs │ │ ├── error_messages │ │ │ ├── client_error_message │ │ │ │ ├── client_error_message_tests.rs │ │ │ │ └── mod.rs │ │ │ ├── data_error_message │ │ │ │ ├── data_error_message_tests.rs │ │ │ │ └── mod.rs │ │ │ └── mod.rs │ │ └── mod.rs │ │ ├── mod.rs │ │ └── types │ │ ├── client_error_types_tests.rs │ │ ├── data_error_types_tests.rs │ │ ├── data_type_tests.rs │ │ ├── message_types_tests.rs │ │ └── mod.rs ├── sbe_schema │ ├── example_schema │ │ ├── fix-binary.xml │ │ └── new-order-single-schema.xml │ └── schema.xml └── schema.sbeir ├── flv_services ├── qdgw │ ├── Cargo.toml │ └── src │ │ ├── main.rs │ │ └── service │ │ ├── handle │ │ ├── handle_client_login.rs │ │ ├── handle_client_logout.rs │ │ ├── handle_client_utils.rs │ │ ├── handle_data_start.rs │ │ ├── handle_data_start_ohlcv_data.rs │ │ ├── handle_data_start_trade_data.rs │ │ ├── handle_data_stop.rs │ │ ├── handle_data_stop_all.rs │ │ ├── handle_message.rs │ │ └── mod.rs │ │ ├── mod.rs │ │ ├── run.rs │ │ └── utils │ │ ├── mod.rs │ │ ├── utils_data_encoding.rs │ │ ├── utils_send_data.rs │ │ └── utils_send_error.rs └── symdb │ ├── Cargo.toml │ └── src │ ├── main.rs │ └── service.rs ├── flv_specs ├── db_specs │ ├── Cargo.toml │ ├── src │ │ ├── clickhouse │ │ │ └── mod.rs │ │ ├── lib.rs │ │ └── prelude.rs │ └── tests │ │ └── mod.rs ├── exchange_specs │ ├── Cargo.toml │ ├── src │ │ ├── default │ │ │ └── mod.rs │ │ ├── lib.rs │ │ └── prelude.rs │ └── tests │ │ ├── default │ │ └── mod.rs │ │ └── mod.rs ├── message_specs │ ├── Cargo.toml │ ├── src │ │ ├── iggy │ │ │ └── mod.rs │ │ ├── lib.rs │ │ └── prelude.rs │ └── tests │ │ └── mod.rs └── service_specs │ ├── Cargo.toml │ ├── src │ ├── lib.rs │ ├── prelude.rs │ └── services │ │ ├── mod.rs │ │ ├── qdgw │ │ ├── Dockerfile │ │ └── mod.rs │ │ └── symdb │ │ └── mod.rs │ └── tests │ ├── mod.rs │ ├── qdgw │ └── mod.rs │ └── symdb │ └── mod.rs ├── flv_utils ├── client_utils │ ├── Cargo.toml │ └── src │ │ ├── config_utils │ │ └── mod.rs │ │ ├── counter_utils │ │ ├── atomic_counter.rs │ │ └── mod.rs │ │ ├── csv_utils │ │ └── mod.rs │ │ ├── data_utils │ │ └── mod.rs │ │ ├── file_utils │ │ └── mod.rs │ │ ├── lib.rs │ │ ├── message_utils │ │ ├── handle_error_utils.rs │ │ ├── handle_utils.rs │ │ └── mod.rs │ │ ├── prelude.rs │ │ ├── print_utils │ │ └── mod.rs │ │ └── symbol_utils │ │ └── mod.rs ├── iggy_utils │ ├── Cargo.toml │ ├── src │ │ └── lib.rs │ └── tests │ │ └── mod.rs └── service_utils │ ├── Cargo.toml │ └── src │ ├── lib.rs │ ├── print_utils.rs │ └── shutdown_utils.rs ├── import_config.toml ├── makefile ├── scripts ├── build.sh ├── check.sh ├── clean.sh ├── doc.sh ├── docker.sh ├── example.sh ├── fix.sh ├── format.sh ├── import.sh ├── install_deps.sh ├── qdgw.sh ├── release.sh ├── run.sh ├── sbe.sh ├── symdb.sh ├── test.sh └── update.sh ├── sql ├── all_symbols_with_ids.sql ├── count_rows.sql ├── resample_by_year.sql ├── select_between_dates.sql ├── symbol_mapping.sql ├── symbols_summary.sql ├── top_ten_traded_symbols.sql ├── top_ten_trading_days_by_volume.sql └── vwap.sql └── tools ├── Readme.md └── sbe ├── LICENSE └── sbe-all-1.30.0.jar /.cargo/audit.toml: -------------------------------------------------------------------------------- 1 | [advisories] 2 | ignore = [ 3 | "RUSTSEC-2023-0033", 4 | "RUSTSEC-2023-0003", 5 | "RUSTSEC-2024-0003", 6 | # Resource exhaustion vulnerability in h2 may lead to Denial of Service (DoS) 7 | # https://rustsec.org/advisories/RUSTSEC-2023-0033 8 | # https://rustsec.org/advisories/RUSTSEC-2023-0003 9 | # https://rustsec.org/advisories/RUSTSEC-2024-0003 10 | 11 | "RUSTSEC-2023-0071", 12 | # Marvin Attack: potential key recovery through timing sidechannels 13 | # https://rustsec.org/advisories/RUSTSEC-2023-0071 14 | ] -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | .autometrics 2 | .cargo 3 | .github 4 | .git 5 | data 6 | doc 7 | scripts 8 | sql 9 | target 10 | tools 11 | -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepcausality-rs/fluvio-examples/88d90531c0b1e210bcac825edc78058596cfeb17/.env -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | # To get started with Dependabot version updates, you'll need to specify which 2 | # package ecosystems to update and where the package manifests are located. 3 | # Please see the documentation for all configuration options: 4 | # https://docs.github.com/github/administering-a-repository/configuration-options-for-dependency-updates 5 | 6 | # To receive Dependabot updates when a new version of a GitHub Action (including release-plz) is available 7 | # https://release-plz.ieni.dev/docs/github/update 8 | 9 | version: 2 10 | updates: 11 | - package-ecosystem: "cargo" # See documentation for possible values 12 | directory: "/" # Location of package manifests 13 | schedule: 14 | interval: "daily" 15 | # 16 | - package-ecosystem: "github-actions" 17 | directory: "/" 18 | schedule: 19 | interval: "daily" 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | debug/ 4 | target/ 5 | 6 | # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries 7 | # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html 8 | Cargo.lock 9 | 10 | # These are backup files generated by rustfmt 11 | **/*.rs.bk 12 | 13 | # MSVC Windows builds of rustc generate these, which store debugging information 14 | *.pdb 15 | 16 | # Jetbrains stuff 17 | .idea/ 18 | .idea/* 19 | .idea/*.* 20 | 21 | # Exclude the data directory from git b/c file size exceeds github's 100MB limit 22 | data/Kraken_Trading_History/ 23 | 24 | # clickhouse 25 | ch -------------------------------------------------------------------------------- /BUILD.md: -------------------------------------------------------------------------------- 1 | ## 🛠️ Cargo & Make 2 | 3 | Cargo works as expected, but in addition to cargo, a makefile exists 4 | that abstracts over several additional tools you may have to install 5 | before all make commands work. To do so, please run the following command: 6 | 7 | ```bash 8 | make setup 9 | ``` 10 | 11 | The make install command tests and tries to install all required developer dependencies. 12 | if the automatic install fails, the script will show a link with further installation instructions. 13 | 14 | After all dependencies have been installed, the following commands are ready to use. 15 | 16 | ``` 17 | make 18 | make qdgw Start the Start the Quant Data Gateway (QDGW). 19 | make symdb Start the Symbol Master Database Service (SYMDB) 20 | make example Run the example code in flv_examples. 21 | make build Builds the code base incrementally (fast) for dev. 22 | make check Checks the code base for security vulnerabilities. 23 | make doc Builds, tests, and opens api docs in a browser. 24 | make fix Fixes linting issues as reported by clippy. 25 | make import Imports tick data from CSV into QuestDB. 26 | make format Formats call code according to cargo fmt style. 27 | make setup Tests and installs all make script dependencies. 28 | make run Runs the default binary (QDGW). 29 | make update Update rust, update and build the project. 30 | make test Tests across all crates. 31 | make sbe Generates Rust bindings from the SBE schema. 32 | ``` 33 | 34 | The scripts called by each make command are located in the [script folder.](scripts) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | ## Work in progress. -------------------------------------------------------------------------------- /data/Kraken_Readme.md: -------------------------------------------------------------------------------- 1 | # Kraken Data Download 2 | 3 | Historical market data (also known as trading history or tick data) 4 | provides a detailed record of every trade that happens on our markets, including the following information: 5 | 6 | ## Data schema 7 | 8 | * The exact date and time of each trade, 9 | * The price at which each trade occurred, 10 | * The amount of volume that was traded. 11 | 12 | ## Timestamp resolution 13 | 14 | Kraken returns [millisecond-precision unix timestamps](https://github.com/ccxt/ccxt/issues/6039) 15 | 16 | 17 | ## Download link 18 | 19 | https://support.kraken.com/hc/en-us/articles/360047543791-Downloadable-historical-market-data-time-and-sales- 20 | 21 | **Important**: When downloading the quarterly tick data, you must rename the unzipped folder to 22 | 23 | Kraken_Trading_History 24 | 25 | before copying it in the data folder otherwise the data import will fail due to unrecognized file path. 26 | 27 | 28 | FIX: 29 | 30 | ETH2SETH.csv -------------------------------------------------------------------------------- /doc/analyze_data.md: -------------------------------------------------------------------------------- 1 | # Analyze data with SQL 2 | 3 | ## Top Ten Most Traded Markets 4 | 5 | Let's see the top ten most traded markets of all time on Kraken: 6 | 7 | ``` 8 | SELECT * 9 | FROM kraken_symbols 10 | ORDER BY number_of_rows ASC 11 | LIMIT 10; 12 | ``` 13 | Unsurprisingly, Bitcoin (XBT) and Ethereum (ETH) dominate the top ten most traded markets. To see Ripple (XRP) in the top ten is more surprising given its rocky history of legal disputes. 14 | 15 | ![top_ten_markets.png](img/top_ten_markets.png) 16 | 17 | ## Top Ten Bitcoin Trading Days by Volume (USD) 18 | 19 | Let's see the top ten trading days by volume (USD) for Bitcoin (XBT): 20 | 21 | ``` 22 | SELECT 23 | timestamp datetime, 24 | sum(volume) volume, 25 | 26 | FROM kraken_xbtusd 27 | 28 | SAMPLE BY 1d 29 | ALIGN TO CALENDAR WITH OFFSET '00:00' 30 | ORDER BY volume DESC 31 | limit 10; 32 | ``` 33 | 34 | Apparently, March madness applies to bitcoin as well. The top ten trading days by volume (USD) for Bitcoin (XBT) are: 35 | 36 | ![top_ten_btc_volume_days.png](img/top_ten_btc_volume_days.png) 37 | 38 | 39 | ## Resample Trades to 15min Bars 40 | 41 | Let's resample the raw bitcoin trade data from 2022 into 15 minute bars. 42 | For unknown reasons, the resampling leads to a one second shift that needs to be adjusted by starting sampling at exactly midnight. To do so, we use the align to calender expression followed by an offset in the 24h format. 43 | 44 | ``` 45 | SELECT 46 | timestamp datetime, 47 | first(price) open, 48 | max(price) high, 49 | min(price) low, 50 | last(price) close, 51 | sum(volume) volume, 52 | 53 | FROM kraken_xbtusd 54 | WHERE timestamp IN '2022' 55 | SAMPLE BY 15m 56 | ALIGN TO CALENDAR WITH OFFSET '00:00'; 57 | ``` 58 | ![resamples_bars.png](img/resamples_bars.png) 59 | -------------------------------------------------------------------------------- /doc/img/autometrics_client_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepcausality-rs/fluvio-examples/88d90531c0b1e210bcac825edc78058596cfeb17/doc/img/autometrics_client_login.png -------------------------------------------------------------------------------- /doc/img/autometrics_dashboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepcausality-rs/fluvio-examples/88d90531c0b1e210bcac825edc78058596cfeb17/doc/img/autometrics_dashboard.png -------------------------------------------------------------------------------- /doc/img/query_result_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepcausality-rs/fluvio-examples/88d90531c0b1e210bcac825edc78058596cfeb17/doc/img/query_result_1.png -------------------------------------------------------------------------------- /doc/img/resamples_bars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepcausality-rs/fluvio-examples/88d90531c0b1e210bcac825edc78058596cfeb17/doc/img/resamples_bars.png -------------------------------------------------------------------------------- /doc/img/reverse_symbol_query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepcausality-rs/fluvio-examples/88d90531c0b1e210bcac825edc78058596cfeb17/doc/img/reverse_symbol_query.png -------------------------------------------------------------------------------- /doc/img/symbol_query.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepcausality-rs/fluvio-examples/88d90531c0b1e210bcac825edc78058596cfeb17/doc/img/symbol_query.png -------------------------------------------------------------------------------- /doc/img/top_ten_btc_volume_days.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepcausality-rs/fluvio-examples/88d90531c0b1e210bcac825edc78058596cfeb17/doc/img/top_ten_btc_volume_days.png -------------------------------------------------------------------------------- /doc/img/top_ten_markets.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepcausality-rs/fluvio-examples/88d90531c0b1e210bcac825edc78058596cfeb17/doc/img/top_ten_markets.png -------------------------------------------------------------------------------- /doc/img/web_console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepcausality-rs/fluvio-examples/88d90531c0b1e210bcac825edc78058596cfeb17/doc/img/web_console.png -------------------------------------------------------------------------------- /doc/import_data.md: -------------------------------------------------------------------------------- 1 | # Kraken Data Download 2 | 3 | Historical market data (also known as trading history or tick data) 4 | provides a detailed record of every trade that happens on our markets, including the following information: 5 | 6 | ## Data schema 7 | 8 | * The exact date and time of each trade, 9 | * The price at which each trade occurred, 10 | * The amount of volume that was traded. 11 | 12 | ## Timestamp resolution 13 | 14 | Kraken returns [millisecond-precision unix timestamps](https://github.com/ccxt/ccxt/issues/6039) 15 | 16 | 17 | ## Download link 18 | 19 | You can download either the full dataset (recommended, but but big), or the 20 | quarterly dataset, which is significantly smaller. Either one works. 21 | 22 | https://support.kraken.com/hc/en-us/articles/360047543791-Downloadable-historical-market-data-time-and-sales- 23 | 24 | ## (!!!) Important (!!!) 25 | 26 | Make sure that the unzipped folder is in the project [data folder](../data) ***and*** the absolute path to the data folder is set in the [import_data.toml](../import_config.toml) config file. This is crucial to make the data import work. 27 | -------------------------------------------------------------------------------- /doc/qd_protocol.md: -------------------------------------------------------------------------------- 1 | 2 | # QD communication protocol 3 | 4 | The communication between the QD client and QD gateway follows a simple protocol. 5 | 6 | 1) The client sends a login message with its client ID to the gateway. 7 | * If the client is already logged in, a client error message gets returned. 8 | * If the client is not yet known, the login process starts. Notice, the gateway only returns error messages, but no login success message and that means it is the application’s responsibility to monitor the QD client for errors. If there is no error, it is safe to proceed. 9 | 10 | 2) Once connected, the client can send request either trade data or sampled OHLCV data at a resolution defined in the request message. 11 | * The gateway returns an error if the requested data are unavailable. 12 | * If the data are available, the gateway starts the data streaming. 13 | 14 | 15 | 3) When no further data are needed, the QD client is supposed to send a logout message to the gateway. If the client does not send a logout, the next login attempt with the same client ID will result in an error.   16 | 17 | 18 | ## Important details 19 | 20 | * The QD client upon connection sends the login message automatically. 21 | * When the QD client has been created, the application can immediately request data. 22 | * The application logs out simply by calling the close method of the QD client, which sends the logout message. 23 | -------------------------------------------------------------------------------- /flv_cli/csv_import/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "csv_import" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [[bin]] 12 | name = "csv_import" 13 | path = "src/bin/csv_import/main.rs" 14 | 15 | 16 | [dependencies] 17 | # Internal crates 18 | client_utils = { workspace = true } 19 | common = { workspace = true } 20 | db_specs = { workspace = true } 21 | # External crates 22 | csv = { workspace = true } 23 | chrono = { workspace = true } 24 | encoding_rs = { workspace = true } 25 | klickhouse = { workspace = true } 26 | serde = { workspace = true } 27 | tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros"] } 28 | -------------------------------------------------------------------------------- /flv_cli/csv_import/src/bin/csv_import/types.rs: -------------------------------------------------------------------------------- 1 | use klickhouse::Row; 2 | use serde::{Deserialize, Serialize}; 3 | use std::fmt; 4 | 5 | #[derive(Debug, Row, Serialize, Deserialize)] 6 | pub struct CountRow { 7 | count: u64, 8 | } 9 | 10 | impl CountRow { 11 | pub fn count(&self) -> u64 { 12 | self.count 13 | } 14 | } 15 | 16 | #[derive(Debug, Clone, Row, Serialize, Deserialize)] 17 | pub struct MetaData { 18 | table_name: String, 19 | symbol: String, 20 | symbol_id: u32, 21 | number_of_rows: u64, 22 | } 23 | 24 | impl MetaData { 25 | pub fn new(table_name: String, symbol: String, symbol_id: u32, number_of_rows: u64) -> Self { 26 | Self { 27 | table_name, 28 | symbol, 29 | symbol_id, 30 | number_of_rows, 31 | } 32 | } 33 | } 34 | 35 | impl MetaData { 36 | pub fn table_name(&self) -> &str { 37 | &self.table_name 38 | } 39 | pub fn symbol(&self) -> &str { 40 | &self.symbol 41 | } 42 | pub fn symbol_id(&self) -> u32 { 43 | self.symbol_id 44 | } 45 | pub fn number_of_rows(&self) -> u64 { 46 | self.number_of_rows 47 | } 48 | } 49 | 50 | impl fmt::Display for MetaData { 51 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 52 | write!( 53 | f, 54 | "MetaData {{ table_name: {}, symbol: {}, symbol_id: {}, number_of_rows: {} }}", 55 | self.table_name, self.symbol, self.symbol_id, self.number_of_rows 56 | ) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /flv_cli/iggy_admin/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iggy_admin" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [[bin]] 12 | name = "iggy_admin" 13 | path = "src/main.rs" 14 | 15 | 16 | [dependencies] 17 | # Internal crates 18 | common = {workspace = true } 19 | iggy_utils = {workspace = true } 20 | # External crates 21 | iggy = {workspace = true } 22 | tokio = { workspace = true, features = ["rt", "rt-multi-thread", "macros"] } 23 | -------------------------------------------------------------------------------- /flv_clients/qd_client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "qd_client" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [lib] 12 | name = "qd_client" 13 | path = "src/lib/mod.rs" 14 | 15 | 16 | [dependencies] 17 | # Internal crates 18 | common = { workspace = true } 19 | sbe_messages = { workspace = true } 20 | iggy_utils = {workspace = true} 21 | # External crates 22 | bytes = { workspace = true } 23 | tokio = { workspace = true } 24 | iggy = {workspace = true} 25 | -------------------------------------------------------------------------------- /flv_clients/qd_client/src/lib/getters.rs: -------------------------------------------------------------------------------- 1 | use iggy::clients::client::IggyClient; 2 | use iggy::messages::poll_messages::PollMessages; 3 | 4 | use common::prelude::IggyConfig; 5 | 6 | use crate::QDClient; 7 | 8 | impl QDClient { 9 | pub fn client_id(&self) -> u16 { 10 | self.client_id 11 | } 12 | pub fn producer(&self) -> &IggyClient { 13 | &self.producer 14 | } 15 | pub fn consumer(&self) -> &IggyClient { 16 | &self.consumer 17 | } 18 | pub fn poll_command(&self) -> &PollMessages { 19 | &self.poll_command 20 | } 21 | pub fn consumer_config(&self) -> &IggyConfig { 22 | &self.consumer_config 23 | } 24 | pub fn producer_config(&self) -> &IggyConfig { 25 | &self.producer_config 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /flv_clients/qd_client/src/lib/send_login.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use bytes::Bytes; 4 | use iggy::messages::send_messages::Message; 5 | 6 | use sbe_messages::prelude::ClientLoginMessage; 7 | 8 | use crate::QDClient; 9 | 10 | impl QDClient { 11 | /// Logs in the client by sending a login message to the gateway. 12 | /// 13 | /// # Returns 14 | /// 15 | /// Returns a `Result` with `()` on success, or an `Error` on failure. 16 | /// 17 | /// This constructs a `ClientLoginMessage` with the client ID. 18 | /// It encodes the message to bytes and sends it to the gateway 19 | /// using the `send_message` method. 20 | /// 21 | pub(crate) async fn login(&self) -> Result<(), Box> { 22 | // Construct login message 23 | let message = ClientLoginMessage::new(self.client_id); 24 | 25 | // Encode message 26 | let (_, buffer) = message 27 | .encode() 28 | .expect("[QDClient/login]: Failed to encode message"); 29 | 30 | // Build iggy message wrapper 31 | let message = Message::new(None, Bytes::from(buffer), None); 32 | 33 | // Send message to the gateway 34 | self.send_message(message) 35 | .await 36 | .expect("[QDClient/login]: Failed to send login message!"); 37 | 38 | Ok(()) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /flv_clients/qd_client/src/lib/send_logout.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | 3 | use bytes::Bytes; 4 | use iggy::messages::send_messages::Message; 5 | 6 | use sbe_messages::prelude::ClientLogoutMessage; 7 | 8 | use crate::QDClient; 9 | 10 | impl QDClient { 11 | /// Logs out of the client by sending a logout message to the gateway. 12 | /// 13 | /// # Returns 14 | /// 15 | /// Returns a `Result` with `()` on success, or an `Error` on failure. 16 | /// 17 | /// This constructs a `ClientLogoutMessage` with the client ID. 18 | /// It encodes the message to bytes and sends it to the gateway 19 | /// using the `send_message` method. 20 | /// 21 | pub(crate) async fn logout(&self) -> Result<(), Box> { 22 | // Construct logout message 23 | let logout_message = ClientLogoutMessage::new(self.client_id); 24 | 25 | // Encode message 26 | let (_, buffer) = logout_message 27 | .encode() 28 | .expect("[QDClient/logout]: Failed to encode message"); 29 | 30 | // Build iggy message wrapper 31 | let message = Message::new(None, Bytes::from(buffer), None); 32 | 33 | // Send message to the gateway 34 | self.send_message(message) 35 | .await 36 | .expect("[QDClient/logout]: Failed to send logout message!"); 37 | 38 | Ok(()) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /flv_clients/qd_client/tests/mod.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::{IggyConfig, IggyUser}; 2 | use qd_client::QDClient; 3 | 4 | const CLIENT_ID: u16 = 77; 5 | 6 | // 7 | // Requires that the QDGW server is running on localhost 8 | // Start the server with: 9 | // 10 | // cargo run --bin qdgw 11 | // 12 | 13 | #[tokio::test] 14 | async fn test_new() { 15 | let user = IggyUser::default(); 16 | let client_config = IggyConfig::from_client_id(user, CLIENT_ID as u32, 50000, false); 17 | 18 | // Happy path 19 | let client = QDClient::new(CLIENT_ID, client_config).await; 20 | assert!(client.is_ok()); 21 | 22 | let client = client.unwrap(); 23 | assert_eq!(client.client_id(), CLIENT_ID); 24 | 25 | client.close().await.expect("Failed to close client"); 26 | } 27 | -------------------------------------------------------------------------------- /flv_clients/symdb_client/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symdb_client" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [lib] 12 | name = "symdb_client" 13 | path = "src/lib.rs" 14 | 15 | 16 | [dependencies] 17 | # Internal crates 18 | common = {workspace = true} 19 | proto = {workspace = true} 20 | 21 | # Exteral crates 22 | tokio = { workspace = true } 23 | tonic = { workspace = true } 24 | prost = { workspace = true } -------------------------------------------------------------------------------- /flv_clients/symdb_client/src/error.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::fmt; 3 | use std::fmt::Display; 4 | 5 | #[derive(Debug)] 6 | pub struct SymdbClientError(pub String); 7 | 8 | impl Error for SymdbClientError {} 9 | 10 | impl Display for SymdbClientError { 11 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 12 | write!(f, "SymdbClientError: {}", self.0) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /flv_clients/symdb_client/tests/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod symdb_client_tests; 3 | -------------------------------------------------------------------------------- /flv_common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "common" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [dependencies] 12 | # External crates 13 | chrono = { workspace = true } 14 | iggy = { workspace = true } 15 | rust_decimal = { workspace = true } 16 | serde = { workspace = true } 17 | -------------------------------------------------------------------------------- /flv_common/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod prelude; 2 | 3 | mod types; 4 | -------------------------------------------------------------------------------- /flv_common/src/prelude.rs: -------------------------------------------------------------------------------- 1 | // Errors 2 | pub use crate::types::error_types::InitError; 3 | pub use crate::types::error_types::LookupError; 4 | pub use crate::types::error_types::MessageClientConfigError; 5 | pub use crate::types::error_types::MessageProcessingError; 6 | pub use crate::types::error_types::ValidationError; 7 | // Config types 8 | pub use crate::types::config_types::click_house_config::ClickHouseConfig; 9 | pub use crate::types::config_types::client_channel::ClientChannel; 10 | pub use crate::types::config_types::db_config::DBConfig; 11 | pub use crate::types::config_types::environment_types::EnvironmentType; 12 | pub use crate::types::config_types::host_endpoint::HostEndpoint; 13 | pub use crate::types::config_types::iggy_config::IggyConfig; 14 | pub use crate::types::config_types::iggy_user::IggyUser; 15 | pub use crate::types::config_types::message_client_config::MessageClientConfig; 16 | pub use crate::types::config_types::metric_config::MetricConfig; 17 | pub use crate::types::config_types::service_config::ServiceConfig; 18 | pub use crate::types::config_types::service_id::ServiceID; 19 | // Data Types 20 | pub use crate::types::data_types::ohlcv_bar::OHLCVBar; 21 | pub use crate::types::data_types::sampled_bars::SampledDataBars; 22 | pub use crate::types::data_types::time_resolution::TimeResolution; 23 | pub use crate::types::data_types::trade_bar::TradeBar; 24 | // Exchange Types 25 | pub use crate::types::exchange_types::account_type::AccountType; 26 | pub use crate::types::exchange_types::exchange_id::ExchangeID; 27 | pub use crate::types::exchange_types::security_type::SecurityType; 28 | // Symbol Types 29 | pub use crate::types::symbol_types::symbol::Symbol; 30 | // Time Types 31 | pub use crate::types::time_types::month::Month; 32 | pub use crate::types::time_types::time_scale::TimeScale; 33 | -------------------------------------------------------------------------------- /flv_common/src/types/config_types/host_endpoint.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Formatter}; 2 | 3 | #[derive(Debug, Default, Clone, Eq, PartialEq)] 4 | pub struct HostEndpoint { 5 | /// Host URI. 6 | host_uri: String, 7 | /// Port number. 8 | port: u16, 9 | } 10 | 11 | impl HostEndpoint { 12 | pub fn new(host_uri: String, port: u16) -> Self { 13 | Self { host_uri, port } 14 | } 15 | } 16 | 17 | impl HostEndpoint { 18 | pub fn host_uri(&self) -> &str { 19 | &self.host_uri 20 | } 21 | pub fn port(&self) -> u16 { 22 | self.port 23 | } 24 | } 25 | 26 | impl Display for HostEndpoint { 27 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 28 | write!(f, "host_uri: {}, port: {}", self.host_uri, self.port) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /flv_common/src/types/config_types/iggy_user.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Debug, Serialize, Deserialize, PartialEq, Clone)] 6 | pub struct IggyUser { 7 | username: String, 8 | password: String, 9 | } 10 | 11 | impl IggyUser { 12 | pub fn new(username: &str, password: &str) -> Self { 13 | Self { 14 | username: username.to_string(), 15 | password: password.to_string(), 16 | } 17 | } 18 | pub fn username(&self) -> &str { 19 | &self.username 20 | } 21 | pub fn password(&self) -> &str { 22 | &self.password 23 | } 24 | } 25 | 26 | impl Default for IggyUser { 27 | fn default() -> Self { 28 | Self::new("iggy", "iggy") 29 | } 30 | } 31 | 32 | impl fmt::Display for IggyUser { 33 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 34 | write!(f, "User {{ username: {} }}", self.username,) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /flv_common/src/types/config_types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod click_house_config; 2 | /// Configuration type definitions 3 | /// 4 | /// This module provides configuration data structures used throughout the 5 | /// application. It contains the following modules: 6 | /// 7 | /// - `client_channel`: Configuration for client channels 8 | /// - `db_config`: Database configuration 9 | /// - `environment_types`: Configuration for environments like development or production 10 | /// - `host_endpoint`: Configuration for host endpoints 11 | /// - `message_client_config`: Configuration for message clients 12 | /// - `metric_config`: Configuration for metrics and monitoring 13 | /// - `service_config`: Configuration for services 14 | /// - `service_id`: Configuration for service IDs 15 | /// 16 | /// 17 | /// The modules contain type definitions rather than functional logic. The 18 | /// purpose is to centralize and document the application's configuration 19 | /// data structures. 20 | /// 21 | pub mod client_channel; 22 | pub mod db_config; 23 | pub mod environment_types; 24 | pub mod host_endpoint; 25 | pub mod iggy_config; 26 | pub mod iggy_user; 27 | pub mod message_client_config; 28 | pub mod metric_config; 29 | pub mod service_config; 30 | pub mod service_id; 31 | -------------------------------------------------------------------------------- /flv_common/src/types/data_types/mod.rs: -------------------------------------------------------------------------------- 1 | /// Data type definitions 2 | /// 3 | /// This module defines core data types used throughout the application, 4 | /// organized into submodules: 5 | /// 6 | /// - `ohlcv_bar`: OHLCV (open-high-low-close-volume) bar data. 7 | /// - `sampled_bars`: Grouped OHLCV bars for a time period. 8 | /// - `time_resolution`: Resolution enum for OHLCV bars. 9 | /// - `trade_bar`: Tick/trade data bar. 10 | /// 11 | /// These types represent key financial data like trades, quotes, and 12 | /// OHLCV bars. They are used in messages and by other parts of the system. 13 | /// 14 | /// The types provide strongly typed representations of the data. They 15 | /// encapsulate validation logic and conversions. 16 | /// 17 | /// By centralizing the data type definitions, they can be reused 18 | /// consistently across the system. 19 | pub mod ohlcv_bar; 20 | pub mod sampled_bars; 21 | pub mod time_resolution; 22 | pub mod trade_bar; 23 | -------------------------------------------------------------------------------- /flv_common/src/types/data_types/ohlcv_bar/default.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::OHLCVBar; 2 | use chrono::Utc; 3 | use rust_decimal::Decimal; 4 | 5 | impl Default for OHLCVBar { 6 | fn default() -> Self { 7 | Self { 8 | symbol_id: 1, 9 | date_time: Utc::now(), 10 | open: Decimal::default(), 11 | high: Decimal::default(), 12 | low: Decimal::default(), 13 | close: Decimal::default(), 14 | volume: Decimal::default(), 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /flv_common/src/types/data_types/ohlcv_bar/display.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::OHLCVBar; 2 | use std::fmt; 3 | use std::fmt::Display; 4 | 5 | impl Display for OHLCVBar { 6 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 7 | write!( 8 | f, 9 | "DataTime: {},\n Open {},\n High {},\n Low {},\n Close {},\n Volume {}", 10 | self.date_time, self.open, self.high, self.low, self.close, self.volume 11 | ) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /flv_common/src/types/data_types/ohlcv_bar/getters.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::OHLCVBar; 2 | use chrono::{DateTime, Utc}; 3 | use rust_decimal::Decimal; 4 | 5 | impl OHLCVBar { 6 | pub fn range_change(&self) -> Decimal { 7 | self.close - self.open 8 | } 9 | 10 | pub fn range_percent(&self) -> Decimal { 11 | let one_hundred = Decimal::new(100, 0); 12 | (((self.close - self.open) / self.open) * one_hundred).round_dp(4) 13 | } 14 | } 15 | 16 | impl OHLCVBar { 17 | pub fn date_time(&self) -> DateTime { 18 | self.date_time 19 | } 20 | 21 | pub fn open(&self) -> Decimal { 22 | self.open 23 | } 24 | 25 | pub fn high(&self) -> Decimal { 26 | self.high 27 | } 28 | 29 | pub fn low(&self) -> Decimal { 30 | self.low 31 | } 32 | 33 | pub fn close(&self) -> Decimal { 34 | self.close 35 | } 36 | 37 | pub fn volume(&self) -> Decimal { 38 | self.volume 39 | } 40 | 41 | pub fn symbol_id(&self) -> u16 { 42 | self.symbol_id 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /flv_common/src/types/data_types/ohlcv_bar/mod.rs: -------------------------------------------------------------------------------- 1 | use chrono::{DateTime, Utc}; 2 | use rust_decimal::Decimal; 3 | use serde::{Deserialize, Serialize}; 4 | use std::fmt::Debug; 5 | 6 | mod default; 7 | mod display; 8 | mod getters; 9 | 10 | #[derive(Debug, Eq, Clone, PartialEq, Serialize, Deserialize)] 11 | pub struct OHLCVBar { 12 | symbol_id: u16, 13 | date_time: DateTime, 14 | open: Decimal, 15 | high: Decimal, 16 | low: Decimal, 17 | close: Decimal, 18 | volume: Decimal, 19 | } 20 | 21 | impl OHLCVBar { 22 | /// Creates a new OHLCVBar instance with the provided parameters. 23 | /// 24 | /// # Parameters 25 | /// 26 | /// - `symbol_id` - The symbol ID this bar is for 27 | /// - `date_time` - The date/time of this bar 28 | /// - `open` - The opening price 29 | /// - `high` - The high price 30 | /// - `low` - The low price 31 | /// - `close` - The closing price 32 | /// - `volume` - The volume traded 33 | /// 34 | /// # Returns 35 | /// 36 | /// A new OHLCVBar instance. 37 | pub fn new( 38 | symbol_id: u16, 39 | date_time: DateTime, 40 | open: Decimal, 41 | high: Decimal, 42 | low: Decimal, 43 | close: Decimal, 44 | volume: Decimal, 45 | ) -> Self { 46 | Self { 47 | symbol_id, 48 | date_time, 49 | open, 50 | high, 51 | low, 52 | close, 53 | volume, 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /flv_common/src/types/data_types/trade_bar/default.rs: -------------------------------------------------------------------------------- 1 | use crate::types::data_types::trade_bar::TradeBar; 2 | use chrono::Utc; 3 | use rust_decimal::prelude::Zero; 4 | use rust_decimal::Decimal; 5 | 6 | impl Default for TradeBar { 7 | fn default() -> Self { 8 | Self { 9 | symbol_id: 1, 10 | date_time: Utc::now(), 11 | price: Decimal::zero(), 12 | volume: Decimal::zero(), 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /flv_common/src/types/data_types/trade_bar/display.rs: -------------------------------------------------------------------------------- 1 | use crate::types::data_types::trade_bar::TradeBar; 2 | 3 | impl std::fmt::Display for TradeBar { 4 | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { 5 | write!( 6 | f, 7 | "symbol_id: {}, timestamp: {}: price={}, volume={}", 8 | self.symbol_id, self.date_time, self.price, self.volume 9 | ) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /flv_common/src/types/data_types/trade_bar/getters.rs: -------------------------------------------------------------------------------- 1 | use crate::types::data_types::trade_bar::TradeBar; 2 | use chrono::{DateTime, Utc}; 3 | use rust_decimal::Decimal; 4 | 5 | impl TradeBar { 6 | pub fn date_time(&self) -> DateTime { 7 | self.date_time 8 | } 9 | pub fn price(&self) -> Decimal { 10 | self.price 11 | } 12 | pub fn volume(&self) -> Decimal { 13 | self.volume 14 | } 15 | 16 | pub fn symbol_id(&self) -> u16 { 17 | self.symbol_id 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /flv_common/src/types/data_types/trade_bar/mod.rs: -------------------------------------------------------------------------------- 1 | use chrono::{DateTime, Utc}; 2 | use rust_decimal::Decimal; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | mod default; 6 | mod display; 7 | mod getters; 8 | 9 | #[derive(Debug, Eq, Clone, PartialEq, Serialize, Deserialize)] 10 | pub struct TradeBar { 11 | symbol_id: u16, 12 | date_time: DateTime, 13 | price: Decimal, 14 | volume: Decimal, 15 | } 16 | 17 | impl TradeBar { 18 | /// Creates a new TradeBar with the provided values. 19 | /// 20 | /// # Parameters 21 | /// 22 | /// * `date_time` - The DateTime of the trade bar 23 | /// * `price` - The price for the trade bar as a Decimal 24 | /// * `volume` - The volume for the trade bar as a Decimal 25 | /// 26 | /// # Returns 27 | /// 28 | /// A new TradeBar instance with the given date_time, price and volume. 29 | pub fn new(symbol_id: u16, date_time: DateTime, price: Decimal, volume: Decimal) -> Self { 30 | Self { 31 | symbol_id, 32 | date_time, 33 | price, 34 | volume, 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /flv_common/src/types/exchange_types/account_type.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Formatter}; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Serialize, Deserialize, Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord)] 6 | #[repr(u8)] 7 | pub enum AccountType { 8 | NullVal = 0xff_u8, 9 | #[default] 10 | Spot = 0x1_u8, 11 | Margin = 0x2_u8, 12 | Future = 0x3_u8, 13 | } 14 | 15 | impl From for AccountType { 16 | #[inline] 17 | fn from(v: i32) -> Self { 18 | match v { 19 | 0x1_i32 => Self::Spot, 20 | 0x2_i32 => Self::Margin, 21 | 0x3_i32 => Self::Future, 22 | _ => Self::NullVal, 23 | } 24 | } 25 | } 26 | 27 | impl Display for AccountType { 28 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 29 | match self { 30 | AccountType::NullVal => write!(f, "NullVal"), 31 | AccountType::Spot => write!(f, "Spot"), 32 | AccountType::Margin => write!(f, "Margin"), 33 | AccountType::Future => write!(f, "Future"), 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /flv_common/src/types/exchange_types/exchange_id.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | use std::fmt::{Display, Formatter}; 3 | 4 | /// The ExchangeID enum represents supported exchange identifiers. 5 | /// 6 | /// The variants are: 7 | /// 8 | /// - NullVal - A null or unset value, default variant. 9 | /// - Kraken - The Kraken exchange. 10 | /// 11 | /// The enum is represented as a u8 under the hood. 12 | /// 13 | #[derive( 14 | Serialize, Deserialize, Clone, Copy, Debug, Default, Hash, PartialEq, Eq, PartialOrd, Ord, 15 | )] 16 | #[repr(u8)] 17 | pub enum ExchangeID { 18 | #[default] 19 | NullVal = 0_u8, 20 | Kraken = 1_u8, 21 | } 22 | 23 | impl From for ExchangeID { 24 | /// Create an ExchangeID from a u8 value. 25 | /// 26 | /// # Parameters 27 | /// 28 | /// * `v` - The u8 value to convert to an ExchangeID 29 | /// 30 | /// # Returns 31 | /// 32 | /// Returns the corresponding ExchangeID for the provided u8: 33 | /// 34 | /// - 0 -> ExchangeID::NullVal 35 | /// - 1 -> ExchangeID::Kraken 36 | /// 37 | /// If the u8 does not match a valid mapping, returns ExchangeID::NullVal. 38 | #[inline] 39 | fn from(v: u8) -> Self { 40 | match v { 41 | 0 => Self::NullVal, 42 | 1 => Self::Kraken, 43 | _ => Self::NullVal, 44 | } 45 | } 46 | } 47 | 48 | impl Display for ExchangeID { 49 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 50 | match self { 51 | ExchangeID::NullVal => write!(f, "NullVal"), 52 | ExchangeID::Kraken => write!(f, "Kraken"), 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /flv_common/src/types/exchange_types/mod.rs: -------------------------------------------------------------------------------- 1 | /// Exchange metadata type definitions 2 | /// 3 | /// This module provides types to represent metadata related to exchanges. 4 | /// 5 | /// The types provided include: 6 | /// 7 | /// - `AccountType`: The type of exchange account. 8 | /// - `ExchangeID`: Identifier for an exchange. 9 | /// - `SecurityType`: The security type like spot, futures, etc. 10 | /// 11 | /// These types are used in messages and data types to provide additional 12 | /// context and metadata related to exchanges. For example, associating a 13 | /// trade message with the exchange it came from. 14 | /// 15 | /// The types are simple Rust enums represented as u8s. They define 16 | /// variants for each supported exchange attribute value. This provides 17 | /// strong typing of exchange metadata. 18 | /// 19 | /// Converting between u8 values and variants is handled by trait 20 | /// implementations like `From` and `Into`. 21 | /// 22 | pub mod account_type; 23 | pub mod exchange_id; 24 | pub mod security_type; 25 | -------------------------------------------------------------------------------- /flv_common/src/types/exchange_types/security_type.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Formatter}; 2 | 3 | use serde::{Deserialize, Serialize}; 4 | 5 | #[derive(Serialize, Deserialize, Debug, Default, Clone, Eq, PartialEq)] 6 | pub enum SecurityType { 7 | UnknownSecurityType, 8 | #[default] 9 | Spot, 10 | Index, 11 | Future, 12 | PerpetualFuture, 13 | Option, 14 | FutureOption, 15 | } 16 | 17 | impl Display for SecurityType { 18 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 19 | match self { 20 | SecurityType::UnknownSecurityType => write!(f, "UnknownSecurityType"), 21 | SecurityType::Spot => write!(f, "Spot"), 22 | SecurityType::Index => write!(f, "Index"), 23 | SecurityType::Future => write!(f, "Future"), 24 | SecurityType::PerpetualFuture => write!(f, "PerpetualFuture"), 25 | SecurityType::Option => write!(f, "Option"), 26 | SecurityType::FutureOption => write!(f, "FutureOption"), 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /flv_common/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | /// Common data type definitions 2 | /// 3 | /// This crate provides common data types used throughout the application. 4 | /// 5 | /// It contains the following modules: 6 | /// 7 | /// - `config_types`: Configuration data structures. 8 | /// - `data_types`: Core domain data types. 9 | /// - `error_types`: Error types. 10 | /// - `exchange_types`: Exchange metadata types. 11 | /// - `symbol_types`: Symbol types. 12 | /// - `time_types`: Time and date types. 13 | /// 14 | /// The goal is to centralize key data types for consistency. Other 15 | /// crates can import just the types they need. 16 | /// 17 | /// Using common types improves interoperability and reduces duplication. 18 | /// The types provide validation, serialization, and other functionality. 19 | /// 20 | pub mod config_types; 21 | pub mod data_types; 22 | 23 | pub mod error_types; 24 | pub mod exchange_types; 25 | pub mod symbol_types; 26 | pub mod time_types; 27 | -------------------------------------------------------------------------------- /flv_common/src/types/symbol_types/mod.rs: -------------------------------------------------------------------------------- 1 | /// Symbol types 2 | /// 3 | /// This module defines common types for working with symbols and symbol metadata. 4 | /// 5 | /// # Types 6 | /// 7 | /// - `Symbol` - Main symbol struct containing symbol metadata like name, id, etc. 8 | /// 9 | pub mod symbol; 10 | -------------------------------------------------------------------------------- /flv_common/src/types/time_types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod month; 2 | pub mod time_scale; 3 | -------------------------------------------------------------------------------- /flv_common/src/types/time_types/time_scale.rs: -------------------------------------------------------------------------------- 1 | use std::fmt::{Display, Formatter}; 2 | 3 | #[derive(Debug, Default, Copy, Clone, Hash, Eq, PartialEq)] 4 | #[repr(u8)] 5 | pub enum TimeScale { 6 | #[default] 7 | NoScale = 0, 8 | Second = 1, 9 | Minute = 2, 10 | Hour = 3, 11 | Day = 4, 12 | Week = 5, 13 | Month = 6, 14 | Quarter = 7, 15 | Year = 8, 16 | } 17 | 18 | impl From for TimeScale { 19 | fn from(value: u8) -> Self { 20 | match value { 21 | 0 => TimeScale::NoScale, 22 | 1 => TimeScale::Second, 23 | 2 => TimeScale::Minute, 24 | 3 => TimeScale::Hour, 25 | 4 => TimeScale::Day, 26 | 5 => TimeScale::Week, 27 | 6 => TimeScale::Month, 28 | 7 => TimeScale::Quarter, 29 | 8 => TimeScale::Year, 30 | _ => TimeScale::NoScale, 31 | } 32 | } 33 | } 34 | 35 | impl Display for TimeScale { 36 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 37 | write!(f, "{:?}", self) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /flv_common/tests/errors/init_error_tests.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::InitError; 2 | 3 | #[test] 4 | fn test_new_init_error() { 5 | let error = InitError("test error".to_string()); 6 | assert_eq!("InitError: test error", error.to_string()); 7 | } 8 | 9 | #[test] 10 | fn test_debug() { 11 | let error = InitError("test error".to_string()); 12 | assert_eq!("InitError(\"test error\")", format!("{:?}", error)); 13 | } 14 | 15 | #[test] 16 | fn test_display() { 17 | let error = InitError("test error".to_string()); 18 | assert_eq!("InitError: test error", format!("{}", error)); 19 | } 20 | -------------------------------------------------------------------------------- /flv_common/tests/errors/message_client_config_error_tests.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::MessageClientConfigError; 2 | 3 | #[test] 4 | fn test_new() { 5 | let err = MessageClientConfigError("error message".to_string()); 6 | assert_eq!( 7 | format!("{}", err), 8 | "MessageClientConfigError: error message" 9 | ); 10 | } 11 | 12 | #[test] 13 | fn test_debug() { 14 | let err = MessageClientConfigError("error message".to_string()); 15 | 16 | assert_eq!( 17 | format!("{}", err), 18 | "MessageClientConfigError: error message" 19 | ); 20 | assert_eq!( 21 | format!("{:?}", err), 22 | "MessageClientConfigError(\"error message\")" 23 | ); 24 | } 25 | 26 | #[test] 27 | fn test_display() { 28 | let err = MessageClientConfigError("error message".to_string()); 29 | assert_eq!( 30 | format!("{}", err), 31 | "MessageClientConfigError: error message" 32 | ); 33 | } 34 | -------------------------------------------------------------------------------- /flv_common/tests/errors/message_processing_error_tests.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::MessageProcessingError; 2 | 3 | #[test] 4 | fn test_new() { 5 | let error = MessageProcessingError("test error".to_string()); 6 | assert_eq!("MessageProcessingError: test error", error.to_string()); 7 | } 8 | 9 | #[test] 10 | fn test_debug() { 11 | let error = MessageProcessingError("test error".to_string()); 12 | assert_eq!( 13 | "MessageProcessingError(\"test error\")", 14 | format!("{:?}", error) 15 | ); 16 | } 17 | 18 | #[test] 19 | fn test_display() { 20 | let error = MessageProcessingError("test error".to_string()); 21 | assert_eq!("MessageProcessingError: test error", format!("{}", error)); 22 | } 23 | -------------------------------------------------------------------------------- /flv_common/tests/errors/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod init_error_tests; 3 | mod message_client_config_error_tests; 4 | mod message_processing_error_tests; 5 | -------------------------------------------------------------------------------- /flv_common/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod errors; 2 | mod types; 3 | -------------------------------------------------------------------------------- /flv_common/tests/types/config_types/click_house_config_tests.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::ClickHouseConfig; 2 | 3 | #[test] 4 | fn test_click_house_config_new() { 5 | let config = ClickHouseConfig::new( 6 | "http://example.com".to_string(), 7 | 1234, 8 | "user".to_string(), 9 | "pass".to_string(), 10 | "db".to_string(), 11 | ); 12 | assert_eq!(config.url(), "http://example.com"); 13 | assert_eq!(config.port(), 1234); 14 | assert_eq!(config.username(), "user"); 15 | assert_eq!(config.password(), "pass"); 16 | assert_eq!(config.database(), "db"); 17 | } 18 | 19 | #[test] 20 | fn test_click_house_config_default() { 21 | let config = ClickHouseConfig::default(); 22 | assert_eq!(config.url(), "127.0.0.1"); 23 | assert_eq!(config.port(), 9000); 24 | assert_eq!(config.username(), ""); 25 | assert_eq!(config.password(), ""); 26 | assert_eq!(config.database(), "default"); 27 | } 28 | 29 | #[test] 30 | fn test_click_house_config_connection_string() { 31 | let config = ClickHouseConfig::default(); 32 | assert_eq!(config.connection_string(), "127.0.0.1:9000"); 33 | } 34 | 35 | #[test] 36 | fn test_click_house_config_accessors() { 37 | let config = ClickHouseConfig::default(); 38 | assert_eq!(config.url(), "127.0.0.1"); 39 | assert_eq!(config.port(), 9000); 40 | assert_eq!(config.username(), ""); 41 | assert_eq!(config.password(), ""); 42 | assert_eq!(config.database(), "default"); 43 | } 44 | 45 | #[test] 46 | fn test_click_house_config_display() { 47 | let config = ClickHouseConfig::default(); 48 | 49 | let expected = "ClickHouseConfig { url: 127.0.0.1, port: 9000, database: default, username: }"; 50 | let actual = format!("{}", config); 51 | assert_eq!(expected, actual); 52 | } 53 | -------------------------------------------------------------------------------- /flv_common/tests/types/config_types/client_channel_tests.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::ClientChannel; 2 | 3 | #[test] 4 | fn test_display() { 5 | let data = ClientChannel::DataChannel; 6 | assert_eq!(format!("{}", data), "DataChannel"); 7 | 8 | let control = ClientChannel::ControlChannel; 9 | assert_eq!(format!("{}", control), "ControlChannel"); 10 | 11 | let execution = ClientChannel::ExecutionChannel; 12 | assert_eq!(format!("{}", execution), "ExecutionChannel"); 13 | 14 | let heartbeat = ClientChannel::HeartbeatChannel; 15 | assert_eq!(format!("{}", heartbeat), "HeartbeatChannel"); 16 | } 17 | 18 | #[test] 19 | fn test_from_u8() { 20 | assert_eq!(ClientChannel::from(0), ClientChannel::DataChannel); 21 | assert_eq!(ClientChannel::from(1), ClientChannel::ControlChannel); 22 | assert_eq!(ClientChannel::from(2), ClientChannel::ErrorChannel); 23 | assert_eq!(ClientChannel::from(3), ClientChannel::ExecutionChannel); 24 | assert_eq!(ClientChannel::from(4), ClientChannel::HeartbeatChannel); 25 | } 26 | -------------------------------------------------------------------------------- /flv_common/tests/types/config_types/db_config_tests.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::DBConfig; 2 | 3 | fn get_db_config() -> DBConfig { 4 | DBConfig::new(27017, "localhost".to_string()) 5 | } 6 | 7 | #[test] 8 | fn test_new() { 9 | let config = get_db_config(); 10 | assert_eq!(config.port(), 27017); 11 | assert_eq!(config.host(), "localhost"); 12 | } 13 | 14 | #[test] 15 | fn test_new_with_pg_config() { 16 | let config = DBConfig::new_with_pg_config( 17 | 27017, 18 | "localhost".to_string(), 19 | "pguser".to_string(), 20 | "pgpass".to_string(), 21 | "pgdb".to_string(), 22 | 5432, 23 | 10, 24 | ); 25 | 26 | assert_eq!(config.port(), 27017); 27 | assert_eq!(config.host(), "localhost"); 28 | assert_eq!(config.pg_user(), "pguser"); 29 | assert_eq!(config.pg_password(), "pgpass"); 30 | assert_eq!(config.pg_database(), "pgdb"); 31 | assert_eq!(config.pg_port(), 5432); 32 | } 33 | 34 | #[test] 35 | fn test_pg_connection_string() { 36 | let config = get_db_config(); 37 | 38 | // 8812 is the postgres default port. 39 | let expected = "user=admin password=quest host=localhost port=8812 dbname=qdb"; 40 | 41 | assert_eq!(expected, config.pg_connection_string()); 42 | } 43 | 44 | #[test] 45 | fn test_display() { 46 | let config = DBConfig::new_with_pg_config( 47 | 27017, 48 | "localhost".to_string(), 49 | "pguser".to_string(), 50 | "pgpass".to_string(), 51 | "pgdb".to_string(), 52 | 5432, 53 | 10, 54 | ); 55 | 56 | let expected = "DBConfig {\n port: 27017,\n host: localhost,\n pg_user: pguser,\n pg_database: pgdb\n pg_port: 5432\n}"; 57 | 58 | assert_eq!(format!("{}", config), expected); 59 | } 60 | -------------------------------------------------------------------------------- /flv_common/tests/types/config_types/environment_types_tests.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::EnvironmentType; 2 | 3 | #[test] 4 | fn test_from_u8() { 5 | let local = EnvironmentType::from(0); 6 | assert_eq!(local, EnvironmentType::Local); 7 | 8 | let cluster = EnvironmentType::from(1); 9 | assert_eq!(cluster, EnvironmentType::Cluster); 10 | } 11 | 12 | #[test] 13 | fn test_from_string() { 14 | let result = EnvironmentType::from_string("local"); 15 | assert!(result.is_ok()); 16 | assert_eq!(result.unwrap(), EnvironmentType::Local); 17 | 18 | let result = EnvironmentType::from_string("invalid"); 19 | assert!(result.is_err()); 20 | } 21 | 22 | #[test] 23 | fn test_debug() { 24 | let local = EnvironmentType::Local; 25 | assert_eq!(format!("{:?}", local), "Local"); 26 | 27 | let cluster = EnvironmentType::Cluster; 28 | assert_eq!(format!("{:?}", cluster), "Cluster"); 29 | } 30 | 31 | #[test] 32 | fn test_display() { 33 | let local = EnvironmentType::Local; 34 | assert_eq!(format!("{}", local), "Local"); 35 | 36 | let cluster = EnvironmentType::Cluster; 37 | assert_eq!(format!("{}", cluster), "Cluster"); 38 | } 39 | -------------------------------------------------------------------------------- /flv_common/tests/types/config_types/message_client_config_tests.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::MessageClientConfig; 2 | 3 | #[test] 4 | fn test_new() { 5 | let id = 100; 6 | let config = MessageClientConfig::new(id); 7 | 8 | assert_eq!(config.id(), id); 9 | assert_eq!(config.name(), "client-100"); 10 | } 11 | 12 | #[test] 13 | fn test_default() { 14 | let default = MessageClientConfig::default(); 15 | 16 | assert_eq!(default.id(), 100); 17 | assert_eq!(default.name(), "default-client-100"); 18 | } 19 | 20 | #[test] 21 | fn test_channel_getters() { 22 | let id = 100; 23 | let config = MessageClientConfig::new(id); 24 | 25 | assert_eq!(config.control_channel(), "client-100-control"); 26 | assert_eq!(config.data_channel(), "client-100-data"); 27 | assert_eq!(config.execution_channel(), "client-100-execution"); 28 | } 29 | 30 | #[test] 31 | fn test_id() { 32 | let id = 100; 33 | let config = MessageClientConfig::new(id); 34 | 35 | assert_eq!(config.id(), id); 36 | } 37 | 38 | #[test] 39 | fn test_name() { 40 | let id = 100; 41 | let config = MessageClientConfig::new(id); 42 | 43 | assert_eq!(config.name(), "client-100"); 44 | } 45 | 46 | #[test] 47 | fn test_display() { 48 | let id = 100; 49 | let config = MessageClientConfig::new(id); 50 | 51 | let actual = format!("{}", config); 52 | let expected = "MessageClientConfig { id: 100, name: client-100 }".to_string(); 53 | 54 | assert_eq!(actual, expected); 55 | } 56 | -------------------------------------------------------------------------------- /flv_common/tests/types/config_types/metric_config_tests.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::MetricConfig; 2 | 3 | #[test] 4 | fn test_new() { 5 | let config = MetricConfig::new("metrics".to_string(), "localhost".to_string(), 8080); 6 | 7 | assert_eq!(config.metric_uri(), "metrics"); 8 | assert_eq!(config.metric_host(), "localhost"); 9 | assert_eq!(config.metric_port(), 8080); 10 | } 11 | 12 | #[test] 13 | fn test_metric_uri() { 14 | let config = MetricConfig::new("custom".to_string(), "localhost".to_string(), 8081); 15 | 16 | assert_eq!(config.metric_uri(), "custom"); 17 | } 18 | 19 | #[test] 20 | fn test_metric_host() { 21 | let config = MetricConfig::new("metrics".to_string(), "metricshost".to_string(), 8082); 22 | 23 | assert_eq!(config.metric_host(), "metricshost"); 24 | } 25 | 26 | #[test] 27 | fn test_metric_port() { 28 | let config = MetricConfig::new("metrics".to_string(), "localhost".to_string(), 3000); 29 | 30 | assert_eq!(config.metric_port(), 3000); 31 | } 32 | -------------------------------------------------------------------------------- /flv_common/tests/types/config_types/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod click_house_config_tests; 3 | #[cfg(test)] 4 | mod client_channel_tests; 5 | #[cfg(test)] 6 | mod db_config_tests; 7 | #[cfg(test)] 8 | mod message_client_config_tests; 9 | 10 | #[cfg(test)] 11 | mod service_id_tests; 12 | 13 | #[cfg(test)] 14 | mod environment_types_tests; 15 | #[cfg(test)] 16 | mod metric_config_tests; 17 | #[cfg(test)] 18 | mod service_config_tests; 19 | -------------------------------------------------------------------------------- /flv_common/tests/types/config_types/service_id_tests.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::ServiceID; 2 | 3 | #[test] 4 | fn test_default() { 5 | let service_name = ServiceID::default(); 6 | assert_eq!(service_name, ServiceID::Default); 7 | } 8 | 9 | #[test] 10 | fn test_from_u8() { 11 | assert_eq!(ServiceID::from(0x0_u8), ServiceID::Default); 12 | assert_eq!(ServiceID::from(99_u8), ServiceID::Database); 13 | assert_eq!(ServiceID::from(0x1_u8), ServiceID::QDGW); 14 | assert_eq!(ServiceID::from(0x2_u8), ServiceID::SYMDB); 15 | assert_eq!(ServiceID::from(0x42_u8), ServiceID::Default); 16 | } 17 | 18 | #[test] 19 | fn test_debug() { 20 | let e = ServiceID::Default; 21 | assert_eq!(format!("{:?}", e), "Default"); 22 | 23 | let e = ServiceID::Database; 24 | assert_eq!(format!("{:?}", e), "Database"); 25 | 26 | let e = ServiceID::QDGW; 27 | assert_eq!(format!("{:?}", e), "QDGW"); 28 | 29 | let e = ServiceID::SYMDB; 30 | assert_eq!(format!("{:?}", e), "SYMDB"); 31 | } 32 | 33 | #[test] 34 | fn test_from_string() { 35 | assert_eq!(ServiceID::from_string("Default"), Some(ServiceID::Default)); 36 | assert_eq!( 37 | ServiceID::from_string("Database"), 38 | Some(ServiceID::Database) 39 | ); 40 | assert_eq!(ServiceID::from_string("QDGW"), Some(ServiceID::QDGW)); 41 | assert_eq!(ServiceID::from_string("SYMDB"), Some(ServiceID::SYMDB)); 42 | assert_eq!(ServiceID::from_string("Unknown"), None); 43 | } 44 | 45 | #[test] 46 | fn test_display() { 47 | assert_eq!(format!("{}", ServiceID::Default), "Default"); 48 | assert_eq!(format!("{}", ServiceID::Database), "Database"); 49 | assert_eq!(format!("{}", ServiceID::QDGW), "QDGW"); 50 | assert_eq!(format!("{}", ServiceID::SYMDB), "SYMDB"); 51 | } 52 | -------------------------------------------------------------------------------- /flv_common/tests/types/data_types/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod ohlcv_bar_tests; 3 | 4 | #[cfg(test)] 5 | mod time_resolution_tests; 6 | 7 | #[cfg(test)] 8 | mod trade_bar_tests; 9 | -------------------------------------------------------------------------------- /flv_common/tests/types/data_types/ohlcv_bar_tests.rs: -------------------------------------------------------------------------------- 1 | use chrono::Utc; 2 | use common::prelude::OHLCVBar; 3 | use rust_decimal::Decimal; 4 | 5 | #[test] 6 | fn test_new() { 7 | let symbol_id = 1; 8 | let date_time = Utc::now(); 9 | let open = Decimal::default(); 10 | let high = Decimal::default(); 11 | let low = Decimal::default(); 12 | let close = Decimal::default(); 13 | let volume = Decimal::default(); 14 | 15 | let data_bar = OHLCVBar::new(symbol_id, date_time, open, high, low, close, volume); 16 | 17 | assert_eq!(data_bar.symbol_id(), symbol_id); 18 | assert_eq!(data_bar.date_time(), date_time); 19 | assert_eq!(data_bar.open(), open); 20 | assert_eq!(data_bar.high(), high); 21 | assert_eq!(data_bar.low(), low); 22 | assert_eq!(data_bar.close(), close); 23 | assert_eq!(data_bar.volume(), volume); 24 | } 25 | 26 | #[test] 27 | fn test_default() { 28 | let symbol_id = 1; 29 | let open = Decimal::default(); 30 | let high = Decimal::default(); 31 | let low = Decimal::default(); 32 | let close = Decimal::default(); 33 | let volume = Decimal::default(); 34 | 35 | let default_bar = OHLCVBar::default(); 36 | 37 | assert_eq!(default_bar.symbol_id(), symbol_id); 38 | assert_eq!(default_bar.open(), open); 39 | assert_eq!(default_bar.high(), high); 40 | assert_eq!(default_bar.low(), low); 41 | assert_eq!(default_bar.close(), close); 42 | assert_eq!(default_bar.volume(), volume); 43 | } 44 | -------------------------------------------------------------------------------- /flv_common/tests/types/data_types/time_resolution_tests.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::TimeResolution; 2 | 3 | #[test] 4 | fn test_from_u8() { 5 | assert_eq!(TimeResolution::from(0_u8), TimeResolution::NoValue); 6 | assert_eq!(TimeResolution::from(1_u8), TimeResolution::OneMin); 7 | assert_eq!(TimeResolution::from(2_u8), TimeResolution::FiveMin); 8 | assert_eq!(TimeResolution::from(3_u8), TimeResolution::FifteenMin); 9 | assert_eq!(TimeResolution::from(4_u8), TimeResolution::ThirtyMin); 10 | assert_eq!(TimeResolution::from(5_u8), TimeResolution::OneHour); 11 | assert_eq!(TimeResolution::from(6_u8), TimeResolution::OneDay); 12 | assert_eq!(TimeResolution::from(7_u8), TimeResolution::OneWeek); 13 | assert_eq!(TimeResolution::from(8_u8), TimeResolution::OneMonth); 14 | assert_eq!(TimeResolution::from(9_u8), TimeResolution::OneYear); 15 | } 16 | 17 | #[test] 18 | fn test_display() { 19 | assert_eq!(TimeResolution::NoValue.to_string(), "NoValue"); 20 | assert_eq!(TimeResolution::OneMin.to_string(), "1 minute"); 21 | assert_eq!(TimeResolution::FiveMin.to_string(), "5 minute"); 22 | assert_eq!(TimeResolution::FifteenMin.to_string(), "15 minute"); 23 | assert_eq!(TimeResolution::ThirtyMin.to_string(), "30 minute"); 24 | assert_eq!(TimeResolution::OneHour.to_string(), "1 hour"); 25 | assert_eq!(TimeResolution::OneDay.to_string(), "1 day"); 26 | assert_eq!(TimeResolution::OneMonth.to_string(), "1 month"); 27 | assert_eq!(TimeResolution::OneYear.to_string(), "1 year"); 28 | } 29 | -------------------------------------------------------------------------------- /flv_common/tests/types/data_types/trade_bar_tests.rs: -------------------------------------------------------------------------------- 1 | use chrono::{Datelike, Utc}; 2 | use common::prelude::TradeBar; 3 | use rust_decimal::prelude::Zero; 4 | use rust_decimal::Decimal; 5 | 6 | #[test] 7 | fn test_new() { 8 | let bar = TradeBar::new(1, Utc::now(), Decimal::from(100), Decimal::from(50)); 9 | 10 | assert_eq!(bar.symbol_id(), 1); 11 | assert_eq!(bar.price(), Decimal::from(100)); 12 | assert_eq!(bar.volume(), Decimal::from(50)); 13 | } 14 | 15 | #[test] 16 | fn test_default() { 17 | let bar = TradeBar::default(); 18 | 19 | assert_eq!(bar.symbol_id(), 1); 20 | assert_eq!(bar.date_time().day(), Utc::now().day()); 21 | assert_eq!(bar.price(), Decimal::zero()); 22 | assert_eq!(bar.volume(), Decimal::zero()); 23 | } 24 | 25 | #[test] 26 | fn test_symbol_id() { 27 | let bar = TradeBar::new(1, Utc::now(), Decimal::from(100), Decimal::from(50)); 28 | 29 | assert_eq!(bar.symbol_id(), 1); 30 | } 31 | 32 | #[test] 33 | fn test_price() { 34 | let bar = TradeBar::new(1, Utc::now(), Decimal::from(100), Decimal::from(50)); 35 | assert_eq!(bar.price(), Decimal::from(100)); 36 | } 37 | 38 | #[test] 39 | fn test_volume() { 40 | let bar = TradeBar::new(1, Utc::now(), Decimal::from(100), Decimal::from(50)); 41 | assert_eq!(bar.volume(), Decimal::from(50)); 42 | } 43 | 44 | #[test] 45 | fn test_display() { 46 | let bar = TradeBar::new(1, Utc::now(), Decimal::from(100), Decimal::from(50)); 47 | 48 | let expected = format!( 49 | "symbol_id: {}, timestamp: {}: price={}, volume={}", 50 | bar.symbol_id(), 51 | bar.date_time(), 52 | bar.price(), 53 | bar.volume() 54 | ); 55 | 56 | assert_eq!(expected, format!("{}", bar)); 57 | } 58 | -------------------------------------------------------------------------------- /flv_common/tests/types/exchange_types/account_type_tests.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::AccountType; 2 | 3 | #[test] 4 | fn test_unknown_account_type_display() { 5 | let account_type = AccountType::NullVal; 6 | assert_eq!(account_type.to_string(), "NullVal"); 7 | } 8 | 9 | #[test] 10 | fn test_spot_account_type_display() { 11 | let account_type = AccountType::Spot; 12 | assert_eq!(account_type.to_string(), "Spot"); 13 | } 14 | 15 | #[test] 16 | fn test_margin_account_type_display() { 17 | let account_type = AccountType::Margin; 18 | assert_eq!(account_type.to_string(), "Margin"); 19 | } 20 | 21 | #[test] 22 | fn test_future_account_type_display() { 23 | let account_type = AccountType::Future; 24 | assert_eq!(account_type.to_string(), "Future"); 25 | } 26 | -------------------------------------------------------------------------------- /flv_common/tests/types/exchange_types/exchange_id_tests.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::ExchangeID; 2 | 3 | #[test] 4 | fn test_from_valid_values() { 5 | assert_eq!(ExchangeID::from(0x0), ExchangeID::NullVal); 6 | assert_eq!(ExchangeID::from(0x1), ExchangeID::Kraken); 7 | } 8 | 9 | #[test] 10 | fn test_from_invalid_values() { 11 | assert_eq!(ExchangeID::from(0x0), ExchangeID::NullVal); 12 | } 13 | 14 | #[test] 15 | fn test_null_val() { 16 | let exchange_id = ExchangeID::NullVal; 17 | assert_eq!(format!("{}", exchange_id), "NullVal"); 18 | } 19 | 20 | #[test] 21 | fn test_kraken() { 22 | let exchange_id = ExchangeID::Kraken; 23 | assert_eq!(format!("{}", exchange_id), "Kraken"); 24 | } 25 | -------------------------------------------------------------------------------- /flv_common/tests/types/exchange_types/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod account_type_tests; 3 | #[cfg(test)] 4 | mod exchange_id_tests; 5 | #[cfg(test)] 6 | mod security_type_tests; 7 | -------------------------------------------------------------------------------- /flv_common/tests/types/mod.rs: -------------------------------------------------------------------------------- 1 | mod config_types; 2 | 3 | mod data_types; 4 | 5 | mod exchange_types; 6 | mod symbol_types; 7 | mod time_types; 8 | -------------------------------------------------------------------------------- /flv_common/tests/types/symbol_types/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod symbol_tests; 3 | -------------------------------------------------------------------------------- /flv_common/tests/types/symbol_types/symbol_tests.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::{ExchangeID, Symbol}; 2 | use rust_decimal::Decimal; 3 | use std::str::FromStr; 4 | 5 | fn get_new_symbol() -> Symbol { 6 | Symbol::new( 7 | "BTCUSD".to_string(), 8 | "BTC-USD".to_string(), 9 | ExchangeID::Kraken, 10 | "BTC".to_string(), 11 | "USD".to_string(), 12 | Decimal::from_str("0.000001").unwrap(), 13 | Decimal::from_str("0.001").unwrap(), 14 | ) 15 | } 16 | 17 | #[test] 18 | fn test_symbol_id_global() { 19 | let symbol = get_new_symbol(); 20 | 21 | assert_eq!(symbol.symbol_id_global(), "BTCUSD"); 22 | } 23 | 24 | #[test] 25 | fn test_symbol_id_exchange() { 26 | let symbol = get_new_symbol(); 27 | 28 | assert_eq!(symbol.symbol_id_exchange(), "BTC-USD"); 29 | } 30 | 31 | #[test] 32 | fn test_exchange_id() { 33 | let symbol = get_new_symbol(); 34 | 35 | assert_eq!(symbol.exchange_id(), &ExchangeID::Kraken); 36 | } 37 | 38 | #[test] 39 | fn test_asset_base_exchange() { 40 | let symbol = get_new_symbol(); 41 | 42 | assert_eq!(symbol.asset_base_exchange(), "BTC"); 43 | } 44 | 45 | #[test] 46 | fn test_asset_quote_exchange() { 47 | let symbol = get_new_symbol(); 48 | 49 | assert_eq!(symbol.asset_quote_exchange(), "USD"); 50 | } 51 | 52 | #[test] 53 | fn test_price_precision() { 54 | let symbol = get_new_symbol(); 55 | 56 | assert_eq!( 57 | symbol.price_precision(), 58 | Decimal::from_str("0.000001").unwrap() 59 | ); 60 | } 61 | 62 | #[test] 63 | fn test_size_precision() { 64 | let symbol = get_new_symbol(); 65 | 66 | assert_eq!(symbol.size_precision(), Decimal::from_str("0.001").unwrap()); 67 | } 68 | -------------------------------------------------------------------------------- /flv_common/tests/types/time_types/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod month_tests; 3 | 4 | #[cfg(test)] 5 | mod time_scale_tests; 6 | -------------------------------------------------------------------------------- /flv_common/tests/types/time_types/time_scale_tests.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::TimeScale; 2 | 3 | #[test] 4 | fn test_from_u8() { 5 | assert_eq!(TimeScale::from(0), TimeScale::NoScale); 6 | assert_eq!(TimeScale::from(1), TimeScale::Second); 7 | assert_eq!(TimeScale::from(2), TimeScale::Minute); 8 | assert_eq!(TimeScale::from(3), TimeScale::Hour); 9 | assert_eq!(TimeScale::from(4), TimeScale::Day); 10 | assert_eq!(TimeScale::from(5), TimeScale::Week); 11 | assert_eq!(TimeScale::from(6), TimeScale::Month); 12 | assert_eq!(TimeScale::from(7), TimeScale::Quarter); 13 | assert_eq!(TimeScale::from(8), TimeScale::Year); 14 | assert_eq!(TimeScale::from(9), TimeScale::NoScale); 15 | } 16 | 17 | #[test] 18 | fn test_display() { 19 | let no_scale = TimeScale::NoScale; 20 | let second = TimeScale::Second; 21 | 22 | assert_eq!(format!("{}", no_scale), "NoScale"); 23 | assert_eq!(format!("{}", second), "Second"); 24 | } 25 | -------------------------------------------------------------------------------- /flv_components/config_manager/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "config_manager" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [lib] 12 | name = "config_manager" 13 | path = "src/lib.rs" 14 | 15 | 16 | [dependencies] 17 | # Internal crates 18 | common = { workspace = true } 19 | db_specs = { workspace = true } 20 | exchange_specs = { workspace = true } 21 | message_specs = { workspace = true } 22 | service_specs = { workspace = true } 23 | -------------------------------------------------------------------------------- /flv_components/config_manager/tests/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod config_manager_tests; 3 | -------------------------------------------------------------------------------- /flv_components/db_query_manager/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "db_query_manager" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [lib] 12 | name = "db_query_manager" 13 | path = "src/lib.rs" 14 | 15 | 16 | [dependencies] 17 | # Internal crates 18 | common = { workspace = true } 19 | # External crates 20 | chrono = { workspace = true } 21 | klickhouse = { workspace = true } 22 | futures = { workspace = true } 23 | rust_decimal = { workspace = true } 24 | tokio = { workspace = true } 25 | serde = { workspace = true } 26 | -------------------------------------------------------------------------------- /flv_components/db_query_manager/src/error.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::ValidationError; 2 | use std::error::Error; 3 | use std::fmt; 4 | 5 | /// Custom error type for DB query errors 6 | #[derive(Debug)] 7 | pub enum QueryError { 8 | QueryFailed(String), 9 | InvalidTableName(ValidationError), 10 | EmptyTableName(ValidationError), 11 | TableNameTooLong(ValidationError), 12 | TableDoesNotExist(String, String), 13 | } 14 | 15 | impl Error for QueryError {} 16 | 17 | impl fmt::Display for QueryError { 18 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 19 | match self { 20 | QueryError::QueryFailed(e) => 21 | write!(f, "Query to DB failed: {e}"), 22 | 23 | QueryError::InvalidTableName(e) => 24 | write!(f, "Invalid table name provided: Only use alphanumeric characters and underscores as table name. Error: {e}"), 25 | 26 | QueryError::EmptyTableName(e) => 27 | write!(f, "Empty table name provided: Table must have a name. Error: {e}"), 28 | 29 | QueryError::TableNameTooLong(e) => 30 | write!(f, "Table name exceeds maximum length: Table can only be 63 characters long. Error: {e}"), 31 | 32 | QueryError::TableDoesNotExist(table_name, e) => 33 | write!(f, "Table does not exist: Table {table_name} does not exist. Error: {e}"), 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /flv_components/db_query_manager/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod error; 2 | mod query_gen; 3 | mod query_ohlcv; 4 | mod query_symbols; 5 | mod query_trades; 6 | mod query_utils; 7 | mod stream_ohlcv; 8 | mod stream_trades; 9 | pub mod types; 10 | 11 | use common::prelude::ClickHouseConfig; 12 | use klickhouse::{Client, ClientOptions}; 13 | use std::fmt::Error; 14 | 15 | const FN_NAME: &str = "[QueryDBManager]:"; 16 | 17 | pub struct QueryDBManager { 18 | client: Client, 19 | } 20 | 21 | impl QueryDBManager { 22 | /// Creates a new QueryDBManager instance. 23 | /// 24 | /// # Arguments 25 | /// 26 | /// * `db_config: ClickHouseConfig` - The database configuration containing connection parameters. 27 | /// 28 | /// # Returns 29 | /// 30 | /// A new QueryDBManager instance connected to the database. 31 | /// 32 | /// # Errors 33 | /// 34 | /// Will return an error if the connection to the database fails. 35 | /// 36 | /// # Example 37 | /// 38 | pub async fn new(db_config: ClickHouseConfig) -> Result { 39 | let destination = db_config.connection_string(); 40 | let client = Client::connect(destination.clone(), ClientOptions::default()) 41 | .await 42 | .expect(format!("{} Failed to connect to {}", FN_NAME, &destination).as_str()); 43 | 44 | Ok(Self { client }) 45 | } 46 | } 47 | 48 | impl QueryDBManager { 49 | pub async fn is_open(&self) -> bool { 50 | !self.client.is_closed() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /flv_components/db_query_manager/src/stream_ohlcv.rs: -------------------------------------------------------------------------------- 1 | use crate::types::OHLCVRow; 2 | use crate::{QueryDBManager, FN_NAME}; 3 | use common::prelude::TimeResolution; 4 | use futures::stream::BoxStream; 5 | use futures::StreamExt; 6 | use klickhouse::KlickhouseError; 7 | 8 | impl QueryDBManager { 9 | pub async fn stream_ohlcv<'a>( 10 | &'a self, 11 | symbol_table: &str, 12 | time_resolution: &TimeResolution, 13 | ) -> BoxStream> { 14 | // Sanitize table name input to prevent SQL injection. 15 | let sanitized_name = self 16 | .sanitize_table_name(symbol_table) 17 | .expect("Failed to sanitize table name"); 18 | 19 | // Build the query 20 | let query = self.build_get_ohlcv_bars_query(sanitized_name, time_resolution); 21 | 22 | // Return the stream of rows 23 | self.client 24 | .query::(query) 25 | .await 26 | .expect(format!("{} Failed to execute stream_ohlcv query ", FN_NAME).as_str()) 27 | .boxed() 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /flv_components/db_query_manager/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod db_query_manager_tests; 2 | -------------------------------------------------------------------------------- /flv_components/symbol_manager/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symbol_manager" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [lib] 12 | name = "symbol_manager" 13 | path = "src/lib.rs" 14 | 15 | 16 | [dependencies] 17 | # Internal crates 18 | common = { workspace = true } 19 | # External crates 20 | lru = { workspace = true } 21 | -------------------------------------------------------------------------------- /flv_components/symbol_manager/src/getters.rs: -------------------------------------------------------------------------------- 1 | use crate::SymbolManager; 2 | 3 | impl SymbolManager { 4 | /// Returns the number of symbols stored in this SymbolManager. 5 | /// 6 | /// # Returns 7 | /// 8 | /// The number of symbols as a `usize`. 9 | /// 10 | /// # Functionality 11 | /// 12 | /// Simply returns the `number_of_symbols` field which tracks the total count of 13 | /// symbols in this SymbolManager. 14 | pub fn number_of_symbols(&self) -> usize { 15 | self.number_of_symbols 16 | } 17 | 18 | /// Returns the number of exchanges stored in this SymbolManager. 19 | /// 20 | /// # Returns 21 | /// 22 | /// The number of exchanges as a `usize`. 23 | /// 24 | /// # Functionality 25 | /// 26 | /// Simply returns the `number_of_exchanges` field which tracks the total count of 27 | /// exchanges in this SymbolManager. 28 | pub fn number_of_exchanges(&self) -> usize { 29 | self.number_of_exchanges 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /flv_components/symbol_manager/tests/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod symbol_manager_tests; 3 | -------------------------------------------------------------------------------- /flv_examples/basic_data_stream/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "basic_data_stream" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [[bin]] 12 | name = "basic_data_stream" 13 | path = "src/main.rs" 14 | 15 | 16 | [dependencies] 17 | # Intrnal crates 18 | common = { workspace = true } 19 | client_utils = { workspace = true } 20 | qd_client = { workspace = true } 21 | sbe_messages = { workspace = true } 22 | # External crates 23 | futures = {workspace = true} 24 | tokio = { workspace = true } 25 | fluvio = { workspace = true } -------------------------------------------------------------------------------- /flv_examples/causal_data_inference/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "causal_data_inference" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [[bin]] 12 | name = "causal_data_inference" 13 | path = "src/main.rs" 14 | 15 | 16 | [dependencies] 17 | # Intrnal crates 18 | causal_model = { workspace = true } 19 | common = { workspace = true } 20 | config_manager = { workspace = true } 21 | client_utils = { workspace = true } 22 | qd_client = { workspace = true } 23 | sbe_messages = { workspace = true } 24 | # External crates 25 | deep_causality = {workspace = true} 26 | fluvio = { workspace = true } 27 | futures = {workspace = true} 28 | rust_decimal = {workspace = true} 29 | tokio = { workspace = true } 30 | -------------------------------------------------------------------------------- /flv_examples/causal_model/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "causal_model" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [lib] 12 | name = "causal_model" 13 | path = "src/mod.rs" 14 | 15 | 16 | [dependencies] 17 | # Intrnal crates 18 | client_utils = { workspace = true } 19 | common = { workspace = true } 20 | config_manager = { workspace = true } 21 | 22 | # External crates 23 | chrono = { workspace = true } 24 | deep_causality = { workspace = true } 25 | rust_decimal = { workspace = true } 26 | tokio = { workspace = true } 27 | -------------------------------------------------------------------------------- /flv_examples/causal_model/src/context/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod build_context; 2 | -------------------------------------------------------------------------------- /flv_examples/causal_model/src/extensions/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod range; 2 | pub mod time_index; 3 | -------------------------------------------------------------------------------- /flv_examples/causal_model/src/extensions/range.rs: -------------------------------------------------------------------------------- 1 | use deep_causality::prelude::Datable; 2 | 3 | use crate::types::bar_range::BarRange; 4 | 5 | // Extension trait http://xion.io/post/code/rust-extension-traits.html 6 | 7 | /// The Rangeable trait defines the behavior for types that can provide a data range. 8 | /// 9 | /// This trait requires that the implementor also implements the Datable trait. 10 | /// 11 | /// # Methods 12 | /// 13 | /// * `data_range(&self) -> BarRange` - Returns a BarRange representing the data range for this value. 14 | /// The BarRange contains the following fields: 15 | /// 16 | /// * `high` - The highest price during the bar interval 17 | /// * `close` - The closing price at the end of the bar interval 18 | /// * `close_above_open` - Whether the closing price is above the opening price 19 | /// * `close_below_open` - Whether the closing price is below the opening price 20 | /// 21 | pub trait RangeExt: Datable { 22 | fn data_range(&self) -> BarRange; 23 | } 24 | -------------------------------------------------------------------------------- /flv_examples/causal_model/src/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod context; 2 | pub mod extensions; 3 | pub mod model; 4 | pub mod prelude; 5 | pub mod types; 6 | pub mod utils; 7 | -------------------------------------------------------------------------------- /flv_examples/causal_model/src/model/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod model; 2 | -------------------------------------------------------------------------------- /flv_examples/causal_model/src/prelude.rs: -------------------------------------------------------------------------------- 1 | // Context 2 | pub use crate::context; 3 | // Extensions 4 | pub use crate::extensions::range::RangeExt; 5 | pub use crate::extensions::time_index::TimeIndexExt; 6 | // Model 7 | pub use crate::model; 8 | // Types 9 | pub use crate::types::alias::*; 10 | pub use crate::types::bar_range::BarRange; 11 | pub use crate::types::range_data::RangeData; 12 | // Utils 13 | pub use crate::utils::context_utils; 14 | pub use crate::utils::time_utils; 15 | -------------------------------------------------------------------------------- /flv_examples/causal_model/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod alias; 2 | pub mod bar_range; 3 | pub mod range_data; 4 | -------------------------------------------------------------------------------- /flv_examples/causal_model/src/types/range_data.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{BarRange, RangeExt}; 2 | use deep_causality::prelude::{Datable, Identifiable}; 3 | use std::fmt::{Display, Formatter}; 4 | 5 | /// The CustomData struct represents custom data with an identifier and data range. 6 | /// 7 | /// It implements several traits to provide the required functionality: 8 | /// 9 | /// - Datable - Marks this as data that can be used in causal inference 10 | /// - Identifiable - Provides a unique ID for this data 11 | /// - Rangeable - Provides a price range (BarRange) for this data 12 | /// - Display - Allows formatting the data as a string 13 | /// 14 | /// # Fields 15 | /// 16 | /// * `id` - A unique identifier for this data 17 | /// * `data_range` - The price range for this data as a BarRange 18 | /// 19 | /// # Methods 20 | /// 21 | /// * `new` - Constructs a new CustomData instance 22 | /// * `id` - Returns the unique ID for this data 23 | /// * `data_range` - Returns the BarRange representing the price range 24 | /// * `fmt` - Formats the data as a string for display 25 | /// 26 | #[derive(Debug, Copy, Clone, Hash, Eq, PartialEq)] 27 | pub struct RangeData { 28 | id: u64, 29 | data_range: BarRange, 30 | } 31 | 32 | impl RangeData { 33 | pub fn new(id: u64, data_range: BarRange) -> Self { 34 | Self { id, data_range } 35 | } 36 | } 37 | 38 | impl Datable for RangeData {} 39 | 40 | impl Identifiable for RangeData { 41 | fn id(&self) -> u64 { 42 | self.id 43 | } 44 | } 45 | 46 | impl RangeExt for RangeData { 47 | fn data_range(&self) -> BarRange { 48 | self.data_range 49 | } 50 | } 51 | 52 | impl Display for RangeData { 53 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 54 | write!(f, "id: {} range: {}", self.id, self.data_range) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /flv_examples/causal_model/src/utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod context_utils; 2 | pub mod time_utils; 3 | -------------------------------------------------------------------------------- /flv_examples/causal_model/tests/context/build_context_tests.rs: -------------------------------------------------------------------------------- 1 | use causal_model::context::build_context; 2 | use client_utils::data_utils; 3 | use common::prelude::{ExchangeID, SampledDataBars, ServiceID}; 4 | use config_manager::ConfigManager; 5 | use deep_causality::prelude::{ContextuableGraph, Identifiable, TimeScale}; 6 | use std::env; 7 | 8 | const JTO_EUR: &str = "jtoeur"; // JPY in EUR 2420 trades ~ 1 months 9 | 10 | async fn get_data() -> SampledDataBars { 11 | env::set_var("ENV", "Local"); 12 | 13 | let cfg_manager = async { ConfigManager::new(ServiceID::Default) }.await; 14 | let exchange_id = ExchangeID::Kraken; 15 | let symbol_id = JTO_EUR; 16 | 17 | data_utils::load_data(&cfg_manager, symbol_id, exchange_id) 18 | .await 19 | .expect("[get_data]: Failed to load data.") 20 | } 21 | 22 | #[tokio::test] 23 | async fn test_build_time_data_context() { 24 | let data = get_data().await; 25 | let time_scale = TimeScale::Year; 26 | let capacity = 10; 27 | 28 | let result = build_context::build_time_data_context(&data, &time_scale, capacity); 29 | assert!(result.is_ok()); 30 | 31 | let context = result.unwrap(); 32 | // Three nodes in total: root, year, month 33 | assert_eq!(context.node_count(), 5); 34 | 35 | let root = context 36 | .get_node(0) 37 | .expect("[test_build_time_data_context]: Failed to get root node."); 38 | assert_eq!(root.id(), 1); 39 | 40 | let year = context 41 | .get_node(1) 42 | .expect("[test_build_time_data_context]: Failed to get year node."); 43 | assert_eq!(year.id(), 2); 44 | 45 | let month = context 46 | .get_node(2) 47 | .expect("[test_build_time_data_context]: Failed to get month node."); 48 | assert_eq!(month.id(), 3); 49 | } 50 | -------------------------------------------------------------------------------- /flv_examples/causal_model/tests/context/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod build_context_tests; 3 | -------------------------------------------------------------------------------- /flv_examples/causal_model/tests/extensions/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod time_indexable_tests; 3 | -------------------------------------------------------------------------------- /flv_examples/causal_model/tests/extensions/time_indexable_tests.rs: -------------------------------------------------------------------------------- 1 | use causal_model::prelude::{CustomContext, TimeIndexExt}; 2 | use deep_causality::prelude::Context; 3 | 4 | fn get_indexable_context() -> CustomContext<'static> { 5 | Context::with_capacity(1, "Causal Context", 10) 6 | } 7 | 8 | #[test] 9 | fn test_get_current_year_index() { 10 | let mut indexable = get_indexable_context(); 11 | assert_eq!(indexable.get_current_year_index(), None); 12 | 13 | indexable.set_current_year_index(2022); 14 | assert_eq!(indexable.get_current_year_index(), Some(&2022)); 15 | } 16 | 17 | #[test] 18 | fn test_get_current_month_index() { 19 | let mut indexable = get_indexable_context(); 20 | assert_eq!(indexable.get_current_month_index(), None); 21 | 22 | indexable.set_current_month_index(6); 23 | assert_eq!(indexable.get_current_month_index(), Some(&6)); 24 | } 25 | 26 | #[test] 27 | fn test_get_previous_year_index() { 28 | let mut indexable = get_indexable_context(); 29 | assert_eq!(indexable.get_previous_year_index(), None); 30 | 31 | indexable.set_previous_year_index(2021); 32 | assert_eq!(indexable.get_previous_year_index(), Some(&2021)); 33 | } 34 | 35 | #[test] 36 | fn test_get_previous_month_index() { 37 | let mut indexable = get_indexable_context(); 38 | assert_eq!(indexable.get_previous_month_index(), None); 39 | 40 | indexable.set_previous_month_index(5); 41 | assert_eq!(indexable.get_previous_month_index(), Some(&5)); 42 | } 43 | -------------------------------------------------------------------------------- /flv_examples/causal_model/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod context; 2 | mod extensions; 3 | mod model; 4 | mod types; 5 | mod utils; 6 | -------------------------------------------------------------------------------- /flv_examples/causal_model/tests/model/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod model_builder_tests; 3 | -------------------------------------------------------------------------------- /flv_examples/causal_model/tests/model/model_builder_tests.rs: -------------------------------------------------------------------------------- 1 | use causal_model::prelude::model::model; 2 | use causal_model::prelude::CustomContext; 3 | use deep_causality::prelude::Identifiable; 4 | 5 | #[test] 6 | fn test_build_causal_model() { 7 | let context = CustomContext::with_capacity(0, "test_build_causal_model", 10); 8 | 9 | let model = model::build_causal_model(&context); 10 | 11 | assert_eq!(model.id(), 42); 12 | assert_eq!( 13 | model.description(), 14 | "Causal Model: Checks for a potential monthly long breakout" 15 | ); 16 | } 17 | -------------------------------------------------------------------------------- /flv_examples/causal_model/tests/types/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod bar_range_tests; 3 | #[cfg(test)] 4 | mod range_data_tests; 5 | -------------------------------------------------------------------------------- /flv_examples/causal_model/tests/types/range_data_tests.rs: -------------------------------------------------------------------------------- 1 | use causal_model::prelude::{BarRange, RangeData, RangeExt}; 2 | use deep_causality::prelude::Identifiable; 3 | use rust_decimal::Decimal; 4 | 5 | #[test] 6 | fn test_new() { 7 | let id = 1; 8 | let data_range = BarRange::new( 9 | Decimal::from(70), 10 | Decimal::from(100), 11 | Decimal::from(90), 12 | false, 13 | true, 14 | ); 15 | 16 | let range_data = RangeData::new(id, data_range); 17 | 18 | assert_eq!(id, range_data.id()); 19 | assert_eq!(data_range, range_data.data_range()); 20 | } 21 | 22 | #[test] 23 | fn test_display() { 24 | let id = 1; 25 | let data_range = BarRange::new( 26 | Decimal::from(70), 27 | Decimal::from(100), 28 | Decimal::from(90), 29 | false, 30 | true, 31 | ); 32 | let range_data = RangeData::new(id, data_range); 33 | 34 | let expected = "id: 1 range: BarRange { open: 70 high: 100, close: 90, close_above_open: false, close_below_open: true }"; 35 | assert_eq!(expected, format!("{}", range_data)); 36 | } 37 | -------------------------------------------------------------------------------- /flv_examples/causal_model/tests/utils/augment_data_tests.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /flv_examples/causal_model/tests/utils/counter_tests.rs: -------------------------------------------------------------------------------- 1 | use client_utils::prelude::atomic_counter::RelaxedAtomicCounter; 2 | 3 | #[test] 4 | fn test_increment() { 5 | let counter = RelaxedAtomicCounter::new(); 6 | 7 | let v1 = counter.increment_and_get(); 8 | assert_eq!(v1, 1); 9 | 10 | let v2 = counter.increment_and_get(); 11 | assert_eq!(v2, 2); 12 | 13 | let v3 = counter.increment_and_get(); 14 | assert_eq!(v3, 3); 15 | } 16 | 17 | #[test] 18 | fn test_display() { 19 | let counter = RelaxedAtomicCounter::new(); 20 | 21 | counter.increment_and_get(); 22 | 23 | assert_eq!(format!("{}", counter), "1"); 24 | 25 | counter.increment_and_get(); 26 | 27 | assert_eq!(format!("{}", counter), "2"); 28 | } 29 | -------------------------------------------------------------------------------- /flv_examples/causal_model/tests/utils/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod counter_tests; 3 | 4 | #[cfg(test)] 5 | mod augment_data_tests; 6 | #[cfg(test)] 7 | mod time_scale_utils_tests; 8 | -------------------------------------------------------------------------------- /flv_examples/causal_model/tests/utils/time_scale_utils_tests.rs: -------------------------------------------------------------------------------- 1 | use causal_model::prelude::time_utils::get_time_scale_control_map; 2 | use deep_causality::prelude::TimeScale; 3 | 4 | #[test] 5 | fn test_get_boolean_control_map() { 6 | let no_scale = get_time_scale_control_map(&TimeScale::NoScale); 7 | assert_eq!( 8 | no_scale, 9 | vec![true, true, true, true, true, true, true, true] 10 | ); 11 | 12 | let second = get_time_scale_control_map(&TimeScale::Second); 13 | assert_eq!(second, vec![true, true, true, true, true, true, true, true]); 14 | 15 | let minute = get_time_scale_control_map(&TimeScale::Minute); 16 | assert_eq!( 17 | minute, 18 | vec![true, true, true, true, true, true, true, false] 19 | ); 20 | 21 | let hour = get_time_scale_control_map(&TimeScale::Hour); 22 | assert_eq!(hour, vec![true, true, true, true, true, true, false, false]); 23 | 24 | let day = get_time_scale_control_map(&TimeScale::Day); 25 | assert_eq!(day, vec![true, true, true, true, true, false, false, false]); 26 | 27 | let week = get_time_scale_control_map(&TimeScale::Week); 28 | assert_eq!( 29 | week, 30 | vec![true, true, true, true, false, false, false, false] 31 | ); 32 | 33 | let month = get_time_scale_control_map(&TimeScale::Month); 34 | assert_eq!( 35 | month, 36 | vec![true, true, true, false, false, false, false, false] 37 | ); 38 | 39 | let quarter = get_time_scale_control_map(&TimeScale::Quarter); 40 | assert_eq!( 41 | quarter, 42 | vec![true, true, false, false, false, false, false, false] 43 | ); 44 | 45 | let year = get_time_scale_control_map(&TimeScale::Year); 46 | assert_eq!( 47 | year, 48 | vec![true, false, false, false, false, false, false, false] 49 | ); 50 | } 51 | -------------------------------------------------------------------------------- /flv_examples/symbol_master/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symbol_master" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [[bin]] 12 | name = "symbol_master" 13 | path = "src/main.rs" 14 | 15 | 16 | [dependencies] 17 | # Internal crates 18 | common = { workspace = true } 19 | client_utils = { workspace = true } 20 | qd_client = { workspace = true } 21 | sbe_messages = { workspace = true } 22 | symdb_client = { workspace = true } 23 | # External crates 24 | futures = {workspace = true} 25 | tokio = { workspace = true } 26 | fluvio = { workspace = true } 27 | -------------------------------------------------------------------------------- /flv_examples/symbol_master/src/utils.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::HostEndpoint; 2 | 3 | /// Returns the configuration for the SymdbClient. 4 | /// 5 | /// This creates a new HostEndpoint with: 6 | /// - host: "0.0.0.0" 7 | /// - port: 7070 8 | /// 9 | /// # Returns 10 | /// 11 | /// A HostEndpoint struct containing the SymdbClient configuration. 12 | /// 13 | /// # Example 14 | /// 15 | /// ``` 16 | /// let config = get_symdb_config(); 17 | /// let client = SymdbClient::new(config); 18 | /// ``` 19 | /// 20 | pub(crate) fn get_symdb_config() -> HostEndpoint { 21 | HostEndpoint::new("0.0.0.0".to_string(), 7070) 22 | } 23 | -------------------------------------------------------------------------------- /flv_proto/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "proto" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [lib] 12 | name = "proto" 13 | path = "src/lib.rs" 14 | 15 | 16 | [dependencies] 17 | tokio = { workspace = true } 18 | tonic = { workspace = true } 19 | prost = { workspace = true } 20 | 21 | 22 | [build-dependencies] 23 | tonic-build = { workspace = true } 24 | 25 | -------------------------------------------------------------------------------- /flv_proto/build.rs: -------------------------------------------------------------------------------- 1 | fn main() -> Result<(), Box> { 2 | tonic_build::configure() 3 | .compile_protos(&["proto/symdb.proto"], &["proto"]) 4 | .expect("Failed to compile proto specification"); 5 | 6 | Ok(()) 7 | } 8 | -------------------------------------------------------------------------------- /flv_proto/proto/symdb.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | package proto; 3 | 4 | // Service definition. 5 | 6 | service SYMDBService { 7 | rpc LookupExchangeName(LookupExchangeNameRequest) returns (LookupExchangeNameResponse){} 8 | rpc LookupSymbol(LookupSymbolRequest) returns (LookupSymbolResponse){} 9 | rpc LookupSymbolID(LookupSymbolIDRequest) returns (LookupSymbolIDResponse){} 10 | } 11 | 12 | // Request 13 | 14 | message LookupExchangeNameRequest { 15 | int32 exchange_id = 1; 16 | } 17 | 18 | message LookupSymbolRequest { 19 | int32 exchange_id = 1; 20 | int32 symbol_id = 2; 21 | } 22 | 23 | message LookupSymbolIDRequest { 24 | int32 exchange_id = 1; 25 | string symbol = 2; 26 | } 27 | 28 | // Response 29 | 30 | message LookupExchangeNameResponse { 31 | string exchange_name =1; 32 | } 33 | 34 | message LookupSymbolResponse { 35 | string exchange_name =1; 36 | string symbol =2; 37 | } 38 | 39 | message LookupSymbolIDResponse { 40 | string exchange_name =1; 41 | int32 symbol_id =2; 42 | } -------------------------------------------------------------------------------- /flv_proto/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod binding { 2 | tonic::include_proto!("proto"); 3 | } 4 | -------------------------------------------------------------------------------- /flv_sbe/bindings/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sbe_bindings" 3 | version = "0.1.0" 4 | authors = ["sbetool"] 5 | description = "Fluvio DeepCausality Schema" 6 | edition = "2021" 7 | 8 | [lib] 9 | name = "sbe_bindings" 10 | path = "src/lib.rs" 11 | -------------------------------------------------------------------------------- /flv_sbe/bindings/src/client_error_type.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] 2 | #[repr(u8)] 3 | pub enum ClientErrorType { 4 | UnknownClientError = 0x0_u8, 5 | ClientAlreadyLoggedIn = 0x1_u8, 6 | ClientLogInError = 0x2_u8, 7 | ClientNotLoggedIn = 0x3_u8, 8 | ClientLogOutError = 0x4_u8, 9 | #[default] 10 | NullVal = 0xff_u8, 11 | } 12 | impl From for ClientErrorType { 13 | #[inline] 14 | fn from(v: u8) -> Self { 15 | match v { 16 | 0x0_u8 => Self::UnknownClientError, 17 | 0x1_u8 => Self::ClientAlreadyLoggedIn, 18 | 0x2_u8 => Self::ClientLogInError, 19 | 0x3_u8 => Self::ClientNotLoggedIn, 20 | 0x4_u8 => Self::ClientLogOutError, 21 | _ => Self::NullVal, 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /flv_sbe/bindings/src/exchange_id.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] 2 | #[repr(u8)] 3 | pub enum ExchangeID { 4 | BNB = 0x1_u8, 5 | VEX = 0x2_u8, 6 | #[default] 7 | NullVal = 0xff_u8, 8 | } 9 | impl From for ExchangeID { 10 | #[inline] 11 | fn from(v: u8) -> Self { 12 | match v { 13 | 0x1_u8 => Self::BNB, 14 | 0x2_u8 => Self::VEX, 15 | _ => Self::NullVal, 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /flv_sbe/bindings/src/message_type.rs: -------------------------------------------------------------------------------- 1 | #[derive(Clone, Copy, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] 2 | #[repr(u16)] 3 | pub enum MessageType { 4 | UnknownMessageType = 0x0_u16, 5 | ClientLogin = 0x65_u16, 6 | ClientLogout = 0x66_u16, 7 | StartData = 0xc9_u16, 8 | StopData = 0xca_u16, 9 | StopAllData = 0xcb_u16, 10 | DataBar = 0xcc_u16, 11 | FirstDataBar = 0xcd_u16, 12 | LastDataBar = 0xce_u16, 13 | TradeBar = 0xcf_u16, 14 | FirstTradeBar = 0xd0_u16, 15 | LastTradeBar = 0xd1_u16, 16 | ClientError = 0x321_u16, 17 | DataError = 0x322_u16, 18 | #[default] 19 | NullVal = 0xffff_u16, 20 | } 21 | impl From for MessageType { 22 | #[inline] 23 | fn from(v: u16) -> Self { 24 | match v { 25 | 0x0_u16 => Self::UnknownMessageType, 26 | 0x65_u16 => Self::ClientLogin, 27 | 0x66_u16 => Self::ClientLogout, 28 | 0xc9_u16 => Self::StartData, 29 | 0xca_u16 => Self::StopData, 30 | 0xcb_u16 => Self::StopAllData, 31 | 0xcc_u16 => Self::DataBar, 32 | 0xcd_u16 => Self::FirstDataBar, 33 | 0xce_u16 => Self::LastDataBar, 34 | 0xcf_u16 => Self::TradeBar, 35 | 0xd0_u16 => Self::FirstTradeBar, 36 | 0xd1_u16 => Self::LastTradeBar, 37 | 0x321_u16 => Self::ClientError, 38 | 0x322_u16 => Self::DataError, 39 | _ => Self::NullVal, 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "sbe_messages" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | 13 | [lib] 14 | name = "sbe_messages" 15 | path = "src/lib.rs" 16 | 17 | 18 | [dependencies] 19 | # Internal crates 20 | common = { workspace = true } 21 | sbe_bindings = { workspace = true } 22 | # External crates 23 | rust_decimal = { workspace = true } 24 | serde = { workspace = true } 25 | chrono = { workspace = true } -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/errors/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | /// SbeEncodeError struct definition. 4 | /// 5 | /// Used to represent SBE encoding errors. 6 | /// 7 | /// # Fields 8 | /// 9 | /// `0` - Error message string 10 | /// 11 | /// # Implements 12 | /// 13 | /// `Clone`, `Debug`, `PartialEq`, `Eq`, `PartialOrd`, `Ord`, `Hash` - Rust defaults 14 | /// `fmt::Display` - Custom Display implementation to print error messages 15 | /// `std::error::Error` - Implements std::error::Error trait 16 | /// 17 | #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 18 | pub struct SbeEncodeError(pub String); 19 | 20 | impl fmt::Display for SbeEncodeError { 21 | #[inline] 22 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 23 | write!(f, "SbeEncodeError: {}", self.0) 24 | } 25 | } 26 | 27 | impl std::error::Error for SbeEncodeError {} 28 | 29 | /// SbeDecodeError struct definition. 30 | /// 31 | /// Used to represent SBE decoding errors. 32 | /// 33 | /// # Fields 34 | /// 35 | /// `0` - Error message string 36 | /// 37 | /// # Implements 38 | /// 39 | /// `Clone`, `Debug`, `PartialEq`, `Eq`, `PartialOrd`, `Ord`, `Hash` - Rust defaults 40 | /// `fmt::Display` - Custom Display implementation to print error messages 41 | /// `std::error::Error` - Implements std::error::Error trait 42 | /// 43 | #[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] 44 | pub struct SbeDecodeError(pub String); 45 | 46 | impl std::error::Error for SbeDecodeError {} 47 | 48 | impl fmt::Display for SbeDecodeError { 49 | #[inline] 50 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 51 | write!(f, "SbeDecodeError: {}", self.0) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod errors; 2 | pub mod messages; 3 | pub mod prelude; 4 | pub mod types; 5 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/client_messages/client_login/display.rs: -------------------------------------------------------------------------------- 1 | use crate::messages::client_messages::client_login::ClientLoginMessage; 2 | use std::fmt; 3 | 4 | impl fmt::Display for ClientLoginMessage { 5 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 6 | write!( 7 | f, 8 | "ClientLoginMessage {{ client_id: {} }}", 9 | self.client_id(), 10 | ) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/client_messages/client_login/getters.rs: -------------------------------------------------------------------------------- 1 | use crate::messages::client_messages::client_login::ClientLoginMessage; 2 | use crate::prelude::MessageType; 3 | 4 | impl ClientLoginMessage { 5 | pub fn message_type(&self) -> &MessageType { 6 | &self.message_type 7 | } 8 | pub fn client_id(&self) -> u16 { 9 | self.client_id 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/client_messages/client_login/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::MessageType; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | mod display; 5 | mod getters; 6 | mod sbe_decode; 7 | mod sbe_encode; 8 | 9 | #[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] 10 | pub struct ClientLoginMessage { 11 | message_type: MessageType, 12 | client_id: u16, 13 | } 14 | 15 | impl ClientLoginMessage { 16 | /// Creates a new ClientLoginMessage instance. 17 | /// 18 | /// Sets the message_type to ClientLogin. 19 | /// 20 | /// # Arguments 21 | /// 22 | /// * `client_id` - u16 client ID 23 | /// 24 | /// # Returns 25 | /// 26 | /// ClientLoginMessage instance 27 | 28 | pub fn new(client_id: u16) -> Self { 29 | let message_type = MessageType::ClientLogin; 30 | 31 | Self { 32 | message_type, 33 | client_id, 34 | } 35 | } 36 | } 37 | 38 | impl From<&[u8]> for ClientLoginMessage { 39 | /// Implements the From trait to decode a ClientLoginMessage from a byte slice. 40 | /// 41 | /// Calls the sbe_decode::decode_client_login_message function to decode the message. 42 | /// 43 | /// # Arguments 44 | /// 45 | /// * `value` - Byte slice to decode 46 | /// 47 | /// # Returns 48 | /// 49 | /// Decoded ClientLoginMessage 50 | /// 51 | /// # Errors 52 | /// 53 | /// Panics if decode fails 54 | #[inline] 55 | fn from(value: &[u8]) -> Self { 56 | sbe_decode::decode_client_login_message(value).expect("Failed to decode ClientLoginMessage") 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/client_messages/client_login/sbe_decode.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{ClientLoginMessage, MessageType}; 2 | use sbe_bindings::client_login_codec::SBE_TEMPLATE_ID; 3 | use sbe_bindings::{ClientLoginDecoder, MessageHeaderDecoder, ReadBuf, SbeResult}; 4 | 5 | /// Decodes a ClientLoginMessage from a byte buffer. 6 | /// 7 | /// # Arguments 8 | /// 9 | /// * `buffer` - Byte buffer to decode 10 | /// 11 | /// # Returns 12 | /// 13 | /// Decoded ClientLoginMessage 14 | /// 15 | /// # Errors 16 | /// 17 | /// Returns Err if decode fails 18 | /// 19 | /// # Process 20 | /// 21 | /// - Create default ClientLoginDecoder 22 | /// - Wrap buffer in ReadBuf 23 | /// - Decode header and validate template ID 24 | /// - Decode message_type and validate 25 | /// - Decode client_id 26 | /// - Create and return ClientLoginMessage 27 | /// 28 | pub fn decode_client_login_message(buffer: &[u8]) -> SbeResult { 29 | let mut csg = ClientLoginDecoder::default(); 30 | let buf = ReadBuf::new(buffer); 31 | 32 | let header = MessageHeaderDecoder::default().wrap(buf, 0); 33 | assert_eq!(SBE_TEMPLATE_ID, header.template_id()); 34 | csg = csg.header(header); 35 | 36 | let sbe_message_type = csg.message_type(); 37 | let message_type = MessageType::from(sbe_message_type as u16); 38 | assert_eq!(message_type, MessageType::ClientLogin); 39 | 40 | let client_id = csg.client_id(); 41 | 42 | let message = ClientLoginMessage::new(client_id); 43 | 44 | Ok(message) 45 | } 46 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/client_messages/client_logout/display.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::ClientLogoutMessage; 2 | use std::fmt; 3 | 4 | impl fmt::Display for ClientLogoutMessage { 5 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 6 | write!( 7 | f, 8 | "ClientLogoutMessage {{ client_id: {} }}", 9 | self.client_id() 10 | ) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/client_messages/client_logout/getters.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{ClientLogoutMessage, MessageType}; 2 | 3 | impl ClientLogoutMessage { 4 | pub fn message_type(&self) -> &MessageType { 5 | &self.message_type 6 | } 7 | pub fn client_id(&self) -> u16 { 8 | self.client_id 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/client_messages/client_logout/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::MessageType; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | mod display; 5 | mod getters; 6 | mod sbe_decode; 7 | mod sbe_encode; 8 | 9 | #[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] 10 | pub struct ClientLogoutMessage { 11 | message_type: MessageType, 12 | client_id: u16, 13 | } 14 | 15 | impl ClientLogoutMessage { 16 | /// Creates a new ClientLogoutMessage instance. 17 | /// 18 | /// Sets the message_type to ClientLogout. 19 | /// 20 | /// # Arguments 21 | /// 22 | /// * `client_id` - u16 client ID 23 | /// 24 | /// # Returns 25 | /// 26 | /// ClientLogoutMessage instance 27 | /// 28 | pub fn new(client_id: u16) -> Self { 29 | let message_type = MessageType::ClientLogout; 30 | Self { 31 | message_type, 32 | client_id, 33 | } 34 | } 35 | } 36 | 37 | impl From<&[u8]> for ClientLogoutMessage { 38 | /// Implements the From trait to decode a ClientLogoutMessage from a byte slice. 39 | /// 40 | /// Calls the sbe_decode::decode_client_logout_message function to decode the message. 41 | /// 42 | /// # Arguments 43 | /// 44 | /// * `value` - Byte slice to decode 45 | /// 46 | /// # Returns 47 | /// 48 | /// Decoded ClientLogoutMessage 49 | /// 50 | /// # Errors 51 | /// 52 | /// Panics if decode fails 53 | #[inline] 54 | fn from(value: &[u8]) -> Self { 55 | sbe_decode::decode_client_logout_message(value) 56 | .expect("Failed to decode ClientLoginMessage") 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/client_messages/client_logout/sbe_decode.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{ClientLogoutMessage, MessageType}; 2 | use sbe_bindings::client_logout_codec::SBE_TEMPLATE_ID; 3 | use sbe_bindings::{ClientLogoutDecoder, MessageHeaderDecoder, ReadBuf, SbeResult}; 4 | 5 | /// Decodes a ClientLogoutMessage from a byte buffer. 6 | /// 7 | /// # Arguments 8 | /// 9 | /// * `buffer` - Byte buffer to decode 10 | /// 11 | /// # Returns 12 | /// 13 | /// Decoded ClientLogoutMessage 14 | /// 15 | /// # Errors 16 | /// 17 | /// Returns Err if decode fails 18 | /// 19 | /// # Process 20 | /// 21 | /// - Create default ClientLogoutDecoder 22 | /// - Wrap buffer in ReadBuf 23 | /// - Decode header and validate template ID 24 | /// - Decode message_type and validate 25 | /// - Decode client_id 26 | /// - Create and return ClientLogoutMessage 27 | pub fn decode_client_logout_message(buffer: &[u8]) -> SbeResult { 28 | let mut csg = ClientLogoutDecoder::default(); 29 | let buf = ReadBuf::new(buffer); 30 | 31 | let header = MessageHeaderDecoder::default().wrap(buf, 0); 32 | assert_eq!(SBE_TEMPLATE_ID, header.template_id()); 33 | csg = csg.header(header); 34 | 35 | let sbe_message_type = csg.message_type(); 36 | let message_type = MessageType::from(sbe_message_type as u16); 37 | assert_eq!(message_type, MessageType::ClientLogout); 38 | 39 | let client_id = csg.client_id(); 40 | 41 | let message = ClientLogoutMessage::new(client_id); 42 | 43 | Ok(message) 44 | } 45 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/client_messages/client_logout/sbe_encode.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::SbeEncodeError; 2 | use crate::prelude::ClientLogoutMessage; 3 | use sbe_bindings::MessageType as SbeMessageType; 4 | use sbe_bindings::{message_header_codec, ClientLogoutEncoder, Encoder, WriteBuf}; 5 | 6 | impl ClientLogoutMessage { 7 | /// Encodes a ClientLogoutMessage to a byte buffer. 8 | /// 9 | /// # Arguments 10 | /// 11 | /// * `self` - ClientLogoutMessage to encode 12 | /// 13 | /// # Returns 14 | /// 15 | /// (usize, `Vec`) - Tuple of encoded size and byte buffer 16 | /// 17 | /// # Errors 18 | /// 19 | /// Returns Err if encoding fails 20 | /// 21 | /// # Process 22 | /// 23 | /// - Create a 12 byte buffer 24 | /// - Create default ClientLogoutEncoder 25 | /// - Wrap buffer in WriteBuf 26 | /// - Encode header 27 | /// - Encode message_type 28 | /// - Encode client_id 29 | /// - Return encoded size and buffer 30 | /// 31 | pub fn encode(&self) -> Result<(usize, Vec), SbeEncodeError> { 32 | // precise buffer size is 12 bytes for the entire message. 33 | let mut buffer = vec![0u8; 12]; 34 | 35 | let mut csg = ClientLogoutEncoder::default(); 36 | 37 | csg = csg.wrap( 38 | WriteBuf::new(buffer.as_mut_slice()), 39 | message_header_codec::ENCODED_LENGTH, 40 | ); 41 | 42 | csg = csg.header(0).parent().expect("Failed to encode header"); 43 | 44 | let value = SbeMessageType::from(self.message_type as u16); 45 | csg.message_type(value); 46 | 47 | let value = self.client_id; 48 | csg.client_id(value); 49 | 50 | let limit = csg.get_limit(); 51 | Ok((limit, buffer)) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/client_messages/mod.rs: -------------------------------------------------------------------------------- 1 | /// Module containing client request messages. 2 | /// 3 | /// This includes messages like: 4 | /// 5 | /// - ClientLoginMessage 6 | /// - ClientLogoutMessage 7 | /// 8 | /// 9 | /// The client messages are exposed in the prelude for convenient importing. 10 | /// 11 | /// # Exports 12 | /// 13 | /// - `client_login` - ClientLoginMessage 14 | /// - `client_logout` - ClientLogoutMessage 15 | /// 16 | pub mod client_login; 17 | pub mod client_logout; 18 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/mod.rs: -------------------------------------------------------------------------------- 1 | /// Module containing data subscription messages. 2 | /// 3 | /// This includes messages like: 4 | /// 5 | /// - StartDataMessage 6 | /// - StopDataMessage 7 | /// - StartAllDataMessage 8 | /// - StopAllDataMessage 9 | /// - OHLCVBarMessage 10 | /// - TradeBarMessage 11 | /// 12 | /// Grouping data subscription messages together keeps them organized 13 | /// separately from client and error messages. 14 | /// 15 | /// The data messages are exposed in the prelude for convenient importing. 16 | /// 17 | /// # Exports 18 | /// 19 | /// - `ohlcv_bar` - OHLCVBarMessage 20 | /// - `ohlcv_bar_first` - OHLCVBarFirstMessage 21 | /// - `ohlcv_bar_last` - OHLCVBarLastMessage 22 | /// - `start_data` - StartDataMessage 23 | /// - `stop_data` - StopDataMessage 24 | /// - `start_all_data` - StartAllDataMessage 25 | /// - `stop_all_data` - StopAllDataMessage 26 | /// - `trade_bar` - TradeBarMessage 27 | /// - `trade_bar_first` - TradeBarFirstMessage 28 | /// - `trade_bar_last` - TradeBarLastMessage 29 | /// 30 | pub mod ohlcv_bar; 31 | pub mod ohlcv_bar_first; 32 | pub mod ohlcv_bar_last; 33 | pub mod start_data; 34 | pub mod stop_all_data; 35 | pub mod stop_data; 36 | pub mod trade_bar; 37 | pub mod trade_bar_first; 38 | pub mod trade_bar_last; 39 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/ohlcv_bar/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::{SbeDecodeError, SbeEncodeError}; 2 | use common::prelude::OHLCVBar; 3 | use serde::{Deserialize, Serialize}; 4 | 5 | pub mod sbe_decoder; 6 | pub mod sbe_encoder; 7 | 8 | #[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] 9 | pub struct SbeOHLCVBar {} 10 | 11 | impl SbeOHLCVBar { 12 | pub fn new() -> Self { 13 | Self {} 14 | } 15 | } 16 | 17 | impl SbeOHLCVBar { 18 | /// Encodes an OHLCVBar into an SBE message buffer. 19 | /// 20 | /// # Parameters 21 | /// 22 | /// - `bar` - The OHLCVBar to encode 23 | /// 24 | /// # Returns 25 | /// 26 | /// A Result containing: 27 | /// 28 | /// - The size of the encoded message 29 | /// - The encoded message buffer 30 | pub fn encode(bar: OHLCVBar) -> Result<(usize, Vec), SbeEncodeError> { 31 | sbe_encoder::encode_data_bar_message(bar) 32 | } 33 | 34 | /// Decodes an SBE message buffer into an OHLCVBar. 35 | /// 36 | /// # Parameters 37 | /// 38 | /// - `buffer` - The SBE encoded message buffer 39 | /// 40 | /// # Returns 41 | /// 42 | /// A Result containing the decoded OHLCVBar or a decoding error. 43 | #[inline] 44 | pub fn decode(buffer: &[u8]) -> Result { 45 | sbe_decoder::decode_data_bar_message(buffer) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/ohlcv_bar_first/display.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::FirstOHLCVBar; 2 | use std::fmt; 3 | 4 | impl fmt::Display for FirstOHLCVBar { 5 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 6 | write!( 7 | f, 8 | "FirstOHLCVBar {{ message_type: {:?}, symbol_id: {} }}", 9 | self.message_type, self.symbol_id 10 | ) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/ohlcv_bar_first/getters.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{FirstOHLCVBar, MessageType}; 2 | 3 | impl FirstOHLCVBar { 4 | pub fn message_type(&self) -> MessageType { 5 | self.message_type 6 | } 7 | pub fn symbol_id(&self) -> u16 { 8 | self.symbol_id 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/ohlcv_bar_first/mod.rs: -------------------------------------------------------------------------------- 1 | mod display; 2 | mod getters; 3 | mod sbe_decode; 4 | mod sbe_encode; 5 | 6 | use crate::prelude::MessageType; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | #[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] 10 | pub struct FirstOHLCVBar { 11 | message_type: MessageType, 12 | symbol_id: u16, 13 | } 14 | 15 | impl FirstOHLCVBar { 16 | /// Creates a new FirstOHLCVBar instance. 17 | /// 18 | /// Sets the message_type to FirstOHLCVBar. 19 | /// 20 | /// # Arguments 21 | /// 22 | /// * `symbol_id` - u16 symbol ID 23 | /// 24 | /// # Returns 25 | /// 26 | /// FirstOHLCVBar instance 27 | /// 28 | pub fn new(symbol_id: u16) -> Self { 29 | let message_type = MessageType::FirstOHLCVBar; 30 | Self { 31 | message_type, 32 | symbol_id, 33 | } 34 | } 35 | } 36 | 37 | impl From<&[u8]> for FirstOHLCVBar { 38 | /// Implements the From trait to decode a FirstOHLCVBar from a byte slice. 39 | /// 40 | /// Calls the sbe_decode::decode_first_data_bar_message function to decode the message. 41 | /// 42 | /// # Arguments 43 | /// 44 | /// * `value` - Byte slice to decode 45 | /// 46 | /// # Returns 47 | /// 48 | /// Decoded FirstOHLCVBar 49 | /// 50 | /// # Errors 51 | /// 52 | /// Panics if decode fails 53 | /// 54 | #[inline] 55 | fn from(value: &[u8]) -> Self { 56 | sbe_decode::decode_first_data_bar_message(value) 57 | .expect("Failed to decode FirstDataBar message") 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/ohlcv_bar_first/sbe_decode.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{FirstOHLCVBar, MessageType}; 2 | use sbe_bindings::first_data_bar_codec::SBE_TEMPLATE_ID; 3 | use sbe_bindings::{FirstDataBarDecoder, MessageHeaderDecoder, ReadBuf, SbeResult}; 4 | 5 | /// Decodes a FirstOHLCVBar message from a byte buffer. 6 | /// 7 | /// # Arguments 8 | /// 9 | /// * `buffer` - Byte buffer to decode 10 | /// 11 | /// # Returns 12 | /// 13 | /// Decoded FirstOHLCVBar 14 | /// 15 | /// # Errors 16 | /// 17 | /// Returns Err if decode fails 18 | /// 19 | /// # Process 20 | /// 21 | /// - Create default FirstDataBarDecoder 22 | /// - Wrap buffer in ReadBuf 23 | /// - Decode header and validate template ID 24 | /// - Decode and validate message_type 25 | /// - Decode symbol_id 26 | /// - Create and return FirstOHLCVBar 27 | /// 28 | pub fn decode_first_data_bar_message(buffer: &[u8]) -> SbeResult { 29 | let mut csg = FirstDataBarDecoder::default(); 30 | let buf = ReadBuf::new(buffer); 31 | 32 | let header = MessageHeaderDecoder::default().wrap(buf, 0); 33 | assert_eq!(SBE_TEMPLATE_ID, header.template_id()); 34 | csg = csg.header(header); 35 | 36 | let sbe_message_type = csg.message_type(); 37 | let message_type = MessageType::from(sbe_message_type as u16); 38 | assert_eq!(message_type, MessageType::FirstOHLCVBar); 39 | 40 | let symbol_id = csg.symbol_id(); 41 | 42 | let message = FirstOHLCVBar::new(symbol_id); 43 | 44 | Ok(message) 45 | } 46 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/ohlcv_bar_first/sbe_encode.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::SbeEncodeError; 2 | use crate::prelude::FirstOHLCVBar; 3 | use sbe_bindings::MessageType as SbeMessageType; 4 | use sbe_bindings::{message_header_codec, Encoder, FirstDataBarEncoder, WriteBuf}; 5 | 6 | impl FirstOHLCVBar { 7 | /// Encodes a FirstOHLCVBar to a byte buffer. 8 | /// 9 | /// # Arguments 10 | /// 11 | /// * `self` - FirstOHLCVBar to encode 12 | /// 13 | /// # Returns 14 | /// 15 | /// (usize, `Vec`) - Tuple containing encoded size and byte buffer 16 | /// 17 | /// # Errors 18 | /// 19 | /// Returns Err if encoding fails 20 | /// 21 | /// # Process 22 | /// 23 | /// - Create 12 byte buffer 24 | /// - Create default FirstDataBarEncoder 25 | /// - Wrap buffer in WriteBuf 26 | /// - Encode header 27 | /// - Encode message_type 28 | /// - Encode symbol_id 29 | /// - Return encoded size and buffer 30 | /// 31 | pub fn encode(&self) -> Result<(usize, Vec), SbeEncodeError> { 32 | let mut buffer = vec![0u8; 12]; 33 | 34 | let mut csg = FirstDataBarEncoder::default(); 35 | 36 | csg = csg.wrap( 37 | WriteBuf::new(buffer.as_mut_slice()), 38 | message_header_codec::ENCODED_LENGTH, 39 | ); 40 | 41 | csg = csg.header(0).parent().expect("Failed to encode header"); 42 | 43 | let value = SbeMessageType::from(self.message_type as u16); 44 | csg.message_type(value); 45 | 46 | let value = self.symbol_id; 47 | csg.symbol_id(value); 48 | 49 | let limit = csg.get_limit(); 50 | Ok((limit, buffer)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/ohlcv_bar_last/display.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::LastOHLCVBar; 2 | use std::fmt; 3 | 4 | impl fmt::Display for LastOHLCVBar { 5 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 6 | write!( 7 | f, 8 | "LastOHLCVBar {{ message_type: {:?}, symbol_id: {} }}", 9 | self.message_type, self.symbol_id 10 | ) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/ohlcv_bar_last/getters.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{LastOHLCVBar, MessageType}; 2 | 3 | impl LastOHLCVBar { 4 | pub fn message_type(&self) -> MessageType { 5 | self.message_type 6 | } 7 | pub fn symbol_id(&self) -> u16 { 8 | self.symbol_id 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/ohlcv_bar_last/mod.rs: -------------------------------------------------------------------------------- 1 | mod display; 2 | mod getters; 3 | mod sbe_decode; 4 | mod sbe_encode; 5 | 6 | use crate::prelude::MessageType; 7 | use serde::{Deserialize, Serialize}; 8 | 9 | #[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] 10 | pub struct LastOHLCVBar { 11 | message_type: MessageType, 12 | symbol_id: u16, 13 | } 14 | 15 | impl LastOHLCVBar { 16 | /// Creates a new LastOHLCVBar instance. 17 | /// 18 | /// Sets the message_type to LastOHLCVBar. 19 | /// 20 | /// # Arguments 21 | /// 22 | /// * `symbol_id` - u16 symbol ID 23 | /// 24 | /// # Returns 25 | /// 26 | /// LastOHLCVBar instance 27 | /// 28 | pub fn new(symbol_id: u16) -> Self { 29 | let message_type = MessageType::LastOHLCVBar; 30 | Self { 31 | message_type, 32 | symbol_id, 33 | } 34 | } 35 | } 36 | 37 | impl From<&[u8]> for LastOHLCVBar { 38 | /// Implements the From trait to decode a LastOHLCVBar from a byte slice. 39 | /// 40 | /// Calls the sbe_decode::decode_last_data_bar_message function to decode the message. 41 | /// 42 | /// # Arguments 43 | /// 44 | /// * `value` - Byte slice to decode 45 | /// 46 | /// # Returns 47 | /// 48 | /// Decoded LastOHLCVBar 49 | /// 50 | /// # Errors 51 | /// 52 | /// Panics if decode fails 53 | /// 54 | #[inline] 55 | fn from(value: &[u8]) -> Self { 56 | sbe_decode::decode_last_data_bar_message(value) 57 | .expect("Failed to decode LastDataBar message") 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/ohlcv_bar_last/sbe_decode.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{LastOHLCVBar, MessageType}; 2 | use sbe_bindings::last_data_bar_codec::SBE_TEMPLATE_ID; 3 | use sbe_bindings::{LastDataBarDecoder, MessageHeaderDecoder, ReadBuf, SbeResult}; 4 | 5 | /// Decodes a LastOHLCVBar message from a byte buffer. 6 | /// 7 | /// # Arguments 8 | /// 9 | /// * `buffer` - Byte buffer to decode 10 | /// 11 | /// # Returns 12 | /// 13 | /// Decoded LastOHLCVBar 14 | /// 15 | /// # Errors 16 | /// 17 | /// Returns Err if decode fails 18 | /// 19 | /// # Process 20 | /// 21 | /// - Create default LastDataBarDecoder 22 | /// - Wrap buffer in ReadBuf 23 | /// - Decode header and validate template ID 24 | /// - Decode and validate message_type 25 | /// - Decode symbol_id 26 | /// - Create and return LastOHLCVBar 27 | /// 28 | pub fn decode_last_data_bar_message(buffer: &[u8]) -> SbeResult { 29 | let mut csg = LastDataBarDecoder::default(); 30 | let buf = ReadBuf::new(buffer); 31 | 32 | let header = MessageHeaderDecoder::default().wrap(buf, 0); 33 | assert_eq!(SBE_TEMPLATE_ID, header.template_id()); 34 | csg = csg.header(header); 35 | 36 | let sbe_message_type = csg.message_type(); 37 | let message_type = MessageType::from(sbe_message_type as u16); 38 | assert_eq!(message_type, MessageType::LastOHLCVBar); 39 | 40 | let symbol_id = csg.symbol_id(); 41 | 42 | let message = LastOHLCVBar::new(symbol_id); 43 | 44 | Ok(message) 45 | } 46 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/ohlcv_bar_last/sbe_encode.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::SbeEncodeError; 2 | use crate::prelude::LastOHLCVBar; 3 | use sbe_bindings::MessageType as SbeMessageType; 4 | use sbe_bindings::{message_header_codec, Encoder, LastDataBarEncoder, WriteBuf}; 5 | 6 | impl LastOHLCVBar { 7 | /// Encodes a LastOHLCVBar to a byte buffer. 8 | /// 9 | /// # Arguments 10 | /// 11 | /// * `self` - LastOHLCVBar to encode 12 | /// 13 | /// # Returns 14 | /// 15 | /// (usize, `Vec`) - Tuple containing encoded size and byte buffer 16 | /// 17 | /// # Errors 18 | /// 19 | /// Returns Err if encoding fails 20 | /// 21 | /// # Process 22 | /// 23 | /// - Create 12 byte buffer 24 | /// - Create default LastDataBarEncoder 25 | /// - Wrap buffer in WriteBuf 26 | /// - Encode header 27 | /// - Encode message_type 28 | /// - Encode symbol_id 29 | /// - Return encoded size and buffer 30 | /// 31 | pub fn encode(&self) -> Result<(usize, Vec), SbeEncodeError> { 32 | // precise buffer size is 12 bytes for the entire message. 33 | let mut buffer = vec![0u8; 12]; 34 | 35 | let mut csg = LastDataBarEncoder::default(); 36 | 37 | csg = csg.wrap( 38 | WriteBuf::new(buffer.as_mut_slice()), 39 | message_header_codec::ENCODED_LENGTH, 40 | ); 41 | 42 | csg = csg.header(0).parent().expect("Failed to encode header"); 43 | 44 | let value = SbeMessageType::from(self.message_type as u16); 45 | csg.message_type(value); 46 | 47 | let value = self.symbol_id; 48 | csg.symbol_id(value); 49 | 50 | let limit = csg.get_limit(); 51 | Ok((limit, buffer)) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/start_data/display.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::StartDataMessage; 2 | use std::fmt; 3 | 4 | impl fmt::Display for StartDataMessage { 5 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 6 | write!(f, 7 | "StartDataMessage[message_type: {}, client_id: {}, exchange_id: {}, symbol_id: {} time_resolution: {} data_type: {}]", 8 | self.message_type, self.client_id, self.exchange_id, self.symbol_id, self.time_resolution, self.data_type_id 9 | ) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/start_data/getter.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{DataType, MessageType, StartDataMessage}; 2 | use common::prelude::{ExchangeID, TimeResolution}; 3 | 4 | impl StartDataMessage { 5 | pub fn message_type(&self) -> &MessageType { 6 | &self.message_type 7 | } 8 | pub fn client_id(&self) -> &u16 { 9 | &self.client_id 10 | } 11 | pub fn exchange_id(&self) -> &ExchangeID { 12 | &self.exchange_id 13 | } 14 | pub fn symbol_id(&self) -> &u16 { 15 | &self.symbol_id 16 | } 17 | pub fn data_type_id(&self) -> &DataType { 18 | &self.data_type_id 19 | } 20 | pub fn time_resolution(&self) -> &TimeResolution { 21 | &self.time_resolution 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/stop_all_data/display.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::StopAllDataMessage; 2 | use std::fmt; 3 | 4 | impl fmt::Display for StopAllDataMessage { 5 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 6 | write!( 7 | f, 8 | "StopAllDataMessage[message_type: {}, client_id: {}, exchange_id: {}]", 9 | self.message_type, self.client_id, self.exchange_id 10 | ) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/stop_all_data/getter.rs: -------------------------------------------------------------------------------- 1 | use crate::messages::data_messages::stop_all_data::StopAllDataMessage; 2 | use crate::prelude::MessageType; 3 | use common::prelude::ExchangeID; 4 | 5 | impl StopAllDataMessage { 6 | pub fn message_type(&self) -> &MessageType { 7 | &self.message_type 8 | } 9 | pub fn client_id(&self) -> &u16 { 10 | &self.client_id 11 | } 12 | pub fn exchange_id(&self) -> &ExchangeID { 13 | &self.exchange_id 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/stop_all_data/mod.rs: -------------------------------------------------------------------------------- 1 | use serde::{Deserialize, Serialize}; 2 | 3 | use common::prelude::ExchangeID; 4 | 5 | use crate::prelude::MessageType; 6 | 7 | mod display; 8 | mod getter; 9 | mod sbe_decode; 10 | mod sbe_encode; 11 | 12 | #[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] 13 | pub struct StopAllDataMessage { 14 | message_type: MessageType, 15 | client_id: u16, 16 | exchange_id: ExchangeID, 17 | } 18 | 19 | impl StopAllDataMessage { 20 | /// Creates a new StopAllDataMessage instance. 21 | /// 22 | /// Sets the message_type to StopAllData. 23 | /// 24 | /// # Arguments 25 | /// 26 | /// * `client_id` - u16 client ID 27 | /// * `exchange_id` - ExchangeID exchange ID 28 | /// 29 | /// # Returns 30 | /// 31 | /// StopAllDataMessage instance 32 | /// 33 | pub fn new(client_id: u16, exchange_id: ExchangeID) -> Self { 34 | let message_type = MessageType::StopAllData; 35 | Self { 36 | message_type, 37 | client_id, 38 | exchange_id, 39 | } 40 | } 41 | } 42 | 43 | impl From<&[u8]> for StopAllDataMessage { 44 | /// Implements the From trait to decode a StopAllDataMessage from a byte slice. 45 | /// 46 | /// Calls the sbe_decode::decode_stop_all_data_message function to decode the message. 47 | /// 48 | /// # Arguments 49 | /// 50 | /// * `value` - Byte slice to decode 51 | /// 52 | /// # Returns 53 | /// 54 | /// Decoded StopAllDataMessage 55 | /// 56 | /// # Errors 57 | /// 58 | /// Panics if decode fails 59 | /// 60 | #[inline] 61 | fn from(value: &[u8]) -> Self { 62 | sbe_decode::decode_stop_all_data_message(value) 63 | .expect("Failed to decode start data message") 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/stop_all_data/sbe_decode.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{MessageType, StopAllDataMessage}; 2 | use common::prelude::ExchangeID; 3 | use sbe_bindings::{MessageHeaderDecoder, ReadBuf, SbeResult, StopAllDataMsgDecoder}; 4 | 5 | use sbe_bindings::stop_all_data_msg_codec::SBE_TEMPLATE_ID; 6 | 7 | /// Decodes a StopAllDataMessage from a byte buffer. 8 | /// 9 | /// # Arguments 10 | /// 11 | /// * `buffer` - Byte buffer to decode 12 | /// 13 | /// # Returns 14 | /// 15 | /// Decoded StopAllDataMessage 16 | /// 17 | /// # Errors 18 | /// 19 | /// Returns Err if decode fails 20 | /// 21 | /// # Process 22 | /// 23 | /// - Create default StopAllDataMsgDecoder 24 | /// - Wrap buffer in ReadBuf 25 | /// - Decode header and validate template ID 26 | /// - Decode and validate message_type 27 | /// - Decode client_id 28 | /// - Decode and create exchange_id 29 | /// - Create and return StopAllDataMessage 30 | /// 31 | pub fn decode_stop_all_data_message(buffer: &[u8]) -> SbeResult { 32 | let mut csg = StopAllDataMsgDecoder::default(); 33 | 34 | let buf = ReadBuf::new(buffer); 35 | 36 | let header = MessageHeaderDecoder::default().wrap(buf, 0); 37 | assert_eq!(SBE_TEMPLATE_ID, header.template_id()); 38 | csg = csg.header(header); 39 | 40 | let sbe_message_type = csg.message_type(); 41 | let message_type = MessageType::from(sbe_message_type as u16); 42 | assert_eq!(message_type, MessageType::StopAllData); 43 | 44 | let client_id = csg.client_id(); 45 | 46 | let sbe_exchange_id = csg.exchange_id(); 47 | let exchange_id = ExchangeID::from(sbe_exchange_id); 48 | 49 | let message = StopAllDataMessage { 50 | message_type, 51 | client_id, 52 | exchange_id, 53 | }; 54 | 55 | Ok(message) 56 | } 57 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/stop_data/display.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::StopDataMessage; 2 | 3 | use std::fmt; 4 | 5 | impl fmt::Display for StopDataMessage { 6 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 7 | write!( 8 | f, 9 | "StopDataMessage[message_type: {}, client_id: {}, exchange_id: {}, symbol_id: {}, data_type: {}]", 10 | self.message_type, self.client_id, self.exchange_id, self.symbol_id, self.data_type_id, 11 | ) 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/stop_data/getter.rs: -------------------------------------------------------------------------------- 1 | use crate::messages::data_messages::stop_data::StopDataMessage; 2 | use crate::prelude::{DataType, MessageType}; 3 | use common::prelude::ExchangeID; 4 | 5 | impl StopDataMessage { 6 | pub fn message_type(&self) -> &MessageType { 7 | &self.message_type 8 | } 9 | pub fn client_id(&self) -> &u16 { 10 | &self.client_id 11 | } 12 | pub fn exchange_id(&self) -> &ExchangeID { 13 | &self.exchange_id 14 | } 15 | pub fn symbol_id(&self) -> &u16 { 16 | &self.symbol_id 17 | } 18 | 19 | pub fn data_type_id(&self) -> &DataType { 20 | &self.data_type_id 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/trade_bar/mod.rs: -------------------------------------------------------------------------------- 1 | mod sbe_decode; 2 | mod sbe_encode; 3 | 4 | use crate::errors::{SbeDecodeError, SbeEncodeError}; 5 | use common::prelude::TradeBar; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | #[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] 9 | pub struct SbeTradeBar {} 10 | 11 | impl SbeTradeBar { 12 | pub fn new() -> Self { 13 | Self {} 14 | } 15 | } 16 | 17 | impl SbeTradeBar { 18 | /// Encodes a TradeBar message to a byte buffer. 19 | /// 20 | /// # Arguments 21 | /// 22 | /// * `bar` - TradeBar to encode 23 | /// 24 | /// # Returns 25 | /// 26 | /// (usize, `Vec`) - Tuple containing encoded size and byte buffer 27 | /// 28 | /// # Errors 29 | /// 30 | /// Returns Err if encoding fails 31 | /// 32 | /// # Remarks 33 | /// 34 | /// Calls sbe_encode::encode_trade_bar_message to perform encoding 35 | /// 36 | pub fn encode(bar: TradeBar) -> Result<(usize, Vec), SbeEncodeError> { 37 | sbe_encode::encode_trade_bar_message(bar) 38 | } 39 | 40 | /// Decodes a TradeBar message from a byte buffer. 41 | /// 42 | /// # Arguments 43 | /// 44 | /// * `buffer` - Byte buffer containing encoded TradeBar message 45 | /// 46 | /// # Returns 47 | /// 48 | /// Decoded TradeBar on success 49 | /// 50 | /// # Errors 51 | /// 52 | /// Returns Err if decoding fails 53 | /// 54 | /// # Remarks 55 | /// 56 | /// Calls sbe_decode::decode_trade_bar_message to perform decoding 57 | /// 58 | pub fn decode(buffer: &[u8]) -> Result { 59 | sbe_decode::decode_trade_bar_message(buffer) 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/trade_bar_first/display.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::FirstTradeBar; 2 | use std::fmt; 3 | 4 | impl fmt::Display for FirstTradeBar { 5 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 6 | write!( 7 | f, 8 | "FirstTradeBar {{ message_type: {}, symbol_id: {} }}", 9 | self.message_type, self.symbol_id 10 | ) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/trade_bar_first/getters.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{FirstTradeBar, MessageType}; 2 | 3 | impl FirstTradeBar { 4 | pub fn message_type(&self) -> MessageType { 5 | self.message_type 6 | } 7 | pub fn symbol_id(&self) -> u16 { 8 | self.symbol_id 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/trade_bar_first/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::MessageType; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | mod display; 5 | mod getters; 6 | mod sbe_decode; 7 | mod sbe_encode; 8 | 9 | #[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] 10 | pub struct FirstTradeBar { 11 | message_type: MessageType, 12 | symbol_id: u16, 13 | } 14 | 15 | impl FirstTradeBar { 16 | /// Creates a new FirstTradeBar instance. 17 | /// 18 | /// # Arguments 19 | /// 20 | /// * `symbol_id` - Symbol ID for the bar 21 | /// 22 | /// # Returns 23 | /// 24 | /// New FirstTradeBar instance 25 | /// 26 | /// # Remarks 27 | /// 28 | /// Sets message_type to FirstTradeBar 29 | 30 | pub fn new(symbol_id: u16) -> Self { 31 | let message_type = MessageType::FirstTradeBar; 32 | Self { 33 | message_type, 34 | symbol_id, 35 | } 36 | } 37 | } 38 | 39 | impl From<&[u8]> for FirstTradeBar { 40 | /// Decodes a FirstTradeBar message from a byte buffer. 41 | /// 42 | /// # Arguments 43 | /// 44 | /// * `value` - Byte buffer containing encoded FirstTradeBar message 45 | /// 46 | /// # Returns 47 | /// 48 | /// Decoded FirstTradeBar on success 49 | /// 50 | /// # Errors 51 | /// 52 | /// Returns Err if decoding fails 53 | /// 54 | /// # Remarks 55 | /// 56 | /// Calls sbe_decode::decode_first_data_bar_message to decode message 57 | /// 58 | #[inline] 59 | fn from(value: &[u8]) -> Self { 60 | sbe_decode::decode_first_data_bar_message(value).expect("Failed to decode FirstTradeBar") 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/trade_bar_first/sbe_decode.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{FirstTradeBar, MessageType}; 2 | use sbe_bindings::first_trade_bar_codec::SBE_TEMPLATE_ID; 3 | use sbe_bindings::{FirstTradeBarDecoder, MessageHeaderDecoder, ReadBuf, SbeResult}; 4 | 5 | /// Decodes a FirstTradeBar message from a byte buffer. 6 | /// 7 | /// # Arguments 8 | /// 9 | /// * `buffer` - Byte buffer containing encoded FirstTradeBar message 10 | /// 11 | /// # Returns 12 | /// 13 | /// Decoded FirstTradeBar on success 14 | /// 15 | /// # Errors 16 | /// 17 | /// Returns Err if decoding fails 18 | /// 19 | /// # Process 20 | /// 21 | /// - Create default FirstTradeBarDecoder 22 | /// - Wrap buffer in ReadBuf 23 | /// - Decode header and validate template ID 24 | /// - Decode and validate message_type 25 | /// - Decode symbol_id 26 | /// - Create and return FirstTradeBar 27 | /// 28 | pub fn decode_first_data_bar_message(buffer: &[u8]) -> SbeResult { 29 | let mut csg = FirstTradeBarDecoder::default(); 30 | 31 | let buf = ReadBuf::new(buffer); 32 | 33 | let header = MessageHeaderDecoder::default().wrap(buf, 0); 34 | assert_eq!(SBE_TEMPLATE_ID, header.template_id()); 35 | csg = csg.header(header); 36 | 37 | let sbe_message_type = csg.message_type(); 38 | let message_type = MessageType::from(sbe_message_type as u16); 39 | assert_eq!(message_type, MessageType::FirstTradeBar); 40 | 41 | let symbol_id = csg.symbol_id(); 42 | 43 | let message = FirstTradeBar::new(symbol_id); 44 | 45 | Ok(message) 46 | } 47 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/trade_bar_first/sbe_encode.rs: -------------------------------------------------------------------------------- 1 | use sbe_bindings::MessageType as SbeMessageType; 2 | use sbe_bindings::{message_header_codec, Encoder, FirstTradeBarEncoder, WriteBuf}; 3 | 4 | use crate::errors::SbeEncodeError; 5 | use crate::prelude::FirstTradeBar; 6 | 7 | impl FirstTradeBar { 8 | /// Encodes a FirstTradeBar message to a byte buffer. 9 | /// 10 | /// # Arguments 11 | /// 12 | /// * `self` - FirstTradeBar to encode 13 | /// 14 | /// # Returns 15 | /// 16 | /// (usize, `Vec`) - Tuple containing encoded size and byte buffer 17 | /// 18 | /// # Errors 19 | /// 20 | /// Returns Err if encoding fails 21 | /// 22 | /// # Process 23 | /// 24 | /// - Create 12 byte buffer 25 | /// - Create default FirstTradeBarEncoder 26 | /// - Wrap buffer in WriteBuf 27 | /// - Encode header 28 | /// - Encode message_type 29 | /// - Encode symbol_id 30 | /// - Return encoded size and buffer 31 | /// 32 | pub fn encode(&self) -> Result<(usize, Vec), SbeEncodeError> { 33 | let mut buffer = vec![0u8; 12]; 34 | 35 | let mut csg = FirstTradeBarEncoder::default(); 36 | 37 | csg = csg.wrap( 38 | WriteBuf::new(buffer.as_mut_slice()), 39 | message_header_codec::ENCODED_LENGTH, 40 | ); 41 | 42 | csg = csg.header(0).parent().expect("Failed to encode header"); 43 | 44 | let value = SbeMessageType::from(self.message_type as u16); 45 | csg.message_type(value); 46 | 47 | let value = self.symbol_id; 48 | csg.symbol_id(value); 49 | 50 | let limit = csg.get_limit(); 51 | Ok((limit, buffer)) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/trade_bar_last/display.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::LastTradeBar; 2 | use std::fmt; 3 | 4 | impl fmt::Display for LastTradeBar { 5 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 6 | write!( 7 | f, 8 | "LastTradeBar {{ message_type: {}, symbol_id: {} }}", 9 | self.message_type, self.symbol_id 10 | ) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/trade_bar_last/getters.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{LastTradeBar, MessageType}; 2 | 3 | impl LastTradeBar { 4 | pub fn message_type(&self) -> MessageType { 5 | self.message_type 6 | } 7 | pub fn symbol_id(&self) -> u16 { 8 | self.symbol_id 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/trade_bar_last/mod.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::MessageType; 2 | use serde::{Deserialize, Serialize}; 3 | 4 | mod display; 5 | mod getters; 6 | mod sbe_decode; 7 | mod sbe_encode; 8 | 9 | #[derive(Serialize, Deserialize, Debug, Default, Clone, PartialEq)] 10 | pub struct LastTradeBar { 11 | message_type: MessageType, 12 | symbol_id: u16, 13 | } 14 | 15 | impl LastTradeBar { 16 | /// Creates a new LastTradeBar instance. 17 | /// 18 | /// # Arguments 19 | /// 20 | /// * `symbol_id` - Symbol ID for the bar 21 | /// 22 | /// # Returns 23 | /// 24 | /// New LastTradeBar instance 25 | /// 26 | /// # Remarks 27 | /// 28 | /// Sets message_type to LastTradeBar 29 | /// 30 | pub fn new(symbol_id: u16) -> Self { 31 | let message_type = MessageType::LastTradeBar; 32 | Self { 33 | message_type, 34 | symbol_id, 35 | } 36 | } 37 | } 38 | 39 | impl From<&[u8]> for LastTradeBar { 40 | /// Decodes a LastTradeBar message from a byte buffer. 41 | /// 42 | /// # Arguments 43 | /// 44 | /// * `value` - Byte buffer containing encoded LastTradeBar message 45 | /// 46 | /// # Returns 47 | /// 48 | /// Decoded LastTradeBar on success 49 | /// 50 | /// # Errors 51 | /// 52 | /// Returns Err if decoding fails 53 | /// 54 | /// # Remarks 55 | /// 56 | /// Calls sbe_decode::decode_last_trade_bar_message to decode message 57 | /// 58 | #[inline] 59 | fn from(value: &[u8]) -> Self { 60 | sbe_decode::decode_last_trade_bar_message(value) 61 | .expect("Failed to decode LastTradeBar message") 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/trade_bar_last/sbe_decode.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{LastTradeBar, MessageType}; 2 | use sbe_bindings::last_trade_bar_codec::SBE_TEMPLATE_ID; 3 | use sbe_bindings::{LastTradeBarDecoder, MessageHeaderDecoder, ReadBuf, SbeResult}; 4 | 5 | /// Decodes a LastTradeBar message from a byte buffer. 6 | /// 7 | /// # Arguments 8 | /// 9 | /// * `buffer` - Byte buffer containing encoded LastTradeBar message 10 | /// 11 | /// # Returns 12 | /// 13 | /// Decoded LastTradeBar on success 14 | /// 15 | /// # Errors 16 | /// 17 | /// Returns Err if decoding fails 18 | /// 19 | /// # Process 20 | /// 21 | /// - Create default LastTradeBarDecoder 22 | /// - Wrap buffer in ReadBuf 23 | /// - Decode header and validate template ID 24 | /// - Decode and validate message_type 25 | /// - Decode symbol_id 26 | /// - Create and return LastTradeBar 27 | /// 28 | pub fn decode_last_trade_bar_message(buffer: &[u8]) -> SbeResult { 29 | let mut csg = LastTradeBarDecoder::default(); 30 | let buf = ReadBuf::new(buffer); 31 | 32 | let header = MessageHeaderDecoder::default().wrap(buf, 0); 33 | assert_eq!(SBE_TEMPLATE_ID, header.template_id()); 34 | csg = csg.header(header); 35 | 36 | let sbe_message_type = csg.message_type(); 37 | let message_type = MessageType::from(sbe_message_type as u16); 38 | assert_eq!(message_type, MessageType::LastTradeBar); 39 | 40 | let symbol_id = csg.symbol_id(); 41 | 42 | let message = LastTradeBar::new(symbol_id); 43 | 44 | Ok(message) 45 | } 46 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/data_messages/trade_bar_last/sbe_encode.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::SbeEncodeError; 2 | use crate::prelude::LastTradeBar; 3 | use sbe_bindings::MessageType as SbeMessageType; 4 | use sbe_bindings::{message_header_codec, Encoder, LastTradeBarEncoder, WriteBuf}; 5 | 6 | impl LastTradeBar { 7 | /// Encodes a LastTradeBar message to a byte buffer. 8 | /// 9 | /// # Arguments 10 | /// 11 | /// * `self` - LastTradeBar to encode 12 | /// 13 | /// # Returns 14 | /// 15 | /// (usize, `Vec`) - Tuple containing encoded size and byte buffer 16 | /// 17 | /// # Errors 18 | /// 19 | /// Returns Err if encoding fails 20 | /// 21 | /// # Process 22 | /// 23 | /// - Create 12 byte buffer 24 | /// - Create default LastTradeBarEncoder 25 | /// - Wrap buffer in WriteBuf 26 | /// - Encode header 27 | /// - Encode message_type 28 | /// - Encode symbol_id 29 | /// - Return encoded size and buffer 30 | /// 31 | pub fn encode(&self) -> Result<(usize, Vec), SbeEncodeError> { 32 | let mut buffer = vec![0u8; 12]; 33 | 34 | let mut csg = LastTradeBarEncoder::default(); 35 | 36 | csg = csg.wrap( 37 | WriteBuf::new(buffer.as_mut_slice()), 38 | message_header_codec::ENCODED_LENGTH, 39 | ); 40 | 41 | csg = csg.header(0).parent().expect("Failed to encode header"); 42 | 43 | let value = SbeMessageType::from(self.message_type as u16); 44 | csg.message_type(value); 45 | 46 | let value = self.symbol_id; 47 | csg.symbol_id(value); 48 | 49 | let limit = csg.get_limit(); 50 | Ok((limit, buffer)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/error_messages/client_error_message/display.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::ClientErrorMessage; 2 | use std::fmt; 3 | 4 | impl fmt::Display for ClientErrorMessage { 5 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 6 | write!( 7 | f, 8 | "ClientErrorMessage {{ message_type: {:?}, client_id: {}, client_error_type: {:?} }}", 9 | self.message_type, self.client_id, self.client_error_type 10 | ) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/error_messages/client_error_message/getters.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{ClientErrorMessage, ClientErrorType, MessageType}; 2 | 3 | impl ClientErrorMessage { 4 | pub fn message_type(&self) -> MessageType { 5 | self.message_type 6 | } 7 | pub fn client_id(&self) -> u16 { 8 | self.client_id 9 | } 10 | pub fn client_error_type(&self) -> ClientErrorType { 11 | self.client_error_type 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/error_messages/client_error_message/sbe_decode.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{ClientErrorMessage, ClientErrorType, MessageType}; 2 | use sbe_bindings::client_error_codec::SBE_TEMPLATE_ID; 3 | use sbe_bindings::{ClientErrorDecoder, MessageHeaderDecoder, ReadBuf, SbeResult}; 4 | 5 | /// Decodes a ClientErrorMessage from a byte buffer. 6 | /// 7 | /// # Arguments 8 | /// 9 | /// * `buffer` - Byte buffer containing encoded ClientErrorMessage 10 | /// 11 | /// # Returns 12 | /// 13 | /// Decoded ClientErrorMessage on success 14 | /// 15 | /// # Errors 16 | /// 17 | /// Returns Err if decoding fails 18 | /// 19 | /// # Process 20 | /// 21 | /// - Create default ClientErrorDecoder 22 | /// - Wrap buffer in ReadBuf 23 | /// - Decode header and validate template ID 24 | /// - Decode and validate message_type 25 | /// - Decode client_id 26 | /// - Decode and validate client_error_type 27 | /// - Create and return ClientErrorMessage 28 | /// 29 | pub fn decode_client_error_message(buffer: &[u8]) -> SbeResult { 30 | let mut csg = ClientErrorDecoder::default(); 31 | let buf = ReadBuf::new(buffer); 32 | 33 | let header = MessageHeaderDecoder::default().wrap(buf, 0); 34 | assert_eq!(SBE_TEMPLATE_ID, header.template_id()); 35 | csg = csg.header(header); 36 | 37 | let sbe_message_type = csg.message_type(); 38 | let message_type = MessageType::from(sbe_message_type as u16); 39 | assert_eq!(message_type, MessageType::ClientError); 40 | 41 | let client_id = csg.client_id(); 42 | let client_error_type_raw = csg 43 | .client_error_type() 44 | .expect("Failed to decode client error type"); 45 | let client_error_type = ClientErrorType::from(client_error_type_raw); 46 | 47 | let message = ClientErrorMessage::new(client_id, client_error_type); 48 | 49 | Ok(message) 50 | } 51 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/error_messages/client_error_message/sbe_encode.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::SbeEncodeError; 2 | use crate::prelude::ClientErrorMessage; 3 | use sbe_bindings::MessageType as SbeMessageType; 4 | use sbe_bindings::{message_header_codec, ClientErrorEncoder, Encoder, WriteBuf}; 5 | 6 | /// Encodes a ClientErrorMessage to a byte buffer. 7 | /// 8 | /// # Arguments 9 | /// 10 | /// * `self` - ClientErrorMessage to encode 11 | /// 12 | /// # Returns 13 | /// 14 | /// (usize, `Vec`) - Tuple containing encoded size and byte buffer 15 | /// 16 | /// # Errors 17 | /// 18 | /// Returns Err if encoding fails 19 | /// 20 | /// # Process 21 | /// 22 | /// - Create 13 byte buffer 23 | /// - Create default ClientErrorEncoder 24 | /// - Wrap buffer in WriteBuf 25 | /// - Encode header 26 | /// - Encode message_type 27 | /// - Encode client_id 28 | /// - Encode client_error_type 29 | /// - Return encoded size and buffer 30 | /// 31 | impl ClientErrorMessage { 32 | pub fn encode(&self) -> Result<(usize, Vec), SbeEncodeError> { 33 | let mut buffer = vec![0u8; 13]; 34 | 35 | let mut csg = ClientErrorEncoder::default(); 36 | 37 | csg = csg.wrap( 38 | WriteBuf::new(buffer.as_mut_slice()), 39 | message_header_codec::ENCODED_LENGTH, 40 | ); 41 | 42 | csg = csg.header(0).parent().expect("Failed to encode header"); 43 | 44 | let value = SbeMessageType::from(self.message_type as u16); 45 | csg.message_type(value); 46 | 47 | let value = self.client_id; 48 | csg.client_id(value); 49 | 50 | let value = self.client_error_type as u8; 51 | csg.client_error_type(value); 52 | 53 | let limit = csg.get_limit(); 54 | Ok((limit, buffer)) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/error_messages/data_error_message/display.rs: -------------------------------------------------------------------------------- 1 | use crate::messages::error_messages::data_error_message::DataErrorMessage; 2 | use std::fmt::{Display, Formatter}; 3 | 4 | impl Display for DataErrorMessage { 5 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { 6 | write!( 7 | f, 8 | "DataErrorMessage {{ message_type: {:?}, client_id: {}, data_error_type: {:?} }}", 9 | self.message_type, self.client_id, self.data_error_type 10 | ) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/error_messages/data_error_message/getters.rs: -------------------------------------------------------------------------------- 1 | use crate::messages::error_messages::data_error_message::DataErrorMessage; 2 | use crate::prelude::{DataErrorType, MessageType}; 3 | 4 | impl DataErrorMessage { 5 | pub fn message_type(&self) -> MessageType { 6 | self.message_type 7 | } 8 | pub fn client_id(&self) -> u16 { 9 | self.client_id 10 | } 11 | pub fn data_error_type(&self) -> DataErrorType { 12 | self.data_error_type 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/error_messages/data_error_message/sbe_decode.rs: -------------------------------------------------------------------------------- 1 | use crate::prelude::{DataErrorMessage, DataErrorType, MessageType}; 2 | use sbe_bindings::data_error_codec::SBE_TEMPLATE_ID; 3 | use sbe_bindings::{DataErrorDecoder, MessageHeaderDecoder, ReadBuf, SbeResult}; 4 | 5 | /// Decodes a DataErrorMessage from a byte buffer. 6 | /// 7 | /// # Arguments 8 | /// 9 | /// * `buffer` - Byte buffer containing encoded DataErrorMessage 10 | /// 11 | /// # Returns 12 | /// 13 | /// Decoded DataErrorMessage on success 14 | /// 15 | /// # Errors 16 | /// 17 | /// Returns Err if decoding fails 18 | /// 19 | /// # Process 20 | /// 21 | /// - Create default DataErrorDecoder 22 | /// - Wrap buffer in ReadBuf 23 | /// - Decode header and validate template ID 24 | /// - Decode and validate message_type 25 | /// - Decode client_id 26 | /// - Decode and validate data_error_type 27 | /// - Create and return DataErrorMessage 28 | /// 29 | pub fn decode_client_error_message(buffer: &[u8]) -> SbeResult { 30 | let mut csg = DataErrorDecoder::default(); 31 | let buf = ReadBuf::new(buffer); 32 | 33 | let header = MessageHeaderDecoder::default().wrap(buf, 0); 34 | assert_eq!(SBE_TEMPLATE_ID, header.template_id()); 35 | 36 | csg = csg.header(header); 37 | 38 | let sbe_message_type = csg.message_type(); 39 | let message_type = MessageType::from(sbe_message_type as u16); 40 | assert_eq!(message_type, MessageType::DataError); 41 | 42 | let client_id = csg.client_id(); 43 | let data_error_type_raw = csg 44 | .data_error_type() 45 | .expect("Failed to decode client error type"); 46 | 47 | let data_error_type = DataErrorType::from(data_error_type_raw); 48 | 49 | let message = DataErrorMessage::new(client_id, data_error_type); 50 | 51 | Ok(message) 52 | } 53 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/error_messages/data_error_message/sbe_encode.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::SbeEncodeError; 2 | use crate::prelude::DataErrorMessage; 3 | use sbe_bindings::MessageType as SbeMessageType; 4 | use sbe_bindings::{message_header_codec, DataErrorEncoder, Encoder, WriteBuf}; 5 | 6 | impl DataErrorMessage { 7 | /// Encodes a DataErrorMessage to a byte buffer. 8 | /// 9 | /// # Arguments 10 | /// 11 | /// * `self` - DataErrorMessage to encode 12 | /// 13 | /// # Returns 14 | /// 15 | /// (usize, `Vec`) - Tuple containing encoded size and byte buffer 16 | /// 17 | /// # Errors 18 | /// 19 | /// Returns Err if encoding fails 20 | /// 21 | /// # Process 22 | /// 23 | /// - Create 13 byte buffer 24 | /// - Create default DataErrorEncoder 25 | /// - Wrap buffer in WriteBuf 26 | /// - Encode header 27 | /// - Encode message_type 28 | /// - Encode client_id 29 | /// - Encode data_error_type 30 | /// - Return encoded size and buffer 31 | /// 32 | pub fn encode(&self) -> Result<(usize, Vec), SbeEncodeError> { 33 | let mut buffer = vec![0u8; 13]; 34 | 35 | let mut csg = DataErrorEncoder::default(); 36 | 37 | csg = csg.wrap( 38 | WriteBuf::new(buffer.as_mut_slice()), 39 | message_header_codec::ENCODED_LENGTH, 40 | ); 41 | 42 | csg = csg.header(0).parent().expect("Failed to encode header"); 43 | 44 | let value = SbeMessageType::from(self.message_type as u16); 45 | csg.message_type(value); 46 | 47 | let value = self.client_id; 48 | csg.client_id(value); 49 | 50 | let value = self.data_error_type as u8; 51 | csg.data_error_type(value); 52 | 53 | let limit = csg.get_limit(); 54 | Ok((limit, buffer)) 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/error_messages/mod.rs: -------------------------------------------------------------------------------- 1 | /// Module containing error response messages. 2 | /// 3 | /// This includes messages like: 4 | /// 5 | /// - ClientErrorMessage 6 | /// - DataErrorMessage 7 | /// 8 | /// 9 | /// The error messages are exposed in the prelude for convenient importing. 10 | /// 11 | /// # Exports 12 | /// 13 | /// - `client_error_message` - ClientErrorMessage 14 | /// - `data_error_message` - DataErrorMessage 15 | /// 16 | pub mod client_error_message; 17 | pub mod data_error_message; 18 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/messages/mod.rs: -------------------------------------------------------------------------------- 1 | //! Module containing SBE message definitions. 2 | //! 3 | //! This includes modules grouping related message types: 4 | //! 5 | //! - `client_messages` - Messages from clients 6 | //! - `data_messages` - Data subscription messages 7 | //! - `error_messages` - Error response messages 8 | //! 9 | //! Grouping messages into modules provides organization and encapsulation. 10 | //! 11 | //! The messages are exposed in the prelude for convenient importing. 12 | //! 13 | pub mod client_messages; 14 | pub mod data_messages; 15 | pub mod error_messages; 16 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/prelude.rs: -------------------------------------------------------------------------------- 1 | // 2 | pub use crate::errors::*; 3 | // Message types 4 | pub use crate::types::client_error_types::ClientErrorType; 5 | pub use crate::types::data_error_types::DataErrorType; 6 | pub use crate::types::data_type::DataType; 7 | pub use crate::types::message_types::MessageType; 8 | // Client messages 9 | pub use crate::messages::client_messages::client_login::ClientLoginMessage; 10 | pub use crate::messages::client_messages::client_logout::ClientLogoutMessage; 11 | // Data messages 12 | pub use crate::messages::data_messages::ohlcv_bar::SbeOHLCVBar; 13 | pub use crate::messages::data_messages::ohlcv_bar_first::FirstOHLCVBar; 14 | pub use crate::messages::data_messages::ohlcv_bar_last::LastOHLCVBar; 15 | pub use crate::messages::data_messages::start_data::StartDataMessage; 16 | pub use crate::messages::data_messages::stop_all_data::StopAllDataMessage; 17 | pub use crate::messages::data_messages::stop_data::StopDataMessage; 18 | pub use crate::messages::data_messages::trade_bar::SbeTradeBar; 19 | pub use crate::messages::data_messages::trade_bar_first::FirstTradeBar; 20 | pub use crate::messages::data_messages::trade_bar_last::LastTradeBar; 21 | // Error messages 22 | pub use crate::messages::error_messages::client_error_message::ClientErrorMessage; 23 | pub use crate::messages::error_messages::data_error_message::DataErrorMessage; 24 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/src/types/mod.rs: -------------------------------------------------------------------------------- 1 | /// Module containing common data types used across SBE messages. 2 | /// 3 | /// This includes enums representing: 4 | /// 5 | /// - Data types like trades, OHLCV etc. 6 | /// - Data errors for requests 7 | /// - Exchange identifiers 8 | /// - Message types 9 | /// 10 | /// Grouping these common types into a module avoids duplication 11 | /// and provides a single source of truth for type definitions. 12 | /// 13 | /// The types are exposed in the prelude so they can be conveniently 14 | /// imported together via `use sbe_messages::prelude::*`. 15 | /// 16 | /// # Exports 17 | /// 18 | /// - `data_type` - Enumeration of data types 19 | /// - `data_error_types` - Enumeration of data error types 20 | /// - `exchange_id` - Enumeration of exchange identifiers 21 | /// - `message_type` - Enumeration of message types 22 | /// 23 | pub mod client_error_types; 24 | pub mod data_error_types; 25 | pub mod data_type; 26 | pub mod message_types; 27 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/errors/errors_tests.rs: -------------------------------------------------------------------------------- 1 | use sbe_messages::prelude::{SbeDecodeError, SbeEncodeError}; 2 | 3 | #[test] 4 | fn test_sbe_encode_error_display() { 5 | let error = SbeEncodeError("test error".to_string()); 6 | assert_eq!(error.to_string(), "SbeEncodeError: test error"); 7 | } 8 | 9 | #[test] 10 | fn test_sbe_decode_error_display() { 11 | let error = SbeDecodeError("test error".to_string()); 12 | assert_eq!(error.to_string(), "SbeDecodeError: test error"); 13 | } 14 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/errors/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod errors_tests; 3 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/client_messages/client_login/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod client_login_message_tests; 3 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/client_messages/client_logout/mod.rs: -------------------------------------------------------------------------------- 1 | mod client_logout_message_tests; 2 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/client_messages/mod.rs: -------------------------------------------------------------------------------- 1 | mod client_login; 2 | mod client_logout; 3 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/data_messages/mod.rs: -------------------------------------------------------------------------------- 1 | mod ohlcv_bar; 2 | mod ohlcv_bar_first; 3 | mod ohlcv_bar_last; 4 | mod start_data; 5 | mod stop_all_data; 6 | mod stop_data; 7 | mod trade_bar; 8 | mod trade_bar_first; 9 | mod trade_bar_last; 10 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/data_messages/ohlcv_bar/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod ohlcv_bar_tests; 3 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/data_messages/ohlcv_bar/ohlcv_bar_tests.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::OHLCVBar; 2 | use sbe_messages::prelude::SbeOHLCVBar; 3 | 4 | #[test] 5 | fn test_encode_data_bar_message() { 6 | let bar = OHLCVBar::default(); // Create a sample DataBar 7 | 8 | let result = SbeOHLCVBar::encode(bar); 9 | 10 | assert!(result.is_ok()); // Assert encode passes 11 | 12 | let (size, encoded) = result.unwrap(); 13 | assert_eq!(size, 40); // Assert encoded message size matches expected 14 | assert!(!encoded.is_empty()); // Assert non-empty encoded message 15 | } 16 | 17 | #[test] 18 | fn test_decode_data_bar_message() { 19 | // Encode a sample DataBar 20 | let bar = OHLCVBar::default(); 21 | let (size, encoded) = SbeOHLCVBar::encode(bar.clone()).unwrap(); 22 | assert_eq!(size, 40); // Assert encoded message size matches expected 23 | assert!(!encoded.is_empty()); // Assert non-empty encoded message 24 | 25 | // Decode the encoded message 26 | let result = SbeOHLCVBar::decode(&encoded); 27 | 28 | assert!(result.is_ok()); // Assert decode passes 29 | 30 | let decoded_bar = result.unwrap(); 31 | let original_bar = bar.clone(); 32 | 33 | // Compare decoded bar with original bar field by field 34 | // Timestamp seems to have a loss of precision during encoding/decoding 35 | assert_eq!( 36 | decoded_bar.date_time().to_rfc2822(), 37 | original_bar.date_time().to_rfc2822() 38 | ); 39 | assert_eq!(decoded_bar.open(), original_bar.open()); 40 | assert_eq!(decoded_bar.high(), original_bar.high()); 41 | assert_eq!(decoded_bar.low(), original_bar.low()); 42 | assert_eq!(decoded_bar.close(), original_bar.close()); 43 | assert_eq!(decoded_bar.volume(), original_bar.volume()); 44 | } 45 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/data_messages/ohlcv_bar_first/mod.rs: -------------------------------------------------------------------------------- 1 | mod ohlcv_bar_first_tests; 2 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/data_messages/ohlcv_bar_first/ohlcv_bar_first_tests.rs: -------------------------------------------------------------------------------- 1 | use sbe_messages::prelude::{FirstOHLCVBar, MessageType}; 2 | 3 | #[test] 4 | fn test_new() { 5 | let bar = FirstOHLCVBar::new(42); 6 | 7 | assert_eq!(bar.message_type(), MessageType::FirstOHLCVBar); 8 | } 9 | #[test] 10 | fn test_message_type() { 11 | let bar = FirstOHLCVBar::new(42); 12 | 13 | assert_eq!(bar.message_type(), MessageType::FirstOHLCVBar); 14 | } 15 | 16 | #[test] 17 | fn test_encode() { 18 | let bar = FirstOHLCVBar::new(42); 19 | 20 | assert_eq!(bar.message_type(), MessageType::FirstOHLCVBar); 21 | 22 | let enc = bar.encode(); 23 | assert!(enc.is_ok()); 24 | 25 | let (limit, buffer) = enc.unwrap(); 26 | assert_eq!(limit, 12); 27 | 28 | let expected: Vec = vec![4, 0, 205, 0, 1, 0, 1, 0, 205, 0, 42, 0]; 29 | let actual = buffer; 30 | 31 | assert_eq!(expected, actual); 32 | } 33 | 34 | #[test] 35 | fn test_decode() { 36 | let encoded: Vec = vec![4, 0, 205, 0, 1, 0, 1, 0, 205, 0, 42, 0]; 37 | let buffer = encoded.as_slice(); 38 | 39 | let message = FirstOHLCVBar::from(buffer); 40 | assert_eq!(message.message_type(), MessageType::FirstOHLCVBar); 41 | } 42 | 43 | #[test] 44 | fn test_display() { 45 | let bar = FirstOHLCVBar::new(42); 46 | 47 | let expected = "FirstOHLCVBar { message_type: FirstOHLCVBar, symbol_id: 42 }"; 48 | let actual = format!("{}", bar); 49 | 50 | assert_eq!(expected, actual); 51 | } 52 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/data_messages/ohlcv_bar_last/mod.rs: -------------------------------------------------------------------------------- 1 | mod ohlcv_bar_last_tests; 2 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/data_messages/ohlcv_bar_last/ohlcv_bar_last_tests.rs: -------------------------------------------------------------------------------- 1 | use sbe_messages::prelude::{LastOHLCVBar, MessageType}; 2 | 3 | #[test] 4 | fn test_new() { 5 | let bar = LastOHLCVBar::new(42); 6 | 7 | assert_eq!(bar.message_type(), MessageType::LastOHLCVBar); 8 | } 9 | 10 | #[test] 11 | fn test_message_type() { 12 | let bar = LastOHLCVBar::new(42); 13 | 14 | assert_eq!(bar.message_type(), MessageType::LastOHLCVBar); 15 | } 16 | 17 | #[test] 18 | fn test_encode() { 19 | let message = LastOHLCVBar::new(42); 20 | assert_eq!(message.message_type(), MessageType::LastOHLCVBar); 21 | 22 | let enc = message.encode(); 23 | assert!(enc.is_ok()); 24 | 25 | let (limit, buffer) = enc.unwrap(); 26 | assert_eq!(limit, 12); 27 | 28 | let expected: Vec = vec![4, 0, 206, 0, 1, 0, 1, 0, 206, 0, 42, 0]; 29 | let actual = buffer; 30 | 31 | assert_eq!(expected, actual); 32 | } 33 | 34 | #[test] 35 | fn test_decode() { 36 | let encoded: Vec = vec![4, 0, 206, 0, 1, 0, 1, 0, 206, 0, 42, 0]; 37 | let buffer = encoded.as_slice(); 38 | 39 | let message = LastOHLCVBar::from(buffer); 40 | assert_eq!(message.message_type(), MessageType::LastOHLCVBar); 41 | } 42 | 43 | #[test] 44 | fn test_display() { 45 | let bar = LastOHLCVBar::new(42); 46 | 47 | let expected = "LastOHLCVBar { message_type: LastOHLCVBar, symbol_id: 42 }"; 48 | let actual = format!("{}", bar); 49 | 50 | assert_eq!(expected, actual); 51 | } 52 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/data_messages/start_data/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod start_data_message_tests; 3 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/data_messages/stop_all_data/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod stop_all_data_message_tests; 3 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/data_messages/stop_data/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod stop_data_message_tests; 3 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/data_messages/trade_bar/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod trade_bar_tests; 3 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/data_messages/trade_bar_first/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod trade_bar_first_tests; 3 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/data_messages/trade_bar_first/trade_bar_first_tests.rs: -------------------------------------------------------------------------------- 1 | use sbe_messages::prelude::{FirstTradeBar, MessageType}; 2 | 3 | #[test] 4 | fn test_new() { 5 | let bar = FirstTradeBar::new(42); 6 | 7 | assert_eq!(bar.message_type(), MessageType::FirstTradeBar); 8 | } 9 | 10 | #[test] 11 | fn test_message_type() { 12 | let bar = FirstTradeBar::new(42); 13 | assert_eq!(bar.message_type(), MessageType::FirstTradeBar); 14 | } 15 | 16 | #[test] 17 | fn test_symbol_id() { 18 | let bar = FirstTradeBar::new(123); 19 | assert_eq!(bar.symbol_id(), 123); 20 | } 21 | 22 | #[test] 23 | fn test_encode() { 24 | let bar = FirstTradeBar::new(123); 25 | assert_eq!(bar.message_type(), MessageType::FirstTradeBar); 26 | assert_eq!(bar.symbol_id(), 123); 27 | 28 | let enc = bar.encode(); 29 | assert!(enc.is_ok()); 30 | 31 | let (limit, buffer) = enc.unwrap(); 32 | assert_eq!(limit, 12); 33 | 34 | let expected: Vec = vec![4, 0, 208, 0, 1, 0, 1, 0, 208, 0, 123, 0]; 35 | let actual = buffer; 36 | 37 | assert_eq!(expected, actual); 38 | } 39 | 40 | #[test] 41 | fn test_decode() { 42 | let encoded: Vec = vec![4, 0, 208, 0, 1, 0, 1, 0, 208, 0, 123, 0]; 43 | let buffer = encoded.as_slice(); 44 | 45 | let message = FirstTradeBar::from(buffer); 46 | assert_eq!(message.message_type(), MessageType::FirstTradeBar); 47 | } 48 | 49 | #[test] 50 | fn test_display() { 51 | let bar = FirstTradeBar::new(123); 52 | 53 | let expected = "FirstTradeBar { message_type: FirstTradeBar, symbol_id: 123 }"; 54 | 55 | let actual = format!("{}", bar); 56 | 57 | assert_eq!(actual, expected); 58 | } 59 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/data_messages/trade_bar_last/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod trade_bar_last_tests; 3 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/data_messages/trade_bar_last/trade_bar_last_tests.rs: -------------------------------------------------------------------------------- 1 | use sbe_messages::prelude::{LastTradeBar, MessageType}; 2 | 3 | #[test] 4 | fn test_new() { 5 | let bar = LastTradeBar::new(23); 6 | assert_eq!(bar.message_type(), MessageType::LastTradeBar); 7 | } 8 | 9 | #[test] 10 | fn test_message_type() { 11 | let bar = LastTradeBar::new(23); 12 | assert_eq!(bar.message_type(), MessageType::LastTradeBar); 13 | } 14 | 15 | #[test] 16 | fn test_symbol_id() { 17 | let bar = LastTradeBar::new(123); 18 | assert_eq!(bar.symbol_id(), 123); 19 | } 20 | 21 | #[test] 22 | fn test_encode() { 23 | let bar = LastTradeBar::new(23); 24 | assert_eq!(bar.message_type(), MessageType::LastTradeBar); 25 | 26 | let enc = bar.encode(); 27 | assert!(enc.is_ok()); 28 | 29 | let (limit, buffer) = enc.unwrap(); 30 | assert_eq!(limit, 12); 31 | 32 | let expected: Vec = vec![4, 0, 209, 0, 1, 0, 1, 0, 209, 0, 23, 0]; 33 | let actual = buffer; 34 | 35 | assert_eq!(expected, actual); 36 | } 37 | 38 | #[test] 39 | fn test_decode() { 40 | let encoded: Vec = vec![4, 0, 209, 0, 1, 0, 1, 0, 209, 0, 23, 0]; 41 | let buffer = encoded.as_slice(); 42 | 43 | let message = LastTradeBar::from(buffer); 44 | assert_eq!(message.message_type(), MessageType::LastTradeBar); 45 | assert_eq!(message.symbol_id(), 23); 46 | } 47 | 48 | #[test] 49 | fn test_fmt() { 50 | let bar = LastTradeBar::new(123); 51 | 52 | let expected = "LastTradeBar { message_type: LastTradeBar, symbol_id: 123 }"; 53 | let actual = format!("{}", bar); 54 | 55 | assert_eq!(actual, expected); 56 | } 57 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/error_messages/client_error_message/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod client_error_message_tests; 3 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/error_messages/data_error_message/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod data_error_message_tests; 3 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/error_messages/mod.rs: -------------------------------------------------------------------------------- 1 | mod client_error_message; 2 | mod data_error_message; 3 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/messages/mod.rs: -------------------------------------------------------------------------------- 1 | mod client_messages; 2 | mod data_messages; 3 | mod error_messages; 4 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod errors; 2 | mod messages; 3 | mod types; 4 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/types/client_error_types_tests.rs: -------------------------------------------------------------------------------- 1 | use sbe_messages::prelude::ClientErrorType; 2 | 3 | #[test] 4 | fn test_from_u8() { 5 | assert_eq!( 6 | ClientErrorType::from(0), 7 | ClientErrorType::UnknownClientError 8 | ); 9 | assert_eq!( 10 | ClientErrorType::from(1), 11 | ClientErrorType::ClientAlreadyLoggedIn 12 | ); 13 | assert_eq!(ClientErrorType::from(2), ClientErrorType::ClientLogInError); 14 | assert_eq!(ClientErrorType::from(3), ClientErrorType::ClientNotLoggedIn); 15 | assert_eq!(ClientErrorType::from(4), ClientErrorType::ClientLogOutError); 16 | assert_eq!( 17 | ClientErrorType::from(5), 18 | ClientErrorType::UnknownClientError 19 | ); 20 | } 21 | 22 | #[test] 23 | fn test_display() { 24 | let error = ClientErrorType::UnknownClientError; 25 | let expected = "UnknownClientError"; 26 | let actual = format!("{}", error); 27 | assert_eq!(expected, actual); 28 | 29 | let error = ClientErrorType::ClientAlreadyLoggedIn; 30 | let expected = "ClientAlreadyLoggedIn"; 31 | let actual = format!("{}", error); 32 | assert_eq!(expected, actual); 33 | } 34 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/types/data_error_types_tests.rs: -------------------------------------------------------------------------------- 1 | use sbe_messages::prelude::DataErrorType; 2 | 3 | #[test] 4 | fn test_from_u8() { 5 | assert_eq!(DataErrorType::from(0), DataErrorType::UnknownDataError); 6 | assert_eq!(DataErrorType::from(1), DataErrorType::DataTypeNotKnownError); 7 | assert_eq!(DataErrorType::from(2), DataErrorType::DataUnavailableError); 8 | assert_eq!(DataErrorType::from(3), DataErrorType::DataEncodingError); 9 | assert_eq!(DataErrorType::from(4), DataErrorType::DataTableNotFound); 10 | assert_eq!(DataErrorType::from(5), DataErrorType::DataSendError); 11 | assert_eq!(DataErrorType::from(6), DataErrorType::DataChannelError); 12 | assert_eq!(DataErrorType::from(7), DataErrorType::UnknownDataError); 13 | } 14 | 15 | #[test] 16 | fn test_display() { 17 | let error = DataErrorType::UnknownDataError; 18 | let expected = "UnknownDataError"; 19 | let actual = format!("{}", error); 20 | assert_eq!(expected, actual); 21 | 22 | let error = DataErrorType::DataTypeNotKnownError; 23 | let expected = "DataTypeNotKnownError"; 24 | let actual = format!("{}", error); 25 | assert_eq!(expected, actual); 26 | } 27 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/types/data_type_tests.rs: -------------------------------------------------------------------------------- 1 | use sbe_messages::prelude::DataType; 2 | 3 | #[test] 4 | fn test_from_u8() { 5 | assert_eq!(DataType::from(0), DataType::UnknownDataType); 6 | assert_eq!(DataType::from(1), DataType::TradeData); 7 | assert_eq!(DataType::from(2), DataType::OHLCVData); 8 | // assert_eq!(DataType::from(3), DataType::OrderBookData); 9 | // assert_eq!(DataType::from(4), DataType::QuoteData); 10 | assert_eq!(DataType::from(5), DataType::UnknownDataType); 11 | } 12 | 13 | #[test] 14 | fn test_display() { 15 | assert_eq!(format!("{}", DataType::UnknownDataType), "UnknownDataType"); 16 | assert_eq!(format!("{}", DataType::TradeData), "TradeData"); 17 | assert_eq!(format!("{}", DataType::OHLCVData), "OHLCVData"); 18 | // assert_eq!(format!("{}", DataType::OrderBookData), "OrderBookData"); 19 | // assert_eq!(format!("{}", DataType::QuoteData), "QuoteData"); 20 | } 21 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/types/message_types_tests.rs: -------------------------------------------------------------------------------- 1 | use sbe_messages::prelude::MessageType; 2 | 3 | #[test] 4 | fn test_from_u16() { 5 | assert_eq!(MessageType::from(0_u16), MessageType::UnknownMessageType); 6 | assert_eq!(MessageType::from(101_u16), MessageType::ClientLogin); 7 | assert_eq!(MessageType::from(102_u16), MessageType::ClientLogout); 8 | assert_eq!(MessageType::from(201_u16), MessageType::StartData); 9 | assert_eq!(MessageType::from(202_u16), MessageType::StopData); 10 | assert_eq!(MessageType::from(203_u16), MessageType::StopAllData); 11 | assert_eq!(MessageType::from(204_u16), MessageType::OHLCVBar); 12 | assert_eq!(MessageType::from(205_u16), MessageType::FirstOHLCVBar); 13 | assert_eq!(MessageType::from(206_u16), MessageType::LastOHLCVBar); 14 | assert_eq!(MessageType::from(801_u16), MessageType::ClientError); 15 | assert_eq!(MessageType::from(999_u16), MessageType::UnknownMessageType); 16 | } 17 | #[test] 18 | fn test_display() { 19 | let message_type = MessageType::ClientLogin; 20 | assert_eq!(format!("{}", message_type), "ClientLogin"); 21 | 22 | let message_type = MessageType::ClientLogout; 23 | assert_eq!(format!("{}", message_type), "ClientLogout"); 24 | 25 | let message_type = MessageType::StartData; 26 | assert_eq!(format!("{}", message_type), "StartData"); 27 | 28 | let message_type = MessageType::StopData; 29 | assert_eq!(format!("{}", message_type), "StopData"); 30 | 31 | let message_type = MessageType::StopAllData; 32 | assert_eq!(format!("{}", message_type), "StopAllData"); 33 | 34 | let message_type = MessageType::UnknownMessageType; 35 | assert_eq!(format!("{}", message_type), "UnknownMessageType"); 36 | } 37 | -------------------------------------------------------------------------------- /flv_sbe/sbe_messages/tests/types/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod client_error_types_tests; 3 | #[cfg(test)] 4 | mod data_error_types_tests; 5 | #[cfg(test)] 6 | mod data_type_tests; 7 | #[cfg(test)] 8 | mod message_types_tests; 9 | -------------------------------------------------------------------------------- /flv_sbe/schema.sbeir: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepcausality-rs/fluvio-examples/88d90531c0b1e210bcac825edc78058596cfeb17/flv_sbe/schema.sbeir -------------------------------------------------------------------------------- /flv_services/qdgw/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "qdgw" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [[bin]] 12 | name = "qdgw" 13 | path = "src/main.rs" 14 | 15 | 16 | [dependencies] 17 | # Internal crates 18 | common = {workspace = true} 19 | config_manager = {workspace = true} 20 | db_query_manager = {workspace = true} 21 | iggy_utils = {workspace = true} 22 | symbol_manager = {workspace = true} 23 | sbe_messages = {workspace = true} 24 | service_utils = {workspace = true} 25 | # External crates 26 | autometrics = { workspace = true } 27 | futures = { workspace = true } 28 | iggy = { workspace = true } 29 | tokio = { workspace = true } 30 | warp = { workspace = true } 31 | -------------------------------------------------------------------------------- /flv_services/qdgw/src/service/handle/handle_client_utils.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::MessageProcessingError; 2 | 3 | use crate::service::Server; 4 | 5 | impl Server { 6 | /// Checks if a client with the given ID is logged in. 7 | /// 8 | /// Locks the client manager mutex and checks if the client ID exists. 9 | /// 10 | /// # Parameters 11 | /// 12 | /// - `client_id`: The ID of the client to check 13 | /// 14 | /// # Returns 15 | /// 16 | /// A `Result` with a `bool` indicating whether the client is logged in, or a 17 | /// `MessageProcessingError` if there was an issue checking the client status. 18 | /// 19 | pub(crate) async fn check_client_login( 20 | &self, 21 | client_id: u16, 22 | ) -> Result { 23 | let client_db = self.client_producers().read().await; 24 | 25 | Ok(client_db.contains_key(&client_id)) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /flv_services/qdgw/src/service/handle/handle_data_stop.rs: -------------------------------------------------------------------------------- 1 | use crate::service::Server; 2 | use autometrics::autometrics; 3 | use common::prelude::MessageProcessingError; 4 | use sbe_messages::prelude::StopDataMessage; 5 | 6 | impl Server { 7 | #[autometrics] 8 | pub(crate) async fn handle_stop_date( 9 | &self, 10 | stop_data_msg: &StopDataMessage, 11 | ) -> Result<(), MessageProcessingError> { 12 | // Remove debug print 13 | println!("[QDGW/handle_stop_date]: stop_data: {:?}", stop_data_msg); 14 | println!("[QDGW/handle_stop_date]: NOT IMPLEMENTED"); 15 | 16 | Ok(()) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /flv_services/qdgw/src/service/handle/handle_data_stop_all.rs: -------------------------------------------------------------------------------- 1 | use crate::service::Server; 2 | use autometrics::autometrics; 3 | use common::prelude::MessageProcessingError; 4 | use sbe_messages::prelude::StopAllDataMessage; 5 | 6 | impl Server { 7 | #[autometrics] 8 | pub(crate) async fn handle_stop_all_data( 9 | &self, 10 | stop_all_data_msg: &StopAllDataMessage, 11 | ) -> Result<(), MessageProcessingError> { 12 | // Remove debug print 13 | println!( 14 | "[QDGW/handle_stop_date]: stop_all_data: {:?}", 15 | stop_all_data_msg 16 | ); 17 | 18 | println!("[QDGW/handle_stop_date]: NOT IMPLEMENTED",); 19 | 20 | Ok(()) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /flv_services/qdgw/src/service/handle/mod.rs: -------------------------------------------------------------------------------- 1 | mod handle_client_login; 2 | mod handle_client_logout; 3 | mod handle_data_start; 4 | mod handle_data_start_ohlcv_data; 5 | mod handle_data_start_trade_data; 6 | mod handle_data_stop; 7 | mod handle_data_stop_all; 8 | mod handle_message; 9 | mod handle_client_utils; 10 | -------------------------------------------------------------------------------- /flv_services/qdgw/src/service/utils/mod.rs: -------------------------------------------------------------------------------- 1 | mod utils_send_data; 2 | mod utils_data_encoding; 3 | mod utils_send_error; 4 | -------------------------------------------------------------------------------- /flv_services/symdb/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "symdb" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [[bin]] 12 | name = "symdb" 13 | path = "src/main.rs" 14 | 15 | 16 | [dependencies] 17 | # Internal crates 18 | common = {workspace = true} 19 | config_manager = {workspace = true} 20 | db_query_manager = {workspace = true} 21 | proto = {workspace = true} 22 | symbol_manager = {workspace = true} 23 | 24 | 25 | service_utils = {workspace = true} 26 | 27 | # External crates 28 | autometrics = { workspace = true } 29 | tokio = { workspace = true } 30 | tonic = { workspace = true } 31 | tonic-health = { workspace = true } 32 | prost = { workspace = true } 33 | warp = { workspace = true } 34 | -------------------------------------------------------------------------------- /flv_specs/db_specs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "db_specs" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [lib] 12 | name = "db_specs" 13 | path = "src/lib.rs" 14 | 15 | 16 | [dependencies] 17 | # Internal crates 18 | common = {workspace = true} 19 | -------------------------------------------------------------------------------- /flv_specs/db_specs/src/clickhouse/mod.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::ClickHouseConfig; 2 | 3 | pub fn get_local_db_config() -> ClickHouseConfig { 4 | ClickHouseConfig::default() 5 | } 6 | 7 | pub fn get_cluster_db_config() -> ClickHouseConfig { 8 | ClickHouseConfig::new( 9 | "http://clickhouse.default.svc.cluster.local".to_string(), 10 | 8123, 11 | "username".to_string(), 12 | "password".to_string(), 13 | "default".to_string(), 14 | ) 15 | } 16 | -------------------------------------------------------------------------------- /flv_specs/db_specs/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod clickhouse; 2 | pub mod prelude; 3 | -------------------------------------------------------------------------------- /flv_specs/db_specs/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::clickhouse::get_cluster_db_config; 2 | pub use crate::clickhouse::get_local_db_config; 3 | -------------------------------------------------------------------------------- /flv_specs/db_specs/tests/mod.rs: -------------------------------------------------------------------------------- 1 | use db_specs::prelude::{get_cluster_db_config, get_local_db_config}; 2 | 3 | #[test] 4 | fn test_get_local_db_config() { 5 | let local_config = get_local_db_config(); 6 | // Assuming ClickHouseConfig::default() default values 7 | assert_eq!(local_config.url(), "127.0.0.1".to_string()); 8 | assert_eq!(local_config.port(), 9000); 9 | assert_eq!(local_config.username(), "".to_string()); 10 | assert_eq!(local_config.password(), "".to_string()); 11 | assert_eq!(local_config.database(), "default".to_string()); 12 | } 13 | 14 | #[test] 15 | fn test_get_cluster_db_config() { 16 | let cluster_config = get_cluster_db_config(); 17 | assert_eq!( 18 | cluster_config.url(), 19 | "http://clickhouse.default.svc.cluster.local".to_string() 20 | ); 21 | assert_eq!(cluster_config.port(), 8123); 22 | assert_eq!(cluster_config.username(), "username".to_string()); 23 | assert_eq!(cluster_config.password(), "password".to_string()); 24 | assert_eq!(cluster_config.database(), "default".to_string()); 25 | } 26 | -------------------------------------------------------------------------------- /flv_specs/exchange_specs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "exchange_specs" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [lib] 12 | name = "exchange_specs" 13 | path = "src/lib.rs" 14 | 15 | 16 | [dependencies] 17 | # Internal crates 18 | common = {workspace = true} 19 | -------------------------------------------------------------------------------- /flv_specs/exchange_specs/src/default/mod.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::ExchangeID; 2 | use std::collections::HashMap; 3 | 4 | const KRK_SYMBOL_TABLE: &str = "kraken_symbols"; 5 | 6 | /// Get all supported exchanges. 7 | /// 8 | /// # Returns 9 | /// 10 | /// A vector containing all supported ExchangeID variants. 11 | /// Currently only returns Kraken. 12 | pub fn get_all_exchanges() -> Vec { 13 | vec![ExchangeID::Kraken] 14 | } 15 | /// Get a vector of exchange ID and name pairs. 16 | /// 17 | /// # Returns 18 | /// 19 | /// A vector of tuples containing the u16 ID and name string 20 | /// for each supported exchange. Currently only returns Kraken. 21 | pub fn get_all_exchanges_ids_names() -> Vec<(u16, String)> { 22 | vec![(ExchangeID::Kraken as u16, "kraken".to_string())] 23 | } 24 | 25 | /// Get a HashMap of symbol tables for supported exchanges. 26 | /// 27 | /// The key is the ExchangeID and the value is the symbol table name. 28 | /// 29 | /// # Returns 30 | /// 31 | /// A HashMap mapping ExchangeID to symbol table name string. 32 | /// Currently only contains mapping for Kraken. 33 | pub fn get_exchange_symbol_tables() -> HashMap { 34 | let mut tables = HashMap::new(); 35 | tables.insert(ExchangeID::Kraken, KRK_SYMBOL_TABLE.to_string()); 36 | 37 | tables 38 | } 39 | -------------------------------------------------------------------------------- /flv_specs/exchange_specs/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod default; 2 | pub mod prelude; 3 | -------------------------------------------------------------------------------- /flv_specs/exchange_specs/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::default::get_all_exchanges; 2 | pub use crate::default::get_all_exchanges_ids_names; 3 | pub use crate::default::get_exchange_symbol_tables; 4 | -------------------------------------------------------------------------------- /flv_specs/exchange_specs/tests/default/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use common::prelude::ExchangeID; 4 | use exchange_specs::prelude::{ 5 | get_all_exchanges, get_all_exchanges_ids_names, get_exchange_symbol_tables, 6 | }; 7 | 8 | const KRK_SYMBOL_TABLE: &str = "kraken_symbols"; 9 | 10 | #[test] 11 | fn test_get_all_exchanges() { 12 | let exchanges = get_all_exchanges(); 13 | 14 | assert_eq!(exchanges.len(), 1); 15 | assert_eq!(exchanges[0], ExchangeID::Kraken); 16 | } 17 | 18 | #[test] 19 | fn test_get_all_exchanges_ids_names() { 20 | let id_names = get_all_exchanges_ids_names(); 21 | 22 | assert_eq!(id_names.len(), 1); 23 | assert_eq!( 24 | id_names[0], 25 | (ExchangeID::Kraken as u16, "kraken".to_string()) 26 | ); 27 | } 28 | 29 | #[test] 30 | fn test_get_exchange_symbol_tables() { 31 | let tables = get_exchange_symbol_tables(); 32 | 33 | assert_eq!(tables.len(), 1); 34 | assert_eq!(tables[&ExchangeID::Kraken], KRK_SYMBOL_TABLE); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /flv_specs/exchange_specs/tests/mod.rs: -------------------------------------------------------------------------------- 1 | mod default; 2 | -------------------------------------------------------------------------------- /flv_specs/message_specs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "message_specs" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | 12 | [lib] 13 | name = "message_specs" 14 | path = "src/lib.rs" 15 | 16 | 17 | [dependencies] 18 | # Internal crates 19 | common = {workspace = true} 20 | # External crates 21 | iggy = {workspace = true} -------------------------------------------------------------------------------- /flv_specs/message_specs/src/iggy/mod.rs: -------------------------------------------------------------------------------- 1 | use iggy::identifier::Identifier; 2 | 3 | use common::prelude::{IggyConfig, IggyUser}; 4 | 5 | const MESSAGES_PER_BATCH: u32 = 50; 6 | const AUTO_COMMIT: bool = false; 7 | 8 | pub fn get_local_iggy_config(client_id: u32) -> IggyConfig { 9 | let user = IggyUser::default(); 10 | IggyConfig::from_client_id(user, client_id, MESSAGES_PER_BATCH, AUTO_COMMIT) 11 | } 12 | 13 | pub fn get_cluster_iggy_config(client_id: u32) -> IggyConfig { 14 | let user = IggyUser::default(); 15 | IggyConfig::new( 16 | user, 17 | "iggy.default.svc.cluster.local", 18 | Identifier::numeric(client_id).unwrap(), 19 | format!("stream_{}", client_id), 20 | Identifier::numeric(client_id).unwrap(), 21 | format!("topic_{}", client_id), 22 | client_id, 23 | MESSAGES_PER_BATCH, 24 | AUTO_COMMIT, 25 | ) 26 | } 27 | -------------------------------------------------------------------------------- /flv_specs/message_specs/src/lib.rs: -------------------------------------------------------------------------------- 1 | mod iggy; 2 | pub mod prelude; 3 | -------------------------------------------------------------------------------- /flv_specs/message_specs/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::iggy::get_cluster_iggy_config; 2 | pub use crate::iggy::get_local_iggy_config; 3 | -------------------------------------------------------------------------------- /flv_specs/message_specs/tests/mod.rs: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /flv_specs/service_specs/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "service_specs" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [lib] 12 | name = "service_specs" 13 | path = "src/lib.rs" 14 | 15 | 16 | [dependencies] 17 | # Internal crates 18 | common = {workspace = true} -------------------------------------------------------------------------------- /flv_specs/service_specs/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod prelude; 2 | 3 | mod services; 4 | -------------------------------------------------------------------------------- /flv_specs/service_specs/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::services::qdgw::get_qdgw_service_config; 2 | pub use crate::services::symdb::get_symdb_service_config; 3 | -------------------------------------------------------------------------------- /flv_specs/service_specs/src/services/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod qdgw; 2 | pub mod symdb; 3 | -------------------------------------------------------------------------------- /flv_specs/service_specs/tests/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod qdgw; 3 | #[cfg(test)] 4 | mod symdb; 5 | -------------------------------------------------------------------------------- /flv_specs/service_specs/tests/qdgw/mod.rs: -------------------------------------------------------------------------------- 1 | #[cfg(test)] 2 | mod tests { 3 | use common::prelude::ServiceID; 4 | use service_specs::prelude::get_qdgw_service_config; 5 | 6 | #[test] 7 | fn test_qdgw_service_config() { 8 | let config = get_qdgw_service_config(); 9 | 10 | assert_eq!(config.svc_id(), ServiceID::QDGW); 11 | assert_eq!(config.name(), "qdgwv1"); 12 | assert_eq!(config.version(), 1); 13 | assert!(!config.online()); 14 | assert_eq!( 15 | config.description(), 16 | "QDGW QDGW (Quantitative Data Gateway) gives access to quantitative tick data" 17 | ); 18 | assert_eq!(config.health_check_uri(), "health"); 19 | assert_eq!(config.local_host(), "0.0.0.0"); 20 | assert_eq!(config.local_port(), vec![9000, 9003, 9005, 9010, 8080]); 21 | assert_eq!( 22 | config.cluster_host(), 23 | "qdgw-service.default.svc.cluster.local" 24 | ); 25 | assert_eq!(config.cluster_port(), vec![9000, 9003, 9005, 9010, 8080]); 26 | assert_eq!(config.dependencies(), &None); 27 | assert_eq!(config.metrics().metric_uri(), "metrics"); 28 | assert_eq!(config.metrics().metric_host(), "0.0.0.0"); 29 | assert_eq!(config.metrics().metric_port(), 8080); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /flv_specs/service_specs/tests/symdb/mod.rs: -------------------------------------------------------------------------------- 1 | use common::prelude::ServiceID; 2 | use service_specs::prelude::get_symdb_service_config; 3 | 4 | #[test] 5 | fn test_get_symdb_service_config() { 6 | let config = get_symdb_service_config(); 7 | 8 | assert_eq!(config.svc_id(), ServiceID::SYMDB); 9 | assert_eq!(config.name(), "symdbv1"); 10 | assert_eq!(config.version(), 1); 11 | assert!(!config.online()); 12 | assert_eq!( 13 | config.description(), 14 | "SYMDB (Symbol Master Database) gives access to central symbol to ID mapping)" 15 | ); 16 | assert_eq!(config.health_check_uri(), "health"); 17 | 18 | assert_eq!(config.local_host(), "0.0.0.0"); 19 | assert_eq!(config.local_port(), vec![7070, 8081]); 20 | 21 | assert_eq!( 22 | config.cluster_host(), 23 | "symdb-service.default.svc.cluster.local" 24 | ); 25 | assert_eq!(config.cluster_port(), vec![7070, 8081]); 26 | 27 | assert!(config.dependencies().is_none()); 28 | assert_eq!(config.metrics().metric_uri(), "metrics"); 29 | assert_eq!(config.metrics().metric_host(), "0.0.0.0"); 30 | assert_eq!(config.metrics().metric_port(), 8081); 31 | } 32 | -------------------------------------------------------------------------------- /flv_utils/client_utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "client_utils" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 11 | 12 | [dependencies] 13 | # Internal dependencies 14 | common = { workspace = true } 15 | config_manager = { workspace = true } 16 | db_query_manager = { workspace = true } 17 | db_specs = { workspace = true } 18 | sbe_messages = { workspace = true } 19 | symbol_manager = { workspace = true } 20 | # External crates 21 | futures = {workspace = true} 22 | fluvio = { workspace = true } 23 | config-file = { workspace = true } 24 | csv = { workspace = true } 25 | chrono = { workspace = true } 26 | encoding_rs = { workspace = true } 27 | rust_decimal = { workspace = true } 28 | serde = { workspace = true } -------------------------------------------------------------------------------- /flv_utils/client_utils/src/counter_utils/mod.rs: -------------------------------------------------------------------------------- 1 | ///Client utilities for Fluvio examples 2 | /// 3 | ///This crate provides common utilities used across Fluvio examples. 4 | /// 5 | ///It contains functions for: 6 | /// 7 | ///- Printing formatted output 8 | ///- Handling data streams 9 | ///- Handling errors 10 | ///- Working with symbols 11 | ///- Loading historical data 12 | /// 13 | ///The utilities provide building blocks for writing Fluvio clients 14 | ///and consumers in a consistent way. 15 | /// 16 | ///# Modules 17 | /// 18 | /// - atomic_counter - Atomic counter for unique IDs 19 | ///- print_utils - Formatted printing of headers, messages 20 | ///- handle_utils - Handle streaming data and errors 21 | ///- symbol_utils - Lookup symbol IDs and names 22 | ///- data_utils - Functions for loading historical data 23 | ///- handle_error_utils - Print formatted errors from streams 24 | /// 25 | pub mod atomic_counter; 26 | -------------------------------------------------------------------------------- /flv_utils/client_utils/src/file_utils/mod.rs: -------------------------------------------------------------------------------- 1 | use std::fs::DirEntry; 2 | use std::path::{Path, PathBuf}; 3 | use std::{fs, io}; 4 | 5 | /// Gets a vector of file paths from the given directory. 6 | /// 7 | /// # Parameters 8 | /// 9 | /// * `path` - The path of the directory to get file paths from. Must implement `AsRef`. 10 | /// 11 | /// # Returns 12 | /// 13 | /// A Result containing a vector of `PathBuf` representing the file paths found, or an `io::Error` if the directory could not be read. 14 | /// 15 | /// # Remarks 16 | /// 17 | /// This filters out hidden files and directories, only returning file paths for visible files. 18 | pub fn get_file_paths_from_directory

(path: P) -> Result, io::Error> 19 | where 20 | P: AsRef, 21 | { 22 | fs::read_dir(path) 23 | .expect("Failed to read directory") 24 | .filter(|r| !is_file_hidden(r.as_ref().unwrap())) 25 | .map(|r| r.map(|d| d.path())) 26 | .filter(|r| r.is_ok() && r.as_deref().unwrap().is_file()) 27 | .collect() 28 | } 29 | 30 | /// Checks if the given file entry is hidden. 31 | /// 32 | /// # Parameters 33 | /// 34 | /// * `entry` - The DirEntry to check if hidden 35 | /// 36 | /// # Returns 37 | /// 38 | /// Returns true if the entry's file name starts with a '.', false otherwise 39 | pub fn is_file_hidden(entry: &DirEntry) -> bool { 40 | // https://rust-lang-nursery.github.io/rust-cookbook/file/dir.html 41 | entry 42 | .file_name() 43 | .to_str() 44 | .map(|s| s.starts_with('.')) 45 | .unwrap_or(false) 46 | } 47 | -------------------------------------------------------------------------------- /flv_utils/client_utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod config_utils; 2 | mod counter_utils; 3 | pub mod csv_utils; 4 | pub mod data_utils; 5 | pub mod file_utils; 6 | pub mod message_utils; 7 | pub mod prelude; 8 | pub mod print_utils; 9 | pub mod symbol_utils; 10 | -------------------------------------------------------------------------------- /flv_utils/client_utils/src/message_utils/mod.rs: -------------------------------------------------------------------------------- 1 | pub mod handle_error_utils; 2 | pub mod handle_utils; 3 | -------------------------------------------------------------------------------- /flv_utils/client_utils/src/prelude.rs: -------------------------------------------------------------------------------- 1 | pub use crate::config_utils; 2 | pub use crate::counter_utils::atomic_counter; 3 | pub use crate::csv_utils; 4 | pub use crate::data_utils; 5 | pub use crate::file_utils; 6 | pub use crate::message_utils::handle_error_utils; 7 | pub use crate::message_utils::handle_utils; 8 | pub use crate::print_utils; 9 | pub use crate::symbol_utils; 10 | -------------------------------------------------------------------------------- /flv_utils/client_utils/src/print_utils/mod.rs: -------------------------------------------------------------------------------- 1 | use std::time::Duration; 2 | 3 | pub fn print_example_header(example: &str) { 4 | println!(); 5 | println!("=========================================="); 6 | println!("Running example: {}", example); 7 | println!("=========================================="); 8 | println!(); 9 | } 10 | 11 | pub fn print_import_header() { 12 | println!(); 13 | println!("import_kraken: Imports trade tick data from CSV into QuestDB."); 14 | println!(); 15 | } 16 | 17 | pub fn dbg_print(vrb: bool, msg: &str) { 18 | if vrb { 19 | println!("{msg}"); 20 | println!(); 21 | } 22 | } 23 | 24 | pub fn print_duration(elapsed: &Duration) { 25 | println!("Program took {:?} seconds.", elapsed.as_secs()); 26 | println!(); 27 | } 28 | -------------------------------------------------------------------------------- /flv_utils/iggy_utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "iggy_utils" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [lib] 12 | name = "iggy_utils" 13 | path = "src/lib.rs" 14 | 15 | 16 | [dependencies] 17 | # Internal crates 18 | common = {workspace = true } 19 | # External crates 20 | iggy = {workspace = true } 21 | tokio = {workspace = true } -------------------------------------------------------------------------------- /flv_utils/service_utils/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "service_utils" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | rust-version.workspace = true 6 | readme.workspace = true 7 | repository.workspace = true 8 | authors.workspace = true 9 | 10 | 11 | [lib] 12 | name = "service_utils" 13 | path = "src/lib.rs" 14 | 15 | 16 | [dependencies] 17 | # Internal crates 18 | common = {workspace = true} 19 | # External crates 20 | tokio = { workspace = true } 21 | -------------------------------------------------------------------------------- /flv_utils/service_utils/src/lib.rs: -------------------------------------------------------------------------------- 1 | pub mod print_utils; 2 | pub mod shutdown_utils; 3 | -------------------------------------------------------------------------------- /import_config.toml: -------------------------------------------------------------------------------- 1 | data_folder="data/Kraken_Trading_History" 2 | -------------------------------------------------------------------------------- /scripts/build.sh: -------------------------------------------------------------------------------- 1 | # bin/sh 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | 7 | command cargo build 8 | 9 | #command RUSTFLAGS="-Z threads=8" cargo +nightly build 10 | -------------------------------------------------------------------------------- /scripts/check.sh: -------------------------------------------------------------------------------- 1 | # bin/sh 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | 7 | # Code formatting 8 | # This prevents Clippy from reporting formatting errors 9 | # https://github.com/rust-lang/rustfmt 10 | command cargo fmt --all 11 | 12 | 13 | # Check for outdated dependencies 14 | # https://github.com/kbknapp/cargo-outdated 15 | command cargo outdated --workspace 16 | 17 | 18 | # Scan for unused dependencies 19 | # https://crates.io/crates/cargo-udeps 20 | command cargo +nightly udeps --all-targets 21 | 22 | 23 | # Scan again to report all unfixed vulnerabilities 24 | # https://crates.io/crates/cargo-audit 25 | command cargo audit 26 | 27 | 28 | # Check a package and all of its dependencies for errors. 29 | # https://doc.rust-lang.org/cargo/commands/cargo-check.html 30 | command cargo check --all-targets 31 | 32 | # Consider checking each crate for re-exporting external types 33 | # https://crates.io/crates/cargo-check-external-types 34 | # cargo +nightly check-external-types 35 | 36 | 37 | # Check for linter errors 38 | # https://github.com/rust-lang/rust-clippy 39 | command cargo clippy --all-targets 40 | 41 | 42 | # Check code formatting 43 | # https://github.com/rust-lang/rustfmt 44 | command cargo fmt --all --check 45 | -------------------------------------------------------------------------------- /scripts/clean.sh: -------------------------------------------------------------------------------- 1 | # bin/sh 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | 7 | command cargo clean 8 | 9 | command rm -rf sbe/bindings 10 | command rm sbe_hashes.md5 11 | -------------------------------------------------------------------------------- /scripts/doc.sh: -------------------------------------------------------------------------------- 1 | # bin/sh 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | 7 | command cargo test --doc 8 | 9 | command cargo doc --no-deps --workspace --open -------------------------------------------------------------------------------- /scripts/docker.sh: -------------------------------------------------------------------------------- 1 | # bin/sh 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | 7 | # Copy a temporary Dockerfile to the root directory 8 | command cp flv_specs/service_specs/src/services/qdgw/Dockerfile Dockerfile_qdgw 9 | # Build the image with the Docker deamon. 10 | command docker build -t qdgw:latest -f Dockerfile_qdgw . 11 | # Remove temporary Dockerfile 12 | command rm Dockerfile_qdgw 13 | -------------------------------------------------------------------------------- /scripts/example.sh: -------------------------------------------------------------------------------- 1 | # bin/sh 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | # Bash Select (Make Menu) https://linuxize.com/post/bash-select/ 7 | 8 | echo "" 9 | echo "-----------------------------------------" 10 | echo "Select the number of the example to run: " 11 | echo "-----------------------------------------" 12 | echo "1) Base: Basic Data Stream Example. Requires QDGW running" 13 | echo "2) CausalModel: Real-Time Causal Inference Example. Requires QDGW running" 14 | echo "3) SymbolMaster Example. Requires QDGW and SYMDB running" 15 | echo "4) quit: Exit" 16 | echo "" 17 | echo "-----------------------------------------" 18 | echo "Start QDGW: make qdgw" 19 | echo "Start SYMDB: make symdb" 20 | echo "-----------------------------------------" 21 | echo "" 22 | 23 | select opt in Base CausalModel SymbolMaster quit; 24 | do 25 | case $opt in 26 | 27 | Base) 28 | echo "Selected example: Basic QD Client Example" 29 | command cargo run --bin basic_data_stream 30 | break 31 | ;; 32 | 33 | CausalModel) 34 | echo "Selected example: Causal Inference QD Client Example" 35 | command cargo run --bin causal_data_inference 36 | break 37 | ;; 38 | 39 | SymbolMaster) 40 | echo "Selected example: Symbol Master Example" 41 | command cargo run --bin symbol_master 42 | break 43 | ;; 44 | 45 | quit) 46 | echo "Exiting!" 47 | exit 0 48 | ;; 49 | 50 | *) 51 | echo "Invalid option $REPLY" 52 | ;; 53 | esac 54 | done -------------------------------------------------------------------------------- /scripts/fix.sh: -------------------------------------------------------------------------------- 1 | # bin/sh 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | 7 | command cargo fix --lib --allow-dirty 8 | 9 | command cargo clippy --fix --allow-dirty -------------------------------------------------------------------------------- /scripts/format.sh: -------------------------------------------------------------------------------- 1 | # bin/sh 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | 7 | # Code formatting 8 | # https://github.com/rust-lang/rustfmt 9 | command cargo fmt --all -------------------------------------------------------------------------------- /scripts/import.sh: -------------------------------------------------------------------------------- 1 | # bin/sh 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | RUSTFLAGS='-C target-cpu=native' cargo run --bin import_kraken --release 7 | -------------------------------------------------------------------------------- /scripts/qdgw.sh: -------------------------------------------------------------------------------- 1 | # bin/sh 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | 7 | # Better performance 8 | RUSTFLAGS='-C target-cpu=native' cargo run --bin qdgw --release 9 | 10 | # Faster compile 11 | #cargo run --bin qdgw 12 | -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | # bin/sh 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | # https://users.rust-lang.org/t/how-to-best-ensure-target-cpu-native/53167 7 | RUSTFLAGS='-C target-cpu=native' cargo build --release -------------------------------------------------------------------------------- /scripts/run.sh: -------------------------------------------------------------------------------- 1 | # bin/sh 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | 7 | # Better performance 8 | RUSTFLAGS='-C target-cpu=native' cargo run --bin qdgw --release 9 | 10 | # Faster compile 11 | #cargo run --bin qdgw 12 | -------------------------------------------------------------------------------- /scripts/sbe.sh: -------------------------------------------------------------------------------- 1 | # bin/sh 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | # Ensure JAVA is in path 7 | #command source "$HOME/.sdkman/bin/sdkman-init.sh" 8 | 9 | 10 | # Generate SBE Bindings for Rust using the SBE tool 11 | # https://github.com/real-logic/simple-binary-encoding?tab=readme-ov-file 12 | command java -Dsbe.generate.ir=true -Dsbe.target.language=Rust -Dsbe.target.namespace=sbe -Dsbe.output.dir=flv_sbe/ -Dsbe.errorLog=yes -jar tools/sbe/sbe-all-1.30.0.jar flv_sbe/sbe_schema/schema.xml 13 | echo "Done: SBE Bindings generated!" 14 | exit 0 15 | -------------------------------------------------------------------------------- /scripts/symdb.sh: -------------------------------------------------------------------------------- 1 | # bin/sh 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | 7 | # Better performance 8 | RUSTFLAGS='-C target-cpu=native' cargo run --bin symdb --release 9 | 10 | # Faster compile 11 | #cargo run --bin qdgw 12 | -------------------------------------------------------------------------------- /scripts/test.sh: -------------------------------------------------------------------------------- 1 | # bin/sh 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | 7 | # https://nexte.st/book/installing-from-source.html 8 | # cargo install cargo-nextest --locked 9 | 10 | command cargo test --doc 11 | 12 | command cargo nextest run 13 | 14 | # Once the project got more than 500 tests, the following commands most likely executes them way faster 15 | # because of significantly better CPU optimizations. Run a quick comparison to see the difference. 16 | 17 | # RUSTFLAGS='-C target-cpu=native' cargo test --doc --release 18 | # RUSTFLAGS='-C target-cpu=native' cargo nextest run --release -------------------------------------------------------------------------------- /scripts/update.sh: -------------------------------------------------------------------------------- 1 | # bin/sh 2 | set -o errexit 3 | set -o nounset 4 | set -o pipefail 5 | 6 | 7 | command echo "" 8 | command echo "Checking for rustup update" 9 | command rustup upgrade 10 | 11 | 12 | command echo "" 13 | command echo "Checking for rustc update" 14 | command rustup update stable 15 | 16 | 17 | command echo "" 18 | command echo "Running git pull to update local repo" 19 | command git pull 20 | 21 | 22 | command echo "" 23 | command echo "Build project" 24 | command command cargo build -------------------------------------------------------------------------------- /sql/all_symbols_with_ids.sql: -------------------------------------------------------------------------------- 1 | SELECT symbol, symbol_id, 2 | FROM kraken_symbols; -------------------------------------------------------------------------------- /sql/count_rows.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | count 3 | FROM kraken_xbtusd 4 | ; -------------------------------------------------------------------------------- /sql/resample_by_year.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | timestamp datetime, 3 | first(price) open, 4 | max(price) high, 5 | min(price) low, 6 | last(price) close, 7 | sum(volume) volume, 8 | 9 | FROM kraken_xbtusd 10 | 11 | -- https://questdb.io/docs/reference/operators/date-time/ 12 | WHERE timestamp IN '2022' 13 | SAMPLE BY 5m 14 | ALIGN TO CALENDAR WITH OFFSET '00:00' 15 | -- ORDER BY timestamp DESC 16 | ; -------------------------------------------------------------------------------- /sql/select_between_dates.sql: -------------------------------------------------------------------------------- 1 | SELECT * 2 | FROM kraken_xbtusd 3 | WHERE timestamp 4 | BETWEEN '2022-01-01T00:00:23.000000Z' 5 | AND '2022-01-01T00:00:42.000000Z' 6 | ; -------------------------------------------------------------------------------- /sql/symbol_mapping.sql: -------------------------------------------------------------------------------- 1 | SELECT symbol, symbol_id 2 | FROM kraken_symbols 3 | WHERE symbol='xbteur'; -------------------------------------------------------------------------------- /sql/symbols_summary.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | count, 3 | min(number_of_rows), 4 | max(number_of_rows), 5 | avg(number_of_rows), 6 | sum(number_of_rows), 7 | 8 | FROM kraken_symbols 9 | ; -------------------------------------------------------------------------------- /sql/top_ten_traded_symbols.sql: -------------------------------------------------------------------------------- 1 | 2 | -- Most traded symbols 3 | SELECT * 4 | FROM kraken_symbols 5 | ORDER BY number_of_rows DESC 6 | LIMIT 10 7 | ; 8 | 9 | -- Least traded symbols 10 | SELECT * 11 | FROM kraken_symbols 12 | ORDER BY number_of_rows ASC 13 | LIMIT 10 14 | ; 15 | -------------------------------------------------------------------------------- /sql/top_ten_trading_days_by_volume.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | timestamp datetime, 3 | sum(volume) volume, 4 | 5 | FROM kraken_xbtusd 6 | 7 | SAMPLE BY 1d 8 | ALIGN TO CALENDAR WITH OFFSET '00:00' 9 | ORDER BY volume DESC 10 | limit 10; 11 | -------------------------------------------------------------------------------- /sql/vwap.sql: -------------------------------------------------------------------------------- 1 | SELECT 2 | timestamp, 3 | sum(price * volume) / sum(volume) AS vwap, 4 | sum(volume) as volume, 5 | 6 | FROM kraken_xbteur 7 | WHERE timestamp IN '2023' 8 | SAMPLE BY 5m 9 | ALIGN TO CALENDAR WITH OFFSET '00:00' 10 | ; -------------------------------------------------------------------------------- /tools/Readme.md: -------------------------------------------------------------------------------- 1 | # Tools 2 | 3 | ## SBE 4 | 5 | Generates Rust bindings from the SBE XML schema located in sbe/schema/schema.xml 6 | 7 | Script that calls the SBE tool is located in scripts/sbe.sh 8 | -------------------------------------------------------------------------------- /tools/sbe/sbe-all-1.30.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deepcausality-rs/fluvio-examples/88d90531c0b1e210bcac825edc78058596cfeb17/tools/sbe/sbe-all-1.30.0.jar --------------------------------------------------------------------------------