diff --git a/.gitignore b/.gitignore index 1dc60a1b..3c4ed4ee 100644 --- a/.gitignore +++ b/.gitignore @@ -148,3 +148,5 @@ docs/_build/ # Other .project .venv/ +target/ +obj/ diff --git a/README.md b/README.md index ada42fa0..bf747e81 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,3 @@ -Breaking changes in v6: Greetings, Ladies and Gentlemen, we've introduced some simplifications and breaking changes to our build process. Compared to v5, it should be much easier. If you have any questions, feel free to ping us on Discord https://discord.gg/b5EKcp9s8T. Thank you. - **Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)* @@ -8,7 +6,7 @@ Breaking changes in v6: Greetings, Ladies and Gentlemen, we've introduced some s - [Branches](#branches) - [Build](#build) - [C++](#c) - - [Python](#python) + - [non-C++](#non-c) - [Constants](#constants) - [Examples](#examples) - [Documentations](#documentations) @@ -53,17 +51,16 @@ Breaking changes in v6: Greetings, Ladies and Gentlemen, we've introduced some s # ccapi * A header-only C++ library for streaming market data and executing trades directly from cryptocurrency exchanges (i.e. the connections are between your server and the exchange server without anything in-between). -* Bindings for other languages such as Python are provided. +* Bindings for other languages such as Python and Java are provided. * Code closely follows Bloomberg's API: https://www.bloomberg.com/professional/support/api-library/. * It is ultra fast thanks to very careful optimizations: move semantics, regex optimization, locality of reference, lock contention minimization, etc. * Supported exchanges: - * Market data: coinbase, gemini, kraken, kraken-futures, bitstamp, bitfinex, bitmex, binance-us, binance, binance-usds-futures, binance-coin-futures, huobi, huobi-usdt-swap, huobi-coin-swap, okx, erisx (Cboe Digital), kucoin, kucoin-futures, deribit, gateio, gateio-perpetual-futures, cryptocom, bybit, bybit-derivatives, ascendex, bitget, bitget-futures, bitmart, mexc, mexc-futures, whitebit. + * Market Data: coinbase, gemini, kraken, kraken-futures, bitstamp, bitfinex, bitmex, binance-us, binance, binance-usds-futures, binance-coin-futures, huobi, huobi-usdt-swap, huobi-coin-swap, okx, erisx (Cboe Digital), kucoin, kucoin-futures, deribit, gateio, gateio-perpetual-futures, cryptocom, bybit, bybit-derivatives, ascendex, bitget, bitget-futures, bitmart, mexc, mexc-futures, whitebit. * Execution Management: coinbase, gemini, kraken, kraken-futures, bitstamp, bitfinex, bitmex, binance-us, binance, binance-usds-futures, binance-coin-futures, huobi, huobi-usdt-swap, huobi-coin-swap, okx, erisx (Cboe Digital), kucoin, kucoin-futures, deribit, gateio, gateio-perpetual-futures, cryptocom, bybit, bybit-derivatives, ascendex, bitget, bitget-futures, bitmart, mexc. * FIX: coinbase, gemini. * A spot market making application is provided as an end-to-end solution for liquidity providers. * A single order execution application is provided as an end-to-end solution for executing large orders. * To spur innovation and industry collaboration, this library is open for use by the public without cost. -* For historical market data, see https://github.com/crypto-chassis/cryptochassis-api-docs. * We specialize in market data collection, high speed trading system, infrastructure optimization, and proprietary market making. Hire us as engineers, liquidity providers, traders, or asset managers. * Join us on Discord https://discord.gg/b5EKcp9s8T and Medium https://cryptochassis.medium.com. @@ -103,26 +100,29 @@ Breaking changes in v6: Greetings, Ladies and Gentlemen, we've introduced some s * "string table overflow at offset \". Try to add optimization flag `-O1` or `-O2`. See https://stackoverflow.com/questions/14125007/gcc-string-table-overflow-error-during-compilation. * On Windows, if you still encounter resource related issues, try to add optimization flag `-O3 -DNDEBUG`. -### Python -* Require Python 3, SWIG, and CMake. - * SWIG: On macOS, `brew install SWIG`. On Linux, `sudo apt-get install -y swig`. On Windows, http://www.swig.org/Doc4.0/Windows.html#Windows. +### non-C++ +* Require SWIG and CMake. + * SWIG: On macOS, `brew install SWIG`. On Linux, `sudo apt-get install -y swig`. * CMake: https://cmake.org/download/. * Run the following commands. ``` mkdir binding/build cd binding/build rm -rf * (if rebuild from scratch) -cmake -DBUILD_PYTHON=ON -DINSTALL_PYTHON=ON .. (optional: -DBUILD_VERSION=) +cmake -DBUILD_PYTHON=ON -DBUILD_VERSION=1.0.0 .. (Use -DBUILD_JAVA=ON if the target language is Java) cmake --build . -cmake --install . ``` -* If a virtual environment (managed by `venv` or `conda`) is active (i.e. the `activate` script has been evaluated), the package will be installed into the virtual environment rather than globally. -* Currently not working on Windows. +* Python: If a virtual environment (managed by `venv` or `conda`) is active (i.e. the `activate` script has been evaluated), the package will be installed into the virtual environment rather than globally. * Troubleshoot: - * "CMake Error at python/CMakeLists.txt:... (message): Require Python 3". Try to create and activate a virtual environment (managed by `venv` or `conda`) with Python 3. - * "Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the system variable OPENSSL_ROOT_DIR (missing: OPENSSL_INCLUDE_DIR)". Try `cmake -DOPENSSL_ROOT_DIR=...`. On macOS, you might be missing headers for OpenSSL, `brew install openssl` and `cmake -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl`. On Ubuntu, `sudo apt-get install libssl-dev`. On Windows, `vcpkg install openssl:x64-windows` and `cmake -DOPENSSL_ROOT_DIR=C:/vcpkg/installed/x64-windows-static`. - * "Fatal Python error: Segmentation fault". If the macOS version is relatively new and the Python version is relatively old, please upgrade Python to a relatively new version. - * "‘_PyObject_GC_UNTRACK’ was not declared in this scope". If you use Python >= 3.8, please use SWIG >= 4.0. + * Python: + * "CMake Error at python/CMakeLists.txt:... (message): Require Python 3". Try to create and activate a virtual environment (managed by `venv` or `conda`) with Python 3. + * "Could NOT find OpenSSL, try to set the path to OpenSSL root folder in the system variable OPENSSL_ROOT_DIR (missing: OPENSSL_INCLUDE_DIR)". Try `cmake -DOPENSSL_ROOT_DIR=...`. On macOS, you might be missing headers for OpenSSL, `brew install openssl` and `cmake -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl`. On Ubuntu, `sudo apt-get install libssl-dev`. On Windows, `vcpkg install openssl:x64-windows` and `cmake -DOPENSSL_ROOT_DIR=C:/vcpkg/installed/x64-windows-static`. + * "Fatal Python error: Segmentation fault". If the macOS version is relatively new and the Python version is relatively old, please upgrade Python to a relatively new version. + * "‘_PyObject_GC_UNTRACK’ was not declared in this scope". If you use Python >= 3.8, please use SWIG >= 4.0. + * Java: + * "Could NOT find JNI (missing: JAVA_INCLUDE_PATH JAVA_INCLUDE_PATH2 JAVA_AWT_INCLUDE_PATH)". Check that the environment variable `JAVA_HOME` is correct. + * "../Main.java:1: error: package com.cryptochassis.ccapi does not exist". Check that `javac`'s classpath includes `binding/build/java/packaging/1.0.0/ccapi-1.0.0.jar`. + * "Exception in thread "main" java.lang.UnsatisfiedLinkError: no ccapi_binding_java in java.library.path: ...". Check that `java`'s `java.library.path` property includes `binding/build/java/packaging/1.0.0`. See https://stackoverflow.com/questions/1403788/java-lang-unsatisfiedlinkerror-no-dll-in-java-library-path. ## Constants [`include/ccapi_cpp/ccapi_macro.h`](include/ccapi_cpp/ccapi_macro.h) @@ -143,8 +143,23 @@ cmake --build . --target [Python](binding/python/example) * Python API is nearly identical to C++ API and covers nearly all the functionalities from C++ API. -* Build and install the Python binding as shown [above](#python). -* Run `python3 main.py`. +* Build and install the Python binding as shown [above](#non-c). +* Inside a concrete example directory (e.g. binding/python/example/market_data_simple_subscription), run +``` +python3 main.py +``` + +[Java](binding/java/example) +* Java API is nearly identical to C++ API and covers nearly all the functionalities from C++ API. +* Build and install the Java binding as shown [above](#non-c). +* Inside a concrete example directory (e.g. binding/python/example/market_data_simple_subscription), run +``` +mkdir build +cd build +rm -rf * (if rebuild from scratch) +javac -cp ../../../../build/java/packaging/1.0.0/ccapi-1.0.0.jar -d . ../Main.java +java -cp .:../../../../build/java/packaging/1.0.0/ccapi-1.0.0.jar -Djava.library.path=../../../../build/java/packaging/1.0.0 Main +``` ## Documentations @@ -156,7 +171,7 @@ For a specific exchange and instrument, get recents trades. **Code 1:** -[C++](example/src/market_data_simple_request/main.cpp) / [Python](binding/python/example/src/market_data_simple_request/main.py) +[C++](example/src/market_data_simple_request/main.cpp) / [Python](binding/python/example/market_data_simple_request/main.py) ``` #include "ccapi_cpp/ccapi_session.h" namespace ccapi { @@ -221,7 +236,7 @@ Bye ``` * Request operation types: `GET_INSTRUMENT`, `GET_INSTRUMENTS`, `GET_RECENT_TRADES`, `GET_RECENT_AGG_TRADES`(only applicable to binance family: https://binance-docs.github.io/apidocs/spot/en/#compressed-aggregate-trades-list). * Request parameter names: `LIMIT`, `INSTRUMENT_TYPE`. Instead of these convenient names you can also choose to use arbitrary parameter names and they will be passed to the exchange's native API. See [this example](example/src/market_data_advanced_request/main.cpp). -* Message's `time` represents the exchange's reported timestamp. Its `timeReceived` represents the library's receiving timestamp. `time` can be retrieved by `getTime` method and `timeReceived` can be retrieved by `getTimeReceived` method. (For Python, please use `getTimeUnix` and `getTimeReceivedUnix` methods or `getTimeISO` and `getTimeReceivedISO` methods). +* Message's `time` represents the exchange's reported timestamp. Its `timeReceived` represents the library's receiving timestamp. `time` can be retrieved by `getTime` method and `timeReceived` can be retrieved by `getTimeReceived` method. (For non-C++, please use `getTimeUnix` and `getTimeReceivedUnix` methods or `getTimeISO` and `getTimeReceivedISO` methods). **Objective 2:** @@ -229,7 +244,7 @@ For a specific exchange and instrument, whenever the best bid's or ask's price o **Code 2:** -[C++](example/src/market_data_simple_subscription/main.cpp) / [Python](binding/python/example/src/market_data_simple_subscription/main.py) +[C++](example/src/market_data_simple_subscription/main.cpp) / [Python](binding/python/example/market_data_simple_subscription/main.py) / [Java](binding/java/example/market_data_simple_subscription/Main.java) ``` #include "ccapi_cpp/ccapi_session.h" namespace ccapi { @@ -403,7 +418,7 @@ For a specific exchange and instrument, submit a simple limit order. **Code 1:** -[C++](example/src/execution_management_simple_request/main.cpp) / [Python](binding/python/example/src/execution_management_simple_request/main.py) +[C++](example/src/execution_management_simple_request/main.cpp) / [Python](binding/python/example/execution_management_simple_request/main.py) / [Java](binding/java/example/execution_management_simple_request/Main.java) ``` #include "ccapi_cpp/ccapi_session.h" namespace ccapi { @@ -493,7 +508,7 @@ For a specific exchange and instrument, receive order updates. **Code 2:** -[C++](example/src/execution_management_simple_subscription/main.cpp) / [Python](binding/python/example/src/execution_management_simple_subscription/main.py) +[C++](example/src/execution_management_simple_subscription/main.cpp) / [Python](binding/python/example/execution_management_simple_subscription/main.py) ``` #include "ccapi_cpp/ccapi_session.h" namespace ccapi { @@ -736,7 +751,7 @@ For a specific exchange and instrument, submit a simple limit order. **Code:** -[C++](example/src/fix_simple/main.cpp) / [Python](binding/python/example/src/fix_simple/main.py) +[C++](example/src/fix_simple/main.cpp) / [Python](binding/python/example/fix_simple/main.py) ``` #include "ccapi_cpp/ccapi_session.h" namespace ccapi { @@ -880,7 +895,7 @@ An example can be found [here](example/src/market_data_advanced_subscription/mai #### Enable library logging -[C++](example/src/enable_library_logging/main.cpp) / [Python](binding/python/example/src/enable_library_logging/main.py) +[C++](example/src/enable_library_logging/main.cpp) / [Python](binding/python/example/enable_library_logging/main.py) Extend a subclass, e.g. `MyLogger`, from class `Logger` and override method `logMessage`. Assign a `MyLogger` pointer to `Logger::logger`. Add one of the following macros in the compiler command line: `CCAPI_ENABLE_LOG_TRACE`, `CCAPI_ENABLE_LOG_DEBUG`, `CCAPI_ENABLE_LOG_INFO`, `CCAPI_ENABLE_LOG_WARN`, `CCAPI_ENABLE_LOG_ERROR`, `CCAPI_ENABLE_LOG_FATAL`. Enable logging if you'd like to inspect raw responses/messages from the exchange for troubleshooting purposes. ``` diff --git a/binding/CMakeLists.txt b/binding/CMakeLists.txt index df075259..07718425 100644 --- a/binding/CMakeLists.txt +++ b/binding/CMakeLists.txt @@ -6,6 +6,10 @@ project(${NAME} LANGUAGES CXX) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}") +if(POLICY CMP0122) +cmake_policy(SET CMP0122 NEW) +endif() + # Apple: Don't modify install_name when touching RPATH. if(POLICY CMP0068) cmake_policy(SET CMP0068 NEW) @@ -73,9 +77,14 @@ else() endif() option(BUILD_PYTHON "Build Python Library" OFF) -option(INSTALL_PYTHON "Install Python Library" OFF) message(STATUS "Build Python: ${BUILD_PYTHON}") +option(BUILD_JAVA "Build Java Library" OFF) +message(STATUS "Build Java: ${BUILD_JAVA}") + +option(BUILD_CSHARP "Build C# Library" OFF) +message(STATUS "Build C#: ${BUILD_CSHARP}") + set(CMAKE_CXX_STANDARD 17) if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -pthread") @@ -141,7 +150,7 @@ link_libraries(OpenSSL::Crypto OpenSSL::SSL) set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(SOURCE_LOGGER ${CCAPI_PROJECT_DIR}/binding/ccapi_logger.cpp) -set(CMAKE_SWIG_FLAGS) +# set(CMAKE_SWIG_FLAGS) find_package(SWIG REQUIRED) include(UseSWIG) if(BUILD_TEST) @@ -212,4 +221,30 @@ add_compile_definitions(CCAPI_ENABLE_EXCHANGE_WHITEBIT) find_package(ZLIB REQUIRED) link_libraries(ZLIB::ZLIB) -add_subdirectory(python) +if(BUILD_PYTHON) +configure_file( + swig_interface.i.in + ${CMAKE_BINARY_DIR}/python/swig_interface.i +@ONLY) +set(SWIG_INTERFACE ${CMAKE_BINARY_DIR}/python/swig_interface.i) + add_subdirectory(python) +endif() + +if(BUILD_JAVA) +configure_file( + swig_interface.i.in + ${CMAKE_BINARY_DIR}/java/swig_interface.i +@ONLY) +set(SWIG_INTERFACE ${CMAKE_BINARY_DIR}/java/swig_interface.i) + add_subdirectory(java) +endif() + +if(BUILD_CSHARP) +file(READ csharp/swig_interface_ccapi_language_specific.i SWIG_INTERFACE_CCAPI_LANGUAGE_SPECIFIC) +configure_file( + swig_interface.i.in + ${CMAKE_BINARY_DIR}/csharp/swig_interface.i +@ONLY) +set(SWIG_INTERFACE ${CMAKE_BINARY_DIR}/csharp/swig_interface.i) + add_subdirectory(csharp) +endif() diff --git a/binding/csharp/CMakeLists.txt b/binding/csharp/CMakeLists.txt new file mode 100644 index 00000000..be4def3c --- /dev/null +++ b/binding/csharp/CMakeLists.txt @@ -0,0 +1,70 @@ +set(NAME binding_csharp) +project(${NAME}) +set(SWIG_TARGET_NAME ccapi_${NAME}) + +# Find dotnet cli +find_program(DOTNET_EXECUTABLE NAMES dotnet REQUIRED) +if(NOT DOTNET_EXECUTABLE) + message(FATAL_ERROR "Check for dotnet Program: not found") +else() + message(STATUS "Found dotnet Program: ${DOTNET_EXECUTABLE}") +endif() + +execute_process( + COMMAND ${DOTNET_EXECUTABLE} --version + OUTPUT_VARIABLE DOTNET_EXECUTABLE_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE +) +message(STATUS "Dotnet version: ${DOTNET_EXECUTABLE_VERSION}") + +# set(CSHARP_DOMAIN_NAME "cryptochassis") +# set(CSHARP_DOMAIN_EXTENSION "com") + +# set(CSHARP_GROUP "${CSHARP_DOMAIN_EXTENSION}.${CSHARP_DOMAIN_NAME}") +# set(CSHARP_ARTIFACT "ccapi") + +set(CSHARP_NAMESPACE "ccapi") + +set_property(SOURCE ${SWIG_INTERFACE} PROPERTY CPLUSPLUS ON) +set_property(SOURCE ${SWIG_INTERFACE} PROPERTY COMPILE_OPTIONS "-namespace;${CSHARP_NAMESPACE};-dllimport;${SWIG_TARGET_NAME}.so") +# set_property(SOURCE ${SWIG_INTERFACE} PROPERTY COMPILE_OPTIONS "-package;${CSHARP_PACKAGE};-doxygen") + +swig_add_library(${SWIG_TARGET_NAME} + LANGUAGE csharp + OUTPUT_DIR ${CMAKE_BINARY_DIR}/csharp/${SWIG_TARGET_NAME} +SOURCES ${SWIG_INTERFACE} ${SOURCE_LOGGER}) + +if(NOT CCAPI_LEGACY_USE_WEBSOCKETPP) + add_dependencies(${SWIG_TARGET_NAME} boost rapidjson hffix) +endif() +# set_property(TARGET ${SWIG_TARGET_NAME} PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON) +# target_include_directories(${SWIG_TARGET_NAME} +# PRIVATE +# ${JNI_INCLUDE_DIRS} +# ) + + + +set(PACKAGING_DIR packaging) +set(PACKAGING_DIR_FULL ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGING_DIR}/${BUILD_VERSION}) +file(MAKE_DIRECTORY ${PACKAGING_DIR_FULL}) + +set(SRC_DIR_FULL ${CMAKE_CURRENT_BINARY_DIR}/src) +file(MAKE_DIRECTORY ${SRC_DIR_FULL}) + +configure_file( + ccapi.csproj.in + ${SRC_DIR_FULL}/ccapi.csproj + @ONLY) +set(CSHARP_PACKAGING_TARGET_NAME csharp_${PACKAGING_DIR}) +add_custom_target(${CSHARP_PACKAGING_TARGET_NAME} ALL + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/${SWIG_TARGET_NAME}/*.cs ${SRC_DIR_FULL} + # COMMAND ${DOTNET_EXECUTABLE} build -c ${CMAKE_BUILD_TYPE} ccapi.csproj + COMMAND ${DOTNET_EXECUTABLE} build -c ${CMAKE_BUILD_TYPE} -o ${PACKAGING_DIR_FULL} ccapi.csproj + COMMAND ${CMAKE_COMMAND} -E copy $ ${PACKAGING_DIR_FULL} + WORKING_DIRECTORY ${SRC_DIR_FULL} +) +add_dependencies(${CSHARP_PACKAGING_TARGET_NAME} ${SWIG_TARGET_NAME}) + +# COMMAND ${Java_JAVAC_EXECUTABLE} -d ${PACKAGING_DIR_FULL} ${CMAKE_CURRENT_BINARY_DIR}/${SWIG_TARGET_NAME}/*.java +# COMMAND ${CMAKE_COMMAND} -E copy $ ${PACKAGING_DIR_FULL} diff --git a/binding/csharp/ccapi.csproj.in b/binding/csharp/ccapi.csproj.in new file mode 100644 index 00000000..b9251e94 --- /dev/null +++ b/binding/csharp/ccapi.csproj.in @@ -0,0 +1,8 @@ + + + + net6.0 + enable + + + diff --git a/binding/csharp/example/execution_management_simple_request/MainProgram.cs b/binding/csharp/example/execution_management_simple_request/MainProgram.cs new file mode 100644 index 00000000..fa90336d --- /dev/null +++ b/binding/csharp/example/execution_management_simple_request/MainProgram.cs @@ -0,0 +1,38 @@ +class MainProgram + { + class MyEventHandler : ccapi.EventHandler { + public override bool ProcessEvent(ccapi.Event event_, ccapi.Session session) { + System.Console.WriteLine(System.String.Format("Received an event:\n%s", event_.ToStringPretty(2, 2))); + return true; + } + } + static void Main(string[] args) + { + if(System.Environment.GetEnvironmentVariable("OKX_API_KEY") is null) { + System.Console.Error.WriteLine("Please set environment variable OKX_API_KEY"); + return; + } + if(System.Environment.GetEnvironmentVariable("OKX_API_SECRET") is null) { + System.Console.Error.WriteLine("Please set environment variable OKX_API_SECRET"); + return; + } + if(System.Environment.GetEnvironmentVariable("OKX_API_PASSPHRASE") is null) { + System.Console.Error.WriteLine("Please set environment variable OKX_API_PASSPHRASE"); + return; + } + var eventHandler = new MyEventHandler(); + var option = new ccapi.SessionOptions(); + var config = new ccapi.SessionConfigs(); + var session = new ccapi.Session(option, config, eventHandler); + var request = new ccapi.Request(Request.Operation.CREATE_ORDER, "okx", "BTC-USDT"); + var param = new ccapi.MapStringString(); + param.Add("SIDE","BUY"); + param.Add("QUANTITY","0.0005"); + param.Add("LIMIT_PRICE","20000"); + request.AppendParam(param); + session.SendRequest(request); + System.Threading.Thread.Sleep(10000); + session.Stop(); + System.Console.WriteLine("Bye"); + } + } diff --git a/binding/csharp/example/execution_management_simple_request/main.csproj b/binding/csharp/example/execution_management_simple_request/main.csproj new file mode 100644 index 00000000..798d616e --- /dev/null +++ b/binding/csharp/example/execution_management_simple_request/main.csproj @@ -0,0 +1,13 @@ + + + Exe + net6.0 + enable + to be provided in command line + + + + $(CcapiLibraryPath) + + + diff --git a/binding/csharp/example/market_data_simple_subscription/MainProgram.cs b/binding/csharp/example/market_data_simple_subscription/MainProgram.cs new file mode 100644 index 00000000..ced50acf --- /dev/null +++ b/binding/csharp/example/market_data_simple_subscription/MainProgram.cs @@ -0,0 +1,35 @@ +class MainProgram + { + class MyEventHandler : ccapi.EventHandler { + public override bool ProcessEvent(ccapi.Event event_, ccapi.Session session) { + if (event_.GetType_() == ccapi.Event.Type.SUBSCRIPTION_DATA) { + foreach (var message in event_.GetMessageList()) { + System.Console.WriteLine(System.String.Format("Best bid and ask at {0} are:", message.GetTimeISO())); + foreach (var element in message.GetElementList()){ + var elementNameValueMap = element.GetNameValueMap(); + foreach(var entry in elementNameValueMap) + { + var name = entry.Key; + var value = entry.Value; + System.Console.WriteLine(System.String.Format(" {0} = {1}", name, value)); + } + } + } + } + return true; + } + } + static void Main(string[] args) + { + var eventHandler = new MyEventHandler(); + var option = new ccapi.SessionOptions(); + var config = new ccapi.SessionConfigs(); + var session = new ccapi.Session(option, config, eventHandler); + var subscriptionList = new ccapi.SubscriptionList(); + subscriptionList.Add(new ccapi.Subscription("okx", "BTC-USDT", "MARKET_DEPTH")); + session.Subscribe(subscriptionList); + System.Threading.Thread.Sleep(10000); + session.Stop(); + System.Console.WriteLine("Bye"); + } + } diff --git a/binding/csharp/example/market_data_simple_subscription/main.csproj b/binding/csharp/example/market_data_simple_subscription/main.csproj new file mode 100644 index 00000000..798d616e --- /dev/null +++ b/binding/csharp/example/market_data_simple_subscription/main.csproj @@ -0,0 +1,13 @@ + + + Exe + net6.0 + enable + to be provided in command line + + + + $(CcapiLibraryPath) + + + diff --git a/binding/csharp/swig_interface_ccapi_language_specific.i b/binding/csharp/swig_interface_ccapi_language_specific.i new file mode 100644 index 00000000..310823e9 --- /dev/null +++ b/binding/csharp/swig_interface_ccapi_language_specific.i @@ -0,0 +1,2 @@ +%rename("%(camelcase)s", %$isfunction, %$ismember, %$ispublic) ""; +%rename("%(regex:/^(toString|getType|setType)$/\\u\\1_/)s") ""; diff --git a/binding/java/CMakeLists.txt b/binding/java/CMakeLists.txt new file mode 100644 index 00000000..48260a82 --- /dev/null +++ b/binding/java/CMakeLists.txt @@ -0,0 +1,47 @@ +set(NAME binding_java) +project(${NAME}) +set(SWIG_TARGET_NAME ccapi_${NAME}) + +# Find Java and JNI +find_package(Java COMPONENTS Development REQUIRED) +message(STATUS "Java_VERSION: ${Java_VERSION}") +message(STATUS "Java_JAVA_EXECUTABLE: ${Java_JAVA_EXECUTABLE}") +find_package(JNI REQUIRED) +message(STATUS "JNI_FOUND: ${JNI_FOUND}") + +set(JAVA_DOMAIN_NAME "cryptochassis") +set(JAVA_DOMAIN_EXTENSION "com") + +set(JAVA_GROUP "${JAVA_DOMAIN_EXTENSION}.${JAVA_DOMAIN_NAME}") +set(JAVA_ARTIFACT "ccapi") + +set(JAVA_PACKAGE "${JAVA_GROUP}.${JAVA_ARTIFACT}") + +set_property(SOURCE ${SWIG_INTERFACE} PROPERTY CPLUSPLUS ON) +set_property(SOURCE ${SWIG_INTERFACE} PROPERTY COMPILE_OPTIONS "-package;${JAVA_PACKAGE};-doxygen") + +swig_add_library(${SWIG_TARGET_NAME} + LANGUAGE java + OUTPUT_DIR ${CMAKE_BINARY_DIR}/java/${SWIG_TARGET_NAME} +SOURCES ${SWIG_INTERFACE} ${SOURCE_LOGGER}) + +if(NOT CCAPI_LEGACY_USE_WEBSOCKETPP) + add_dependencies(${SWIG_TARGET_NAME} boost rapidjson hffix) +endif() +set_property(TARGET ${SWIG_TARGET_NAME} PROPERTY SWIG_USE_TARGET_INCLUDE_DIRECTORIES ON) +target_include_directories(${SWIG_TARGET_NAME} + PRIVATE + ${JNI_INCLUDE_DIRS} +) + +set(PACKAGING_DIR packaging) +set(PACKAGING_DIR_FULL ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGING_DIR}/${BUILD_VERSION}) +file(MAKE_DIRECTORY ${PACKAGING_DIR_FULL}) +set(JAVA_PACKAGING_TARGET_NAME java_${PACKAGING_DIR}) +add_custom_target(${JAVA_PACKAGING_TARGET_NAME} ALL + COMMAND ${Java_JAVAC_EXECUTABLE} -d ${PACKAGING_DIR_FULL} ${CMAKE_CURRENT_BINARY_DIR}/${SWIG_TARGET_NAME}/*.java + COMMAND ${Java_JAR_EXECUTABLE} cf ${JAVA_ARTIFACT}-${BUILD_VERSION}.jar ${JAVA_DOMAIN_EXTENSION}/${JAVA_DOMAIN_NAME}/${JAVA_ARTIFACT}/** + COMMAND ${CMAKE_COMMAND} -E copy $ ${PACKAGING_DIR_FULL} + WORKING_DIRECTORY ${PACKAGING_DIR_FULL} +) +add_dependencies(${JAVA_PACKAGING_TARGET_NAME} ${SWIG_TARGET_NAME}) diff --git a/binding/java/example/execution_management_simple_request/Main.java b/binding/java/example/execution_management_simple_request/Main.java new file mode 100644 index 00000000..8a7eaf05 --- /dev/null +++ b/binding/java/example/execution_management_simple_request/Main.java @@ -0,0 +1,49 @@ +import com.cryptochassis.ccapi.Event; +import com.cryptochassis.ccapi.EventHandler; +import com.cryptochassis.ccapi.MapStringString; +import com.cryptochassis.ccapi.Request; +import com.cryptochassis.ccapi.Session; +import com.cryptochassis.ccapi.SessionConfigs; +import com.cryptochassis.ccapi.SessionOptions; + +public class Main { + static class MyEventHandler extends EventHandler { + @Override + public boolean processEvent(Event event, Session session) { + System.out.println(String.format("Received an event:\n%s", event.toStringPretty(2, 2))); + return true; + } + } + public static void main(String[] args) { + if (System.getenv("OKX_API_KEY") == null) { + System.err.println("Please set environment variable OKX_API_KEY"); + return; + } + if (System.getenv("OKX_API_SECRET") == null) { + System.err.println("Please set environment variable OKX_API_SECRET"); + return; + } + if (System.getenv("OKX_API_PASSPHRASE") == null) { + System.err.println("Please set environment variable OKX_API_PASSPHRASE"); + return; + } + System.loadLibrary("ccapi_binding_java"); + var eventHandler = new MyEventHandler(); + var option = new SessionOptions(); + var config = new SessionConfigs(); + var session = new Session(option, config, eventHandler); + var request = new Request(Request.Operation.CREATE_ORDER, "okx", "BTC-USDT"); + var param = new MapStringString(); + param.put("SIDE", "BUY"); + param.put("QUANTITY", "0.0005"); + param.put("LIMIT_PRICE", "20000"); + request.appendParam(param); + session.sendRequest(request); + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + } + session.stop(); + System.out.println("Bye"); + } +} diff --git a/binding/java/example/market_data_simple_subscription/Main.java b/binding/java/example/market_data_simple_subscription/Main.java new file mode 100644 index 00000000..7968bdd7 --- /dev/null +++ b/binding/java/example/market_data_simple_subscription/Main.java @@ -0,0 +1,45 @@ +import com.cryptochassis.ccapi.Event; +import com.cryptochassis.ccapi.EventHandler; +import com.cryptochassis.ccapi.Session; +import com.cryptochassis.ccapi.SessionConfigs; +import com.cryptochassis.ccapi.SessionOptions; +import com.cryptochassis.ccapi.Subscription; +import com.cryptochassis.ccapi.SubscriptionList; + +public class Main { + static class MyEventHandler extends EventHandler { + @Override + public boolean processEvent(Event event, Session session) { + if (event.getType() == Event.Type.SUBSCRIPTION_DATA) { + for (var message : event.getMessageList()) { + System.out.println(String.format("Best bid and ask at %s are:", message.getTimeISO())); + for (var element : message.getElementList()) { + var elementNameValueMap = element.getNameValueMap(); + for (var entry : elementNameValueMap.entrySet()) { + var name = entry.getKey(); + var value = entry.getValue(); + System.out.println(String.format(" %s = %s", name, value)); + } + } + } + } + return true; + } + } + public static void main(String[] args) { + System.loadLibrary("ccapi_binding_java"); + var eventHandler = new MyEventHandler(); + var option = new SessionOptions(); + var config = new SessionConfigs(); + var session = new Session(option, config, eventHandler); + var subscriptionList = new SubscriptionList(); + subscriptionList.add(new Subscription("okx", "BTC-USDT", "MARKET_DEPTH")); + session.subscribe(subscriptionList); + try { + Thread.sleep(10000); + } catch (InterruptedException e) { + } + session.stop(); + System.out.println("Bye"); + } +} diff --git a/binding/python/CMakeLists.txt b/binding/python/CMakeLists.txt index 0e836ae0..bb5717d8 100644 --- a/binding/python/CMakeLists.txt +++ b/binding/python/CMakeLists.txt @@ -1,6 +1,3 @@ -if(NOT BUILD_PYTHON) - return() -endif() set(NAME binding_python) project(${NAME}) set(SWIG_TARGET_NAME ccapi_${NAME}) @@ -12,10 +9,10 @@ message(STATUS "Python_EXECUTABLE: ${Python_EXECUTABLE}") if(Python_VERSION VERSION_LESS 3) message(FATAL_ERROR "Require Python 3") endif() -list(APPEND CMAKE_SWIG_FLAGS "-py3") -set(SWIG_INTERFACE ${CCAPI_PROJECT_DIR}/binding/swig_interface.i) +# list(APPEND CMAKE_SWIG_FLAGS "-py3") +# set(SWIG_INTERFACE ${CCAPI_PROJECT_DIR}/binding/swig_interface.i) set_property(SOURCE ${SWIG_INTERFACE} PROPERTY CPLUSPLUS ON) -set_property(SOURCE ${SWIG_INTERFACE} PROPERTY COMPILE_OPTIONS "-builtin;-threads") +set_property(SOURCE ${SWIG_INTERFACE} PROPERTY COMPILE_OPTIONS "-py3;-builtin;-threads;-doxygen") swig_add_library(${SWIG_TARGET_NAME} LANGUAGE python OUTPUT_DIR ${CMAKE_BINARY_DIR}/python/${SWIG_TARGET_NAME} @@ -38,68 +35,52 @@ configure_file( ${CMAKE_CURRENT_BINARY_DIR}/setup.py.in @ONLY) file(GENERATE - OUTPUT $/setup.py + OUTPUT setup.py INPUT ${CMAKE_CURRENT_BINARY_DIR}/setup.py.in) # Find if python module MODULE_NAME is available, # if not install it to the Python user install directory. -function(search_python_module MODULE_NAME) - execute_process( - COMMAND ${Python_EXECUTABLE} -c "import ${MODULE_NAME}; print(${MODULE_NAME}.__version__)" - RESULT_VARIABLE _RESULT - OUTPUT_VARIABLE MODULE_VERSION - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - if(${_RESULT} STREQUAL "0") - message(STATUS "Found python module: ${MODULE_NAME} (found version \"${MODULE_VERSION}\")") - else() - message(WARNING "Can't find python module \"${MODULE_NAME}\", user install it using pip...") - execute_process( - COMMAND ${Python_EXECUTABLE} -m pip install --upgrade ${MODULE_NAME} - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - endif() -endfunction() +# function(search_python_module MODULE_NAME) +# execute_process( +# COMMAND ${Python_EXECUTABLE} -c "import ${MODULE_NAME}; print(${MODULE_NAME}.__version__)" +# RESULT_VARIABLE _RESULT +# OUTPUT_VARIABLE MODULE_VERSION +# ERROR_QUIET +# OUTPUT_STRIP_TRAILING_WHITESPACE +# ) +# if(${_RESULT} STREQUAL "0") +# message(STATUS "Found python module: ${MODULE_NAME} (found version \"${MODULE_VERSION}\")") +# else() +# message(WARNING "Can't find python module \"${MODULE_NAME}\", user install it using pip...") +# execute_process( +# COMMAND ${Python_EXECUTABLE} -m pip install --upgrade ${MODULE_NAME} +# OUTPUT_STRIP_TRAILING_WHITESPACE +# ) +# endif() +# endfunction() # Look for required python modules -search_python_module(setuptools) -search_python_module(wheel) +# search_python_module(setuptools) +# search_python_module(wheel) set(PACKAGING_DIR packaging) -set(PACKAGING_DIR_FULL ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGING_DIR}) +set(PACKAGING_DIR_FULL ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGING_DIR}/${BUILD_VERSION}) file(MAKE_DIRECTORY ${PACKAGING_DIR_FULL}) set(PYTHON_PACKAGING_TARGET_NAME python_${PACKAGING_DIR}) add_custom_target(${PYTHON_PACKAGING_TARGET_NAME} ALL - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/$/setup.py ${PACKAGING_DIR_FULL} + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/setup.py ${PACKAGING_DIR_FULL} COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/${SWIG_TARGET_NAME}/ccapi.py ${PACKAGING_DIR_FULL} COMMAND ${CMAKE_COMMAND} -E copy $ ${PACKAGING_DIR_FULL} COMMAND ${Python_EXECUTABLE} setup.py bdist_wheel + COMMAND ${Python_EXECUTABLE} -m pip install --upgrade . WORKING_DIRECTORY ${PACKAGING_DIR_FULL} - ) +) add_dependencies(${PYTHON_PACKAGING_TARGET_NAME} ${SWIG_TARGET_NAME}) -install(CODE " - execute_process(COMMAND ${Python_EXECUTABLE} -m pip install --upgrade . - WORKING_DIRECTORY ${PACKAGING_DIR_FULL}) -") - # Test if(BUILD_TEST) set(TEST_DIR ${CMAKE_CURRENT_BINARY_DIR}/test) - set(VENV_DIR ${TEST_DIR}/.venv) - if(WIN32) - set(VENV_Python_EXECUTABLE "${VENV_DIR}\\Scripts\\python.exe") - else() - set(VENV_Python_EXECUTABLE ${VENV_DIR}/bin/python) - endif() configure_file(test/test.py ${TEST_DIR}/test.py COPYONLY) - add_custom_command(TARGET ${PYTHON_PACKAGING_TARGET_NAME} POST_BUILD - COMMAND ${Python_EXECUTABLE} -m venv ${VENV_DIR} - # Must not call it in a folder containing the setup.py otherwise pip call it - # (i.e. "python setup.py bdist") while we want to consume the wheel package - COMMAND ${VENV_Python_EXECUTABLE} -m pip install --find-links=${PACKAGING_DIR_FULL}/dist ccapi - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) add_test(NAME test - COMMAND ${VENV_Python_EXECUTABLE} test.py + COMMAND ${Python_EXECUTABLE} test.py WORKING_DIRECTORY ${TEST_DIR}) endif() diff --git a/binding/python/example/src/enable_library_logging/main.py b/binding/python/example/enable_library_logging/main.py similarity index 100% rename from binding/python/example/src/enable_library_logging/main.py rename to binding/python/example/enable_library_logging/main.py diff --git a/binding/python/example/src/enable_library_logging/user_specified_cmake_include.cmake b/binding/python/example/enable_library_logging/user_specified_cmake_include.cmake similarity index 100% rename from binding/python/example/src/enable_library_logging/user_specified_cmake_include.cmake rename to binding/python/example/enable_library_logging/user_specified_cmake_include.cmake diff --git a/binding/python/example/src/execution_management_simple_request/main.py b/binding/python/example/execution_management_simple_request/main.py similarity index 100% rename from binding/python/example/src/execution_management_simple_request/main.py rename to binding/python/example/execution_management_simple_request/main.py diff --git a/binding/python/example/src/execution_management_simple_subscription/main.py b/binding/python/example/execution_management_simple_subscription/main.py similarity index 100% rename from binding/python/example/src/execution_management_simple_subscription/main.py rename to binding/python/example/execution_management_simple_subscription/main.py diff --git a/binding/python/example/src/fix_simple/main.py b/binding/python/example/fix_simple/main.py similarity index 100% rename from binding/python/example/src/fix_simple/main.py rename to binding/python/example/fix_simple/main.py diff --git a/binding/python/example/src/handle_exception/main.py b/binding/python/example/handle_exception/main.py similarity index 100% rename from binding/python/example/src/handle_exception/main.py rename to binding/python/example/handle_exception/main.py diff --git a/binding/python/example/src/market_data_multiple_subscription/main.py b/binding/python/example/market_data_multiple_subscription/main.py similarity index 84% rename from binding/python/example/src/market_data_multiple_subscription/main.py rename to binding/python/example/market_data_multiple_subscription/main.py index a496d513..1daea3e6 100644 --- a/binding/python/example/src/market_data_multiple_subscription/main.py +++ b/binding/python/example/market_data_multiple_subscription/main.py @@ -6,7 +6,8 @@ def __init__(self): def processEvent(self, event: Event, session: Session) -> bool: if event.getType() == Event.Type_SUBSCRIPTION_DATA: for message in event.getMessageList(): - print(f'Best bid and ask at {message.getTimeISO()} are:') + correlationId = message.getCorrelationIdList()[0] + print(f'{correlationId}: Best bid and ask at {message.getTimeISO()} are:') for element in message.getElementList(): elementNameValueMap = element.getNameValueMap() for name, value in elementNameValueMap.items(): @@ -18,8 +19,8 @@ def processEvent(self, event: Event, session: Session) -> bool: config = SessionConfigs() session = Session(option, config, eventHandler) subscriptionList = SubscriptionList() - subscriptionList.append(Subscription('coinbase', 'BTC-USD', 'MARKET_DEPTH')) - subscriptionList.append(Subscription('coinbase', 'ETH-USD', 'MARKET_DEPTH')) + subscriptionList.append(Subscription('coinbase', 'BTC-USD', 'MARKET_DEPTH', '', 'BTC')) + subscriptionList.append(Subscription('coinbase', 'ETH-USD', 'MARKET_DEPTH', '', 'ETH')) session.subscribe(subscriptionList) time.sleep(10) session.stop() diff --git a/binding/python/example/src/market_data_simple_request/main.py b/binding/python/example/market_data_simple_request/main.py similarity index 100% rename from binding/python/example/src/market_data_simple_request/main.py rename to binding/python/example/market_data_simple_request/main.py diff --git a/binding/python/example/src/market_data_simple_subscription/main.py b/binding/python/example/market_data_simple_subscription/main.py similarity index 100% rename from binding/python/example/src/market_data_simple_subscription/main.py rename to binding/python/example/market_data_simple_subscription/main.py diff --git a/binding/python/example/src/__init__.py b/binding/python/example/src/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/binding/python/example/src/enable_library_logging/__init__.py b/binding/python/example/src/enable_library_logging/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/binding/python/example/src/execution_management_simple_request/__init__.py b/binding/python/example/src/execution_management_simple_request/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/binding/python/example/src/execution_management_simple_subscription/__init__.py b/binding/python/example/src/execution_management_simple_subscription/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/binding/python/example/src/fix_simple/__init__.py b/binding/python/example/src/fix_simple/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/binding/python/example/src/handle_exception/__init__.py b/binding/python/example/src/handle_exception/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/binding/python/example/src/market_data_multiple_subscription/__init__.py b/binding/python/example/src/market_data_multiple_subscription/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/binding/python/example/src/market_data_simple_request/__init__.py b/binding/python/example/src/market_data_simple_request/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/binding/python/example/src/market_data_simple_subscription/__init__.py b/binding/python/example/src/market_data_simple_subscription/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/binding/python/setup.py.in b/binding/python/setup.py.in index 6f22d55f..f496749d 100644 --- a/binding/python/setup.py.in +++ b/binding/python/setup.py.in @@ -19,4 +19,5 @@ setup( packages=[''], py_modules = ['ccapi'], package_data={'': ['$']}, + install_requires=['setuptools', 'wheel'], ) diff --git a/binding/swig_interface.i b/binding/swig_interface.i.in similarity index 79% rename from binding/swig_interface.i rename to binding/swig_interface.i.in index 6b5057b9..0f6a39d2 100644 --- a/binding/swig_interface.i +++ b/binding/swig_interface.i.in @@ -34,18 +34,21 @@ SWIG_exception(SWIG_RuntimeError, s.c_str()); } } -%template(map_string_string) std::map; -%template(map_int_string) std::map; -%template(pair_int_string) std::pair; -%template(pair_long_long_long_long) std::pair; -%template(vector_pair_int_string) std::vector >; +%template(MapStringString) std::map; +%template(MapIntString) std::map; +%template(PairIntString) std::pair; +%template(PairLongLongLongLong) std::pair; +%template(VectorPairIntString) std::vector >; %template(ElementList) std::vector; -%template(vector_string) std::vector; +%template(VectorString) std::vector; %template(MessageList) std::vector; -%template(map_string_map_string_string) std::map >; +%template(MapStringMapStringString) std::map >; %template(EventList) std::vector; %template(SubscriptionList) std::vector; %template(RequestList) std::vector; + +@SWIG_INTERFACE_CCAPI_LANGUAGE_SPECIFIC@ + %include "ccapi_cpp/ccapi_macro.h" %include "ccapi_cpp/ccapi_util.h" %include "ccapi_cpp/ccapi_element.h" diff --git a/format.sh b/format.sh new file mode 100755 index 00000000..9fd3229a --- /dev/null +++ b/format.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +./format_cpp.sh +./format_java.sh diff --git a/format_cpp.sh b/format_cpp.sh new file mode 100755 index 00000000..4beec773 --- /dev/null +++ b/format_cpp.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +find . -type f -not -path "*/dependency/*" -not -path "*/build/*" -not -name "ccapi_hmac.h" \( -name "*.h" -or -name "*.cpp" -or -name "*.hpp" \) -exec clang-format -i -style=file {} \+ diff --git a/format_java.sh b/format_java.sh new file mode 100755 index 00000000..ff318858 --- /dev/null +++ b/format_java.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +find . -type f -path "*/binding/*" -not -path "*/dependency/*" -not -path "*/build/*" -name "*.java" -exec clang-format -i -style=file {} \+ diff --git a/include/ccapi_cpp/ccapi_fix_connection.h b/include/ccapi_cpp/ccapi_fix_connection.h index 32d10ae2..4fe4ee3e 100644 --- a/include/ccapi_cpp/ccapi_fix_connection.h +++ b/include/ccapi_cpp/ccapi_fix_connection.h @@ -6,11 +6,11 @@ #include "ccapi_cpp/ccapi_subscription.h" namespace beast = boost::beast; namespace ccapi { +/** + * This class represents a TCP socket connection for the FIX API. + */ template class FixConnection CCAPI_FINAL { - /** - * This class represents a TCP socket connection for the FIX API. - */ public: FixConnection(std::string host, std::string port, Subscription subscription, std::shared_ptr streamPtr) : host(host), port(port), subscription(subscription), streamPtr(streamPtr) { diff --git a/include/ccapi_cpp/ccapi_queue.h b/include/ccapi_cpp/ccapi_queue.h index bbcf5810..a0df0a03 100644 --- a/include/ccapi_cpp/ccapi_queue.h +++ b/include/ccapi_cpp/ccapi_queue.h @@ -6,10 +6,10 @@ #include "ccapi_cpp/ccapi_logger.h" namespace ccapi { -template /** * This class represents a generic FIFO queue. */ +template class Queue { public: std::string EXCEPTION_QUEUE_FULL = "queue is full"; diff --git a/include/ccapi_cpp/ccapi_request.h b/include/ccapi_cpp/ccapi_request.h index 19905e96..4b02d4c4 100644 --- a/include/ccapi_cpp/ccapi_request.h +++ b/include/ccapi_cpp/ccapi_request.h @@ -7,6 +7,15 @@ #include "ccapi_cpp/ccapi_macro.h" #include "ccapi_cpp/ccapi_util_private.h" +// We use macros instead of static constants in the Request class so that SWIG can properly generate C# bindings +#define CCAPI_REQUEST_OPERATION_TYPE_CUSTOM 0x100 +#define CCAPI_REQUEST_OPERATION_TYPE_GENERIC_PUBLIC_REQUEST 0x200 +#define CCAPI_REQUEST_OPERATION_TYPE_GENERIC_PRIVATE_REQUEST 0x300 +#define CCAPI_REQUEST_OPERATION_TYPE_FIX 0x400 +#define CCAPI_REQUEST_OPERATION_TYPE_MARKET_DATA 0x500 +#define CCAPI_REQUEST_OPERATION_TYPE_EXECUTION_MANAGEMENT 0x600 +#define CCAPI_REQUEST_OPERATION_TYPE_EXECUTION_MANAGEMENT_ORDER CCAPI_REQUEST_OPERATION_TYPE_EXECUTION_MANAGEMENT +#define CCAPI_REQUEST_OPERATION_TYPE_EXECUTION_MANAGEMENT_ACCOUNT 0x700 namespace ccapi { /** * A single request. Request objects are created using Request constructors. They are used with Session::sendRequest() or Session::sendRequestByWebsocket() or @@ -16,29 +25,21 @@ namespace ccapi { */ class Request CCAPI_FINAL { public: - static constexpr int operationTypeCustom = 0x100; - static constexpr int operationTypeGenericPublicRequest = 0x200; - static constexpr int operationTypeGenericPrivateRequest = 0x300; - static constexpr int operationTypeFix = 0x400; - static constexpr int operationTypeMarketData = 0x500; - static constexpr int operationTypeExecutionManagement = 0x600; - static constexpr int operationTypeExecutionManagementOrder = operationTypeExecutionManagement; - static constexpr int operationTypeExecutionManagementAccount = 0x700; enum class Operation { - CUSTOM = operationTypeCustom, - GENERIC_PUBLIC_REQUEST = operationTypeGenericPublicRequest, - GENERIC_PRIVATE_REQUEST = operationTypeGenericPrivateRequest, - FIX = operationTypeFix, - GET_RECENT_TRADES = operationTypeMarketData, + CUSTOM = CCAPI_REQUEST_OPERATION_TYPE_CUSTOM, + GENERIC_PUBLIC_REQUEST = CCAPI_REQUEST_OPERATION_TYPE_GENERIC_PUBLIC_REQUEST, + GENERIC_PRIVATE_REQUEST = CCAPI_REQUEST_OPERATION_TYPE_GENERIC_PRIVATE_REQUEST, + FIX = CCAPI_REQUEST_OPERATION_TYPE_FIX, + GET_RECENT_TRADES = CCAPI_REQUEST_OPERATION_TYPE_MARKET_DATA, GET_RECENT_AGG_TRADES, GET_INSTRUMENT, GET_INSTRUMENTS, - CREATE_ORDER = operationTypeExecutionManagementOrder, + CREATE_ORDER = CCAPI_REQUEST_OPERATION_TYPE_EXECUTION_MANAGEMENT_ORDER, CANCEL_ORDER, GET_ORDER, GET_OPEN_ORDERS, CANCEL_OPEN_ORDERS, - GET_ACCOUNTS = operationTypeExecutionManagementAccount, + GET_ACCOUNTS = CCAPI_REQUEST_OPERATION_TYPE_EXECUTION_MANAGEMENT_ACCOUNT, GET_ACCOUNT_BALANCES, GET_ACCOUNT_POSITIONS, }; @@ -111,7 +112,7 @@ class Request CCAPI_FINAL { } else if (operation == Operation::FIX) { this->serviceName = CCAPI_FIX; } else { - this->serviceName = static_cast(operation) >= operationTypeExecutionManagement ? CCAPI_EXECUTION_MANAGEMENT : CCAPI_MARKET_DATA; + this->serviceName = static_cast(operation) >= CCAPI_REQUEST_OPERATION_TYPE_EXECUTION_MANAGEMENT ? CCAPI_EXECUTION_MANAGEMENT : CCAPI_MARKET_DATA; } if (this->correlationId.empty()) { this->correlationId = UtilString::generateRandomString(CCAPI_CORRELATION_ID_GENERATED_LENGTH); diff --git a/include/ccapi_cpp/service/ccapi_execution_management_service.h b/include/ccapi_cpp/service/ccapi_execution_management_service.h index 2a96c8e4..a5c8ea3f 100644 --- a/include/ccapi_cpp/service/ccapi_execution_management_service.h +++ b/include/ccapi_cpp/service/ccapi_execution_management_service.h @@ -115,10 +115,11 @@ class ExecutionManagementService : public Service { Request::Operation operation = request.getOperation(); message.setType(this->requestOperationToMessageTypeMap.at(operation)); auto castedOperation = static_cast(operation); - if (castedOperation >= Request::operationTypeExecutionManagementOrder && castedOperation < Request::operationTypeExecutionManagementAccount) { + if (castedOperation >= CCAPI_REQUEST_OPERATION_TYPE_EXECUTION_MANAGEMENT_ORDER && + castedOperation < CCAPI_REQUEST_OPERATION_TYPE_EXECUTION_MANAGEMENT_ACCOUNT) { this->extractOrderInfoFromRequest(elementList, request, operation, document); message.setElementList(elementList); - } else if (castedOperation >= Request::operationTypeExecutionManagementAccount) { + } else if (castedOperation >= CCAPI_REQUEST_OPERATION_TYPE_EXECUTION_MANAGEMENT_ACCOUNT) { this->extractAccountInfoFromRequest(elementList, request, operation, document); message.setElementList(elementList); } diff --git a/include/ccapi_cpp/service/ccapi_fix_service.h b/include/ccapi_cpp/service/ccapi_fix_service.h index 3ddbde9f..09624268 100644 --- a/include/ccapi_cpp/service/ccapi_fix_service.h +++ b/include/ccapi_cpp/service/ccapi_fix_service.h @@ -11,11 +11,11 @@ #include "hffix.hpp" namespace hff = hffix; namespace ccapi { -template /** * Defines a service which provides access to exchange API and normalizes them. This is a base class that implements generic functionalities for dealing with * exchange FIX APIs. The Session object is responsible for routing requests and subscriptions to the desired concrete service. */ +template class FixService : public Service { public: FixService(std::function*)> eventHandler, SessionOptions sessionOptions, SessionConfigs sessionConfigs,