diff --git a/.github/workflows/publishpackage.yml b/.github/workflows/publishpackage.yml index d985ac4..199a900 100644 --- a/.github/workflows/publishpackage.yml +++ b/.github/workflows/publishpackage.yml @@ -11,8 +11,8 @@ jobs: build: strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - python-version: [3.6, 3.7, 3.8] + os: [ubuntu-18.04, windows-latest, macos-latest] + python-version: [3.6, 3.7, 3.8, 3.9] runs-on: ${{ matrix.os }} @@ -34,12 +34,11 @@ jobs: poetry install --no-root shell: bash - name: Create tarball - if: matrix.os == 'ubuntu-latest' && matrix.python-version == 3.7 + if: matrix.python-version == 3.7 run: | task build -f sdist shell: bash - name: Create wheel - if: matrix.os == 'windows-latest' || matrix.os == 'macos-latest' run: | task build -f wheel shell: bash @@ -52,7 +51,7 @@ jobs: publish: - runs-on: ubuntu-latest + runs-on: ubuntu-18.04 needs: build steps: - uses: actions/checkout@v2 diff --git a/.github/workflows/test_on_pr.yml b/.github/workflows/test_on_pr.yml new file mode 100644 index 0000000..ad393e3 --- /dev/null +++ b/.github/workflows/test_on_pr.yml @@ -0,0 +1,76 @@ +# This workflow will check package installation and runs tests on PR +name: Test on PR + +on: + pull_request: + types: [opened, reopened, synchronize] + +jobs: + installation: + strategy: + matrix: + os: [ubuntu-18.04, windows-latest, macos-latest] + python-version: [3.6, 3.7, 3.8, 3.9] + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Install poetry + run: | + pip install poetry + shell: bash + - name: Set poetry env + run: | + poetry config virtualenvs.create false + poetry install --no-root --no-dev + pip install taskipy cython + task build -f sdist + pip uninstall --yes taskipy cython + shell: bash + - name: Install package artifact + run: | + pip install dist/dxfeed* + pip uninstall --yes dxfeed + shell: bash + tests: + needs: installation + strategy: + matrix: + os: [ubuntu-18.04, windows-latest, macos-latest] + python-version: [3.6, 3.7, 3.8, 3.9] + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v2 + with: + submodules: true + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v1 + with: + python-version: ${{ matrix.python-version }} + - name: Install poetry + run: | + pip install poetry + shell: bash + - name: Set poetry env + run: | + poetry config virtualenvs.create false + poetry install --no-root + shell: bash + - name: Run tests + run: | + task test + shell: bash + - name: Generate doc + run: | + task html_docs + shell: bash + if: matrix.os == 'ubuntu-18.04' && matrix.python-version == 3.7 diff --git a/.github/workflows/testdevelop.yml b/.github/workflows/testdevelop.yml index 8706b04..1d4f1b2 100644 --- a/.github/workflows/testdevelop.yml +++ b/.github/workflows/testdevelop.yml @@ -12,7 +12,7 @@ jobs: installation: strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + os: [ubuntu-18.04, windows-latest, macos-latest] python-version: [3.6, 3.7, 3.8, 3.9] runs-on: ${{ matrix.os }} @@ -46,7 +46,7 @@ jobs: needs: installation strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + os: [ubuntu-18.04, windows-latest, macos-latest] python-version: [3.6, 3.7, 3.8, 3.9] runs-on: ${{ matrix.os }} @@ -76,4 +76,4 @@ jobs: run: | task html_docs shell: bash - if: matrix.os == 'ubuntu-latest' && matrix.python-version == 3.7 + if: matrix.os == 'ubuntu-18.04' && matrix.python-version == 3.7 diff --git a/.github/workflows/testpackage.yml b/.github/workflows/testpackage.yml index 6d90cfc..1603b3a 100644 --- a/.github/workflows/testpackage.yml +++ b/.github/workflows/testpackage.yml @@ -13,8 +13,8 @@ jobs: installation: strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] - python-version: [3.6, 3.7, 3.8] + os: [ubuntu-18.04, windows-latest, macos-latest] + python-version: [3.6, 3.7, 3.8, 3.9] runs-on: ${{ matrix.os }} @@ -47,7 +47,7 @@ jobs: needs: installation strategy: matrix: - os: [ubuntu-latest, windows-latest, macos-latest] + os: [ubuntu-18.04, windows-latest, macos-latest] python-version: [3.6, 3.7, 3.8] runs-on: ${{ matrix.os }} @@ -77,4 +77,4 @@ jobs: run: | task html_docs shell: bash - if: matrix.os == 'ubuntu-latest' && matrix.python-version == 3.7 + if: matrix.os == 'ubuntu-18.04' && matrix.python-version == 3.7 diff --git a/.gitignore b/.gitignore index a99dcc9..c8d504c 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,11 @@ _build* Pipfile* dist* +dist +build/** +dxfeed/core/**/*.c +dxfeed/tmp + +setup.py +poetry.lock +dxfeed.egg-info diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index c34da26..0000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "dxfeed/dxfeed-c-api"] - path = dxfeed/dxfeed-c-api - url = https://github.com/dxFeed/dxfeed-c-api.git diff --git a/README.md b/README.md index f8cb9d4..f0d0010 100644 --- a/README.md +++ b/README.md @@ -34,12 +34,6 @@ pip3 install dxfeed ## Installation from sources -Reminder: initialize and pull git submodule after cloning the repo: -```bash -git submodule init -git submodule update -``` - To install dxfeed from source you need Poetry. It provides a custom installer. This is the recommended way of installing poetry according to [documentation](https://python-poetry.org/docs/) diff --git a/build.py b/build.py index f0aa453..d520794 100644 --- a/build.py +++ b/build.py @@ -1,7 +1,126 @@ +import os +import platform +import struct +from io import BytesIO +from pathlib import Path +from urllib.request import urlopen +from zipfile import ZipFile +import shutil from setuptools import Extension, find_packages from setuptools.dist import Distribution -from pathlib import Path -import platform +import toml + + +class Downloader(object): + """Class that downloads a dxFeed C API bundle and places the necessary include or lib files""" + + def __init__(self, version: str, path_to_extract: Path, + path_to_copy_includes: Path, path_to_copy_libs: Path): + """ + Parameters + ---------- + version : str + The dxFeed C API version + path_to_extract : Path + The temporary directory where the bundle should be unpacked + path_to_copy_includes : Path + The directory where the include files should be placed + path_to_copy_libs + The directory where the library files should be placed + """ + + self.__version = version + self.__path_to_extract = path_to_extract + self.__path_to_copy_includes = path_to_copy_includes + self.__path_to_copy_libs = path_to_copy_libs + + def download(self, dxfeed_c_api_bundle_url_template: str) -> str: + """ + The method that does the bulk of the work of downloading and placement files. + + + Parameters + ---------- + dxfeed_c_api_bundle_url_template: str + The URL template for zipped dxFeed C API bundle. Parameters used: + {version} - The C API version + {os} - The target OS + + Returns + ------- + : str + The resulting name of the library, which is required to build an extension + + """ + c_api_extracted_root_path = self.__path_to_extract / f'dxfeed-c-api-{self.__version}-no-tls' + is_x64 = 8 * struct.calcsize('P') == 64 + + url_template_os = 'centos' # Since centos uses an earlier version of libc + + if platform.system() == 'Darwin': + url_template_os = 'macosx' + elif platform.system() == 'Windows': + url_template_os = 'windows' + + if (not os.path.exists(self.__path_to_extract)) or (not os.path.exists(c_api_extracted_root_path)): + url = dxfeed_c_api_bundle_url_template.format(version=self.__version, os=url_template_os) + print(f'Downloading the "{url}"') + resp = urlopen(url) + zipfile = ZipFile(BytesIO(resp.read())) + print(f'Extracting to "{self.__path_to_extract}"') + zipfile.extractall(self.__path_to_extract) + + c_api_x64_suffix = '_64' if is_x64 else '' + resulting_c_api_library_name = f'DXFeed{c_api_x64_suffix}' + + # Determine and fixing paths for unpacked artifacts. + # The workaround is related to the packaging feature on POSIX systems in the dxFeed API' CI\CD. + if is_x64: + if platform.system() != 'Windows': + c_api_extracted_root_path = c_api_extracted_root_path / f'DXFeedAll-{self.__version}-x64-no-tls' + + c_api_extracted_library_path = c_api_extracted_root_path / 'bin' / 'x64' + else: + if platform.system() == 'Windows': + c_api_extracted_library_path = c_api_extracted_root_path / 'bin' / 'x86' + else: + raise RuntimeError('Unsupported platform') + + # Determine possible prefixes and extensions for library files + lib_file_name_prefixes = [''] * 2 + lib_file_name_extensions = ['dll', 'lib'] + + if platform.system() != 'Windows': + lib_file_name_prefixes = ['lib'] + lib_file_name_extensions = ['dylib'] if platform.system() == 'Darwin' else ['so'] + + # Create paths for libraries to be copied + c_api_library_file_source_paths = [] + c_api_library_file_dest_paths = [] + + for idx, prefix in enumerate(lib_file_name_prefixes): + file_name = f'{prefix}{resulting_c_api_library_name}.{lib_file_name_extensions[idx]}' + c_api_library_file_source_paths.append(c_api_extracted_library_path / file_name) + c_api_library_file_dest_paths.append(self.__path_to_copy_libs / file_name) + + print('Copying all headers') + # Copying all headers + if not Path(self.__path_to_copy_includes).exists(): + shutil.copytree(c_api_extracted_root_path / 'include', self.__path_to_copy_includes) + + # Create a directory where we will copy libraries, if necessary + if not Path(self.__path_to_copy_libs).exists(): + print(f'Creating the {self.__path_to_copy_libs} path') + os.makedirs(self.__path_to_copy_libs) + + print('Copying the required libraries') + # Copying the required libraries + for idx, path in enumerate(c_api_library_file_source_paths): + print(f' {path} -> {c_api_library_file_dest_paths[idx]}') + shutil.copyfile(path, c_api_library_file_dest_paths[idx]) + + return resulting_c_api_library_name + try: from Cython.Build import cythonize @@ -12,49 +131,62 @@ use_cython = True ext = 'pyx' -# Get all dxfeed c api c files to be compiled into separate lib root_path = Path(__file__).resolve().parent -source_files_directory = root_path / 'dxfeed' / 'dxfeed-c-api' / 'src' -source_files_paths = [str(path) for path in source_files_directory.glob('*.c')] -libs = list() -include_dirs = [str(source_files_directory.parent / 'include'), - str(source_files_directory)] -# Build dxfeed c library -dxfeed_c_lib_args = dict() + +pyproject = toml.load(root_path / 'pyproject.toml') +c_api_version = pyproject['build']['native-dependencies']['dxfeed_c_api'] +if c_api_version == 'env': + c_api_version = os.getenv('DXFEED_C_API_VERSION') + +c_api_root_dir = root_path / 'dxfeed' / 'dxfeed-c-api' +path_to_extract = root_path / 'dxfeed' / 'tmp' +c_api_include_dir = c_api_root_dir / 'include' +c_api_bin_dir = root_path / 'dxfeed' / 'core' + +downloader = Downloader(c_api_version, path_to_extract, c_api_include_dir, c_api_bin_dir) +c_api_library_name = downloader.download(pyproject['build']['native-dependencies']['dxfeed_c_api_bundle_url_template']) + if platform.system() == 'Windows': - source_files_paths.remove(str(source_files_directory / 'Linux.c')) - libs.append('ws2_32') + runtime_library_dirs = None + extra_link_args = None +elif platform.system() == 'Darwin': + runtime_library_dirs = None + extra_link_args = ['-Wl,-rpath,@loader_path'] else: - source_files_paths.remove(str(source_files_directory / 'Win32.c')) -dxfeed_c_lib_args.update({'sources': source_files_paths, - 'include_dirs': include_dirs}) + runtime_library_dirs = ['$ORIGIN'] + extra_link_args = None -if platform.system() == 'Darwin': - dxfeed_c_lib_args.update({'macros': [('MACOSX', 'TRUE')]}) +c_api_include_dirs = [str(c_api_include_dir)] -dxfeed_c = ('dxfeed_c', dxfeed_c_lib_args) +libs = [c_api_library_name] +if platform.system() == 'Windows': + libs.append('ws2_32') extensions = [Extension('dxfeed.core.utils.helpers', ['dxfeed/core/utils/helpers.' + ext], - include_dirs=include_dirs), + include_dirs=c_api_include_dirs), Extension('dxfeed.core.utils.handler', ['dxfeed/core/utils/handler.' + ext], - include_dirs=include_dirs), + include_dirs=c_api_include_dirs), Extension('dxfeed.core.listeners.listener', ['dxfeed/core/listeners/listener.' + ext], - include_dirs=include_dirs), - Extension('dxfeed.core.DXFeedPy', ['dxfeed/core/DXFeedPy.' + ext], libraries=libs, - include_dirs=include_dirs)] + include_dirs=c_api_include_dirs), + Extension('dxfeed.core.DXFeedPy', ['dxfeed/core/DXFeedPy.' + ext], library_dirs=[str(c_api_bin_dir)], + runtime_library_dirs=runtime_library_dirs, + extra_link_args=extra_link_args, + libraries=libs, + include_dirs=c_api_include_dirs)] if use_cython: extensions = cythonize(extensions, language_level=3) + def build(setup_kwargs): setup_kwargs.update({ 'ext_modules': extensions, 'zip_safe': False, - 'libraries': [dxfeed_c], 'packages': find_packages(), - 'include_dirs': include_dirs + 'include_dirs': c_api_include_dirs }) + def build_extensions(): """ Function for building extensions inplace for docs and tests diff --git a/clear.py b/clear.py new file mode 100644 index 0000000..a295b4e --- /dev/null +++ b/clear.py @@ -0,0 +1,20 @@ +import shutil +from pathlib import Path +from itertools import chain + +c_files = Path('dxfeed/core').glob('**/*.c') +cpp_files = Path('dxfeed/core').glob('**/*.cpp') +pyd_files = Path('dxfeed/core').glob('**/*.pyd') +so_files = Path('dxfeed/core').glob('**/*.so') +dll_files = Path('dxfeed/core').glob('**/*.dll') +lib_files = Path('dxfeed/core').glob('*.lib') +dylib_files = Path('dxfeed/core').glob('**/*.dylib') + +for file_path in chain(c_files, cpp_files, pyd_files, so_files, dll_files, lib_files, dylib_files): + file_path.unlink() + +if Path('dxfeed/tmp').exists(): + shutil.rmtree('dxfeed/tmp') + +if Path('dxfeed/dxfeed-c-api/include').exists(): + shutil.rmtree('dxfeed/dxfeed-c-api/include') diff --git a/dxfeed/core/listeners/listener.pyx b/dxfeed/core/listeners/listener.pyx index 59ea437..76b6a7f 100644 --- a/dxfeed/core/listeners/listener.pyx +++ b/dxfeed/core/listeners/listener.pyx @@ -133,8 +133,8 @@ cdef void profile_default_listener(int event_type, div_freq=p[i].div_freq, exd_div_amount=p[i].exd_div_amount, exd_div_date=p[i].exd_div_date, - high_price=p[i]._52_high_price, - low_price=p[i]._52_low_price, + high_price=p[i].high_52_week_price, + low_price=p[i].low_52_week_price, shares=p[i].shares, free_float=p[i].free_float, high_limit_price=p[i].high_limit_price, diff --git a/dxfeed/core/pxd_include/DXErrorCodes.pxd b/dxfeed/core/pxd_include/DXErrorCodes.pxd index 35e682a..3f4b1c6 100644 --- a/dxfeed/core/pxd_include/DXErrorCodes.pxd +++ b/dxfeed/core/pxd_include/DXErrorCodes.pxd @@ -1,6 +1,14 @@ from dxfeed.core.pxd_include.DXTypes cimport * -cdef extern from "DXErrorCodes.h": +cdef extern from "": + ctypedef enum dx_log_level_t: + dx_ll_trace = -2, + dx_ll_debug = -1, + dx_ll_info = 0, + dx_ll_warn = 1, + dx_ll_error = 2 + +cdef extern from "": ctypedef enum dx_error_code_t: # # /* common error codes */ @@ -176,4 +184,6 @@ cdef extern from "DXErrorCodes.h": # # /* -------------------------------------------------------------------------- */ cdef dxf_const_string_t dx_get_error_description (dx_error_code_t code) + +cdef dx_log_level_t dx_get_log_level(dx_error_code_t code) \ No newline at end of file diff --git a/dxfeed/core/pxd_include/DXFeed.pxd b/dxfeed/core/pxd_include/DXFeed.pxd index 8d43287..02bbad6 100644 --- a/dxfeed/core/pxd_include/DXFeed.pxd +++ b/dxfeed/core/pxd_include/DXFeed.pxd @@ -8,7 +8,7 @@ from dxfeed.core.pxd_include.EventData cimport * # /* -------------------------------------------------------------------------- */ -cdef extern from "DXFeed.h": +cdef extern from "": cdef int DXF_SUCCESS = 1 cdef int DXF_FAILURE = 0 @@ -33,6 +33,10 @@ cdef extern from "DXFeed.h": dxf_connection_status_t new_status, void* user_data) + ctypedef void (*dxf_conn_on_server_heartbeat_notifier_t) (dxf_connection_t connection, dxf_long_t server_millis, + dxf_int_t server_lag_mark, dxf_int_t connection_rtt, + void * user_data); + # /* the low level callback types, required in case some thread-specific initialization must be performed # on the client side on the thread creation/destruction */ @@ -165,6 +169,24 @@ cdef extern from "DXFeed.h": void* user_data, dxf_connection_t* connection) +#/** +# * @ingroup c-api-connection-functions +# * +# * @brief Sets a server heartbeat notifier's callback to the connection. +# * +# * @details This notifier will be invoked when the new heartbeat arrives from a server and contains non empty payload +# * +# * @param[in] connection The handle of a previously created connection +# * @param[in] notifier The notifier callback function pointer +# * @param[in] user_data The data to be passed to the callback function +# * +# * @return {@link DXF_SUCCESS} on successful set of the heartbeat notifier's or {@link DXF_FAILURE} on error; +# * {@link dxf_get_last_error} can be used to retrieve the error code and description in case of failure; +# */ + ERRORCODE dxf_set_on_server_heartbeat_notifier(dxf_connection_t connection, + dxf_conn_on_server_heartbeat_notifier_t notifier, + void* user_data) + # /* # * Closes a connection. # @@ -183,6 +205,28 @@ cdef extern from "DXFeed.h": ERRORCODE dxf_create_subscription (dxf_connection_t connection, int event_types, dxf_subscription_t* subscription) + +#/** +# * @ingroup c-api-basic-subscription-functions +# * +# * @brief Creates a subscription with the specified parameters and the subscription flags. +# * +# * @details +# * +# * @param[in] connection A handle of a previously created connection which the subscription will be using +# * @param[in] event_types A bitmask of the subscription event types. See {@link dx_event_id_t} and +# * {@link DX_EVENT_BIT_MASK} for information on how to create an event type bitmask +# * @param[in] subscr_flags A bitmask of the subscription event flags. See {@link dx_event_subscr_flag} +# * @param[out] subscription A handle of the created subscription +# * +# * @return {@link DXF_SUCCESS} on successful subscription creation or {@link DXF_FAILURE} on error; +# * {@link dxf_get_last_error} can be used to retrieve the error code and description in case of failure; +# * a handle to newly created subscription is returned via ```subscription``` out parameter +# */ + ERRORCODE dxf_create_subscription_with_flags(dxf_connection_t connection, int event_types, + dx_event_subscr_flag subscr_flags, + dxf_subscription_t* subscription) + # /* # * Creates a subscription with the specified parameters. # @@ -195,6 +239,27 @@ cdef extern from "DXFeed.h": ERRORCODE dxf_create_subscription_timed(dxf_connection_t connection, int event_types, dxf_long_t time, dxf_subscription_t* subscription) +#/** +# * @ingroup c-api-basic-subscription-functions +# * +# * @brief Creates a timed subscription with the specified parameters and the subscription flags. +# * +# * @details +# * +# * @param[in] connection A handle of a previously created connection which the subscription will be using +# * @param[in] event_types A bitmask of the subscription event types. See {@link dx_event_id_t} and +# * {@link DX_EVENT_BIT_MASK} for information on how to create an event type bitmask +# * @param[in] time UTC time in the past (unix time in milliseconds) +# * @param[in] subscr_flags A bitmask of the subscription event flags. See {@link dx_event_subscr_flag} +# * @param[out] subscription A handle of the created subscription +# * +# * @return {@link DXF_SUCCESS} on successful subscription creation or {@link DXF_FAILURE} on error; +# * {@link dxf_get_last_error} can be used to retrieve the error code and description in case of failure; +# * a handle to newly created subscription is returned via ```subscription``` out parameter +# */ + ERRORCODE dxf_create_subscription_timed_with_flags(dxf_connection_t connection, int event_types, + dxf_long_t time, dx_event_subscr_flag subscr_flags, + dxf_subscription_t* subscription) # /* # * Closes a subscription. @@ -375,6 +440,30 @@ cdef extern from "DXFeed.h": # */ ERRORCODE dxf_initialize_logger (const char* file_name, int rewrite_file, int show_timezone_info, int verbose) + +#/** +# * @ingroup c-api-common +# * +# * @brief Initializes the internal logger with data transfer logging. +# * +# * @details Various actions and events, including the errors, are being logged +# * throughout the framework. They may be stored into the file. +# * +# * @param[in] file_name A full path to the file where the log is to be stored +# * @param[in] rewrite_file A flag defining the file open mode; if it's nonzero then the log file will be rewritten +# * @param[in] show_timezone_info A flag defining the time display option in the log file; if it's nonzero then +# * the time will be displayed with the timezone suffix +# * @param[in] verbose A flag defining the logging mode; if it's nonzero then the verbose logging will be +# * enabled +# * @param[in] log_data_transfer A flag defining the logging mode; if it's nonzero then the data transfer (portions of +# * received and sent data) logging will be enabled +# * +# * @return {@link DXF_SUCCESS} on successful logger initialization or {@link DXF_FAILURE} on error; +# * {@link dxf_get_last_error} can be used to retrieve the error code and description in case of failure; +# */ + ERRORCODE dxf_initialize_logger_v2(const char * file_name, int rewrite_file, int show_timezone_info, + int verbose, int log_data_transfer) + # /* # * Clear current sources and add new one to subscription # * Warning: you must configure order source before dxf_add_symbols/dxf_add_symbol call @@ -554,6 +643,28 @@ cdef extern from "DXFeed.h": dxf_const_string_t symbol, const char** sources, dxf_price_level_book_t* book) +#/** +# * @ingroup c-api-price-level-book +# * +# * @brief Creates Price Level book with the specified parameters. +# * +# * @details +# * +# * @param[in] connection A handle of a previously created connection which the subscription will be using +# * @param[in] symbol The symbol to use +# * @param[in] sources Order sources for Order. Each element can be one of following: +# * "NTV", "ntv", "NFX", "ESPD", "XNFI", "ICE", "ISE", "DEA", "DEX", "BYX", "BZX", "BATE", +# * "CHIX", "CEUX", "BXTR", "IST", "BI20", "ABE", "FAIR", "GLBX", "glbx", "ERIS", "XEUR", "xeur", "CFE", "C2OX", "SMFE", +# * "smfe", "iex", "MEMX", "memx". NULL-list means "all sources" +# * @param[in] sources_count The number of sources. 0 means "all sources" +# * @param[out] book A handle of the created price level book +# * +# * @return {@link DXF_SUCCESS} if price level book has been successfully created or {@link DXF_FAILURE} on error; +# * {@link dxf_get_last_error} can be used to retrieve the error code and description in case of failure; +# * *price level book* itself is returned via out parameter +# */ + ERRORCODE dxf_create_price_level_book_v2(dxf_connection_t connection, dxf_const_string_t symbol, + const char** sources, int sources_count, dxf_price_level_book_t* book) # /* # * Closes a price level book. # * All the data associated with it will be freed. @@ -701,4 +812,14 @@ cdef extern from "DXFeed.h": # */ ERRORCODE dxf_free(void* pointer) + dxf_const_string_t dxf_get_order_action_wstring_name(dxf_order_action_t action); + + const char * dxf_get_order_action_string_name(dxf_order_action_t action); + + ERRORCODE dxf_load_config_from_wstring(dxf_const_string_t config); + + ERRORCODE dxf_load_config_from_string(const char * config); + + ERRORCODE dxf_load_config_from_file(const char * file_name); + #endif /* _H_INCLUDED */ diff --git a/dxfeed/core/pxd_include/DXTypes.pxd b/dxfeed/core/pxd_include/DXTypes.pxd index 1d53270..c9edff4 100644 --- a/dxfeed/core/pxd_include/DXTypes.pxd +++ b/dxfeed/core/pxd_include/DXTypes.pxd @@ -3,7 +3,7 @@ #ifndef DX_TYPES_H_INCLUDED #define DX_TYPES_H_INCLUDED -cdef extern from "DXTypes.h": +cdef extern from "": ctypedef int ERRORCODE ctypedef void* dxf_subscription_t ctypedef void* dxf_connection_t @@ -17,7 +17,7 @@ cdef extern from "DXTypes.h": #pxd_include from libc.stddef cimport wchar_t -cdef extern from "DXTypes.h": +cdef extern from "": ctypedef unsigned char dxf_bool_t # 8 bit ctypedef char dxf_byte_t # 8 bit ctypedef unsigned char dxf_ubyte_t # 8 bit @@ -61,7 +61,7 @@ cdef extern from "DXTypes.h": #endif /* _WIN32/POSIX */ -cdef extern from "DXTypes.h": +cdef extern from "": ctypedef dxf_uint_t dxf_event_flags_t ctypedef struct dxf_byte_array_t: diff --git a/dxfeed/core/pxd_include/EventData.pxd b/dxfeed/core/pxd_include/EventData.pxd index 9724e48..b10d413 100644 --- a/dxfeed/core/pxd_include/EventData.pxd +++ b/dxfeed/core/pxd_include/EventData.pxd @@ -7,12 +7,26 @@ from dxfeed.core.pxd_include cimport DXTypes as dxt #define OUT #endif /* OUT */ +cdef extern from "": + enum dxf_order_action_t: + dxf_oa_undefined = 0, + dxf_oa_new = 1, + dxf_oa_replace = 2, + dxf_oa_modify = 3, + dxf_oa_delete = 4, + dxf_oa_partial = 5, + dxf_oa_execute = 6, + dxf_oa_trade = 7, + dxf_oa_bust = 8, + dxf_oa_last = dxf_oa_bust + + # /* -------------------------------------------------------------------------- */ # /* # * Event type constants # */ # /* -------------------------------------------------------------------------- */ -cdef extern from "EventData.h": +cdef extern from "": enum dx_event_id_t: dx_eid_begin = 0, dx_eid_trade = dx_eid_begin, @@ -57,14 +71,38 @@ cdef extern from "EventData.h": # // The length of record suffix including including the terminating null character #define DXF_RECORD_SUFFIX_SIZE 5 DEF DXF_RECORD_SUFFIX_SIZE = 5 -cdef extern from "EventData.h": +cdef extern from "": cdef int DXF_RECORD_SUFFIX_SIZE = DXF_RECORD_SUFFIX_SIZE + +cdef extern from "": + ctypedef enum dx_event_subscr_flag_t: +# /// (0x0) Used for default subscription + dx_esf_default = 0x0u, +# /// (0x1) Used for subscribing on one record only in case of snapshots + dx_esf_single_record = 0x1u, +# /// (0x2) Used with #dx_esf_single_record flag and for #dx_eid_order (Order) event + dx_esf_sr_market_maker_order = 0x2u, +# /// (0x4) Used for time series subscription + dx_esf_time_series = 0x4u, +# /// (0x8) Used for regional quotes + dx_esf_quotes_regional = 0x8u, +# /// (0x10) Used for wildcard ("*") subscription + dx_esf_wildcard = 0x10u, +# /// (0x20) Used for forcing subscription to ticker data + dx_esf_force_ticker = 0x20u, +# /// (0x40) Used for forcing subscription to stream data + dx_esf_force_stream = 0x40u, +# /// (0x80) Used for forcing subscription to history data + dx_esf_force_history = 0x80u + + ctypedef dx_event_subscr_flag_t dx_event_subscr_flag + # /* -------------------------------------------------------------------------- */ # /* # * Source suffix array # */ # /* -------------------------------------------------------------------------- */ -cdef extern from "EventData.h": +cdef extern from "": struct dx_suffix_t: dxt.dxf_char_t suffix[DXF_RECORD_SUFFIX_SIZE] @@ -109,18 +147,19 @@ cdef extern from "EventData.h": dxt.dxf_int_t time_nanos, dxt.dxf_char_t exchange_code, dxt.dxf_double_t price, - dxt.dxf_int_t size, + dxt.dxf_double_t size, # /* This field is absent in TradeETH */ dxt.dxf_int_t tick, - # /* This field is absent in TradeETH */ dxt.dxf_double_t change, - dxt.dxf_int_t raw_flags, + dxt.dxf_dayid_t day_id, dxt.dxf_double_t day_volume, dxt.dxf_double_t day_turnover, + dxt.dxf_int_t raw_flags, dxf_direction_t direction, dxt.dxf_bool_t is_eth, dxf_order_scope_t scope + ctypedef dxf_trade_t dxf_trade_eth_t # /* Quote -------------------------------------------------------------------- */ @@ -131,11 +170,11 @@ cdef extern from "EventData.h": dxt.dxf_long_t bid_time, dxt.dxf_char_t bid_exchange_code, dxt.dxf_double_t bid_price, - dxt.dxf_int_t bid_size, + dxt.dxf_double_t bid_size, dxt.dxf_long_t ask_time, dxt.dxf_char_t ask_exchange_code, dxt.dxf_double_t ask_price, - dxt.dxf_int_t ask_size, + dxt.dxf_double_t ask_size, dxf_order_scope_t scope @@ -157,7 +196,7 @@ cdef extern from "EventData.h": dxt.dxf_dayid_t prev_day_id, dxt.dxf_double_t prev_day_close_price, dxt.dxf_double_t prev_day_volume, - dxt.dxf_int_t open_interest, + dxt.dxf_double_t open_interest, dxt.dxf_int_t raw_flags, dxt.dxf_char_t exchange_code, dxf_price_type_t day_close_price_type, @@ -182,11 +221,11 @@ cdef extern from "EventData.h": ctypedef struct dxf_profile_t: dxt.dxf_double_t beta, dxt.dxf_double_t eps, - dxt.dxf_int_t div_freq, + dxt.dxf_double_t div_freq, dxt.dxf_double_t exd_div_amount, dxt.dxf_dayid_t exd_div_date, - dxt.dxf_double_t _52_high_price, - dxt.dxf_double_t _52_low_price, + dxt.dxf_double_t high_52_week_price, + dxt.dxf_double_t low_52_week_price, dxt.dxf_double_t shares, dxt.dxf_double_t free_float, dxt.dxf_double_t high_limit_price, @@ -213,18 +252,26 @@ cdef extern from "EventData.h": ctypedef struct dxf_order_t: + dxt.dxf_char_t source[DXF_RECORD_SUFFIX_SIZE], dxt.dxf_event_flags_t event_flags, dxt.dxf_long_t index, dxt.dxf_long_t time, - dxt.dxf_int_t time_nanos, dxt.dxf_int_t sequence, + dxt.dxf_int_t time_nanos, + dxf_order_action_t action, + dxt.dxf_long_t action_time, + dxt.dxf_long_t order_id, + dxt.dxf_long_t aux_order_id, dxt.dxf_double_t price, - dxt.dxf_int_t size, - dxt.dxf_int_t count, - dxf_order_scope_t scope, - dxf_order_side_t side, + dxt.dxf_double_t size, + dxt.dxf_double_t executed_size, + dxt.dxf_double_t count, + dxt.dxf_long_t trade_id, + dxt.dxf_double_t trade_price, + dxt.dxf_double_t trade_size, dxt.dxf_char_t exchange_code, - dxt.dxf_char_t source[DXF_RECORD_SUFFIX_SIZE], + dxf_order_side_t side, + dxf_order_scope_t scope, # _inner_oso # probably there should be some name (p58 of cython book) dxt.dxf_const_string_t market_maker, dxt.dxf_const_string_t spread_symbol @@ -243,7 +290,7 @@ cdef extern from "EventData.h": dxt.dxf_long_t time, dxt.dxf_char_t exchange_code, dxt.dxf_double_t price, - dxt.dxf_int_t size, + dxt.dxf_double_t size, dxt.dxf_double_t bid_price, dxt.dxf_double_t ask_price, dxt.dxf_const_string_t exchange_sale_conditions, @@ -274,7 +321,7 @@ cdef extern from "EventData.h": dxt.dxf_double_t vwap, dxt.dxf_double_t bid_volume, dxt.dxf_double_t ask_volume, - dxt.dxf_int_t open_interest, + dxt.dxf_double_t open_interest, dxt.dxf_double_t imp_volatility @@ -296,8 +343,16 @@ cdef extern from "EventData.h": ctypedef rd.dx_theo_price_t dxf_theo_price_t # /* Underlying --------------------------------------------------------------- */ -# /* Event and record are the same */ - ctypedef rd.dx_underlying_t dxf_underlying_t + ctypedef struct dxf_underlying_t: + dxt.dxf_double_t volatility, + dxt.dxf_double_t front_volatility, + dxt.dxf_double_t back_volatility, + dxt.dxf_double_t call_volume, + dxt.dxf_double_t put_volume, + dxt.dxf_double_t option_volume, + dxt.dxf_double_t put_call_ratio + + ctypedef dxf_underlying_t dxf_underlying # /* Series ------------------------------------------------------------------- */ ctypedef struct dxf_series_t: @@ -307,6 +362,9 @@ cdef extern from "EventData.h": dxt.dxf_int_t sequence, dxt.dxf_dayid_t expiration, dxt.dxf_double_t volatility, + dxt.dxf_double_t call_volume, + dxt.dxf_double_t put_volume, + dxt.dxf_double_t option_volume, dxt.dxf_double_t put_call_ratio, dxt.dxf_double_t forward_price, dxt.dxf_double_t dividend, @@ -445,7 +503,7 @@ cdef extern from "EventData.h": # * Various event functions # */ # /* -------------------------------------------------------------------------- */ -cdef extern from "EventData.h": +cdef extern from "": cdef dxt.dxf_const_string_t dx_event_type_to_string (int event_type) cdef int dx_get_event_data_struct_size (int event_id) cdef dx_event_id_t dx_get_event_id_by_bitmask (int event_bitmask) @@ -538,7 +596,7 @@ cdef extern from "EventData.h": # /* -------------------------------------------------------------------------- */ ctypedef struct dxf_price_level_element_t: dxt.dxf_double_t price - dxt.dxf_long_t size + dxt.dxf_double_t size dxt.dxf_long_t time diff --git a/dxfeed/core/pxd_include/RecordData.pxd b/dxfeed/core/pxd_include/RecordData.pxd index f096ad5..b47cda8 100644 --- a/dxfeed/core/pxd_include/RecordData.pxd +++ b/dxfeed/core/pxd_include/RecordData.pxd @@ -31,7 +31,7 @@ from dxfeed.core.pxd_include.DXTypes cimport * # * Record type constants # */ # /* -------------------------------------------------------------------------- */ -cdef extern from "RecordData.h": +cdef extern from "": ctypedef enum dx_record_info_id_t: dx_rid_begin = 0, dx_rid_trade = dx_rid_begin, @@ -74,12 +74,13 @@ cdef extern from "RecordData.h": dxf_int_t time_nanos dxf_char_t exchange_code dxf_double_t price - dxf_int_t size + dxf_double_t size dxf_int_t tick dxf_double_t change - dxf_int_t flags + dxf_dayid_t day_id; dxf_double_t day_volume dxf_double_t day_turnover + dxf_int_t flags ctypedef dx_trade_t dx_trade_eth_t @@ -89,11 +90,11 @@ cdef extern from "RecordData.h": dxf_int_t bid_time dxf_char_t bid_exchange_code dxf_double_t bid_price - dxf_int_t bid_size + dxf_double_t bid_size dxf_int_t ask_time dxf_char_t ask_exchange_code dxf_double_t ask_price - dxf_int_t ask_size + dxf_double_t ask_size ctypedef struct dx_summary_t: dxf_dayid_t day_id @@ -104,18 +105,18 @@ cdef extern from "RecordData.h": dxf_dayid_t prev_day_id dxf_double_t prev_day_close_price dxf_double_t prev_day_volume - dxf_int_t open_interest + dxf_double_t open_interest dxf_int_t flags ctypedef struct dx_profile_t: dxf_double_t beta dxf_double_t eps - dxf_int_t div_freq + dxf_double_t div_freq dxf_double_t exd_div_amount dxf_dayid_t exd_div_date - dxf_double_t _52_high_price - dxf_double_t _52_low_price + dxf_double_t high_price_52 + dxf_double_t low_price_52 dxf_double_t shares dxf_double_t free_float dxf_double_t high_limit_price @@ -126,28 +127,35 @@ cdef extern from "RecordData.h": dxf_const_string_t description dxf_const_string_t status_reason -cdef extern from "RecordData.h": +cdef extern from "": ctypedef struct dx_market_maker_t: dxf_char_t mm_exchange dxf_int_t mm_id dxf_int_t mmbid_time dxf_double_t mmbid_price - dxf_int_t mmbid_size - dxf_int_t mmbid_count + dxf_double_t mmbid_size + dxf_double_t mmbid_count dxf_int_t mmask_time dxf_double_t mmask_price - dxf_int_t mmask_size - dxf_int_t mmask_count + dxf_double_t mmask_size + dxf_double_t mmask_count ctypedef struct dx_order_t: dxf_int_t index dxf_int_t time dxf_int_t time_nanos dxf_int_t sequence + dxf_long_t action_time; + dxf_long_t order_id; + dxf_long_t aux_order_id; dxf_double_t price - dxf_int_t size - dxf_int_t count + dxf_double_t size + dxf_double_t executed_size; + dxf_double_t count dxf_int_t flags + dxf_long_t trade_id; + dxf_double_t trade_price; + dxf_double_t trade_size; dxf_int_t mmid @@ -156,10 +164,17 @@ cdef extern from "RecordData.h": dxf_int_t time dxf_int_t time_nanos dxf_int_t sequence + dxf_long_t action_time; + dxf_long_t order_id; + dxf_long_t aux_order_id; dxf_double_t price - dxf_int_t size - dxf_int_t count + dxf_double_t size + dxf_double_t executed_size; + dxf_double_t count dxf_int_t flags + dxf_long_t trade_id; + dxf_double_t trade_price; + dxf_double_t trade_size; dxf_const_string_t spread_symbol @@ -168,7 +183,7 @@ cdef extern from "RecordData.h": dxf_int_t sequence dxf_char_t exchange_code dxf_double_t price - dxf_int_t size + dxf_double_t size dxf_double_t bid_price dxf_double_t ask_price dxf_int_t exchange_sale_conditions @@ -188,7 +203,7 @@ cdef extern from "RecordData.h": dxf_double_t vwap dxf_double_t bid_volume dxf_double_t ask_volume - dxf_int_t open_interest + dxf_double_t open_interest dxf_double_t imp_volatility ctypedef struct dx_greeks_t: @@ -216,6 +231,8 @@ cdef extern from "RecordData.h": dxf_double_t volatility dxf_double_t front_volatility dxf_double_t back_volatility + dxf_double_t call_volume; + dxf_double_t put_volume; dxf_double_t put_call_ratio ctypedef struct dx_series_t: @@ -224,6 +241,8 @@ cdef extern from "RecordData.h": dxf_int_t sequence dxf_dayid_t expiration dxf_double_t volatility + dxf_double_t call_volume; + dxf_double_t put_volume; dxf_double_t put_call_ratio dxf_double_t forward_price dxf_double_t dividend diff --git a/dxfeed/core/utils/helpers.pyx b/dxfeed/core/utils/helpers.pyx index c1fd9ec..338fae6 100644 --- a/dxfeed/core/utils/helpers.pyx +++ b/dxfeed/core/utils/helpers.pyx @@ -1,3 +1,4 @@ +import os from warnings import warn from dxfeed.core.pxd_include.DXErrorCodes cimport * from dxfeed.core.pxd_include.DXFeed cimport * @@ -22,8 +23,8 @@ cdef object unicode_from_dxf_const_string_t(dxf_const_string_t wcs, int size=-1) Python unicode string """ if wcs == NULL: - return '' - ret_val = PyUnicode_FromWideChar(wcs, size) + return '' + ret_val = PyUnicode_FromWideChar(wcs, size) return ret_val cdef dxf_const_string_t dxf_const_string_t_from_unicode(object symbol): @@ -88,5 +89,6 @@ def get_include(): List of paths to header files """ out_dir = list() - out_dir.append(str(Path(dxfeed.__file__).resolve().parent.joinpath('dxfeed-c-api', 'include'))) - return out_dir \ No newline at end of file + out_dir.append( + str(os.path.realpath(Path(dxfeed.__file__).resolve().parent / 'dxfeed-c-api' / 'include'))) + return out_dir diff --git a/dxfeed/dxfeed-c-api b/dxfeed/dxfeed-c-api deleted file mode 160000 index d3c05fd..0000000 --- a/dxfeed/dxfeed-c-api +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d3c05fdb5c982bb62946a9c373bcbb1bc0bb44d0 diff --git a/pyproject.toml b/pyproject.toml index c954472..5abe2de 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = 'dxfeed' -version = '0.5.2' +version = '0.5.3' description = 'DXFeed Python API via C API' authors = ['Index Management Team '] build = 'build.py' @@ -12,17 +12,16 @@ classifiers = ['Development Status :: 4 - Beta', 'Operating System :: MacOS', 'Programming Language :: Cython', 'Programming Language :: Python :: 3 :: Only', + 'Programming Language :: Python :: 3.6', + 'Programming Language :: Python :: 3.7', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', 'Topic :: Office/Business :: Financial', 'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)', 'Natural Language :: English'] -include = ['LICENSE', 'dxfeed/**/*.c'] -exclude = ['dxfeed/dxfeed-c-api/wrappers*', - 'dxfeed/dxfeed-c-api/tests*', - 'dxfeed/dxfeed-c-api/scripts*', - 'dxfeed/dxfeed-c-api/lib*', - 'dxfeed/dxfeed-c-api/docs*', - 'dxfeed/dxfeed-c-api/.github*'] +include = ['LICENSE'] +exclude = ['dxfeed/tmp*'] [tool.poetry.urls] 'Source code' = 'https://github.com/dxFeed/dxfeed-python-api' @@ -30,29 +29,29 @@ exclude = ['dxfeed/dxfeed-c-api/wrappers*', [tool.poetry.dependencies] python = '^3.6' -pandas = '^0.25.1' -toml = '^0.10.0' +pandas = [{version = '>=1.0,!=1.1.5,<1.2', python = '>=3.6.1,<3.7'}, {version = '^1.0.0', python = '^3.7'}] +toml = '^0.10.2' # The following dependencies are used for docs generation when installed as extras # (e.g. pip install nornir[docs]) # Currently they have to be specified as non-dev dependencies with optional = true # Refer to: https://github.com/sdispater/poetry/issues/129 # Issue to watch for: https://github.com/python-poetry/poetry/issues/1644 jupyter = { version = '^1.0.0', optional = true } -cython = { version = '^0.29.13', optional = true } +cython = { version = '^0.29.14', optional = true } [tool.poetry.dev-dependencies] -cython = '^0.29.13' -taskipy = '^1.1.3' -pytest = '^5.3.5' -sphinx = '^2.4.4' -sphinx_rtd_theme = '^0.4.3' -pygments = '^2.6.1' +cython = '^0.29.14' +taskipy = '^1.8.1' +pytest = '^6.2.4' +sphinx = '^4.1.2' +sphinx_rtd_theme = '^0.5.2' +pygments = '^2.10' [tool.poetry.extras] docs = ['jupyter', 'cython'] [tool.taskipy.tasks] -clear = 'find dxfeed/core -type f \( -iname *.c -o -iname *.cpp -o -iname *.pyd -o -iname *.so \) -delete' +clear = 'python clear.py' build = 'python build.py && poetry build' build_inplace = 'python -c "from build import *; build_extensions()"' html_docs = 'make html -C docs' @@ -61,5 +60,12 @@ post_build = 'task clear' post_test = 'task clear' [build-system] -requires = ['poetry_core>=1.0.0', 'setuptools', 'cython'] +requires = ['poetry_core>=1.0.0', 'setuptools>=57.4.0', 'cython', 'toml>=0.10.2'] build-backend = 'poetry.core.masonry.api' + +[build.native-dependencies] +dxfeed_c_api = '8.3.0' +# The URL template for zipped dxFeed C API bundle. Parameters used: +# {version} - The C API version +# {os} - The target OS +dxfeed_c_api_bundle_url_template = 'https://github.com/dxFeed/dxfeed-c-api/releases/download/{version}/dxfeed-c-api-{version}-{os}-no-tls.zip' diff --git a/tests/test_dxfeedpy.py b/tests/test_dxfeedpy.py index b8f97c3..3684226 100644 --- a/tests/test_dxfeedpy.py +++ b/tests/test_dxfeedpy.py @@ -90,7 +90,7 @@ def test_symbol_addition(connection): dxc.dxf_add_symbols(sc=sub, symbols=symbols) actual_symbols = dxc.dxf_get_symbols(sc=sub) dxc.dxf_close_subscription(sub) - assert symbols == actual_symbols + assert set(symbols) == set(actual_symbols) def test_symbol_deletion(connection): @@ -110,7 +110,7 @@ def test_wrong_symbol_types_ignored(connection): dxc.dxf_add_symbols(sc=sub, symbols=symbols + [1, 5.0, [], True, {}, ()]) actual_symbols = dxc.dxf_get_symbols(sc=sub) dxc.dxf_close_subscription(sub) - assert symbols == actual_symbols + assert set(symbols) == set(actual_symbols) def test_symbol_clearing(connection): @@ -155,4 +155,4 @@ def test_double_event_handler_attachment(connection): dxc.dxf_add_symbols(sub, symbols) sub.set_event_handler(handler2) assert sub.get_event_handler() is handler2 - assert dxc.dxf_get_symbols(sub) == symbols + assert set(dxc.dxf_get_symbols(sub)) == set(symbols) diff --git a/tests/test_subscription_class.py b/tests/test_subscription_class.py index 5b79ebf..035605c 100644 --- a/tests/test_subscription_class.py +++ b/tests/test_subscription_class.py @@ -42,24 +42,24 @@ def test_subscription_timed_event_type_property(subscription_timed): def test_subscription_event_type_property(subscription): subscription = subscription.add_symbols(ValueStorage.symbols) - assert subscription.symbols == ValueStorage.symbols + assert set(subscription.symbols) == set(ValueStorage.symbols) def test_subscription_timed_event_type_property(subscription_timed): subscription = subscription_timed.add_symbols(ValueStorage.symbols) - assert subscription.symbols == ValueStorage.symbols + assert set(subscription.symbols) == set(ValueStorage.symbols) def test_remove_symbol(subscription): subscription = subscription.add_symbols(ValueStorage.symbols) \ .remove_symbols([ValueStorage.symbols[0]]) - assert subscription.symbols == ValueStorage.symbols[1:] + assert set(subscription.symbols) == set(ValueStorage.symbols[1:]) def test_remove_symbol_timed(subscription_timed): subscription_timed = subscription_timed.add_symbols(ValueStorage.symbols) \ .remove_symbols([ValueStorage.symbols[0]]) - assert subscription_timed.symbols == ValueStorage.symbols[1:] + assert set(subscription_timed.symbols) == set(ValueStorage.symbols[1:]) def test_remove_all_symbols(subscription):