diff --git a/README.md b/README.md index 816a814a..89db58b4 100644 --- a/README.md +++ b/README.md @@ -51,7 +51,7 @@ # 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, Java, and C# are provided. +* Bindings for other languages such as Python, Java, C#, and Go 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: @@ -109,7 +109,7 @@ mkdir binding/build cd binding/build rm -rf * (if rebuild from scratch) -cmake -DBUILD_PYTHON=ON -DBUILD_VERSION=1.0.0 .. (Use -DBUILD_JAVA=ON if the target language is Java, -DBUILD_CSHARP=ON if the target language is C#) +cmake -DBUILD_PYTHON=ON -DBUILD_VERSION=1.0.0 .. (Use -DBUILD_JAVA=ON if the target language is Java, -DBUILD_CSHARP=ON if the target language is C#, -DBUILD_GO=ON if the target language is Go) cmake --build . ``` * The packaged build artifacts are located in the `binding/build//packaging/` directory. SWIG generated raw files and build artifacts are located in the `binding/build//ccapi_binding_` directory. @@ -177,6 +177,19 @@ env LD_LIBRARY_PATH="$LD_LIBRARY_PATH:../../../build/csharp/packaging/1.0.0" dot * "error CS0246: The type or namespace name 'ccapi' could not be found". Check that you aren't missing the ccapi assembly reference. * "System.DllNotFoundException: Unable to load shared library 'ccapi_binding_csharp.so' or one of its dependencies.". Check that environment variable `LD_LIBRARY_PATH` includes `binding/build/csharp/packaging/1.0.0`. +[Go](binding/go/example) +* Go API is nearly identical to C++ API and covers nearly all the functionalities from C++ API. +* Build and install the Go binding as shown [above](#non-c). +* Inside a concrete example directory (e.g. binding/go/example/market_data_simple_subscription), run +``` +go clean (if rebuild from scratch) +source ../../../build/go/packaging/1.0.0/export_compiler_options.sh (this step is important) +go build . +./main +``` +* Troubleshoot: + * Some C/C++ header files not found. Check that you sourced the export_compiler_options.sh file which provides important environment variables needed by the cgo tool. + ## Documentations ### Simple Market Data @@ -187,7 +200,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/market_data_simple_request/main.py) / [Java](binding/java/example/market_data_simple_request/Main.java) / [C#](binding/csharp/example/market_data_simple_request/MainProgram.cs) +[C++](example/src/market_data_simple_request/main.cpp) / [Python](binding/python/example/market_data_simple_request/main.py) / [Java](binding/java/example/market_data_simple_request/Main.java) / [C#](binding/csharp/example/market_data_simple_request/MainProgram.cs) / [Go](binding/go/example/market_data_simple_request/main.go) ``` #include "ccapi_cpp/ccapi_session.h" namespace ccapi { @@ -260,7 +273,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/market_data_simple_subscription/main.py) / [Java](binding/java/example/market_data_simple_subscription/Main.java) / [C#](binding/csharp/example/market_data_simple_subscription/MainProgram.cs) +[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) / [C#](binding/csharp/example/market_data_simple_subscription/MainProgram.cs) / [Go](binding/go/example/market_data_simple_subscription/main.go) ``` #include "ccapi_cpp/ccapi_session.h" namespace ccapi { @@ -434,7 +447,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/execution_management_simple_request/main.py) / [Java](binding/java/example/execution_management_simple_request/Main.java) / [C#](binding/csharp/example/execution_management_simple_request/MainProgram.cs) +[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) / [C#](binding/csharp/example/execution_management_simple_request/MainProgram.cs) / [Go](binding/go/example/execution_management_simple_request/main.go) ``` #include "ccapi_cpp/ccapi_session.h" namespace ccapi { @@ -524,7 +537,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/execution_management_simple_subscription/main.py) / [Java](binding/java/example/execution_management_simple_subscription/Main.java) / [C#](binding/csharp/example/execution_management_simple_subscription/MainProgram.cs) +[C++](example/src/execution_management_simple_subscription/main.cpp) / [Python](binding/python/example/execution_management_simple_subscription/main.py) / [Java](binding/java/example/execution_management_simple_subscription/Main.java) / [C#](binding/csharp/example/execution_management_simple_subscription/MainProgram.cs) / [Go](binding/go/example/execution_management_simple_subscription/main.go) ``` #include "ccapi_cpp/ccapi_session.h" namespace ccapi { @@ -767,7 +780,7 @@ For a specific exchange and instrument, submit a simple limit order. **Code:** -[C++](example/src/fix_simple/main.cpp) / [Python](binding/python/example/fix_simple/main.py) / [Java](binding/java/example/fix_simple/Main.java) / [C#](binding/csharp/example/fix_simple/MainProgram.cs) +[C++](example/src/fix_simple/main.cpp) / [Python](binding/python/example/fix_simple/main.py) / [Java](binding/java/example/fix_simple/Main.java) / [C#](binding/csharp/example/fix_simple/MainProgram.cs) / [Go](binding/go/example/fix_simple/main.go) ``` #include "ccapi_cpp/ccapi_session.h" namespace ccapi { @@ -911,7 +924,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/enable_library_logging/main.py) / [Java](binding/java/example/enable_library_logging/Main.java) / [C#](binding/csharp/example/enable_library_logging/MainProgram.cs) +[C++](example/src/enable_library_logging/main.cpp) / [Python](binding/python/example/enable_library_logging/main.py) / [Java](binding/java/example/enable_library_logging/Main.java) / [C#](binding/csharp/example/enable_library_logging/MainProgram.cs) / [Go](binding/go/example/enable_library_logging/main.go) 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 c306762f..2b6cd4b1 100644 --- a/binding/CMakeLists.txt +++ b/binding/CMakeLists.txt @@ -76,21 +76,18 @@ else() endforeach() endif() -option(BUILD_PYTHON "Build 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") endif() get_filename_component(CCAPI_PROJECT_DIR ../ ABSOLUTE) message(STATUS "CCAPI_PROJECT_DIR: ${CCAPI_PROJECT_DIR}") +find_package(OpenSSL REQUIRED) +message(STATUS "OPENSSL_INCLUDE_DIR: ${OPENSSL_INCLUDE_DIR}") +message(STATUS "OPENSSL_CRYPTO_LIBRARY: ${OPENSSL_CRYPTO_LIBRARY}") +get_filename_component(OPENSSL_CRYPTO_LIBRARY_DIR ${OPENSSL_CRYPTO_LIBRARY} DIRECTORY) +message(STATUS "OPENSSL_SSL_LIBRARY: ${OPENSSL_SSL_LIBRARY}") +get_filename_component(OPENSSL_SSL_LIBRARY_DIR ${OPENSSL_SSL_LIBRARY} DIRECTORY) if(NOT CCAPI_LEGACY_USE_WEBSOCKETPP) message(STATUS "use boost beast websocket") if(NOT BOOST_INCLUDE_DIR) @@ -145,12 +142,19 @@ else() set(HFFIX_INCLUDE_DIR ${CCAPI_PROJECT_DIR}/dependency/hffix/include) include_directories(${CCAPI_PROJECT_DIR}/include ${WEBSOCKETPP_INCLUDE_DIR} ${BOOST_INCLUDE_DIR} ${RAPIDJSON_INCLUDE_DIR} ${HFFIX_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR}) endif() -find_package(OpenSSL REQUIRED) + link_libraries(OpenSSL::Crypto OpenSSL::SSL) +find_package(ZLIB REQUIRED) +link_libraries(ZLIB::ZLIB) +message(STATUS "ZLIB_INCLUDE_DIRS: ${ZLIB_INCLUDE_DIRS}") +message(STATUS "ZLIB_LIBRARIES: ${ZLIB_LIBRARIES}") + set(CMAKE_POSITION_INDEPENDENT_CODE ON) set(SOURCE_LOGGER ${CCAPI_PROJECT_DIR}/binding/ccapi_logger.cpp) find_package(SWIG REQUIRED) +message(STATUS "SWIG_VERSION: ${SWIG_VERSION}") +message(STATUS "SWIG_EXECUTABLE: ${SWIG_EXECUTABLE}") include(UseSWIG) if(BUILD_TEST) include(CTest) @@ -217,33 +221,40 @@ add_compile_definitions(CCAPI_ENABLE_EXCHANGE_MEXC_FUTURES) add_compile_definitions(CCAPI_ENABLE_EXCHANGE_WHITEBIT) -find_package(ZLIB REQUIRED) -link_libraries(ZLIB::ZLIB) +if(CCAPI_ENABLE_LOG_ERROR) + add_compile_definitions(CCAPI_ENABLE_LOG_ERROR) +endif() -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) +if(CCAPI_ENABLE_LOG_WARN) + add_compile_definitions(CCAPI_ENABLE_LOG_WARN) endif() + +if(CCAPI_ENABLE_LOG_INFO) + add_compile_definitions(CCAPI_ENABLE_LOG_INFO) +endif() + +if(CCAPI_ENABLE_LOG_DEBUG) + add_compile_definitions(CCAPI_ENABLE_LOG_DEBUG) +endif() + +if(CCAPI_ENABLE_LOG_TRACE) + add_compile_definitions(CCAPI_ENABLE_LOG_TRACE) +endif() + +set(LANG_LIST "python" "java" "csharp" "go" "javascript") +foreach(LANG IN LISTS LANG_LIST) + string(TOUPPER ${LANG} LANG_UPPER) + option(BUILD_${LANG_UPPER} "Build ${LANG} library" OFF) + message(STATUS "Build ${LANG_UPPER}: ${BUILD_${LANG_UPPER}}") + if(BUILD_${LANG_UPPER}) + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/${LANG}/swig_interface_ccapi_language_specific.i") + file(READ ${LANG}/swig_interface_ccapi_language_specific.i SWIG_INTERFACE_CCAPI_LANGUAGE_SPECIFIC) + endif() + configure_file( + swig_interface.i.in + ${CMAKE_BINARY_DIR}/${LANG}/swig_interface.i + @ONLY) + set(SWIG_INTERFACE ${CMAKE_BINARY_DIR}/${LANG}/swig_interface.i) + add_subdirectory(${LANG}) + endif() +endforeach() diff --git a/binding/csharp/CMakeLists.txt b/binding/csharp/CMakeLists.txt index 56004895..70e37257 100644 --- a/binding/csharp/CMakeLists.txt +++ b/binding/csharp/CMakeLists.txt @@ -58,12 +58,10 @@ add_dependencies(${CSHARP_PACKAGING_TARGET_NAME} ${SWIG_TARGET_NAME}) # Test if(BUILD_TEST) - # message(STATUS "BUILD_TEST") set(TEST_DIR ${CMAKE_CURRENT_BINARY_DIR}/test) file(COPY example DESTINATION ${TEST_DIR}) set(SUBDIRECTORY_LIST "enable_library_logging" "execution_management_simple_request" "execution_management_simple_subscription" "fix_simple" "handle_exception" "market_data_multiple_subscription" "market_data_simple_request" "market_data_simple_subscription") foreach(SUBDIRECTORY IN LISTS SUBDIRECTORY_LIST) - # message(STATUS "SUBDIRECTORY=${SUBDIRECTORY}") set(CSHARP_TEST_TARGET_NAME csharp_test_${SUBDIRECTORY}) add_custom_target(${CSHARP_TEST_TARGET_NAME} ALL COMMAND ${DOTNET_EXECUTABLE} clean diff --git a/binding/csharp/example/enable_library_logging/MainProgram.cs b/binding/csharp/example/enable_library_logging/MainProgram.cs index 066708e0..5e448ffd 100644 --- a/binding/csharp/example/enable_library_logging/MainProgram.cs +++ b/binding/csharp/example/enable_library_logging/MainProgram.cs @@ -1,3 +1,4 @@ +// usage: when generating the binding code, do cmake -DCCAPI_ENABLE_LOG_TRACE=ON ..., see https://github.com/crypto-chassis/ccapi#non-c public class MainProgram { class MyLogger : ccapi.Logger { public override void LogMessage(string severity, string threadId, string timeISO, string fileName, string lineNumber, string message) { diff --git a/binding/csharp/example/enable_library_logging/user_specified_cmake_include.cmake b/binding/csharp/example/enable_library_logging/user_specified_cmake_include.cmake deleted file mode 100644 index ffdace46..00000000 --- a/binding/csharp/example/enable_library_logging/user_specified_cmake_include.cmake +++ /dev/null @@ -1,3 +0,0 @@ -add_compile_definitions(CCAPI_ENABLE_LOG_TRACE) -# usage: when generating the binding code, do cmake -DCMAKE_PROJECT_INCLUDE= ..., see https://github.com/crypto-chassis/ccapi#non-c -# see https://cmake.org/cmake/help/latest/variable/CMAKE_PROJECT_INCLUDE.html diff --git a/binding/csharp/example/handle_exception/MainProgram.cs b/binding/csharp/example/handle_exception/MainProgram.cs index ddd19de2..14015af6 100644 --- a/binding/csharp/example/handle_exception/MainProgram.cs +++ b/binding/csharp/example/handle_exception/MainProgram.cs @@ -5,7 +5,7 @@ public override bool ProcessEvent(ccapi.Event event_, ccapi.Session session) { throw new System.Exception("oops"); } catch (System.Exception e) { System.Console.WriteLine(e.ToString()); - System.Environment.Exit(0); + System.Environment.Exit(1); } return true; } diff --git a/binding/csharp/example/market_data_simple_subscription/MainProgram.cs b/binding/csharp/example/market_data_simple_subscription/MainProgram.cs index 51fb5758..1d5ac5fc 100644 --- a/binding/csharp/example/market_data_simple_subscription/MainProgram.cs +++ b/binding/csharp/example/market_data_simple_subscription/MainProgram.cs @@ -1,7 +1,9 @@ class MainProgram { class MyEventHandler : ccapi.EventHandler { public override bool ProcessEvent(ccapi.Event event_, ccapi.Session session) { - if (event_.GetType_() == ccapi.Event.Type.SUBSCRIPTION_DATA) { + if (event_.GetType_() == ccapi.Event.Type.SUBSCRIPTION_STATUS) { + System.Console.WriteLine(string.Format("Received an event of type SUBSCRIPTION_STATUS:\n{0}", event_.ToStringPretty(2, 2))); + } else if (event_.GetType_() == ccapi.Event.Type.SUBSCRIPTION_DATA) { foreach (var message in event_.GetMessageList()) { System.Console.WriteLine(string.Format("Best bid and ask at {0} are:", message.GetTimeISO())); foreach (var element in message.GetElementList()) { diff --git a/binding/go/CMakeLists.txt b/binding/go/CMakeLists.txt new file mode 100644 index 00000000..5454dfac --- /dev/null +++ b/binding/go/CMakeLists.txt @@ -0,0 +1,129 @@ +set(NAME binding_${LANG}) +project(${NAME}) +set(SWIG_TARGET_NAME ccapi_${NAME}) + +# Find go cli +find_program(GO_EXECUTABLE NAMES go REQUIRED) +if(NOT GO_EXECUTABLE) + message(FATAL_ERROR "Check for go Program: not found") +else() + message(STATUS "Found go Program: ${GO_EXECUTABLE}") +endif() + +execute_process( + COMMAND ${GO_EXECUTABLE} version + OUTPUT_VARIABLE GO_EXECUTABLE_VERSION + OUTPUT_STRIP_TRAILING_WHITESPACE +) +message(STATUS "Go version: ${GO_EXECUTABLE_VERSION}") + +set(GO_PACKAGE "ccapi") + +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${SWIG_TARGET_NAME}) + +add_custom_target(${SWIG_TARGET_NAME} ALL + COMMAND ${SWIG_EXECUTABLE} -outcurrentdir -c++ -go -package ${GO_PACKAGE} -I${CCAPI_PROJECT_DIR}/include ${SWIG_INTERFACE} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/${SWIG_TARGET_NAME} +) +add_dependencies(${SWIG_TARGET_NAME} boost rapidjson hffix) + +set(PACKAGING_DIR packaging) +set(PACKAGING_DIR_FULL ${CMAKE_CURRENT_BINARY_DIR}/${PACKAGING_DIR}/${BUILD_VERSION}) +file(MAKE_DIRECTORY ${PACKAGING_DIR_FULL}) +set(GO_PACKAGING_TARGET_NAME ${LANG}_${PACKAGING_DIR}) + +set(COMPILER_OPTIONS_FILE_PATH ${PACKAGING_DIR_FULL}/export_compiler_options.sh) +file(WRITE ${COMPILER_OPTIONS_FILE_PATH} "") +set(CGO_CXXFLAGS_LIST "") +list(APPEND CGO_CXXFLAGS_LIST -std=c++17 -fPIC -Wno-deprecated-declarations) +if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") +list(APPEND CGO_CXXFLAGS_LIST -g) +else() +list(APPEND CGO_CXXFLAGS_LIST -O3 -DNDEBUG) +endif() +if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU") +list(APPEND CGO_CXXFLAGS_LIST -pthread) +endif() +list(APPEND CGO_CXXFLAGS_LIST -I${CCAPI_PROJECT_DIR}/include -I${BOOST_INCLUDE_DIR} -I${RAPIDJSON_INCLUDE_DIR} -I${HFFIX_INCLUDE_DIR} -I${OPENSSL_INCLUDE_DIR}) +list(APPEND CGO_CXXFLAGS_LIST -DCCAPI_ENABLE_SERVICE_MARKET_DATA -DCCAPI_ENABLE_SERVICE_EXECUTION_MANAGEMENT -DCCAPI_ENABLE_SERVICE_FIX -DCCAPI_ENABLE_EXCHANGE_COINBASE -DCCAPI_ENABLE_EXCHANGE_GEMINI -DCCAPI_ENABLE_EXCHANGE_KRAKEN -DCCAPI_ENABLE_EXCHANGE_KRAKEN_FUTURES -DCCAPI_ENABLE_EXCHANGE_BITSTAMP -DCCAPI_ENABLE_EXCHANGE_BITFINEX -DCCAPI_ENABLE_EXCHANGE_BITMEX -DCCAPI_ENABLE_EXCHANGE_BINANCE_US -DCCAPI_ENABLE_EXCHANGE_BINANCE -DCCAPI_ENABLE_EXCHANGE_BINANCE_MARGIN -DCCAPI_ENABLE_EXCHANGE_BINANCE_USDS_FUTURES -DCCAPI_ENABLE_EXCHANGE_BINANCE_COIN_FUTURES -DCCAPI_ENABLE_EXCHANGE_HUOBI -DCCAPI_ENABLE_EXCHANGE_HUOBI_USDT_SWAP -DCCAPI_ENABLE_EXCHANGE_HUOBI_COIN_SWAP -DCCAPI_ENABLE_EXCHANGE_OKX -DCCAPI_ENABLE_EXCHANGE_ERISX -DCCAPI_ENABLE_EXCHANGE_KUCOIN -DCCAPI_ENABLE_EXCHANGE_KUCOIN_FUTURES -DCCAPI_ENABLE_EXCHANGE_DERIBIT -DCCAPI_ENABLE_EXCHANGE_GATEIO -DCCAPI_ENABLE_EXCHANGE_GATEIO_PERPETUAL_FUTURES -DCCAPI_ENABLE_EXCHANGE_CRYPTOCOM -DCCAPI_ENABLE_EXCHANGE_BYBIT -DCCAPI_ENABLE_EXCHANGE_BYBIT_DERIVATIVES -DCCAPI_ENABLE_EXCHANGE_ASCENDEX -DCCAPI_ENABLE_EXCHANGE_BITGET -DCCAPI_ENABLE_EXCHANGE_BITGET_FUTURES -DCCAPI_ENABLE_EXCHANGE_BITMART -DCCAPI_ENABLE_EXCHANGE_MEXC -DCCAPI_ENABLE_EXCHANGE_MEXC_FUTURES -DCCAPI_ENABLE_EXCHANGE_WHITEBIT) +if(CCAPI_ENABLE_LOG_ERROR) +list(APPEND CGO_CXXFLAGS_LIST -DCCAPI_ENABLE_LOG_ERROR) +endif() +if(CCAPI_ENABLE_LOG_WARN) +list(APPEND CGO_CXXFLAGS_LIST -DCCAPI_ENABLE_LOG_WARN) +endif() +if(CCAPI_ENABLE_LOG_INFO) +list(APPEND CGO_CXXFLAGS_LIST -DCCAPI_ENABLE_LOG_INFO) +endif() +if(CCAPI_ENABLE_LOG_DEBUG) +list(APPEND CGO_CXXFLAGS_LIST -DCCAPI_ENABLE_LOG_DEBUG) +endif() +if(CCAPI_ENABLE_LOG_TRACE) +list(APPEND CGO_CXXFLAGS_LIST -DCCAPI_ENABLE_LOG_TRACE) +endif() +list(JOIN CGO_CXXFLAGS_LIST " " CGO_CXXFLAGS) +message(STATUS "CGO_CXXFLAGS: ${CGO_CXXFLAGS}") +file(APPEND ${COMPILER_OPTIONS_FILE_PATH} "export CGO_CXXFLAGS='${CGO_CXXFLAGS}'\n\n") + +set(CGO_LDFLAGS_LIST "") +if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") +list(APPEND CGO_LDFLAGS_LIST -g) +endif() +list(APPEND CGO_LDFLAGS_LIST -L${OPENSSL_CRYPTO_LIBRARY_DIR} -L${OPENSSL_SSL_LIBRARY_DIR} -lcrypto -lssl) +foreach(ZLIB_LIBRARY IN LISTS ZLIB_LIBRARIES) + get_filename_component(ZLIB_LIBRARY_DIR ${ZLIB_LIBRARY} DIRECTORY) + list(APPEND CGO_LDFLAGS_LIST -L${ZLIB_LIBRARY_DIR} -lz) +endforeach() +list(JOIN CGO_LDFLAGS_LIST " " CGO_LDFLAGS) +message(STATUS "CGO_LDFLAGS: ${CGO_LDFLAGS}") +file(APPEND ${COMPILER_OPTIONS_FILE_PATH} "export CGO_LDFLAGS='${CGO_LDFLAGS}'\n") + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/go.mod.in + ${PACKAGING_DIR_FULL}/go.mod +@ONLY) +add_custom_target(${GO_PACKAGING_TARGET_NAME} ALL + COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_BINARY_DIR}/${SWIG_TARGET_NAME} ${PACKAGING_DIR_FULL} + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/go.mod.in ${PACKAGING_DIR_FULL} + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_SOURCE_DIR}/ccapi_logger.cpp ${PACKAGING_DIR_FULL} + WORKING_DIRECTORY ${PACKAGING_DIR_FULL} +) +add_dependencies(${GO_PACKAGING_TARGET_NAME} ${SWIG_TARGET_NAME}) + +# Test +if(BUILD_TEST) + set(TEST_DIR ${CMAKE_CURRENT_BINARY_DIR}/test) + file(COPY example DESTINATION ${TEST_DIR}) + set(SUBDIRECTORY_LIST "enable_library_logging" "execution_management_simple_request" "execution_management_simple_subscription" "fix_simple" "handle_exception" "market_data_multiple_subscription" "market_data_simple_request" "market_data_simple_subscription") + file(READ ${PACKAGING_DIR_FULL}/export_compiler_options.sh EXPORT_COMPILER_OPTIONS) + string(APPEND EXPORT_COMPILER_OPTIONS "${GO_EXECUTABLE} build .") + file(WRITE ${PACKAGING_DIR_FULL}/build.sh "${EXPORT_COMPILER_OPTIONS}") + foreach(SUBDIRECTORY IN LISTS SUBDIRECTORY_LIST) + set(GO_TEST_TARGET_NAME go_test_${SUBDIRECTORY}) + set(GO_TEST_BUILD_DIRECTORY ${TEST_DIR}/example/${SUBDIRECTORY}) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/go.mod.in_2 + ${GO_TEST_BUILD_DIRECTORY}/go.mod + @ONLY) + add_custom_target(${GO_TEST_TARGET_NAME} ALL + COMMAND bash ${PACKAGING_DIR_FULL}/build.sh + WORKING_DIRECTORY ${GO_TEST_BUILD_DIRECTORY} + ) + add_dependencies(${GO_TEST_TARGET_NAME} ${GO_PACKAGING_TARGET_NAME}) + endforeach() + file(COPY test DESTINATION ${TEST_DIR}) + set(GO_TEST_TARGET_NAME go_test_test) + set(GO_TEST_BUILD_DIRECTORY ${TEST_DIR}/test) + configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/go.mod.in_2 + ${GO_TEST_BUILD_DIRECTORY}/go.mod + @ONLY) + add_custom_target(${GO_TEST_TARGET_NAME} ALL + COMMAND bash ${PACKAGING_DIR_FULL}/build.sh + WORKING_DIRECTORY ${GO_TEST_BUILD_DIRECTORY} + ) + add_dependencies(${GO_TEST_TARGET_NAME} ${GO_PACKAGING_TARGET_NAME}) + add_test(NAME go_test + COMMAND ${GO_TEST_BUILD_DIRECTORY}/main + WORKING_DIRECTORY ${GO_TEST_BUILD_DIRECTORY}) +endif() diff --git a/binding/go/example/enable_library_logging/go.mod b/binding/go/example/enable_library_logging/go.mod new file mode 100644 index 00000000..0e5ae7c4 --- /dev/null +++ b/binding/go/example/enable_library_logging/go.mod @@ -0,0 +1,7 @@ +module main + +require ( + cryptochassis.com/ccapi v1.0.0 +) + +replace cryptochassis.com/ccapi => ../../../build/go/packaging/1.0.0 diff --git a/binding/go/example/enable_library_logging/main.go b/binding/go/example/enable_library_logging/main.go new file mode 100644 index 00000000..221d1237 --- /dev/null +++ b/binding/go/example/enable_library_logging/main.go @@ -0,0 +1,45 @@ +// usage: when generating the binding code, do cmake -DCCAPI_ENABLE_LOG_TRACE=ON ..., see https://github.com/crypto-chassis/ccapi#non-c +package main + +import ( + "cryptochassis.com/ccapi" + "fmt" + "strings" + "sync" + "time" +) + +type MyLogger struct { + ccapi.Logger + m sync.Mutex +} + +func (ml *MyLogger) LogMessage(severity string, threadId string, timeISO string, fileName string, lineNumber string, message string) { + ml.m.Lock() + defer ml.m.Unlock() + fmt.Printf("%s: [%s] {%s:%s} %s%s%s\n", threadId, timeISO, fileName, lineNumber, severity, strings.Repeat(" ", 8), message) +} + +func main() { + ml := &MyLogger{} + ml.Logger = ccapi.NewDirectorLogger(ml) + defer func() { + ccapi.DeleteDirectorLogger(ml.Logger) + }() + ccapi.SetLoggerLogger(ml) + option := ccapi.NewSessionOptions() + defer ccapi.DeleteSessionOptions(option) + config := ccapi.NewSessionConfigs() + defer ccapi.DeleteSessionConfigs(config) + session := ccapi.NewSession(option, config) + defer ccapi.DeleteSession(session) + subscriptionList := ccapi.NewSubscriptionList() + defer ccapi.DeleteSubscriptionList(subscriptionList) + subscription := ccapi.NewSubscription("okx", "BTC-USDT", "MARKET_DEPTH") + defer ccapi.DeleteSubscription(subscription) + subscriptionList.Add(subscription) + session.Subscribe(subscriptionList) + time.Sleep(10 * time.Second) + session.Stop() + fmt.Println("Bye") +} diff --git a/binding/go/example/execution_management_simple_request/go.mod b/binding/go/example/execution_management_simple_request/go.mod new file mode 100644 index 00000000..0e5ae7c4 --- /dev/null +++ b/binding/go/example/execution_management_simple_request/go.mod @@ -0,0 +1,7 @@ +module main + +require ( + cryptochassis.com/ccapi v1.0.0 +) + +replace cryptochassis.com/ccapi => ../../../build/go/packaging/1.0.0 diff --git a/binding/go/example/execution_management_simple_request/main.go b/binding/go/example/execution_management_simple_request/main.go new file mode 100644 index 00000000..589da474 --- /dev/null +++ b/binding/go/example/execution_management_simple_request/main.go @@ -0,0 +1,55 @@ +package main + +import ( + "cryptochassis.com/ccapi" + "fmt" + "os" + "time" +) + +type MyEventHandler struct { + ccapi.EventHandler +} + +func (*MyEventHandler) ProcessEvent(event ccapi.Event, session ccapi.Session) bool { + fmt.Printf("Received an event:\n%s\n", event.ToStringPretty(2, 2)) + return true +} + +func main() { + if len(os.Getenv("OKX_API_KEY")) == 0 { + fmt.Fprintf(os.Stderr, "Please set environment variable OKX_API_KEY\n") + os.Exit(1) + } + if len(os.Getenv("OKX_API_SECRET")) == 0 { + fmt.Fprintf(os.Stderr, "Please set environment variable OKX_API_SECRET\n") + os.Exit(1) + } + if len(os.Getenv("OKX_API_PASSPHRASE")) == 0 { + fmt.Fprintf(os.Stderr, "Please set environment variable OKX_API_PASSPHRASE\n") + os.Exit(1) + } + option := ccapi.NewSessionOptions() + defer ccapi.DeleteSessionOptions(option) + config := ccapi.NewSessionConfigs() + defer ccapi.DeleteSessionConfigs(config) + meh := &MyEventHandler{} + meh.EventHandler = ccapi.NewDirectorEventHandler(meh) + defer func() { + ccapi.DeleteDirectorEventHandler(meh.EventHandler) + }() + session := ccapi.NewSession(option, config, meh.EventHandler) + defer ccapi.DeleteSession(session) + request := ccapi.NewRequest(ccapi.RequestOperation_CREATE_ORDER, "okx", "BTC-USDT") + defer ccapi.DeleteRequest(request) + param := ccapi.NewMapStringString() + defer ccapi.DeleteMapStringString(param) + param.Set("SIDE", "BUY") + param.Set("QUANTITY", "0.0005") + param.Set("LIMIT_PRICE", "20000") + request.AppendParam(param) + session.SendRequest(request) + time.Sleep(10 * time.Second) + session.Stop() + fmt.Println("Bye") +} diff --git a/binding/go/example/execution_management_simple_subscription/go.mod b/binding/go/example/execution_management_simple_subscription/go.mod new file mode 100644 index 00000000..0e5ae7c4 --- /dev/null +++ b/binding/go/example/execution_management_simple_subscription/go.mod @@ -0,0 +1,7 @@ +module main + +require ( + cryptochassis.com/ccapi v1.0.0 +) + +replace cryptochassis.com/ccapi => ../../../build/go/packaging/1.0.0 diff --git a/binding/go/example/execution_management_simple_subscription/main.go b/binding/go/example/execution_management_simple_subscription/main.go new file mode 100644 index 00000000..f1dfbd78 --- /dev/null +++ b/binding/go/example/execution_management_simple_subscription/main.go @@ -0,0 +1,68 @@ +package main + +import ( + "cryptochassis.com/ccapi" + "fmt" + "os" + "time" +) + +type MyEventHandler struct { + ccapi.EventHandler +} + +func (*MyEventHandler) ProcessEvent(event ccapi.Event, session ccapi.Session) bool { + if event.GetType() == ccapi.EventType_SUBSCRIPTION_STATUS { + fmt.Printf("Received an event of type SUBSCRIPTION_STATUS:\n%s\n", event.ToStringPretty(2, 2)) + message := event.GetMessageList().Get(0) + if message.GetType() == ccapi.MessageType_SUBSCRIPTION_STARTED { + request := ccapi.NewRequest(ccapi.RequestOperation_CREATE_ORDER, "okx", "BTC-USDT") + defer ccapi.DeleteRequest(request) + param := ccapi.NewMapStringString() + defer ccapi.DeleteMapStringString(param) + param.Set("SIDE", "BUY") + param.Set("QUANTITY", "0.0005") + param.Set("LIMIT_PRICE", "20000") + request.AppendParam(param) + session.SendRequest(request) + } + } else if event.GetType() == ccapi.EventType_SUBSCRIPTION_DATA { + fmt.Printf("Received an event of type SUBSCRIPTION_DATA:\n%s\n", event.ToStringPretty(2, 2)) + } + return true +} + +func main() { + if len(os.Getenv("OKX_API_KEY")) == 0 { + fmt.Fprintf(os.Stderr, "Please set environment variable OKX_API_KEY\n") + os.Exit(1) + } + if len(os.Getenv("OKX_API_SECRET")) == 0 { + fmt.Fprintf(os.Stderr, "Please set environment variable OKX_API_SECRET\n") + os.Exit(1) + } + if len(os.Getenv("OKX_API_PASSPHRASE")) == 0 { + fmt.Fprintf(os.Stderr, "Please set environment variable OKX_API_PASSPHRASE\n") + os.Exit(1) + } + option := ccapi.NewSessionOptions() + defer ccapi.DeleteSessionOptions(option) + config := ccapi.NewSessionConfigs() + defer ccapi.DeleteSessionConfigs(config) + meh := &MyEventHandler{} + meh.EventHandler = ccapi.NewDirectorEventHandler(meh) + defer func() { + ccapi.DeleteDirectorEventHandler(meh.EventHandler) + }() + session := ccapi.NewSession(option, config, meh.EventHandler) + defer ccapi.DeleteSession(session) + subscriptionList := ccapi.NewSubscriptionList() + defer ccapi.DeleteSubscriptionList(subscriptionList) + subscription := ccapi.NewSubscription("okx", "BTC-USDT", "ORDER_UPDATE") + defer ccapi.DeleteSubscription(subscription) + subscriptionList.Add(subscription) + session.Subscribe(subscriptionList) + time.Sleep(10 * time.Second) + session.Stop() + fmt.Println("Bye") +} diff --git a/binding/go/example/fix_simple/go.mod b/binding/go/example/fix_simple/go.mod new file mode 100644 index 00000000..0e5ae7c4 --- /dev/null +++ b/binding/go/example/fix_simple/go.mod @@ -0,0 +1,7 @@ +module main + +require ( + cryptochassis.com/ccapi v1.0.0 +) + +replace cryptochassis.com/ccapi => ../../../build/go/packaging/1.0.0 diff --git a/binding/go/example/fix_simple/main.go b/binding/go/example/fix_simple/main.go new file mode 100644 index 00000000..14650061 --- /dev/null +++ b/binding/go/example/fix_simple/main.go @@ -0,0 +1,82 @@ +package main + +import ( + "cryptochassis.com/ccapi" + "fmt" + "os" + "time" +) + +type MyEventHandler struct { + ccapi.EventHandler +} + +func (*MyEventHandler) ProcessEvent(event ccapi.Event, session ccapi.Session) bool { + if event.GetType() == ccapi.EventType_AUTHORIZATION_STATUS { + fmt.Printf("Received an event of type AUTHORIZATION_STATUS:\n%s\n", event.ToStringPretty(2, 2)) + message := event.GetMessageList().Get(0) + if message.GetType() == ccapi.MessageType_AUTHORIZATION_SUCCESS { + request := ccapi.NewRequest(ccapi.RequestOperation_FIX, "coinbase", "", "same correlation id for subscription and request") + defer ccapi.DeleteRequest(request) + param := ccapi.NewVectorPairIntString() + defer ccapi.DeleteVectorPairIntString(param) + fixParams := []struct { + tag int + value string + }{ + {35, "D"}, + {11, "6d4eb0fb-2229-469f-873e-557dd78ac11e"}, + {55, "BTC-USD"}, + {54, "1"}, + {44, "20000"}, + {38, "0.001"}, + {40, "2"}, + {59, "1"}, + } + for _, fixParam := range fixParams { + aParam := ccapi.NewPairIntString() + aParam.SetFirst(fixParam.tag) + aParam.SetSecond(fixParam.value) + defer ccapi.DeletePairIntString(aParam) + param.Add(aParam) + } + request.AppendParamFix(param) + session.SendRequest(request) + } + } else if event.GetType() == ccapi.EventType_FIX { + fmt.Printf("Received an event of type FIX:\n%s\n", event.ToStringPretty(2, 2)) + } + return true +} + +func main() { + if len(os.Getenv("COINBASE_API_KEY")) == 0 { + fmt.Fprintf(os.Stderr, "Please set environment variable COINBASE_API_KEY\n") + os.Exit(1) + } + if len(os.Getenv("COINBASE_API_SECRET")) == 0 { + fmt.Fprintf(os.Stderr, "Please set environment variable COINBASE_API_SECRET\n") + os.Exit(1) + } + if len(os.Getenv("COINBASE_API_PASSPHRASE")) == 0 { + fmt.Fprintf(os.Stderr, "Please set environment variable COINBASE_API_PASSPHRASE\n") + os.Exit(1) + } + option := ccapi.NewSessionOptions() + defer ccapi.DeleteSessionOptions(option) + config := ccapi.NewSessionConfigs() + defer ccapi.DeleteSessionConfigs(config) + meh := &MyEventHandler{} + meh.EventHandler = ccapi.NewDirectorEventHandler(meh) + defer func() { + ccapi.DeleteDirectorEventHandler(meh.EventHandler) + }() + session := ccapi.NewSession(option, config, meh.EventHandler) + defer ccapi.DeleteSession(session) + subscription := ccapi.NewSubscription("coinbase", "", "FIX", "", "same correlation id for subscription and request") + defer ccapi.DeleteSubscription(subscription) + session.SubscribeByFix(subscription) + time.Sleep(10 * time.Second) + session.Stop() + fmt.Println("Bye") +} diff --git a/binding/go/example/handle_exception/go.mod b/binding/go/example/handle_exception/go.mod new file mode 100644 index 00000000..0e5ae7c4 --- /dev/null +++ b/binding/go/example/handle_exception/go.mod @@ -0,0 +1,7 @@ +module main + +require ( + cryptochassis.com/ccapi v1.0.0 +) + +replace cryptochassis.com/ccapi => ../../../build/go/packaging/1.0.0 diff --git a/binding/go/example/handle_exception/main.go b/binding/go/example/handle_exception/main.go new file mode 100644 index 00000000..8f4280be --- /dev/null +++ b/binding/go/example/handle_exception/main.go @@ -0,0 +1,47 @@ +package main + +import ( + "cryptochassis.com/ccapi" + "fmt" + "os" + "runtime/debug" + "time" +) + +type MyEventHandler struct { + ccapi.EventHandler +} + +func (*MyEventHandler) ProcessEvent(event ccapi.Event, session ccapi.Session) bool { + defer func() { + if panicInfo := recover(); panicInfo != nil { + fmt.Printf("%v, %s", panicInfo, string(debug.Stack())) + os.Exit(1) + } + }() + panic("oops") + return true +} + +func main() { + option := ccapi.NewSessionOptions() + defer ccapi.DeleteSessionOptions(option) + config := ccapi.NewSessionConfigs() + defer ccapi.DeleteSessionConfigs(config) + meh := &MyEventHandler{} + meh.EventHandler = ccapi.NewDirectorEventHandler(meh) + defer func() { + ccapi.DeleteDirectorEventHandler(meh.EventHandler) + }() + session := ccapi.NewSession(option, config, meh.EventHandler) + defer ccapi.DeleteSession(session) + subscriptionList := ccapi.NewSubscriptionList() + defer ccapi.DeleteSubscriptionList(subscriptionList) + subscription := ccapi.NewSubscription("okx", "BTC-USDT", "MARKET_DEPTH") + defer ccapi.DeleteSubscription(subscription) + subscriptionList.Add(subscription) + session.Subscribe(subscriptionList) + time.Sleep(10 * time.Second) + session.Stop() + fmt.Println("Bye") +} diff --git a/binding/go/example/market_data_multiple_subscription/go.mod b/binding/go/example/market_data_multiple_subscription/go.mod new file mode 100644 index 00000000..0e5ae7c4 --- /dev/null +++ b/binding/go/example/market_data_multiple_subscription/go.mod @@ -0,0 +1,7 @@ +module main + +require ( + cryptochassis.com/ccapi v1.0.0 +) + +replace cryptochassis.com/ccapi => ../../../build/go/packaging/1.0.0 diff --git a/binding/go/example/market_data_multiple_subscription/main.go b/binding/go/example/market_data_multiple_subscription/main.go new file mode 100644 index 00000000..3ade498b --- /dev/null +++ b/binding/go/example/market_data_multiple_subscription/main.go @@ -0,0 +1,71 @@ +package main + +import ( + "cryptochassis.com/ccapi" + "fmt" + "time" +) + +type MyEventHandler struct { + ccapi.EventHandler +} + +func (*MyEventHandler) ProcessEvent(event ccapi.Event, session ccapi.Session) bool { + if event.GetType() == ccapi.EventType_SUBSCRIPTION_DATA { + messageList := event.GetMessageList() + for i := 0; i < int(messageList.Size()); i++ { + message := messageList.Get(i) + correlationId := message.GetCorrelationIdList().Get(0) + fmt.Printf("%s: Best bid and ask at %s are:\n", correlationId, message.GetTimeISO()) + elementList := message.GetElementList() + for j := 0; j < int(elementList.Size()); j++ { + element := elementList.Get(j) + nameValueMap := element.GetNameValueMap() + if nameValueMap.Has_key("BID_PRICE") { + fmt.Printf(" %s = %s\n", "BID_PRICE", nameValueMap.Get("BID_PRICE")) + } + if nameValueMap.Has_key("BID_SIZE") { + fmt.Printf(" %s = %s\n", "BID_SIZE", nameValueMap.Get("BID_SIZE")) + } + if nameValueMap.Has_key("ASK_PRICE") { + fmt.Printf(" %s = %s\n", "ASK_PRICE", nameValueMap.Get("ASK_PRICE")) + } + if nameValueMap.Has_key("ASK_SIZE") { + fmt.Printf(" %s = %s\n", "ASK_SIZE", nameValueMap.Get("ASK_SIZE")) + } + } + } + } + return true +} + +func main() { + option := ccapi.NewSessionOptions() + defer ccapi.DeleteSessionOptions(option) + config := ccapi.NewSessionConfigs() + defer ccapi.DeleteSessionConfigs(config) + meh := &MyEventHandler{} + meh.EventHandler = ccapi.NewDirectorEventHandler(meh) + defer func() { + ccapi.DeleteDirectorEventHandler(meh.EventHandler) + }() + session := ccapi.NewSession(option, config, meh.EventHandler) + defer ccapi.DeleteSession(session) + subscriptionList := ccapi.NewSubscriptionList() + defer ccapi.DeleteSubscriptionList(subscriptionList) + { + subscription := ccapi.NewSubscription("okx", "BTC-USDT", "MARKET_DEPTH", "", "BTC") + defer ccapi.DeleteSubscription(subscription) + subscriptionList.Add(subscription) + } + { + subscription := ccapi.NewSubscription("okx", "ETH-USDT", "MARKET_DEPTH", "", "ETH") + defer ccapi.DeleteSubscription(subscription) + subscriptionList.Add(subscription) + } + + session.Subscribe(subscriptionList) + time.Sleep(10 * time.Second) + session.Stop() + fmt.Println("Bye") +} diff --git a/binding/go/example/market_data_simple_request/go.mod b/binding/go/example/market_data_simple_request/go.mod new file mode 100644 index 00000000..0e5ae7c4 --- /dev/null +++ b/binding/go/example/market_data_simple_request/go.mod @@ -0,0 +1,7 @@ +module main + +require ( + cryptochassis.com/ccapi v1.0.0 +) + +replace cryptochassis.com/ccapi => ../../../build/go/packaging/1.0.0 diff --git a/binding/go/example/market_data_simple_request/main.go b/binding/go/example/market_data_simple_request/main.go new file mode 100644 index 00000000..3ff5e521 --- /dev/null +++ b/binding/go/example/market_data_simple_request/main.go @@ -0,0 +1,40 @@ +package main + +import ( + "cryptochassis.com/ccapi" + "fmt" + "time" +) + +type MyEventHandler struct { + ccapi.EventHandler +} + +func (*MyEventHandler) ProcessEvent(event ccapi.Event, session ccapi.Session) bool { + fmt.Printf("Received an event:\n%s\n", event.ToStringPretty(2, 2)) + return true +} + +func main() { + option := ccapi.NewSessionOptions() + defer ccapi.DeleteSessionOptions(option) + config := ccapi.NewSessionConfigs() + defer ccapi.DeleteSessionConfigs(config) + meh := &MyEventHandler{} + meh.EventHandler = ccapi.NewDirectorEventHandler(meh) + defer func() { + ccapi.DeleteDirectorEventHandler(meh.EventHandler) + }() + session := ccapi.NewSession(option, config, meh.EventHandler) + defer ccapi.DeleteSession(session) + request := ccapi.NewRequest(ccapi.RequestOperation_GET_RECENT_TRADES, "okx", "BTC-USDT") + defer ccapi.DeleteRequest(request) + param := ccapi.NewMapStringString() + defer ccapi.DeleteMapStringString(param) + param.Set("LIMIT", "1") + request.AppendParam(param) + session.SendRequest(request) + time.Sleep(10 * time.Second) + session.Stop() + fmt.Println("Bye") +} diff --git a/binding/go/example/market_data_simple_subscription/go.mod b/binding/go/example/market_data_simple_subscription/go.mod new file mode 100644 index 00000000..0e5ae7c4 --- /dev/null +++ b/binding/go/example/market_data_simple_subscription/go.mod @@ -0,0 +1,7 @@ +module main + +require ( + cryptochassis.com/ccapi v1.0.0 +) + +replace cryptochassis.com/ccapi => ../../../build/go/packaging/1.0.0 diff --git a/binding/go/example/market_data_simple_subscription/main.go b/binding/go/example/market_data_simple_subscription/main.go new file mode 100644 index 00000000..94fff678 --- /dev/null +++ b/binding/go/example/market_data_simple_subscription/main.go @@ -0,0 +1,64 @@ +package main + +import ( + "cryptochassis.com/ccapi" + "fmt" + "time" +) + +type MyEventHandler struct { + ccapi.EventHandler +} + +func (*MyEventHandler) ProcessEvent(event ccapi.Event, session ccapi.Session) bool { + if event.GetType() == ccapi.EventType_SUBSCRIPTION_STATUS { + fmt.Printf("Received an event of type SUBSCRIPTION_STATUS:\n%s\n", event.ToStringPretty(2, 2)) + } else if event.GetType() == ccapi.EventType_SUBSCRIPTION_DATA { + messageList := event.GetMessageList() + for i := 0; i < int(messageList.Size()); i++ { + message := messageList.Get(i) + fmt.Printf("Best bid and ask at %s are:\n", message.GetTimeISO()) + elementList := message.GetElementList() + for j := 0; j < int(elementList.Size()); j++ { + element := elementList.Get(j) + nameValueMap := element.GetNameValueMap() + if nameValueMap.Has_key("BID_PRICE") { + fmt.Printf(" %s = %s\n", "BID_PRICE", nameValueMap.Get("BID_PRICE")) + } + if nameValueMap.Has_key("BID_SIZE") { + fmt.Printf(" %s = %s\n", "BID_SIZE", nameValueMap.Get("BID_SIZE")) + } + if nameValueMap.Has_key("ASK_PRICE") { + fmt.Printf(" %s = %s\n", "ASK_PRICE", nameValueMap.Get("ASK_PRICE")) + } + if nameValueMap.Has_key("ASK_SIZE") { + fmt.Printf(" %s = %s\n", "ASK_SIZE", nameValueMap.Get("ASK_SIZE")) + } + } + } + } + return true +} + +func main() { + option := ccapi.NewSessionOptions() + defer ccapi.DeleteSessionOptions(option) + config := ccapi.NewSessionConfigs() + defer ccapi.DeleteSessionConfigs(config) + meh := &MyEventHandler{} + meh.EventHandler = ccapi.NewDirectorEventHandler(meh) + defer func() { + ccapi.DeleteDirectorEventHandler(meh.EventHandler) + }() + session := ccapi.NewSession(option, config, meh.EventHandler) + defer ccapi.DeleteSession(session) + subscriptionList := ccapi.NewSubscriptionList() + defer ccapi.DeleteSubscriptionList(subscriptionList) + subscription := ccapi.NewSubscription("okx", "BTC-USDT", "MARKET_DEPTH") + defer ccapi.DeleteSubscription(subscription) + subscriptionList.Add(subscription) + session.Subscribe(subscriptionList) + time.Sleep(10 * time.Second) + session.Stop() + fmt.Println("Bye") +} diff --git a/binding/go/go.mod.in b/binding/go/go.mod.in new file mode 100644 index 00000000..d0379da6 --- /dev/null +++ b/binding/go/go.mod.in @@ -0,0 +1 @@ +module cryptochassis.com diff --git a/binding/go/go.mod.in_2 b/binding/go/go.mod.in_2 new file mode 100644 index 00000000..b49f2dfb --- /dev/null +++ b/binding/go/go.mod.in_2 @@ -0,0 +1,7 @@ +module main + +require ( + cryptochassis.com/ccapi v1.0.0 +) + +replace cryptochassis.com/ccapi => @PACKAGING_DIR_FULL@ diff --git a/binding/go/test/go.mod b/binding/go/test/go.mod new file mode 100644 index 00000000..0e5ae7c4 --- /dev/null +++ b/binding/go/test/go.mod @@ -0,0 +1,7 @@ +module main + +require ( + cryptochassis.com/ccapi v1.0.0 +) + +replace cryptochassis.com/ccapi => ../../../build/go/packaging/1.0.0 diff --git a/binding/go/test/main.go b/binding/go/test/main.go new file mode 100644 index 00000000..7ac81958 --- /dev/null +++ b/binding/go/test/main.go @@ -0,0 +1,45 @@ +package main + +import ( + "cryptochassis.com/ccapi" + "fmt" + "time" +) + +type MyEventHandler struct { + ccapi.EventHandler +} + +func (*MyEventHandler) ProcessEvent(event ccapi.Event, session ccapi.Session) bool { + return true +} + +func main() { + option := ccapi.NewSessionOptions() + defer ccapi.DeleteSessionOptions(option) + config := ccapi.NewSessionConfigs() + defer ccapi.DeleteSessionConfigs(config) + meh := &MyEventHandler{} + meh.EventHandler = ccapi.NewDirectorEventHandler(meh) + defer func() { + ccapi.DeleteDirectorEventHandler(meh.EventHandler) + }() + session := ccapi.NewSession(option, config, meh.EventHandler) + defer ccapi.DeleteSession(session) + subscriptionList := ccapi.NewSubscriptionList() + defer ccapi.DeleteSubscriptionList(subscriptionList) + subscription := ccapi.NewSubscription("okx", "BTC-USDT", "MARKET_DEPTH") + defer ccapi.DeleteSubscription(subscription) + subscriptionList.Add(subscription) + session.Subscribe(subscriptionList) + request := ccapi.NewRequest(ccapi.RequestOperation_GET_RECENT_TRADES, "okx", "BTC-USDT") + defer ccapi.DeleteRequest(request) + param := ccapi.NewMapStringString() + defer ccapi.DeleteMapStringString(param) + param.Set("LIMIT", "1") + request.AppendParam(param) + session.SendRequest(request) + time.Sleep(time.Second) + session.Stop() + fmt.Println("Bye") +} diff --git a/binding/java/CMakeLists.txt b/binding/java/CMakeLists.txt index cb11344f..2436b70a 100644 --- a/binding/java/CMakeLists.txt +++ b/binding/java/CMakeLists.txt @@ -48,12 +48,10 @@ add_dependencies(${JAVA_PACKAGING_TARGET_NAME} ${SWIG_TARGET_NAME}) # Test if(BUILD_TEST) - # message(STATUS "BUILD_TEST") set(TEST_DIR ${CMAKE_CURRENT_BINARY_DIR}/test) file(COPY example DESTINATION ${TEST_DIR}) set(SUBDIRECTORY_LIST "enable_library_logging" "execution_management_simple_request" "execution_management_simple_subscription" "fix_simple" "handle_exception" "market_data_multiple_subscription" "market_data_simple_request" "market_data_simple_subscription") foreach(SUBDIRECTORY IN LISTS SUBDIRECTORY_LIST) - # message(STATUS "SUBDIRECTORY=${SUBDIRECTORY}") set(JAVA_TEST_TARGET_NAME java_test_${SUBDIRECTORY}) set(JAVA_TEST_BUILD_DIRECTORY ${TEST_DIR}/example/${SUBDIRECTORY}/build) file(MAKE_DIRECTORY ${JAVA_TEST_BUILD_DIRECTORY}) diff --git a/binding/java/example/enable_library_logging/Main.java b/binding/java/example/enable_library_logging/Main.java index c15dae48..372d08a1 100644 --- a/binding/java/example/enable_library_logging/Main.java +++ b/binding/java/example/enable_library_logging/Main.java @@ -1,3 +1,4 @@ +// usage: when generating the binding code, do cmake -DCCAPI_ENABLE_LOG_TRACE=ON ..., see https://github.com/crypto-chassis/ccapi#non-c import com.cryptochassis.ccapi.Logger; import com.cryptochassis.ccapi.Session; import com.cryptochassis.ccapi.SessionConfigs; diff --git a/binding/java/example/enable_library_logging/user_specified_cmake_include.cmake b/binding/java/example/enable_library_logging/user_specified_cmake_include.cmake deleted file mode 100644 index ffdace46..00000000 --- a/binding/java/example/enable_library_logging/user_specified_cmake_include.cmake +++ /dev/null @@ -1,3 +0,0 @@ -add_compile_definitions(CCAPI_ENABLE_LOG_TRACE) -# usage: when generating the binding code, do cmake -DCMAKE_PROJECT_INCLUDE= ..., see https://github.com/crypto-chassis/ccapi#non-c -# see https://cmake.org/cmake/help/latest/variable/CMAKE_PROJECT_INCLUDE.html diff --git a/binding/java/example/handle_exception/Main.java b/binding/java/example/handle_exception/Main.java index a1598b90..2297c2ff 100644 --- a/binding/java/example/handle_exception/Main.java +++ b/binding/java/example/handle_exception/Main.java @@ -14,7 +14,7 @@ public boolean processEvent(Event event, Session session) { throw new Exception("oops"); } catch (Exception e) { e.printStackTrace(); - System.exit(0); + System.exit(1); } return true; } diff --git a/binding/java/example/market_data_simple_subscription/Main.java b/binding/java/example/market_data_simple_subscription/Main.java index 7968bdd7..0a8d25d9 100644 --- a/binding/java/example/market_data_simple_subscription/Main.java +++ b/binding/java/example/market_data_simple_subscription/Main.java @@ -10,7 +10,9 @@ public class Main { static class MyEventHandler extends EventHandler { @Override public boolean processEvent(Event event, Session session) { - if (event.getType() == Event.Type.SUBSCRIPTION_DATA) { + if (event.getType() == Event.Type.SUBSCRIPTION_STATUS) { + System.out.println(String.format("Received an event of type SUBSCRIPTION_STATUS:\n%s", event.toStringPretty(2, 2))); + } else 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()) { diff --git a/binding/python/example/enable_library_logging/main.py b/binding/python/example/enable_library_logging/main.py index 62f7e4bf..6201a174 100644 --- a/binding/python/example/enable_library_logging/main.py +++ b/binding/python/example/enable_library_logging/main.py @@ -1,3 +1,4 @@ +# usage: when generating the binding code, do cmake -DCCAPI_ENABLE_LOG_TRACE=ON ..., see https://github.com/crypto-chassis/ccapi#non-c from threading import Lock import time from ccapi import Logger, Session, Subscription diff --git a/binding/python/example/enable_library_logging/user_specified_cmake_include.cmake b/binding/python/example/enable_library_logging/user_specified_cmake_include.cmake deleted file mode 100644 index ffdace46..00000000 --- a/binding/python/example/enable_library_logging/user_specified_cmake_include.cmake +++ /dev/null @@ -1,3 +0,0 @@ -add_compile_definitions(CCAPI_ENABLE_LOG_TRACE) -# usage: when generating the binding code, do cmake -DCMAKE_PROJECT_INCLUDE= ..., see https://github.com/crypto-chassis/ccapi#non-c -# see https://cmake.org/cmake/help/latest/variable/CMAKE_PROJECT_INCLUDE.html diff --git a/binding/python/example/market_data_simple_subscription/main.py b/binding/python/example/market_data_simple_subscription/main.py index c23f23c0..cccee76c 100644 --- a/binding/python/example/market_data_simple_subscription/main.py +++ b/binding/python/example/market_data_simple_subscription/main.py @@ -4,7 +4,9 @@ class MyEventHandler(EventHandler): def __init__(self): super().__init__() def processEvent(self, event: Event, session: Session) -> bool: - if event.getType() == Event.Type_SUBSCRIPTION_DATA: + if event.getType() == Event.SUBSCRIPTION_STATUS: + print(f'Received an event of type SUBSCRIPTION_STATUS:\n{event.toStringPretty(2, 2)}') + elif event.getType() == Event.Type_SUBSCRIPTION_DATA: for message in event.getMessageList(): print(f'Best bid and ask at {message.getTimeISO()} are:') for element in message.getElementList(): diff --git a/binding/python/test/main.py b/binding/python/test/main.py index 1cea1517..6756854d 100644 --- a/binding/python/test/main.py +++ b/binding/python/test/main.py @@ -10,9 +10,9 @@ def processEvent(self, event: Event, session: Session) -> bool: option = SessionOptions() config = SessionConfigs() session = Session(option, config, eventHandler) - subscription = Subscription('coinbase', 'BTC-USD', 'MARKET_DEPTH') + subscription = Subscription('okx', 'BTC-USDT', 'MARKET_DEPTH') session.subscribe(subscription) - request = Request(Request.Operation_GET_RECENT_TRADES, 'coinbase', 'BTC-USD') + request = Request(Request.Operation_GET_RECENT_TRADES, 'okx', 'BTC-USDT') request.appendParam({ 'LIMIT':'1', }) diff --git a/example/src/fix_advanced/CMakeLists.txt b/example/src/fix_advanced/CMakeLists.txt index 2cb3b94a..bfe9a151 100644 --- a/example/src/fix_advanced/CMakeLists.txt +++ b/example/src/fix_advanced/CMakeLists.txt @@ -4,6 +4,5 @@ add_compile_definitions(CCAPI_ENABLE_SERVICE_FIX) add_compile_definitions(CCAPI_ENABLE_EXCHANGE_COINBASE) add_executable(${NAME} main.cpp) if(NOT CCAPI_LEGACY_USE_WEBSOCKETPP) - add_dependencies(${NAME} boost rapidjson) + add_dependencies(${NAME} boost rapidjson hffix) endif() -target_include_directories(${NAME} PRIVATE ${HFFIX_INCLUDE_DIR}) diff --git a/example/src/fix_simple/CMakeLists.txt b/example/src/fix_simple/CMakeLists.txt index 0c4922b3..61bf057f 100644 --- a/example/src/fix_simple/CMakeLists.txt +++ b/example/src/fix_simple/CMakeLists.txt @@ -4,6 +4,5 @@ add_compile_definitions(CCAPI_ENABLE_SERVICE_FIX) add_compile_definitions(CCAPI_ENABLE_EXCHANGE_COINBASE) add_executable(${NAME} main.cpp) if(NOT CCAPI_LEGACY_USE_WEBSOCKETPP) - add_dependencies(${NAME} boost rapidjson) + add_dependencies(${NAME} boost rapidjson hffix) endif() -target_include_directories(${NAME} PRIVATE ${HFFIX_INCLUDE_DIR}) diff --git a/example/src/market_data_simple_subscription/main.cpp b/example/src/market_data_simple_subscription/main.cpp index 7f98eda5..ecca75df 100644 --- a/example/src/market_data_simple_subscription/main.cpp +++ b/example/src/market_data_simple_subscription/main.cpp @@ -4,7 +4,9 @@ Logger* Logger::logger = nullptr; // This line is needed. class MyEventHandler : public EventHandler { public: bool processEvent(const Event& event, Session* session) override { - if (event.getType() == Event::Type::SUBSCRIPTION_DATA) { + if (event.getType() == Event::Type::SUBSCRIPTION_STATUS) { + std::cout << "Received an event of type SUBSCRIPTION_STATUS:\n" + event.toStringPretty(2, 2) << std::endl; + } else if (event.getType() == Event::Type::SUBSCRIPTION_DATA) { for (const auto& message : event.getMessageList()) { std::cout << std::string("Best bid and ask at ") + UtilTime::getISOTimestamp(message.getTime()) + " are:" << std::endl; for (const auto& element : message.getElementList()) { diff --git a/format.sh b/format.sh index a896c4fb..eb10330a 100755 --- a/format.sh +++ b/format.sh @@ -1,4 +1,5 @@ #!/usr/bin/env bash ./format_cpp.sh ./format_csharp.sh +./format_go.sh ./format_java.sh diff --git a/format_go.sh b/format_go.sh new file mode 100755 index 00000000..0a26f407 --- /dev/null +++ b/format_go.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +find . -type f -path "*/binding/*" -not -path "*/build/*" -name "*.go" -exec gofmt -w {} \+ diff --git a/include/ccapi_cpp/ccapi_queue.h b/include/ccapi_cpp/ccapi_queue.h index a0df0a03..f1657a82 100644 --- a/include/ccapi_cpp/ccapi_queue.h +++ b/include/ccapi_cpp/ccapi_queue.h @@ -15,6 +15,17 @@ class Queue { std::string EXCEPTION_QUEUE_FULL = "queue is full"; std::string EXCEPTION_QUEUE_EMPTY = "queue is empty"; explicit Queue(const size_t maxSize = 0) : maxSize(maxSize) {} + void pushBack(const T& t) { +#ifndef CCAPI_USE_SINGLE_THREAD + std::lock_guard lock(this->m); +#endif + if (this->maxSize <= 0 || this->queue.size() < this->maxSize) { + CCAPI_LOGGER_TRACE("this->queue.size() = " + size_tToString(this->queue.size())); + this->queue.push_back(t); + } else { + throw std::runtime_error(EXCEPTION_QUEUE_FULL); + } + } void pushBack(T&& t) { #ifndef CCAPI_USE_SINGLE_THREAD std::lock_guard lock(this->m);