├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md └── workflows │ ├── publishpackage.yml │ ├── test_on_pr.yml │ ├── testdevelop.yml │ └── testpackage.yml ├── .gitignore ├── .readthedocs.yml ├── LICENSE ├── README.md ├── build.py ├── clear.py ├── docs ├── Makefile ├── _static │ ├── custom.css │ └── custom.js ├── api.rst ├── basic_usage.rst ├── conf.py ├── core_usage.rst ├── custom_handler.rst ├── custom_listener.rst ├── devs.rst ├── index.rst ├── installation.rst └── zipper.py ├── dxfeed ├── __init__.py ├── core │ ├── DXFeedPy.pyx │ ├── __init__.py │ ├── listeners │ │ ├── __init__.py │ │ ├── listener.pxd │ │ └── listener.pyx │ ├── pxd_include │ │ ├── DXErrorCodes.pxd │ │ ├── DXFeed.pxd │ │ ├── DXTypes.pxd │ │ ├── EventData.pxd │ │ ├── RecordData.pxd │ │ └── __init__.py │ └── utils │ │ ├── __init__.py │ │ ├── data_class.py │ │ ├── handler.pxd │ │ ├── handler.pyx │ │ ├── helpers.pxd │ │ └── helpers.pyx └── wrappers │ ├── __init__.py │ ├── class_utils.py │ ├── endpoint.py │ └── subscription.py ├── examples ├── AllSubscriptionsExample.ipynb ├── CustomHandlerExample.ipynb ├── Low_level_API │ ├── BasicAndTimedSubscription.ipynb │ └── CustomListenerExample.ipynb └── SubscriptionExample.ipynb ├── pyproject.toml └── tests ├── conftest.py ├── test_class_utils.py ├── test_dxfeedpy.py ├── test_endpoint_class.py └── test_subscription_class.py /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | - A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | - Provide reproducible code snippet, please. 15 | 16 | **Behavior** 17 | - Bug case: provide error traceback, please. 18 | - Unexpected behavior case: describe the key points of behavior you face, please. 19 | 20 | **Expected behavior** 21 | - Provide a clear and concise description of what you expected to happen, please. 22 | 23 | **Environment:** 24 | - OS: [e.g. Ubuntu-16.04] 25 | - Python version: [e.g. 3.7.3] 26 | - Package version: [e.g. 0.4.0] 27 | 28 | Note: to get package version run `import pkg_resources; pkg_resources.get_distribution('dxfeed').version` 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the feature and the reason for it** 11 | What functionality you'd like to be added and why. 12 | E.g.: Please, add pandas dataframe support to the default handler. It is the most common way to work with tables in python 13 | 14 | **Describe the solution you'd like** 15 | A clear and concise description of what you want to happen. Expected code snippets are welcome 16 | 17 | **Describe alternatives you've considered** 18 | A clear and concise description of any alternative solutions or features you've considered. 19 | 20 | **Additional context** 21 | Add any other context or screenshots about the feature request here. 22 | -------------------------------------------------------------------------------- /.github/workflows/publishpackage.yml: -------------------------------------------------------------------------------- 1 | # This workflow publishes package to pypi via poetry 2 | 3 | name: Publish package 4 | 5 | on: 6 | push: 7 | tags: 8 | - '*' 9 | 10 | jobs: 11 | build: 12 | strategy: 13 | matrix: 14 | os: [ubuntu-20.04, ubuntu-latest, windows-latest, macos-latest] 15 | python-version: [3.7, 3.8, 3.9] 16 | runs-on: ${{ matrix.os }} 17 | 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | submodules: true 22 | 23 | - name: Set up Python ${{ matrix.python-version }} 24 | uses: actions/setup-python@v5 25 | with: 26 | python-version: ${{ matrix.python-version }} 27 | 28 | - name: Install Poetry 29 | uses: abatilo/actions-poetry@v2 30 | 31 | - name: Set poetry env 32 | run: | 33 | pip install --upgrade pip 34 | poetry install 35 | shell: bash 36 | 37 | - name: Create tarball 38 | if: matrix.python-version == 3.7 39 | run: | 40 | poetry build -f sdist 41 | shell: bash 42 | 43 | - name: Create wheel 44 | run: | 45 | poetry build -f wheel 46 | shell: bash 47 | 48 | - name: Upload artifacts 49 | if: success() 50 | uses: actions/upload-artifact@v2 51 | with: 52 | name: artifacts 53 | path: dist/ 54 | 55 | 56 | publish: 57 | runs-on: ubuntu-20.04 58 | needs: build 59 | steps: 60 | - uses: actions/checkout@v4 61 | 62 | - name: Set up Python 63 | uses: actions/setup-python@v5 64 | with: 65 | python-version: 3.7 66 | 67 | - name: Install Poetry 68 | uses: abatilo/actions-poetry@v2 69 | 70 | - name: Clear dist folder 71 | run: | 72 | rm -rf dist/ 73 | 74 | - name: Download 75 | uses: actions/download-artifact@v2 76 | with: 77 | name: artifacts 78 | path: dist/ 79 | 80 | - name: Publish to PyPI 81 | if: success() 82 | run: | 83 | poetry publish -u ${{ secrets.PYPI_USERNAME }} -p ${{ secrets.PYPI_PASSWORD }} 84 | shell: bash 85 | -------------------------------------------------------------------------------- /.github/workflows/test_on_pr.yml: -------------------------------------------------------------------------------- 1 | # This workflow will check package installation and runs tests on PR 2 | name: Test on PR 3 | 4 | on: 5 | pull_request: 6 | types: [opened, reopened, synchronize] 7 | 8 | jobs: 9 | installation: 10 | strategy: 11 | matrix: 12 | os: [ubuntu-20.04, ubuntu-latest, windows-latest, macos-latest] 13 | python-version: [3.7, 3.8, 3.9] 14 | runs-on: ${{ matrix.os }} 15 | 16 | steps: 17 | - uses: actions/checkout@v4 18 | with: 19 | submodules: true 20 | 21 | - name: Set up Python ${{ matrix.python-version }} 22 | uses: actions/setup-python@v5 23 | with: 24 | python-version: ${{ matrix.python-version }} 25 | 26 | - name: Install Poetry 27 | uses: abatilo/actions-poetry@v2 28 | 29 | - name: Set poetry env 30 | run: | 31 | pip install --upgrade pip 32 | poetry install 33 | poetry build -f sdist 34 | shell: bash 35 | 36 | - name: Install package artifact 37 | run: | 38 | pip install dist/dxfeed* 39 | pip uninstall --yes dxfeed 40 | shell: bash 41 | 42 | tests: 43 | needs: installation 44 | strategy: 45 | matrix: 46 | os: [ubuntu-20.04, ubuntu-latest, windows-latest, macos-latest] 47 | python-version: [3.7, 3.8, 3.9] 48 | runs-on: ${{ matrix.os }} 49 | 50 | steps: 51 | - uses: actions/checkout@v4 52 | with: 53 | submodules: true 54 | 55 | - name: Set up Python ${{ matrix.python-version }} 56 | uses: actions/setup-python@v5 57 | with: 58 | python-version: ${{ matrix.python-version }} 59 | 60 | - name: Install Poetry 61 | uses: abatilo/actions-poetry@v2 62 | 63 | - name: Set poetry env 64 | run: | 65 | pip install --upgrade pip 66 | poetry install 67 | shell: bash 68 | 69 | - name: Run tests 70 | run: | 71 | poetry run task test 72 | shell: bash 73 | 74 | - name: Generate doc 75 | if: matrix.os == 'ubuntu-20.04' && matrix.python-version == 3.7 76 | run: | 77 | poetry run task html_docs 78 | shell: bash 79 | -------------------------------------------------------------------------------- /.github/workflows/testdevelop.yml: -------------------------------------------------------------------------------- 1 | # This workflow will check package installation and runs tests in pre-release branch 2 | # On Ubuntu, Windows and MacOS 3 | # Triggered by push to develop 4 | name: Test package dev mode 5 | 6 | on: 7 | push: 8 | branches: 9 | - develop 10 | 11 | jobs: 12 | installation: 13 | strategy: 14 | matrix: 15 | os: [ubuntu-20.04, ubuntu-latest, windows-latest, macos-latest] 16 | python-version: [3.7, 3.8, 3.9] 17 | runs-on: ${{ matrix.os }} 18 | 19 | steps: 20 | - uses: actions/checkout@v4 21 | with: 22 | submodules: true 23 | 24 | - name: Set up Python ${{ matrix.python-version }} 25 | uses: actions/setup-python@v5 26 | with: 27 | python-version: ${{ matrix.python-version }} 28 | 29 | - name: Install Poetry 30 | uses: abatilo/actions-poetry@v2 31 | 32 | - name: Set poetry env 33 | run: | 34 | pip install --upgrade pip 35 | poetry install 36 | poetry build -f sdist 37 | shell: bash 38 | 39 | - name: Install package artifact 40 | run: | 41 | pip install dist/dxfeed* 42 | pip uninstall --yes dxfeed 43 | shell: bash 44 | 45 | tests: 46 | needs: installation 47 | strategy: 48 | matrix: 49 | os: [ubuntu-20.04, ubuntu-latest, windows-latest, macos-latest] 50 | python-version: [3.7, 3.8, 3.9] 51 | runs-on: ${{ matrix.os }} 52 | 53 | steps: 54 | - uses: actions/checkout@v4 55 | with: 56 | submodules: true 57 | 58 | - name: Set up Python ${{ matrix.python-version }} 59 | uses: actions/setup-python@v5 60 | with: 61 | python-version: ${{ matrix.python-version }} 62 | 63 | - name: Install Poetry 64 | uses: abatilo/actions-poetry@v2 65 | 66 | - name: Set poetry env 67 | run: | 68 | pip install --upgrade pip 69 | poetry install 70 | shell: bash 71 | 72 | - name: Run tests 73 | run: | 74 | poetry run task test 75 | shell: bash 76 | 77 | - name: Generate doc 78 | if: matrix.os == 'ubuntu-20.04' && matrix.python-version == 3.7 79 | run: | 80 | poetry run task html_docs 81 | shell: bash 82 | -------------------------------------------------------------------------------- /.github/workflows/testpackage.yml: -------------------------------------------------------------------------------- 1 | # This workflow will check package installation and runs tests 2 | # On Ubuntu, Windows and MacOS 3 | # Triggered by push to master 4 | 5 | name: Test package 6 | 7 | on: 8 | push: 9 | branches: 10 | - master 11 | 12 | jobs: 13 | installation: 14 | strategy: 15 | matrix: 16 | os: [ubuntu-20.04, ubuntu-latest, windows-latest, macos-latest] 17 | python-version: [3.7, 3.8, 3.9] 18 | runs-on: ${{ matrix.os }} 19 | 20 | steps: 21 | - uses: actions/checkout@v4 22 | with: 23 | submodules: true 24 | 25 | - name: Set up Python ${{ matrix.python-version }} 26 | uses: actions/setup-python@v5 27 | with: 28 | python-version: ${{ matrix.python-version }} 29 | 30 | - name: Install Poetry 31 | uses: abatilo/actions-poetry@v2 32 | 33 | - name: Set poetry env 34 | run: | 35 | pip install --upgrade pip 36 | poetry install 37 | poetry build -f sdist 38 | shell: bash 39 | 40 | - name: Install package artifact 41 | run: | 42 | pip install dist/dxfeed* 43 | pip uninstall --yes dxfeed 44 | shell: bash 45 | 46 | tests: 47 | needs: installation 48 | strategy: 49 | matrix: 50 | os: [ubuntu-20.04, ubuntu-latest, windows-latest, macos-latest] 51 | python-version: [3.7, 3.8, 3.9] 52 | runs-on: ${{ matrix.os }} 53 | 54 | steps: 55 | - uses: actions/checkout@v4 56 | with: 57 | submodules: true 58 | 59 | - name: Set up Python ${{ matrix.python-version }} 60 | uses: actions/setup-python@v5 61 | with: 62 | python-version: ${{ matrix.python-version }} 63 | 64 | - name: Install Poetry 65 | uses: abatilo/actions-poetry@v2 66 | 67 | - name: Set poetry env 68 | run: | 69 | pip install --upgrade pip 70 | poetry install 71 | shell: bash 72 | 73 | - name: Run tests 74 | run: | 75 | poetry run task test 76 | shell: bash 77 | 78 | - name: Generate doc 79 | if: matrix.os == 'ubuntu-20.04' && matrix.python-version == 3.7 80 | run: | 81 | poetry run task html_docs 82 | shell: bash 83 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # IDE 2 | *.idea 3 | 4 | # Python Build files 5 | build/* 6 | *.pyd 7 | 8 | _build* 9 | 10 | *__pycache__ 11 | 12 | *cython_debug* 13 | 14 | *Untitled* 15 | 16 | *.ipynb_checkpoints* 17 | 18 | Pipfile* 19 | dist* 20 | dist 21 | build/** 22 | dxfeed/core/**/*.c 23 | dxfeed/tmp 24 | 25 | setup.py 26 | poetry.lock 27 | dxfeed.egg-info 28 | -------------------------------------------------------------------------------- /.readthedocs.yml: -------------------------------------------------------------------------------- 1 | # Required 2 | version: 2 3 | 4 | # Build documentation in the docs/ directory with Sphinx 5 | sphinx: 6 | configuration: docs/conf.py 7 | 8 | # Optionally build your docs in additional formats such as PDF and ePub 9 | formats: all 10 | 11 | # Optionally set the version of Python and requirements required to build your docs 12 | python: 13 | version: 3.7 14 | install: 15 | - method: pip 16 | path: . 17 | extra_requirements: 18 | - docs 19 | 20 | submodules: 21 | include: all 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Mozilla Public License Version 2.0 2 | ================================== 3 | 4 | 1. Definitions 5 | -------------- 6 | 7 | 1.1. "Contributor" 8 | means each individual or legal entity that creates, contributes to 9 | the creation of, or owns Covered Software. 10 | 11 | 1.2. "Contributor Version" 12 | means the combination of the Contributions of others (if any) used 13 | by a Contributor and that particular Contributor's Contribution. 14 | 15 | 1.3. "Contribution" 16 | means Covered Software of a particular Contributor. 17 | 18 | 1.4. "Covered Software" 19 | means Source Code Form to which the initial Contributor has attached 20 | the notice in Exhibit A, the Executable Form of such Source Code 21 | Form, and Modifications of such Source Code Form, in each case 22 | including portions thereof. 23 | 24 | 1.5. "Incompatible With Secondary Licenses" 25 | means 26 | 27 | (a) that the initial Contributor has attached the notice described 28 | in Exhibit B to the Covered Software; or 29 | 30 | (b) that the Covered Software was made available under the terms of 31 | version 1.1 or earlier of the License, but not also under the 32 | terms of a Secondary License. 33 | 34 | 1.6. "Executable Form" 35 | means any form of the work other than Source Code Form. 36 | 37 | 1.7. "Larger Work" 38 | means a work that combines Covered Software with other material, in 39 | a separate file or files, that is not Covered Software. 40 | 41 | 1.8. "License" 42 | means this document. 43 | 44 | 1.9. "Licensable" 45 | means having the right to grant, to the maximum extent possible, 46 | whether at the time of the initial grant or subsequently, any and 47 | all of the rights conveyed by this License. 48 | 49 | 1.10. "Modifications" 50 | means any of the following: 51 | 52 | (a) any file in Source Code Form that results from an addition to, 53 | deletion from, or modification of the contents of Covered 54 | Software; or 55 | 56 | (b) any new file in Source Code Form that contains any Covered 57 | Software. 58 | 59 | 1.11. "Patent Claims" of a Contributor 60 | means any patent claim(s), including without limitation, method, 61 | process, and apparatus claims, in any patent Licensable by such 62 | Contributor that would be infringed, but for the grant of the 63 | License, by the making, using, selling, offering for sale, having 64 | made, import, or transfer of either its Contributions or its 65 | Contributor Version. 66 | 67 | 1.12. "Secondary License" 68 | means either the GNU General Public License, Version 2.0, the GNU 69 | Lesser General Public License, Version 2.1, the GNU Affero General 70 | Public License, Version 3.0, or any later versions of those 71 | licenses. 72 | 73 | 1.13. "Source Code Form" 74 | means the form of the work preferred for making modifications. 75 | 76 | 1.14. "You" (or "Your") 77 | means an individual or a legal entity exercising rights under this 78 | License. For legal entities, "You" includes any entity that 79 | controls, is controlled by, or is under common control with You. For 80 | purposes of this definition, "control" means (a) the power, direct 81 | or indirect, to cause the direction or management of such entity, 82 | whether by contract or otherwise, or (b) ownership of more than 83 | fifty percent (50%) of the outstanding shares or beneficial 84 | ownership of such entity. 85 | 86 | 2. License Grants and Conditions 87 | -------------------------------- 88 | 89 | 2.1. Grants 90 | 91 | Each Contributor hereby grants You a world-wide, royalty-free, 92 | non-exclusive license: 93 | 94 | (a) under intellectual property rights (other than patent or trademark) 95 | Licensable by such Contributor to use, reproduce, make available, 96 | modify, display, perform, distribute, and otherwise exploit its 97 | Contributions, either on an unmodified basis, with Modifications, or 98 | as part of a Larger Work; and 99 | 100 | (b) under Patent Claims of such Contributor to make, use, sell, offer 101 | for sale, have made, import, and otherwise transfer either its 102 | Contributions or its Contributor Version. 103 | 104 | 2.2. Effective Date 105 | 106 | The licenses granted in Section 2.1 with respect to any Contribution 107 | become effective for each Contribution on the date the Contributor first 108 | distributes such Contribution. 109 | 110 | 2.3. Limitations on Grant Scope 111 | 112 | The licenses granted in this Section 2 are the only rights granted under 113 | this License. No additional rights or licenses will be implied from the 114 | distribution or licensing of Covered Software under this License. 115 | Notwithstanding Section 2.1(b) above, no patent license is granted by a 116 | Contributor: 117 | 118 | (a) for any code that a Contributor has removed from Covered Software; 119 | or 120 | 121 | (b) for infringements caused by: (i) Your and any other third party's 122 | modifications of Covered Software, or (ii) the combination of its 123 | Contributions with other software (except as part of its Contributor 124 | Version); or 125 | 126 | (c) under Patent Claims infringed by Covered Software in the absence of 127 | its Contributions. 128 | 129 | This License does not grant any rights in the trademarks, service marks, 130 | or logos of any Contributor (except as may be necessary to comply with 131 | the notice requirements in Section 3.4). 132 | 133 | 2.4. Subsequent Licenses 134 | 135 | No Contributor makes additional grants as a result of Your choice to 136 | distribute the Covered Software under a subsequent version of this 137 | License (see Section 10.2) or under the terms of a Secondary License (if 138 | permitted under the terms of Section 3.3). 139 | 140 | 2.5. Representation 141 | 142 | Each Contributor represents that the Contributor believes its 143 | Contributions are its original creation(s) or it has sufficient rights 144 | to grant the rights to its Contributions conveyed by this License. 145 | 146 | 2.6. Fair Use 147 | 148 | This License is not intended to limit any rights You have under 149 | applicable copyright doctrines of fair use, fair dealing, or other 150 | equivalents. 151 | 152 | 2.7. Conditions 153 | 154 | Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted 155 | in Section 2.1. 156 | 157 | 3. Responsibilities 158 | ------------------- 159 | 160 | 3.1. Distribution of Source Form 161 | 162 | All distribution of Covered Software in Source Code Form, including any 163 | Modifications that You create or to which You contribute, must be under 164 | the terms of this License. You must inform recipients that the Source 165 | Code Form of the Covered Software is governed by the terms of this 166 | License, and how they can obtain a copy of this License. You may not 167 | attempt to alter or restrict the recipients' rights in the Source Code 168 | Form. 169 | 170 | 3.2. Distribution of Executable Form 171 | 172 | If You distribute Covered Software in Executable Form then: 173 | 174 | (a) such Covered Software must also be made available in Source Code 175 | Form, as described in Section 3.1, and You must inform recipients of 176 | the Executable Form how they can obtain a copy of such Source Code 177 | Form by reasonable means in a timely manner, at a charge no more 178 | than the cost of distribution to the recipient; and 179 | 180 | (b) You may distribute such Executable Form under the terms of this 181 | License, or sublicense it under different terms, provided that the 182 | license for the Executable Form does not attempt to limit or alter 183 | the recipients' rights in the Source Code Form under this License. 184 | 185 | 3.3. Distribution of a Larger Work 186 | 187 | You may create and distribute a Larger Work under terms of Your choice, 188 | provided that You also comply with the requirements of this License for 189 | the Covered Software. If the Larger Work is a combination of Covered 190 | Software with a work governed by one or more Secondary Licenses, and the 191 | Covered Software is not Incompatible With Secondary Licenses, this 192 | License permits You to additionally distribute such Covered Software 193 | under the terms of such Secondary License(s), so that the recipient of 194 | the Larger Work may, at their option, further distribute the Covered 195 | Software under the terms of either this License or such Secondary 196 | License(s). 197 | 198 | 3.4. Notices 199 | 200 | You may not remove or alter the substance of any license notices 201 | (including copyright notices, patent notices, disclaimers of warranty, 202 | or limitations of liability) contained within the Source Code Form of 203 | the Covered Software, except that You may alter any license notices to 204 | the extent required to remedy known factual inaccuracies. 205 | 206 | 3.5. Application of Additional Terms 207 | 208 | You may choose to offer, and to charge a fee for, warranty, support, 209 | indemnity or liability obligations to one or more recipients of Covered 210 | Software. However, You may do so only on Your own behalf, and not on 211 | behalf of any Contributor. You must make it absolutely clear that any 212 | such warranty, support, indemnity, or liability obligation is offered by 213 | You alone, and You hereby agree to indemnify every Contributor for any 214 | liability incurred by such Contributor as a result of warranty, support, 215 | indemnity or liability terms You offer. You may include additional 216 | disclaimers of warranty and limitations of liability specific to any 217 | jurisdiction. 218 | 219 | 4. Inability to Comply Due to Statute or Regulation 220 | --------------------------------------------------- 221 | 222 | If it is impossible for You to comply with any of the terms of this 223 | License with respect to some or all of the Covered Software due to 224 | statute, judicial order, or regulation then You must: (a) comply with 225 | the terms of this License to the maximum extent possible; and (b) 226 | describe the limitations and the code they affect. Such description must 227 | be placed in a text file included with all distributions of the Covered 228 | Software under this License. Except to the extent prohibited by statute 229 | or regulation, such description must be sufficiently detailed for a 230 | recipient of ordinary skill to be able to understand it. 231 | 232 | 5. Termination 233 | -------------- 234 | 235 | 5.1. The rights granted under this License will terminate automatically 236 | if You fail to comply with any of its terms. However, if You become 237 | compliant, then the rights granted under this License from a particular 238 | Contributor are reinstated (a) provisionally, unless and until such 239 | Contributor explicitly and finally terminates Your grants, and (b) on an 240 | ongoing basis, if such Contributor fails to notify You of the 241 | non-compliance by some reasonable means prior to 60 days after You have 242 | come back into compliance. Moreover, Your grants from a particular 243 | Contributor are reinstated on an ongoing basis if such Contributor 244 | notifies You of the non-compliance by some reasonable means, this is the 245 | first time You have received notice of non-compliance with this License 246 | from such Contributor, and You become compliant prior to 30 days after 247 | Your receipt of the notice. 248 | 249 | 5.2. If You initiate litigation against any entity by asserting a patent 250 | infringement claim (excluding declaratory judgment actions, 251 | counter-claims, and cross-claims) alleging that a Contributor Version 252 | directly or indirectly infringes any patent, then the rights granted to 253 | You by any and all Contributors for the Covered Software under Section 254 | 2.1 of this License shall terminate. 255 | 256 | 5.3. In the event of termination under Sections 5.1 or 5.2 above, all 257 | end user license agreements (excluding distributors and resellers) which 258 | have been validly granted by You or Your distributors under this License 259 | prior to termination shall survive termination. 260 | 261 | ************************************************************************ 262 | * * 263 | * 6. Disclaimer of Warranty * 264 | * ------------------------- * 265 | * * 266 | * Covered Software is provided under this License on an "as is" * 267 | * basis, without warranty of any kind, either expressed, implied, or * 268 | * statutory, including, without limitation, warranties that the * 269 | * Covered Software is free of defects, merchantable, fit for a * 270 | * particular purpose or non-infringing. The entire risk as to the * 271 | * quality and performance of the Covered Software is with You. * 272 | * Should any Covered Software prove defective in any respect, You * 273 | * (not any Contributor) assume the cost of any necessary servicing, * 274 | * repair, or correction. This disclaimer of warranty constitutes an * 275 | * essential part of this License. No use of any Covered Software is * 276 | * authorized under this License except under this disclaimer. * 277 | * * 278 | ************************************************************************ 279 | 280 | ************************************************************************ 281 | * * 282 | * 7. Limitation of Liability * 283 | * -------------------------- * 284 | * * 285 | * Under no circumstances and under no legal theory, whether tort * 286 | * (including negligence), contract, or otherwise, shall any * 287 | * Contributor, or anyone who distributes Covered Software as * 288 | * permitted above, be liable to You for any direct, indirect, * 289 | * special, incidental, or consequential damages of any character * 290 | * including, without limitation, damages for lost profits, loss of * 291 | * goodwill, work stoppage, computer failure or malfunction, or any * 292 | * and all other commercial damages or losses, even if such party * 293 | * shall have been informed of the possibility of such damages. This * 294 | * limitation of liability shall not apply to liability for death or * 295 | * personal injury resulting from such party's negligence to the * 296 | * extent applicable law prohibits such limitation. Some * 297 | * jurisdictions do not allow the exclusion or limitation of * 298 | * incidental or consequential damages, so this exclusion and * 299 | * limitation may not apply to You. * 300 | * * 301 | ************************************************************************ 302 | 303 | 8. Litigation 304 | ------------- 305 | 306 | Any litigation relating to this License may be brought only in the 307 | courts of a jurisdiction where the defendant maintains its principal 308 | place of business and such litigation shall be governed by laws of that 309 | jurisdiction, without reference to its conflict-of-law provisions. 310 | Nothing in this Section shall prevent a party's ability to bring 311 | cross-claims or counter-claims. 312 | 313 | 9. Miscellaneous 314 | ---------------- 315 | 316 | This License represents the complete agreement concerning the subject 317 | matter hereof. If any provision of this License is held to be 318 | unenforceable, such provision shall be reformed only to the extent 319 | necessary to make it enforceable. Any law or regulation which provides 320 | that the language of a contract shall be construed against the drafter 321 | shall not be used to construe this License against a Contributor. 322 | 323 | 10. Versions of the License 324 | --------------------------- 325 | 326 | 10.1. New Versions 327 | 328 | Mozilla Foundation is the license steward. Except as provided in Section 329 | 10.3, no one other than the license steward has the right to modify or 330 | publish new versions of this License. Each version will be given a 331 | distinguishing version number. 332 | 333 | 10.2. Effect of New Versions 334 | 335 | You may distribute the Covered Software under the terms of the version 336 | of the License under which You originally received the Covered Software, 337 | or under the terms of any subsequent version published by the license 338 | steward. 339 | 340 | 10.3. Modified Versions 341 | 342 | If you create software not governed by this License, and you want to 343 | create a new license for such software, you may create and use a 344 | modified version of this License if you rename the license and remove 345 | any references to the name of the license steward (except to note that 346 | such modified license differs from this License). 347 | 348 | 10.4. Distributing Source Code Form that is Incompatible With Secondary 349 | Licenses 350 | 351 | If You choose to distribute Source Code Form that is Incompatible With 352 | Secondary Licenses under the terms of this version of the License, the 353 | notice described in Exhibit B of this License must be attached. 354 | 355 | Exhibit A - Source Code Form License Notice 356 | ------------------------------------------- 357 | 358 | This Source Code Form is subject to the terms of the Mozilla Public 359 | License, v. 2.0. If a copy of the MPL was not distributed with this 360 | file, You can obtain one at http://mozilla.org/MPL/2.0/. 361 | 362 | If it is not possible or desirable to put the notice in a particular 363 | file, then You may include the notice in a location (such as a LICENSE 364 | file in a relevant directory) where a recipient would be likely to look 365 | for such a notice. 366 | 367 | You may add additional accurate notices of copyright ownership. 368 | 369 | Exhibit B - "Incompatible With Secondary Licenses" Notice 370 | --------------------------------------------------------- 371 | 372 | This Source Code Form is "Incompatible With Secondary Licenses", as 373 | defined by the Mozilla Public License, v. 2.0. 374 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # dxfeed package 2 | 3 | [![PyPI](https://img.shields.io/pypi/v/dxfeed)](https://pypi.org/project/dxfeed/) 4 | [![Documentation Status](https://readthedocs.org/projects/dxfeed/badge/?version=latest)](https://dxfeed.readthedocs.io/en/latest/?badge=latest) 5 | [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/dxfeed)](https://pypi.org/project/dxfeed/) 6 | ![Platform](https://img.shields.io/badge/platform-win--x64%20%7C%20linux--x64%20%7C%20osx--x64-lightgrey) 7 | [![PyPI - Wheel](https://img.shields.io/pypi/wheel/dxfeed)](https://pypi.org/project/dxfeed/) 8 | [![PyPI - License](https://img.shields.io/pypi/l/dxfeed)](https://github.com/dxFeed/dxfeed-python-api/blob/master/LICENSE) 9 | [![Test workflow](https://github.com/dxFeed/dxfeed-python-api/workflows/Test%20package/badge.svg)](https://github.com/dxFeed/dxfeed-python-api/actions) 10 | 11 | :warning: 12 | This library will be superseded by the Graal Python API based on [GraalVM](https://www.graalvm.org/latest/reference-manual/native-image/) 13 | (similar to [Graal .NET API](https://github.com/dxFeed/dxfeed-graal-net-api#readme)).\ 14 | The current implementation has a number of architectural limitations that will be fixed in the new one.\ 15 | Please note that feature development has been completely halted in this version. 16 | 17 |
18 | 19 | This package provides access to [dxFeed](https://www.dxfeed.com/) streaming data. 20 | The library is build as a thin wrapper over [dxFeed C-API library](https://github.com/dxFeed/dxfeed-c-api). 21 | We use [Cython](https://cython.org/) in this project as it combines flexibility, reliability and 22 | usability in writing C extensions. 23 | 24 | The design of the dxfeed package allows users to write any logic related to events in python as well as 25 | extending lower level Cython functionality. Moreover, one may start working with the API using the default 26 | values like function arguments or a default event handler. 27 | 28 | Documentation: [dxfeed.readthedocs.io](https://dxfeed.readthedocs.io/en/latest/) 29 | 30 | Package distribution: [pypi.org/project/dxfeed](https://pypi.org/project/dxfeed/) 31 | 32 | ## Installation 33 | 34 | **Requirements:** python >= 3.6 and <= 3.9 35 | 36 | On Linux, WSL and macOS, we recommend installing python via [pyenv](https://github.com/pyenv/pyenv): 37 | 38 | An example of how to install python on Ubuntu 20.04: 39 | 40 | ```bash 41 | # Update the package index files on the system 42 | sudo apt-get update 43 | 44 | # Install pyenv dependencies 45 | sudo apt-get install make build-essential libssl-dev zlib1g-dev \ 46 | libbz2-dev libreadline-dev libsqlite3-dev wget curl llvm \ 47 | libncursesw5-dev xz-utils tk-dev libxml2-dev libxmlsec1-dev libffi-dev liblzma-dev 48 | 49 | # Run automatic pyenv installer 50 | curl https://pyenv.run | bash 51 | 52 | # Configure bash shell 53 | echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bashrc 54 | echo 'command -v pyenv >/dev/null || export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bashrc 55 | echo 'eval "$(pyenv init -)"' >> ~/.bashrc 56 | 57 | # Restart bash shell 58 | bash 59 | 60 | # Install python 3.9.16 61 | pyenv install 3.9.16 62 | 63 | # Set python 3.9.16 as global 64 | pyenv global 3.9.16 65 | 66 | # Check python version 67 | python --version 68 | #>>> Python 3.9.16 69 | 70 | ``` 71 | 72 | Install package via PyPI: 73 | 74 | ```python 75 | python -m pip install dxfeed 76 | ``` 77 | 78 | ## Installation from sources 79 | 80 | To install dxfeed from source you need Poetry. It provides a custom installer. 81 | This is the recommended way of installing poetry according to [documentation](https://python-poetry.org/docs/) 82 | 83 | For macOS / Linux / Windows (WSL): 84 | 85 | ```bash 86 | curl -sSL https://install.python-poetry.org | python3 - 87 | ``` 88 | 89 | For Windows (Powershell): 90 | ```powershell 91 | (Invoke-WebRequest -Uri https://install.python-poetry.org -UseBasicParsing).Content | py - 92 | ``` 93 | 94 | In the project root directory (same one where you found this file after 95 | cloning the git repo), execute: 96 | 97 | ```bash 98 | poetry install 99 | ``` 100 | 101 | By default package is installed in 102 | [development mode](https://pip.pypa.io/en/latest/reference/pip_install.html#editable-installs). To rebuild 103 | C extensions, after editing .pyx files: 104 | 105 | ```bash 106 | poetry run task build_inplace # build c extensions 107 | ``` 108 | 109 | ## Basic usage 110 | 111 | Following steps should be performed: 112 | 113 | * Import 114 | * Create Endpoint 115 | * Create Subscription 116 | * Attach event handler 117 | * Add tickers 118 | * Finally close subscription and connection 119 | 120 | ### Import package 121 | 122 | ```python 123 | import dxfeed as dx 124 | from datetime import datetime # for timed subscription 125 | ``` 126 | 127 | ### Configure and create connection with Endpoint class 128 | Create instance of Endpoint class which will connect provided address. 129 | 130 | 131 | ```python 132 | endpoint = dx.Endpoint('demo.dxfeed.com:7300') 133 | ``` 134 | 135 | Endpoint instance contains information about the connection, e.g. connection address or status 136 | 137 | 138 | ```python 139 | print(f'Connected address: {endpoint.address}') 140 | print(f'Connection status: {endpoint.connection_status}') 141 | ``` 142 | 143 | ```text 144 | Connected address: demo.dxfeed.com:7300 145 | Connection status: Connected and authorized 146 | ``` 147 | 148 | ### Configure and create subscription 149 | You should specify event type. For timed subscription (conflated stream) you should also provide time to start subscription from. 150 | 151 | 152 | ```python 153 | trade_sub = endpoint.create_subscription('Trade') 154 | ``` 155 | 156 | **Attach default or custom event handler** - class that process incoming events. For details about custom 157 | event handler look into `CustomHandlerExample.ipynb` jupyter notebook in `exapmles` folder of this repository. 158 | 159 | 160 | ```python 161 | trade_handler = dx.DefaultHandler() 162 | trade_sub = trade_sub.set_event_handler(trade_handler) 163 | ``` 164 | 165 | **Add tikers** you want to receive events for 166 | 167 | 168 | ```python 169 | trade_sub = trade_sub.add_symbols(['C', 'AAPL']) 170 | ``` 171 | 172 | For timed subscription you may provide either datetime object or string. String might be incomplete, in 173 | this case you will get warning with how your provided date parsed automatically. 174 | 175 | 176 | ```python 177 | tns_sub = endpoint.create_subscription('TimeAndSale', date_time=datetime.now()) \ 178 | .add_symbols(['AMZN']) 179 | ``` 180 | 181 | 182 | ```python 183 | candle_sub = endpoint.create_subscription('Candle', date_time='2020-04-16 13:05') 184 | candle_sub = candle_sub.add_symbols(['AAPL', 'MSFT']) 185 | ``` 186 | 187 | We didn't provide subscriptions with event handlers. In such a case DefaultHandler is initiated automatically. 188 | One may get it with `get_event_handler` method. 189 | 190 | ```python 191 | tns_handler = tns_sub.get_event_handler() 192 | candle_handler = candle_sub.get_event_handler() 193 | ``` 194 | 195 | #### Subscription instance properties 196 | 197 | 198 | ```python 199 | print(f'Subscription event type: {tns_sub.event_type}') 200 | print(f'Subscription symbols: {candle_sub.symbols}') 201 | ``` 202 | 203 | ```text 204 | Subscription event type: TimeAndSale 205 | Subscription symbols: ['AAPL', 'MSFT'] 206 | ``` 207 | 208 | ### Access data 209 | In DefaultHandler the data is stored as deque. Its length may be configured, by default 100000 events. 210 | 211 | ```python 212 | candle_handler.get_list() 213 | ``` 214 | 215 | ### Close connection 216 | 217 | 218 | ```python 219 | endpoint.close_connection() 220 | print(f'Connection status: {endpoint.connection_status}') 221 | ``` 222 | 223 | ```text 224 | Connection status: Not connected 225 | ``` 226 | 227 | ### Transform data to pandas DataFrame 228 | 229 | DefaultHandler has `get_dataframe` method, which allows you to get pandas.DataFrame object with events as rows. 230 | 231 | ```python 232 | trade_df = trade_handler.get_dataframe() 233 | tns_df = tns_handler.get_dataframe() 234 | candle_df = candle_handler.get_dataframe() 235 | ``` 236 | -------------------------------------------------------------------------------- /build.py: -------------------------------------------------------------------------------- 1 | import os 2 | import platform 3 | import struct 4 | from io import BytesIO 5 | from pathlib import Path 6 | from urllib.request import urlopen 7 | from zipfile import ZipFile 8 | import shutil 9 | from setuptools import Extension, find_packages 10 | from setuptools.dist import Distribution 11 | import toml 12 | 13 | 14 | class Downloader(object): 15 | """Class that downloads a dxFeed C API bundle and places the necessary include or lib files""" 16 | 17 | def __init__(self, version: str, path_to_extract: Path, 18 | path_to_copy_includes: Path, path_to_copy_libs: Path): 19 | """ 20 | Parameters 21 | ---------- 22 | version : str 23 | The dxFeed C API version 24 | path_to_extract : Path 25 | The temporary directory where the bundle should be unpacked 26 | path_to_copy_includes : Path 27 | The directory where the include files should be placed 28 | path_to_copy_libs 29 | The directory where the library files should be placed 30 | """ 31 | 32 | self.__version = version 33 | self.__path_to_extract = path_to_extract 34 | self.__path_to_copy_includes = path_to_copy_includes 35 | self.__path_to_copy_libs = path_to_copy_libs 36 | 37 | def download(self, dxfeed_c_api_bundle_url_template: str) -> str: 38 | """ 39 | The method that does the bulk of the work of downloading and placement files. 40 | 41 | 42 | Parameters 43 | ---------- 44 | dxfeed_c_api_bundle_url_template: str 45 | The URL template for zipped dxFeed C API bundle. Parameters used: 46 | {version} - The C API version 47 | {os} - The target OS 48 | 49 | Returns 50 | ------- 51 | : str 52 | The resulting name of the library, which is required to build an extension 53 | 54 | """ 55 | c_api_extracted_root_path = self.__path_to_extract / f'dxfeed-c-api-{self.__version}-no-tls' 56 | is_x64 = 8 * struct.calcsize('P') == 64 57 | 58 | url_template_os = 'centos' # Since centos uses an earlier version of libc 59 | 60 | if platform.system() == 'Darwin': 61 | url_template_os = 'macosx' 62 | elif platform.system() == 'Windows': 63 | url_template_os = 'windows' 64 | 65 | if (not os.path.exists(self.__path_to_extract)) or (not os.path.exists(c_api_extracted_root_path)): 66 | url = dxfeed_c_api_bundle_url_template.format(version=self.__version, os=url_template_os) 67 | print(f'Downloading the "{url}"') 68 | resp = urlopen(url) 69 | zipfile = ZipFile(BytesIO(resp.read())) 70 | print(f'Extracting to "{self.__path_to_extract}"') 71 | zipfile.extractall(self.__path_to_extract) 72 | 73 | c_api_x64_suffix = '_64' if is_x64 else '' 74 | resulting_c_api_library_name = f'DXFeed{c_api_x64_suffix}' 75 | 76 | # Determine and fixing paths for unpacked artifacts. 77 | # The workaround is related to the packaging feature on POSIX systems in the dxFeed API' CI\CD. 78 | if is_x64: 79 | if platform.system() != 'Windows': 80 | c_api_extracted_root_path = c_api_extracted_root_path / f'DXFeedAll-{self.__version}-x64-no-tls' 81 | 82 | c_api_extracted_library_path = c_api_extracted_root_path / 'bin' / 'x64' 83 | else: 84 | if platform.system() == 'Windows': 85 | c_api_extracted_library_path = c_api_extracted_root_path / 'bin' / 'x86' 86 | else: 87 | raise RuntimeError('Unsupported platform') 88 | 89 | # Determine possible prefixes and extensions for library files 90 | lib_file_name_prefixes = [''] * 2 91 | lib_file_name_extensions = ['dll', 'lib'] 92 | 93 | if platform.system() != 'Windows': 94 | lib_file_name_prefixes = ['lib'] 95 | lib_file_name_extensions = ['dylib'] if platform.system() == 'Darwin' else ['so'] 96 | 97 | # Create paths for libraries to be copied 98 | c_api_library_file_source_paths = [] 99 | c_api_library_file_dest_paths = [] 100 | 101 | for idx, prefix in enumerate(lib_file_name_prefixes): 102 | file_name = f'{prefix}{resulting_c_api_library_name}.{lib_file_name_extensions[idx]}' 103 | c_api_library_file_source_paths.append(c_api_extracted_library_path / file_name) 104 | c_api_library_file_dest_paths.append(self.__path_to_copy_libs / file_name) 105 | 106 | print('Copying all headers') 107 | # Copying all headers 108 | if not Path(self.__path_to_copy_includes).exists(): 109 | shutil.copytree(c_api_extracted_root_path / 'include', self.__path_to_copy_includes) 110 | 111 | # Create a directory where we will copy libraries, if necessary 112 | if not Path(self.__path_to_copy_libs).exists(): 113 | print(f'Creating the {self.__path_to_copy_libs} path') 114 | os.makedirs(self.__path_to_copy_libs) 115 | 116 | print('Copying the required libraries') 117 | # Copying the required libraries 118 | for idx, path in enumerate(c_api_library_file_source_paths): 119 | print(f' {path} -> {c_api_library_file_dest_paths[idx]}') 120 | shutil.copyfile(path, c_api_library_file_dest_paths[idx]) 121 | 122 | return resulting_c_api_library_name 123 | 124 | 125 | try: 126 | from Cython.Build import cythonize 127 | except ImportError: 128 | use_cython = False 129 | ext = 'c' 130 | else: 131 | use_cython = True 132 | ext = 'pyx' 133 | 134 | root_path = Path(__file__).resolve().parent 135 | 136 | pyproject = toml.load(root_path / 'pyproject.toml') 137 | c_api_version = pyproject['build']['native-dependencies']['dxfeed_c_api'] 138 | if c_api_version == 'env': 139 | c_api_version = os.getenv('DXFEED_C_API_VERSION') 140 | 141 | c_api_root_dir = root_path / 'dxfeed' / 'dxfeed-c-api' 142 | path_to_extract = root_path / 'dxfeed' / 'tmp' 143 | c_api_include_dir = c_api_root_dir / 'include' 144 | c_api_bin_dir = root_path / 'dxfeed' / 'core' 145 | 146 | downloader = Downloader(c_api_version, path_to_extract, c_api_include_dir, c_api_bin_dir) 147 | c_api_library_name = downloader.download(pyproject['build']['native-dependencies']['dxfeed_c_api_bundle_url_template']) 148 | 149 | if platform.system() == 'Windows': 150 | runtime_library_dirs = None 151 | extra_link_args = None 152 | elif platform.system() == 'Darwin': 153 | runtime_library_dirs = None 154 | extra_link_args = ['-Wl,-rpath,@loader_path'] 155 | else: 156 | runtime_library_dirs = ['$ORIGIN'] 157 | extra_link_args = None 158 | 159 | c_api_include_dirs = [str(c_api_include_dir)] 160 | 161 | libs = [c_api_library_name] 162 | if platform.system() == 'Windows': 163 | libs.append('ws2_32') 164 | 165 | extensions = [Extension('dxfeed.core.utils.helpers', ['dxfeed/core/utils/helpers.' + ext], 166 | include_dirs=c_api_include_dirs), 167 | Extension('dxfeed.core.utils.handler', ['dxfeed/core/utils/handler.' + ext], 168 | include_dirs=c_api_include_dirs), 169 | Extension('dxfeed.core.listeners.listener', ['dxfeed/core/listeners/listener.' + ext], 170 | include_dirs=c_api_include_dirs), 171 | Extension('dxfeed.core.DXFeedPy', ['dxfeed/core/DXFeedPy.' + ext], library_dirs=[str(c_api_bin_dir)], 172 | runtime_library_dirs=runtime_library_dirs, 173 | extra_link_args=extra_link_args, 174 | libraries=libs, 175 | include_dirs=c_api_include_dirs)] 176 | 177 | if use_cython: 178 | extensions = cythonize(extensions, language_level=3) 179 | 180 | 181 | def build(setup_kwargs): 182 | setup_kwargs.update({ 183 | 'ext_modules': extensions, 184 | 'zip_safe': False, 185 | 'packages': find_packages(), 186 | 'include_dirs': c_api_include_dirs 187 | }) 188 | 189 | 190 | def build_extensions(): 191 | """ 192 | Function for building extensions inplace for docs and tests 193 | :return: 194 | """ 195 | build_params = {} 196 | build(build_params) 197 | dist = Distribution(attrs=build_params) 198 | build_clib_cmd = dist.get_command_obj('build_clib') 199 | build_clib_cmd.ensure_finalized() 200 | build_clib_cmd.run() 201 | build_ext_cmd = dist.get_command_obj('build_ext') 202 | build_ext_cmd.ensure_finalized() 203 | build_ext_cmd.inplace = 1 204 | build_ext_cmd.run() 205 | -------------------------------------------------------------------------------- /clear.py: -------------------------------------------------------------------------------- 1 | import shutil 2 | from pathlib import Path 3 | from itertools import chain 4 | 5 | c_files = Path('dxfeed/core').glob('**/*.c') 6 | cpp_files = Path('dxfeed/core').glob('**/*.cpp') 7 | pyd_files = Path('dxfeed/core').glob('**/*.pyd') 8 | so_files = Path('dxfeed/core').glob('**/*.so') 9 | dll_files = Path('dxfeed/core').glob('**/*.dll') 10 | lib_files = Path('dxfeed/core').glob('*.lib') 11 | dylib_files = Path('dxfeed/core').glob('**/*.dylib') 12 | 13 | for file_path in chain(c_files, cpp_files, pyd_files, so_files, dll_files, lib_files, dylib_files): 14 | file_path.unlink() 15 | 16 | if Path('dxfeed/tmp').exists(): 17 | shutil.rmtree('dxfeed/tmp') 18 | 19 | if Path('dxfeed/dxfeed-c-api/include').exists(): 20 | shutil.rmtree('dxfeed/dxfeed-c-api/include') 21 | -------------------------------------------------------------------------------- /docs/Makefile: -------------------------------------------------------------------------------- 1 | # Minimal makefile for Sphinx documentation 2 | # 3 | 4 | # You can set these variables from the command line. 5 | SPHINXOPTS = 6 | SPHINXBUILD = sphinx-build 7 | SOURCEDIR = . 8 | BUILDDIR = _build 9 | 10 | # Put it first so that "make" without argument is like "make help". 11 | help: 12 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 13 | 14 | .PHONY: help Makefile 15 | 16 | 17 | # Catch-all target: route all unknown targets to Sphinx using the new 18 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 19 | %: Makefile 20 | @cd ..; python -c 'from build import *; build_extensions()' 21 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 22 | python zipper.py 23 | @cd ..; find dxfeed/core/* -type f \( -name '*.c*' -or -name '*.pyd' \) -delete 24 | 25 | .PHONY: clean 26 | clean: 27 | rm -rf $(BUILDDIR)/* 28 | -------------------------------------------------------------------------------- /docs/_static/custom.css: -------------------------------------------------------------------------------- 1 | .footer { 2 | display: none; 3 | } 4 | .body { 5 | margin-bottom: 50px; 6 | } 7 | .section > dl > dt { 8 | background-color: #DDE7F7; 9 | } 10 | .current.reference.internal { 11 | font-weight: bold; 12 | } 13 | .attribute { 14 | margin-left: 30px; 15 | } 16 | 17 | table { 18 | display: block; 19 | overflow-x: auto; 20 | } 21 | 22 | .highlight-text > .highlight > pre { 23 | background-color: wheat; 24 | display: block; 25 | max-height: 300px; 26 | overflow-y: auto; 27 | } -------------------------------------------------------------------------------- /docs/_static/custom.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function () { 2 | $('a[href^="http://"], a[href^="https://"]').not('a[class*=internal]').attr('target', '_blank'); 3 | }); 4 | -------------------------------------------------------------------------------- /docs/api.rst: -------------------------------------------------------------------------------- 1 | .. _api: 2 | 3 | .. meta:: 4 | :description lang=en: dxfeed package docstrings. 5 | 6 | API Reference 7 | ============= 8 | 9 | This page contains only automatically generated documentation to functions, classes, methods, etc. 10 | 11 | API 12 | ------------------------ 13 | 14 | .. automodule:: dxfeed.wrappers.endpoint 15 | :members: 16 | :inherited-members: 17 | 18 | .. automodule:: dxfeed.wrappers.subscription 19 | :members: 20 | :inherited-members: 21 | 22 | .. automodule:: dxfeed.core.utils.handler 23 | :members: 24 | :inherited-members: 25 | 26 | Low-level API 27 | ----------------------- 28 | 29 | .. automodule:: dxfeed.core.DXFeedPy 30 | :members: 31 | :inherited-members: 32 | 33 | Utils functions 34 | --------------- 35 | 36 | .. automodule:: dxfeed.core.utils.helpers 37 | :members: 38 | :inherited-members: 39 | 40 | .. automodule:: dxfeed.core.utils.data_class 41 | :members: 42 | :inherited-members: 43 | -------------------------------------------------------------------------------- /docs/basic_usage.rst: -------------------------------------------------------------------------------- 1 | .. _basic_usage: 2 | 3 | Basic Usage 4 | =========== 5 | 6 | There are three levels in the dxfeed package. The lowest is the C API 7 | library, the highest is Python wrapper classes. Cython level in the 8 | middle aims to connect these two. Here we are going to look into Python 9 | level. 10 | 11 | Python level, in its turn, mainly consists of three class types: 12 | 13 | 1. Endpoint 14 | 2. Subscription 15 | 3. EventHandler 16 | 17 | The **Endpoint** is responsible for connection management and creating 18 | dependent classes, for example Subscription. One Endpoint may have 19 | several different Subscriptions, but each Subscription is related to one 20 | Endpoint. 21 | 22 | **Subscription** class sets the type of subscription (stream or timed), 23 | the type of events (e.g. Trade, Candle), etc. 24 | 25 | After you specified the data you want to receive, you have to specify 26 | how to process upcoming events. This is where the **EventHandler** class 27 | and its children come into play. Every time an event arrives Cython 28 | event listener will call ``self.update(event)`` method. You have to 29 | inherit from the EventHandler class and redefine the update method. Or 30 | you may use DefaultHandler which stores upcoming data in deque of the 31 | length 100k. 32 | 33 | Import package 34 | ~~~~~~~~~~~~~~ 35 | 36 | .. code:: python3 37 | 38 | import dxfeed as dx 39 | from datetime import datetime # for timed subscription 40 | 41 | Configure and create connection with Endpoint class 42 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 43 | 44 | Create instance of Endpoint class which will connect provided address. 45 | 46 | .. code:: python3 47 | 48 | endpoint = dx.Endpoint('demo.dxfeed.com:7300') 49 | 50 | Endpoint instance contains information about the connection, 51 | e.g. connection address or status 52 | 53 | .. code:: python3 54 | 55 | print(f'Connected address: {endpoint.address}') 56 | print(f'Connection status: {endpoint.connection_status}') 57 | 58 | 59 | .. code:: text 60 | 61 | Connected address: demo.dxfeed.com:7300 62 | Connection status: Connected and authorized 63 | 64 | 65 | Configure and create subscription 66 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 67 | 68 | You should specify event type. For timed subscription (conflated stream) 69 | you should also provide time to start subscription from. 70 | 71 | .. code:: python3 72 | 73 | trade_sub = endpoint.create_subscription('Trade') 74 | 75 | **Set event handler** - class that process incoming events. Here we use 76 | default one 77 | 78 | .. code:: python3 79 | 80 | trade_handler = dx.DefaultHandler() 81 | trade_sub.set_event_handler(trade_handler); 82 | 83 | **Add tikers** you want to recieve events for 84 | 85 | .. code:: python3 86 | 87 | trade_sub = trade_sub.add_symbols(['C', 'IBM']) 88 | 89 | For timed subscription you should provide either datetime object or 90 | string. String might be incomlete, in this case you will get warning 91 | with how your provided date parsed automatically. For Candle event type 92 | along with base symbol, you should specify an aggregation period. You 93 | can also set price type. More details: 94 | https://kb.dxfeed.com/display/DS/REST+API#RESTAPI-Candlesymbols. 95 | 96 | .. code:: python3 97 | 98 | tns_sub = endpoint.create_subscription('TimeAndSale', date_time=datetime.now()) \ 99 | .add_symbols(['AMZN']) 100 | 101 | .. code:: python3 102 | 103 | candle_sub = endpoint.create_subscription('Candle', date_time='2020-04-16 13:05') 104 | candle_sub = candle_sub.add_symbols(['AAPL{=d}', 'MSFT{=d}']) 105 | 106 | 107 | .. code:: text 108 | 109 | c:\job\python-api\dxfeed\wrappers\class_utils.py:38: UserWarning: Datetime argument does not exactly match %Y-%m-%d %H:%M:%S.%f format, date was parsed automatically as 2020-04-16 13:05:00.000000 110 | warn(warn_message, UserWarning) 111 | 112 | 113 | **Note** Two previous subscriptions attached DefaultHandler implicitly. 114 | To retrieve instances just call ``get_event_handler()`` method. 115 | 116 | .. code:: python3 117 | 118 | tns_handler = tns_sub.get_event_handler() 119 | candle_handler = candle_sub.get_event_handler() 120 | 121 | Subscription instance properties 122 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 123 | 124 | .. code:: python3 125 | 126 | print(f'TimeAndSale subscription event type: {tns_sub.event_type}') 127 | print(f'Candle subscription event type: {candle_sub.event_type}') 128 | print(f'Candle subscription symbols: {candle_sub.symbols}') 129 | 130 | 131 | .. code:: text 132 | 133 | TimeAndSale subscription event type: TimeAndSale 134 | Candle subscription event type: Candle 135 | Candle subscription symbols: ['AAPL{=d}', 'MSFT{=d}'] 136 | 137 | 138 | Access data from DefaultHandler instance 139 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 140 | 141 | You can get colums, list or dataframe. You are also allowed to write 142 | handler that stores no data. 143 | 144 | .. code:: python3 145 | 146 | print(f'Trade columns: {trade_handler.columns}') 147 | print(f'Candle columns: {candle_handler.columns}') 148 | 149 | 150 | .. code:: text 151 | 152 | Trade columns: ['Symbol', 'Price', 'ExchangeCode', 'Size', 'Tick', 'Change', 'DayVolume', 'Time', 'IsETH'] 153 | Candle columns: ['Symbol', 'Index', 'Time', 'Sequence', 'Count', 'Open', 'High', 'Low', 'Close', 'Volume', 'VWap', 'BidVolume', 'AskVolume', 'OpenInterest', 'ImpVolatility'] 154 | 155 | 156 | .. code:: python3 157 | 158 | candle_handler.get_list()[-5:] 159 | 160 | 161 | 162 | 163 | .. code:: text 164 | 165 | [['MSFT{=d}', 6816463568083353600, 1587081600000, 0, 189986.0, 179.5, 180.0, 175.87, 178.6, 52765625.0, 177.90622, 24188832.0, 22094602.0, 0, 0.4384], 166 | ['MSFT{=d}', 6816294775868620800, 1587042300000, 0, 189986.0, 179.5, 180.0, 175.87, 178.6, 52765625.0, 177.90622, 24188832.0, 22094602.0, 0, 0.4384], 167 | ['AAPL{=d}', 6839841934068940800, 1592524800000, 0, 827.0, 354.05, 355.55, 353.35, 354.72, 188804.0, 354.45941, 78039.0, 110765.0, 0, 0.3691], 168 | ['AAPL{=d}', 6839841934068940800, 1592524800000, 0, 831.0, 354.05, 355.55, 353.35, 354.9, 189555.0, 354.4611, 78039.0, 111516.0, 0, 0.3691], 169 | ['AAPL{=d}', 6839841934068940800, 1592524800000, 0, 832.0, 354.05, 355.55, 353.35, 354.72, 190055.0, 354.46178, 78539.0, 111516.0, 0, 0.3691]] 170 | 171 | 172 | 173 | .. code:: python3 174 | 175 | candle_handler.get_dataframe().head(3) 176 | 177 | 178 | 179 | 180 | .. raw:: html 181 | 182 |
183 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 |
SymbolIndexTimeSequenceCountOpenHighLowCloseVolumeVWapBidVolumeAskVolumeOpenInterestImpVolatility
0AAPL{=d}68398419340689408002020-06-190827.0354.05355.55353.35354.72188804.0354.4594178039.0110765.000.3691
1AAPL{=d}68394708488945664002020-06-18096172.0351.41353.45349.22351.7324205096.0351.568738565421.010394906.000.3673
2AAPL{=d}68390997637201920002020-06-170110438.0355.15355.40351.09351.5928601626.0353.7099810686232.012141490.000.3713
274 |
275 | 276 | 277 | 278 | Close subscription 279 | ~~~~~~~~~~~~~~~~~~ 280 | 281 | .. code:: python3 282 | 283 | trade_sub.close_subscription() 284 | tns_sub.close_subscription() 285 | candle_sub.close_subscription() 286 | 287 | Close connection 288 | ~~~~~~~~~~~~~~~~ 289 | 290 | .. code:: python3 291 | 292 | endpoint.close_connection() 293 | print(f'Connection status: {endpoint.connection_status}') 294 | 295 | 296 | .. code:: text 297 | 298 | Connection status: Not connected 299 | 300 | -------------------------------------------------------------------------------- /docs/conf.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | 3 | import sys 4 | import toml 5 | from pathlib import Path 6 | from pygments.lexers.python import CythonLexer 7 | from sphinx.highlighting import lexers 8 | 9 | pyproject = toml.load(Path(__file__).parents[1].joinpath('pyproject.toml')) 10 | # -- Path setup -------------------------------------------------------------- 11 | 12 | sys.path.append(str(Path(__file__).parents[1])) 13 | # -- Project information ----------------------------------------------------- 14 | 15 | project = pyproject['tool']['poetry']['name'] 16 | copyright = '2019, dxfeed' 17 | author = 'dxfeed' 18 | 19 | # The short X.Y version 20 | version = pyproject['tool']['poetry']['version'] 21 | # The full version, including alpha/beta/rc tags 22 | release = pyproject['tool']['poetry']['version'] 23 | 24 | 25 | # -- General configuration --------------------------------------------------- 26 | # Add any Sphinx extension module names here, as strings. 27 | extensions = [ 28 | 'sphinx.ext.autodoc', 29 | 'sphinx.ext.coverage', 30 | 'sphinx.ext.napoleon', # numpy style docstrings 31 | 'sphinx.ext.intersphinx' 32 | ] 33 | 34 | # Add any paths that contain templates here, relative to this directory. 35 | templates_path = ['_templates'] 36 | 37 | # The suffix(es) of source filenames. 38 | source_suffix = '.rst' 39 | 40 | # The master toctree document. 41 | master_doc = 'index' 42 | 43 | # The language for content autogenerated by Sphinx. 44 | language = 'en' 45 | 46 | # List of patterns, relative to source directory, that match files and 47 | # directories to ignore when looking for source files. 48 | exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] 49 | 50 | # -- Options for HTML output ------------------------------------------------- 51 | html_static_path = ['_static'] 52 | html_css_files = ['custom.css'] 53 | html_js_files = ['custom.js'] 54 | 55 | # The theme to use for HTML and HTML Help pages. 56 | html_theme = 'alabaster' 57 | html_theme_options = { 58 | 'show_powered_by': False, 59 | 'sidebar_collapse': True 60 | } 61 | 62 | # -- Options for HTMLHelp output --------------------------------------------- 63 | 64 | # Output file base name for HTML help builder. 65 | htmlhelp_basename = 'dxfeeddoc' 66 | 67 | # remove view source link 68 | html_show_sourcelink = False 69 | 70 | # highlight cython 71 | lexers['cython'] = CythonLexer() 72 | -------------------------------------------------------------------------------- /docs/core_usage.rst: -------------------------------------------------------------------------------- 1 | .. _core_usage: 2 | 3 | Low-level API 4 | ============= 5 | 6 | .. note:: 7 | High-Level API have priority support in case of any issues. We highly recommend 8 | to use it and touch the Low-Level API only in special cases. 9 | 10 | This tutorial is about the Cython level of the package, which connects 11 | Python and C API. Though high-level Python classes provide the necessary 12 | functionality safely and conveniently, the user is not restricted to use 13 | low-level functions. 14 | 15 | Import core functions 16 | ~~~~~~~~~~~~~~~~~~~~~ 17 | 18 | Here we deal with low level C styled api, so the import is slightly 19 | differ. 20 | 21 | .. code:: python3 22 | 23 | from dxfeed.core import DXFeedPy as dxc 24 | from dxfeed.core.utils.handler import DefaultHandler 25 | from datetime import datetime # for timed suscription 26 | from dateutil.relativedelta import relativedelta 27 | 28 | Create connection 29 | ~~~~~~~~~~~~~~~~~ 30 | 31 | There are two ways at the moment to create connection: with token or 32 | with specifying connection address. Here we use the latter for 33 | simplicity. 34 | 35 | .. code:: python3 36 | 37 | con = dxc.dxf_create_connection('demo.dxfeed.com:7300') 38 | 39 | Create subscription 40 | ~~~~~~~~~~~~~~~~~~~ 41 | 42 | There are two types of subscriptions: ordinary for delivering stream 43 | data as-is and timed for conflated data. Except type of subscription you 44 | should provide type of events you want to get. Note: some event types, 45 | e.g. Candle, support only timed subscription. 46 | 47 | .. code:: python3 48 | 49 | sub = dxc.dxf_create_subscription(con, 'Trade') 50 | sub_timed = dxc.dxf_create_subscription_timed(con, 'Candle', int((datetime.now() - relativedelta(days=3)).timestamp())) 51 | 52 | Attach event handler 53 | ~~~~~~~~~~~~~~~~~~~~ 54 | 55 | To process incoming data you have to define define ``update(event)`` 56 | method in your EventHandler child class. Or you may use DefaultHandler 57 | which stores upcoming data in deque of the length 100k. In this example 58 | we choose the latter. 59 | 60 | .. code:: python3 61 | 62 | trade_handler = DefaultHandler() 63 | candle_handler = DefaultHandler() 64 | 65 | .. code:: python3 66 | 67 | sub.set_event_handler(trade_handler) 68 | sub_timed.set_event_handler(candle_handler) 69 | 70 | Attach listener 71 | ~~~~~~~~~~~~~~~ 72 | 73 | A special function that processes incoming on the C level events should 74 | be initialized. There are default ones for each event type. 75 | 76 | .. code:: python3 77 | 78 | dxc.dxf_attach_listener(sub) 79 | dxc.dxf_attach_listener(sub_timed) 80 | 81 | Add tickers 82 | ~~~~~~~~~~~ 83 | 84 | Symbols that will be processed should be defined. For Candle event type 85 | along with base symbol, you should specify an aggregation period. You 86 | can also set price type. More details: 87 | https://kb.dxfeed.com/display/DS/REST+API#RESTAPI-Candlesymbols. 88 | 89 | .. code:: python3 90 | 91 | dxc.dxf_add_symbols(sub, ['AAPL', 'MSFT']) 92 | dxc.dxf_add_symbols(sub_timed, ['AAPL{=d}', 'MSFT{=d}']) 93 | 94 | Access data 95 | ~~~~~~~~~~~ 96 | 97 | The DefaultHandler class has ``get_list()`` and ``get_dataframe()`` 98 | methods to access the data. 99 | 100 | .. code:: python3 101 | 102 | trade_handler.get_list()[-5:] 103 | 104 | 105 | 106 | 107 | .. code:: text 108 | 109 | [['MSFT', 196.14, 'X', 100, 2, 0.0, 100.0, 1592510399515, 0], 110 | ['MSFT', 196.27, 'Y', 100, 2, 0.0, 18.0, 1592510398017, 0], 111 | ['MSFT', 196.33, 'Z', 100, 1, 0.0, 2693.0, 1592510399823, 0], 112 | ['AAPL', 351.57, 'D', 200, 1, 0.0, 44022.0, 1592510399435, 0], 113 | ['AAPL', 351.73, 'Q', 1406354, 1, 0.0, 234771.0, 1592510400351, 0]] 114 | 115 | 116 | 117 | .. code:: python3 118 | 119 | candle_handler.get_dataframe().head(3) 120 | 121 | 122 | 123 | 124 | .. raw:: html 125 | 126 |
127 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 |
SymbolIndexTimeSequenceCountOpenHighLowCloseVolumeVWapBidVolumeAskVolumeOpenInterestImpVolatility
0AAPL{=d}68398419340689408002020-06-190807.0354.05355.55353.35354.79184838.0354.4544775518.0109320.000.3690
1AAPL{=d}68394708488945664002020-06-18096172.0351.41353.45349.22351.7324205096.0351.568738565421.010394906.000.3673
2AAPL{=d}68390997637201920002020-06-170110438.0355.15355.40351.09351.5928601626.0353.7099810686232.012141490.000.3713
218 |
219 | 220 | 221 | 222 | Detach listener 223 | ~~~~~~~~~~~~~~~ 224 | 225 | When you are no longer interested in recieving data detach the listener 226 | 227 | .. code:: python3 228 | 229 | dxc.dxf_detach_listener(sub) 230 | dxc.dxf_detach_listener(sub_timed) 231 | 232 | Close connection 233 | ~~~~~~~~~~~~~~~~ 234 | 235 | .. code:: python3 236 | 237 | dxc.dxf_close_connection(con) 238 | 239 | Transform data to pandas DataFrame 240 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 241 | 242 | .. code:: python3 243 | 244 | df1 = trade_handler.get_dataframe() 245 | df1.head(3) 246 | 247 | 248 | 249 | 250 | .. raw:: html 251 | 252 |
253 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 |
SymbolPriceExchangeCodeSizeTickChangeDayVolumeTimeIsETH
0AAPL351.73Q140635410.0234761.02020-06-18 20:00:00.3510
1AAPL351.73Q140635410.041051.02020-06-18 20:00:00.3510
2MSFT196.32Q236451720.0160741.02020-06-18 20:00:00.3270
320 |
321 | 322 | 323 | 324 | .. code:: python3 325 | 326 | df2 = candle_handler.get_dataframe() 327 | df2.head(3) 328 | 329 | 330 | 331 | 332 | .. raw:: html 333 | 334 |
335 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 |
SymbolIndexTimeSequenceCountOpenHighLowCloseVolumeVWapBidVolumeAskVolumeOpenInterestImpVolatility
0AAPL{=d}68398419340689408002020-06-190807.0354.05355.55353.35354.79184838.0354.4544775518.0109320.000.3690
1AAPL{=d}68394708488945664002020-06-18096172.0351.41353.45349.22351.7324205096.0351.568738565421.010394906.000.3673
2AAPL{=d}68390997637201920002020-06-170110438.0355.15355.40351.09351.5928601626.0353.7099810686232.012141490.000.3713
426 |
427 | -------------------------------------------------------------------------------- /docs/custom_handler.rst: -------------------------------------------------------------------------------- 1 | .. _custom_handler: 2 | 3 | Custom Event Handler 4 | ==================== 5 | Here we will look into custom event handler implementation. Unlike the 6 | Basic Usage example, we will not pay much attention to anything behind 7 | the handler. 8 | 9 | Handler is a special object inherited from dxfeed.EventHandler class. It 10 | is attached to the Subscription. When an event comes, internal 11 | structures call the update method of your handler with event as a 12 | parameter. event is a list with data specific to each event type. For 13 | example, for the Trade event type: ‘Symbol’, ‘Price’, ‘ExchangeCode’, 14 | ‘Size’, ‘Tick’, ‘Change’, ‘DayVolume’, ‘Time’, ‘IsETH’. More info here: 15 | https://kb.dxfeed.com/display/DS/dxFeed+API+Market+Events 16 | 17 | After adding symbols or attaching a default listener (what is actually 18 | done implicitly in the first case) list of one-word descriptions of 19 | event data is stored in columns field of your handler object, attached 20 | to Subscription. 21 | 22 | In this example, we will implement the event handler that prints price 23 | and volume changes for candle ask events. It also prints average volume 24 | change for the last 10 ask events. 25 | 26 | Import package 27 | ~~~~~~~~~~~~~~ 28 | 29 | .. code:: python3 30 | 31 | import dxfeed as dx 32 | from dxfeed.core.utils.data_class import DequeWithLock # custom deque with thread lock 33 | from datetime import datetime # for timed subscription 34 | from dateutil.relativedelta import relativedelta 35 | import numpy as np 36 | 37 | Configure and create connection with Endpoint class 38 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 39 | 40 | Create instance of Endpoint class which will connect provided address. 41 | 42 | .. code:: python3 43 | 44 | endpoint = dx.Endpoint('demo.dxfeed.com:7300') 45 | 46 | Configure and create subscription 47 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 48 | 49 | You should specify event type. For timed subscription (conflated stream) 50 | you should also provide time to start subscription from. 51 | 52 | .. code:: python3 53 | 54 | candle_sub = endpoint.create_subscription('Candle', date_time=datetime.now() - relativedelta(days=3)) 55 | 56 | .. code:: ipython3 57 | 58 | class DiffHandler(dx.EventHandler): 59 | def __init__(self): 60 | self.__prev_open = None 61 | self.__prev_high = None 62 | self.__prev_low = None 63 | self.__prev_close = None 64 | self.__prev_volume = None 65 | self.volume_changes = DequeWithLock() 66 | self.counter = 0 67 | 68 | def update(self, events): 69 | for event in events: 70 | if not np.isnan(event.ask_volume): # AskVolume not nan 71 | self.counter += 1 72 | print(f'Symbol: {event.symbol}') 73 | if self.counter == 1: 74 | self.__prev_open = event.open 75 | self.__prev_high = event.high 76 | self.__prev_low = event.low 77 | self.__prev_close = event.close 78 | self.__prev_volume = event.ask_volume 79 | print('First event processed') 80 | print('-------------------') 81 | else: 82 | print(f'Open changed by: {event.open - self.__prev_open}') 83 | self.__prev_open = event.open 84 | print(f'High changed by: {event.high - self.__prev_high}') 85 | self.__prev_high = event.high 86 | print(f'Open changed by: {event.low - self.__prev_low}') 87 | self.__prev_low = event.low 88 | print(f'Close changed by: {event.close - self.__prev_close}') 89 | self.__prev_close = event.close 90 | # Volume logic 91 | vol_change = event.ask_volume - self.__prev_volume 92 | self.volume_changes.safe_append(vol_change) 93 | print(f'Volume changed by: {vol_change}, from {self.__prev_volume}, to {event.ask_volume}') 94 | self.__prev_volume = event.ask_volume 95 | print(f'Ask events prcessed: {self.counter}') 96 | print('-------------------') 97 | if self.counter % 10 == 0: 98 | print(f'Average volume change for 10 past ask events is: {sum(self.volume_changes) / len(self.volume_changes)}') 99 | self.volume_changes.clear() 100 | print('-------------------') 101 | 102 | For Candle event type along with base symbol, you should specify an 103 | aggregation period. You can also set price type. More details: 104 | https://kb.dxfeed.com/display/DS/REST+API#RESTAPI-Candlesymbols 105 | 106 | .. code:: python3 107 | 108 | handler = DiffHandler() 109 | candle_sub.set_event_handler(handler).add_symbols(['AAPL{=d}']); 110 | 111 | 112 | .. code:: text 113 | 114 | Symbol: AAPL{=d} 115 | First event processed 116 | ------------------- 117 | Symbol: AAPL{=d} 118 | Open changed by: -2.6399999999999864 119 | High changed by: -1.0500000000000114 120 | Open changed by: -4.1299999999999955 121 | Close changed by: -1.8199999999999932 122 | Volume changed by: 10387567.0, from 7339.0, to 10394906.0 123 | Ask events prcessed: 2 124 | ------------------- 125 | Symbol: AAPL{=d} 126 | Open changed by: 3.7399999999999523 127 | High changed by: 1.9499999999999886 128 | Open changed by: 1.8699999999999477 129 | Close changed by: -0.1400000000000432 130 | Volume changed by: 1746584.0, from 10394906.0, to 12141490.0 131 | Ask events prcessed: 3 132 | ------------------- 133 | Symbol: AAPL{=d} 134 | Open changed by: 0.0 135 | High changed by: 0.0 136 | Open changed by: 0.0 137 | Close changed by: 0.0 138 | Volume changed by: 0.0, from 12141490.0, to 12141490.0 139 | Ask events prcessed: 4 140 | ------------------- 141 | 142 | 143 | Close subscription 144 | ~~~~~~~~~~~~~~~~~~ 145 | 146 | .. code:: python3 147 | 148 | candle_sub.close_subscription() 149 | 150 | Close connection 151 | ~~~~~~~~~~~~~~~~ 152 | 153 | .. code:: python3 154 | 155 | endpoint.close_connection() 156 | print(f'Connection status: {endpoint.connection_status}') 157 | 158 | 159 | .. code:: text 160 | 161 | Connection status: Not connected 162 | 163 | -------------------------------------------------------------------------------- /docs/custom_listener.rst: -------------------------------------------------------------------------------- 1 | .. _custom_listener: 2 | 3 | Custom listener 4 | =============== 5 | 6 | Since dxfeed v0.4.0, there is another way to implement the logic for 7 | processing incoming events (see Custom Event Handler example). 8 | 9 | This tutorial is for cases that are not covered by previous tutorials. 10 | 11 | Pipeline 12 | ~~~~~~~~ 13 | 14 | 1. Create cython package 15 | 2. Build 16 | 3. Install 17 | 4. Import 18 | 5. Use! 19 | 20 | Create cython package 21 | ~~~~~~~~~~~~~~~~~~~~~ 22 | 23 | **First of all let’s create special directory for custom listener 24 | package** 25 | 26 | .. code:: bash 27 | 28 | rm custom_listener -rf 29 | mkdir custom_listener 30 | 31 | .. code:: bash 32 | 33 | cd custom_listener 34 | pwd 35 | 36 | 37 | .. code:: text 38 | 39 | C:\python-api\examples\Low_level_API\custom_listener 40 | 41 | 42 | Create .pyx file with the whole logic 43 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 44 | 45 | Here we will write listener for Trade event type that will store only 46 | price and ticker. Save in cust.pyx file 47 | 48 | .. code:: python3 49 | 50 | from dxfeed.core.listeners.listener cimport * 51 | from dxfeed.core.utils.helpers cimport unicode_from_dxf_const_string_t 52 | from dxfeed.core.utils.handler cimport EventHandler 53 | 54 | cdef void trade_custom_listener(int event_type, 55 | dxf_const_string_t symbol_name, 56 | const dxf_event_data_t*data, 57 | int data_count, void*user_data) nogil: 58 | cdef dxf_trade_t*trades = data 59 | with gil: 60 | py_data = user_data 61 | 62 | for i in range(data_count): 63 | py_data.cython_internal_update_method([unicode_from_dxf_const_string_t(symbol_name), 64 | trades[i].price]) 65 | 66 | tc = FuncWrapper.make_from_ptr(trade_custom_listener) 67 | 68 | 69 | .. code:: bash 70 | 71 | ls 72 | 73 | 74 | .. code:: text 75 | 76 | cust.pyx 77 | 78 | 79 | - Line 2 imports all type definitions and function wrapper from 80 | installed dxfeed package 81 | - Line 3 imports helper function ``unicode_from_dxf_const_string_t`` 82 | - Line 4 import EventHandler class 83 | - Lines 6-16 stand for listener logic 84 | - nogil and with gil in lines 9 and 11 are important to prevent data 85 | corruption. More details 86 | `stackoverflow `__ 87 | - Line 10 converts the data to trades data structure. **It is important 88 | to know what data structure has each event. This information can be 89 | found in EventData.pxd in the dxfeed package folder** 90 | - Line 12 stands for casting user data which is provided by 91 | subscription 92 | - Lines 14-16 we just append price and symbol to subscription dict 93 | - Line 18, here we wrap function to have access to it from python 94 | 95 | Create setup.py to build the binary file 96 | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 97 | 98 | setup.py contents: 99 | 100 | .. code:: python3 101 | 102 | from Cython.Build import cythonize 103 | from setuptools import setup, Extension 104 | from dxfeed.core.utils.helpers import get_include 105 | 106 | ext = Extension(name="cust", 107 | sources=["cust.pyx"], 108 | include_dirs=get_include() 109 | ) 110 | 111 | setup( 112 | ext_modules=cythonize([ext], language_level=3) 113 | ) 114 | 115 | 116 | 117 | - Line 4 imports dxfeed to get access to ``get_include`` function, 118 | which provide paths to .pxd and .h header files 119 | 120 | Build the binary file 121 | ^^^^^^^^^^^^^^^^^^^^^ 122 | 123 | .. code:: bash 124 | 125 | python setup.py build_ext --inplace 126 | 127 | 128 | .. code:: text 129 | 130 | Compiling cust.pyx because it changed. 131 | [1/1] Cythonizing cust.pyx 132 | running build_ext 133 | building 'cust' extension 134 | ... 135 | Generating code 136 | Finished generating code 137 | copying build\lib.win-amd64-3.7\cust.cp37-win_amd64.pyd -> 138 | 139 | 140 | .. code:: bash 141 | 142 | ls 143 | 144 | 145 | .. code:: text 146 | 147 | build 148 | cust.c 149 | cust.cp37-win_amd64.pyd 150 | cust.pyx 151 | setup.py 152 | 153 | 154 | Import necessary modules 155 | ~~~~~~~~~~~~~~~~~~~~~~~~ 156 | 157 | .. code:: python3 158 | 159 | import cust 160 | from dxfeed.core import DXFeedPy as dxc 161 | from dxfeed.core.utils.handler import EventHandler 162 | 163 | Create Custom Event Handler 164 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~ 165 | 166 | See Custom Event Handler tutorial for more details 167 | 168 | .. code:: python3 169 | 170 | class CustomHandler(EventHandler): 171 | def __init__(self): 172 | self.data = list() 173 | 174 | def update(self, event): 175 | self.data.append(event) 176 | 177 | def get_data(self): 178 | return self.data 179 | 180 | .. code:: python3 181 | 182 | con = dxc.dxf_create_connection() 183 | sub = dxc.dxf_create_subscription(con, 'Trade') 184 | 185 | Attach custom handler 186 | 187 | .. code:: python3 188 | 189 | handler = CustomHandler() 190 | handler.columns = ['Symbol', 'Price'] 191 | sub.set_event_handler(handler) 192 | 193 | Attach custom listener 194 | 195 | .. code:: python3 196 | 197 | dxc.dxf_attach_custom_listener(sub, cust.tc) 198 | dxc.dxf_add_symbols(sub, ['AAPL', 'MSFT']) 199 | 200 | Get data 201 | 202 | .. code:: python3 203 | 204 | handler.get_data()[-3:] 205 | 206 | 207 | 208 | 209 | .. code:: text 210 | 211 | [['MSFT', 196.14], ['MSFT', 196.27], ['MSFT', 196.33]] 212 | 213 | 214 | 215 | .. code:: python3 216 | 217 | dxc.dxf_detach_listener(sub) 218 | -------------------------------------------------------------------------------- /docs/devs.rst: -------------------------------------------------------------------------------- 1 | .. _devs: 2 | 3 | For developers 4 | ============== 5 | 6 | Source code: `github.com/dxFeed/dxfeed-python-api `_ 7 | 8 | Get the code 9 | ------------ 10 | 11 | After cloning repository remember to initialize submodule with c-api library 12 | 13 | .. code-block:: bash 14 | 15 | git clone 16 | cd dxfeed-python-api/ 17 | git submodule init 18 | git submodule update 19 | 20 | Additional requirements 21 | ----------------------- 22 | 23 | All the additional requirements are located in `pyproject.toml` file in 24 | `[tool.poetry.dev-dependencies]` section. Here is a small description for key 25 | additional packages: 26 | 27 | * poetry - for building the project 28 | * taskipy - to set small alias to long bash commands 29 | * sphinx - for automatic documentation generation 30 | * pytest - for testing purposes 31 | 32 | .. note:: 33 | All further commands should be execute from the root directory of the project 34 | 35 | Build package 36 | ------------- 37 | 38 | .. code-block:: bash 39 | 40 | task build 41 | 42 | The upper command get the same arguments as `poetry build` command (e.g. -f sdist). 43 | The built package is in `dist/` folder. 44 | 45 | Testing tool 46 | ------------ 47 | 48 | .. code-block:: bash 49 | 50 | task test 51 | 52 | The upper command starts pytest. All the arguments for `pytest` command are available (e.g. -v) 53 | 54 | Documentation 55 | ------------- 56 | 57 | .. code-block:: bash 58 | 59 | task html_docs 60 | 61 | The upper command starts sphinx documentation building. The files can be found in 62 | `docs/_build/html` folder. Automatically zip archive is created in `docs/_build/` folder. 63 | -------------------------------------------------------------------------------- /docs/index.rst: -------------------------------------------------------------------------------- 1 | .. meta:: 2 | :description lang=en: DxFeed python api for getting realtime and delayed stock exchange data. 3 | :keywords: python, api, dxfeed, stock exchange, market data, marketdata, finance, realtime, delayed, order, candle 4 | 5 | Home 6 | ==== 7 | 8 | This package provides access to `dxFeed `_ streaming data. The library is 9 | a thin wrapper over `dxFeed C-API library `_. 10 | We use `Cython `_ in this project as it combines flexibility, reliability, 11 | and usability in writing C extensions. 12 | 13 | 14 | The design of the dxfeed package allows users to write any logic related to events in python as well as 15 | extending lower level Cython functionality. Moreover, one may start working with the API using the default 16 | values like function arguments or a default event handler. 17 | 18 | On this website, you will find dxfeed usage examples and docstrings to each function, class, 19 | and its methods and fields. 20 | 21 | Source code: `github.com/dxFeed/dxfeed-python-api `_ 22 | 23 | Package distribution: `pypi.org/project/dxfeed `_ 24 | 25 | Table of contents: 26 | ------------------ 27 | 28 | .. toctree:: 29 | :maxdepth: 2 30 | 31 | self 32 | installation.rst 33 | basic_usage.rst 34 | custom_handler.rst 35 | api.rst 36 | core_usage.rst 37 | custom_listener.rst 38 | devs.rst 39 | -------------------------------------------------------------------------------- /docs/installation.rst: -------------------------------------------------------------------------------- 1 | .. _installation: 2 | 3 | Installation 4 | ============ 5 | 6 | .. important:: 7 | Requirements: python >3.6, pandas 8 | 9 | .. code-block:: bash 10 | 11 | pip3 install pandas 12 | 13 | Install package itself with PyPI 14 | 15 | .. code-block:: bash 16 | 17 | pip3 install dxfeed 18 | -------------------------------------------------------------------------------- /docs/zipper.py: -------------------------------------------------------------------------------- 1 | import os 2 | from zipfile import ZipFile 3 | import toml 4 | from pathlib import Path 5 | 6 | cur_path = Path(__file__).resolve() 7 | html_path = str(cur_path.parent.joinpath('_build', 'html')) 8 | pyproject = toml.load(cur_path.parents[1].joinpath('pyproject.toml')) 9 | file_name = str(cur_path.parent.joinpath('_build')) + os.sep +\ 10 | pyproject['tool']['poetry']['name'] + '-html-doc-' +\ 11 | pyproject['tool']['poetry']['version'] + '.zip' 12 | 13 | if __name__ == "__main__": 14 | with ZipFile(file_name, 'w') as zipObj: 15 | for folderName, subfolders, filenames in os.walk(html_path): 16 | for filename in filenames: 17 | filePath = os.path.join(folderName, filename) 18 | zipObj.write(filePath, f'{folderName[len(html_path):]}{os.sep}{filename}') 19 | -------------------------------------------------------------------------------- /dxfeed/__init__.py: -------------------------------------------------------------------------------- 1 | from dxfeed.wrappers.endpoint import Endpoint 2 | from dxfeed.core.utils.handler import EventHandler, DefaultHandler 3 | import pkg_resources 4 | import toml 5 | from pathlib import Path 6 | 7 | 8 | try: 9 | __version__ = pkg_resources.get_distribution('dxfeed').version 10 | except pkg_resources.DistributionNotFound: 11 | pyproject = toml.load(Path(__file__).parents[1] / 'pyproject.toml') 12 | __version__ = pyproject['tool']['poetry']['version'] 13 | -------------------------------------------------------------------------------- /dxfeed/core/DXFeedPy.pyx: -------------------------------------------------------------------------------- 1 | # cython: always_allow_keywords=True 2 | 3 | from dxfeed.core.utils.helpers cimport * 4 | from dxfeed.core.utils.helpers import * 5 | cimport dxfeed.core.pxd_include.DXFeed as clib 6 | cimport dxfeed.core.pxd_include.DXErrorCodes as dxec 7 | cimport dxfeed.core.listeners.listener as lis 8 | from typing import Optional, Union, Iterable 9 | from warnings import warn 10 | from weakref import WeakSet 11 | from dxfeed.core.utils.handler import EventHandler 12 | 13 | # for importing variables 14 | import dxfeed.core.listeners.listener as lis 15 | from dxfeed.core.pxd_include.EventData cimport * 16 | 17 | cpdef int process_last_error(verbose: bool=True): 18 | """ 19 | Function retrieves last error 20 | 21 | Parameters 22 | ---------- 23 | verbose: bool 24 | If True error description is printed 25 | 26 | Returns 27 | ------- 28 | error_code: int 29 | Error code is returned 30 | """ 31 | cdef int error_code 32 | cdef dxf_const_string_t error_descr 33 | cdef int res 34 | 35 | res = clib.dxf_get_last_error(&error_code, &error_descr) 36 | 37 | if res == clib.DXF_SUCCESS: 38 | if error_code == dxec.dx_ec_success and verbose: 39 | print("no error information is stored") 40 | 41 | if verbose: 42 | print("Error occurred and successfully retrieved:\n", 43 | f"error code = {error_code}, description = {unicode_from_dxf_const_string_t(error_descr)}") 44 | 45 | return error_code 46 | 47 | 48 | cdef class ConnectionClass: 49 | """ 50 | Data structure that contains connection 51 | """ 52 | cdef clib.dxf_connection_t connection 53 | cdef object __weakrefs 54 | 55 | def __init__(self): 56 | self.__weakrefs = WeakSet() 57 | 58 | def __dealloc__(self): 59 | dxf_close_connection(self) 60 | 61 | def get_weakrefs(self): 62 | """ 63 | Method to get list of references to all subscriptions related to current connection. 64 | 65 | Returns 66 | ------- 67 | :list 68 | List of weakref objects. Empty list if no refs 69 | """ 70 | return list(self.__weakrefs) 71 | 72 | def add_weakref(self, obj: SubscriptionClass): 73 | """ 74 | Method to link ConnectionClass with its users (e.g. SubscriptionClass) via weak reference. 75 | 76 | Parameters 77 | ---------- 78 | obj: SubscriptionClass 79 | Object to be linked to ConnectionClass 80 | ------- 81 | 82 | """ 83 | self.__weakrefs.add(obj) 84 | 85 | 86 | cdef class SubscriptionClass: 87 | """ 88 | Data structure that contains subscription and related fields 89 | """ 90 | cdef clib.dxf_subscription_t subscription 91 | cdef object __weakref__ # Weak referencing enabling 92 | cdef object event_type_str 93 | cdef dxf_event_listener_t listener 94 | cdef object __event_handler 95 | cdef void *u_data 96 | 97 | def __init__(self): 98 | self.subscription = NULL 99 | self.__event_handler = None 100 | 101 | def __close(self): 102 | """ 103 | Common method for ConnectionClass related classes to finalize everything related to object. 104 | """ 105 | dxf_close_subscription(self) 106 | 107 | def __dealloc__(self): 108 | self.__close() 109 | 110 | def set_event_handler(self, event_handler: EventHandler): 111 | """ 112 | Method to assign event handler to SubscriptionClass. If the SubscriptionClass already has active listener 113 | the method will save the listener. After that listener will be reattached. 114 | 115 | Parameters 116 | ---------- 117 | event_handler: EventHandler 118 | Handler to attach to SubscriptionClass 119 | """ 120 | if self.listener: 121 | if event_handler is not self.__event_handler: 122 | # saving current listener - related data 123 | warn(Warning('Handler replacing')) 124 | tmp_listener = lis.FuncWrapper.make_from_ptr(self.listener) 125 | # reattaching listener 126 | dxf_detach_listener(self) 127 | self.__event_handler = event_handler 128 | self.u_data = self.__event_handler 129 | dxf_attach_custom_listener(self, tmp_listener) 130 | else: 131 | self.__event_handler = event_handler 132 | self.u_data = self.__event_handler 133 | 134 | 135 | def get_event_handler(self): 136 | return self.__event_handler 137 | 138 | 139 | def dxf_create_connection(address: Union[str, unicode, bytes] = 'demo.dxfeed.com:7300'): 140 | """ 141 | Function creates connection to dxfeed given url address 142 | 143 | Parameters 144 | ---------- 145 | address: str 146 | One of possible connection addresses: 147 | 148 | - the single address: `host:port` or just `host` 149 | - address with credentials: `host:port[username=xxx,password=yyy]` 150 | - multiple addresses: `(host1:port1)(host2)(host3:port3[username=xxx,password=yyy])` 151 | - the data from file: `/path/to/file` on nix and `drive:\\path\\to\\file` on Windows 152 | 153 | Default: demo.dxfeed.com:7300 154 | 155 | Returns 156 | ------- 157 | cc: ConnectionClass 158 | Cython ConnectionClass with information about connection 159 | """ 160 | cc = ConnectionClass() 161 | address = address.encode('utf-8') 162 | clib.dxf_create_connection(address, NULL, NULL, NULL, NULL, NULL, &cc.connection) 163 | error_code = process_last_error(verbose=False) 164 | if error_code: 165 | raise RuntimeError(f"In underlying C-API library error {error_code} occurred!") 166 | return cc 167 | 168 | def dxf_create_connection_auth_bearer(address: Union[str, unicode, bytes], 169 | token: Union[str, unicode, bytes]): 170 | """ 171 | Function creates connection to dxfeed given url address and token 172 | 173 | Parameters 174 | ---------- 175 | address: str 176 | dxfeed url address 177 | token: str 178 | dxfeed token 179 | 180 | Returns 181 | ------- 182 | cc: ConnectionClass 183 | Cython ConnectionClass with information about connection 184 | """ 185 | cc = ConnectionClass() 186 | address = address.encode('utf-8') 187 | token = token.encode('utf-8') 188 | clib.dxf_create_connection_auth_bearer(address, token, 189 | NULL, NULL, NULL, NULL, NULL, &cc.connection) 190 | error_code = process_last_error(verbose=False) 191 | if error_code: 192 | raise RuntimeError(f"In underlying C-API library error {error_code} occurred!") 193 | return cc 194 | 195 | def dxf_create_subscription(ConnectionClass cc, event_type: str): 196 | """ 197 | Function creates subscription and writes all relevant information to SubscriptionClass. 198 | 199 | Parameters 200 | ---------- 201 | cc: ConnectionClass 202 | Variable with connection information 203 | event_type: str 204 | Event types: 'Trade', 'Quote', 'Summary', 'Profile', 'Order', 'TimeAndSale', 'Candle', 'TradeETH', 205 | 'SpreadOrder', 'Greeks', 'TheoPrice', 'Underlying', 'Series', 'Configuration' or '' 206 | 207 | Returns 208 | ------- 209 | sc: SubscriptionClass 210 | Cython SubscriptionClass with information about subscription 211 | """ 212 | if not cc.connection: 213 | raise ValueError('Connection is not valid') 214 | correct_types = ['Trade', 'Quote', 'Summary', 'Profile', 'Order', 'TimeAndSale', 'Candle', 'TradeETH', 215 | 'SpreadOrder', 'Greeks', 'TheoPrice', 'Underlying', 'Series', 'Configuration', ] 216 | if event_type not in correct_types: 217 | raise ValueError(f'Incorrect event type! Got {event_type}, expected one of {correct_types}') 218 | 219 | sc = SubscriptionClass() 220 | cc.add_weakref(sc) 221 | sc.event_type_str = event_type 222 | et_type_int = event_type_convert(event_type) 223 | 224 | clib.dxf_create_subscription(cc.connection, et_type_int, &sc.subscription) 225 | 226 | error_code = process_last_error(verbose=False) 227 | if error_code: 228 | raise RuntimeError(f"In underlying C-API library error {error_code} occurred!") 229 | return sc 230 | 231 | def dxf_create_subscription_timed(ConnectionClass cc, event_type: str, time: int): 232 | """ 233 | Creates a timed subscription with the specified parameters. 234 | 235 | Notes 236 | ----- 237 | Default limit for 'Candle' event type is 8000 records. The other event types have default limit of 1000 records. 238 | 239 | Parameters 240 | ---------- 241 | cc: ConnectionClass 242 | Variable with connection information 243 | event_type: str 244 | Event types: 'Trade', 'Quote', 'Summary', 'Profile', 'Order', 'TimeAndSale', 'Candle', 'TradeETH', 245 | 'SpreadOrder', 'Greeks', 'TheoPrice', 'Underlying', 'Series', 'Configuration' or '' 246 | time: int 247 | UTC time in the past (unix time in milliseconds) 248 | 249 | Returns 250 | ------- 251 | sc: SubscriptionClass 252 | Cython SubscriptionClass with information about subscription 253 | """ 254 | if not cc.connection: 255 | raise ValueError('Connection is not valid') 256 | if event_type not in ['Trade', 'Quote', 'Summary', 'Profile', 'Order', 'TimeAndSale', 'Candle', 'TradeETH', 257 | 'SpreadOrder', 'Greeks', 'TheoPrice', 'Underlying', 'Series', 'Configuration', ]: 258 | raise ValueError('Incorrect event type!') 259 | if time < 0 or not isinstance(time, int): 260 | raise ValueError('Time argument should be non-negative integer!') 261 | 262 | sc = SubscriptionClass() 263 | cc.add_weakref(sc) 264 | sc.event_type_str = event_type 265 | et_type_int = event_type_convert(event_type) 266 | 267 | clib.dxf_create_subscription_timed(cc.connection, et_type_int, time, &sc.subscription) 268 | 269 | error_code = process_last_error(verbose=False) 270 | if error_code: 271 | raise RuntimeError(f"In underlying C-API library error {error_code} occurred!") 272 | return sc 273 | 274 | def dxf_add_symbols(SubscriptionClass sc, symbols: Iterable[str]): 275 | """ 276 | Adds symbols to subscription 277 | 278 | Parameters 279 | ---------- 280 | sc: SubscriptionClass 281 | SubscriptionClass with information about subscription 282 | symbols: list 283 | List of symbols to add 284 | """ 285 | if not sc.subscription: 286 | raise ValueError('Subscription is not valid') 287 | for idx, sym in enumerate(symbols): 288 | if not isinstance(sym, str): 289 | warn(f'{sym} has type different from string') 290 | continue 291 | if not clib.dxf_add_symbol(sc.subscription, dxf_const_string_t_from_unicode(sym)): 292 | process_last_error() 293 | 294 | def dxf_attach_listener(SubscriptionClass sc): 295 | """ 296 | Function attaches default listener according to subscription type 297 | 298 | Parameters 299 | ---------- 300 | sc: SubscriptionClass 301 | SubscriptionClass with information about subscription 302 | """ 303 | if not sc.subscription: 304 | raise ValueError('Subscription is not valid') 305 | event_handler = sc.get_event_handler() 306 | if not event_handler: 307 | raise ValueError('Event handler is not defined!') 308 | warn('New order in event fields will be introduced in dxfeed-0.6.0. Please, address event fields ' 309 | 'by name in custom event handler.', 310 | FutureWarning) 311 | 312 | if sc.event_type_str == 'Trade': 313 | event_handler.columns = lis.TRADE_COLUMNS 314 | sc.listener = lis.trade_default_listener 315 | elif sc.event_type_str == 'Quote': 316 | event_handler.columns = lis.QUOTE_COLUMNS 317 | sc.listener = lis.quote_default_listener 318 | elif sc.event_type_str == 'Summary': 319 | event_handler.columns = lis.SUMMARY_COLUMNS 320 | sc.listener = lis.summary_default_listener 321 | elif sc.event_type_str == 'Profile': 322 | event_handler.columns = lis.PROFILE_COLUMNS 323 | sc.listener = lis.profile_default_listener 324 | elif sc.event_type_str == 'TimeAndSale': 325 | event_handler.columns = lis.TIME_AND_SALE_COLUMNS 326 | sc.listener = lis.time_and_sale_default_listener 327 | elif sc.event_type_str == 'Candle': 328 | event_handler.columns = lis.CANDLE_COLUMNS 329 | sc.listener = lis.candle_default_listener 330 | elif sc.event_type_str == 'Order': 331 | event_handler.columns = lis.ORDER_COLUMNS 332 | sc.listener = lis.order_default_listener 333 | elif sc.event_type_str == 'TradeETH': 334 | event_handler.columns = lis.TRADE_COLUMNS 335 | sc.listener = lis.trade_default_listener 336 | elif sc.event_type_str == 'SpreadOrder': 337 | event_handler.columns = lis.ORDER_COLUMNS 338 | sc.listener = lis.order_default_listener 339 | elif sc.event_type_str == 'Greeks': 340 | event_handler.columns = lis.GREEKS_COLUMNS 341 | sc.listener = lis.greeks_default_listener 342 | elif sc.event_type_str == 'TheoPrice': 343 | event_handler.columns = lis.THEO_PRICE_COLUMNS 344 | sc.listener = lis.theo_price_default_listener 345 | elif sc.event_type_str == 'Underlying': 346 | event_handler.columns = lis.UNDERLYING_COLUMNS 347 | sc.listener = lis.underlying_default_listener 348 | elif sc.event_type_str == 'Series': 349 | event_handler.columns = lis.SERIES_COLUMNS 350 | sc.listener = lis.series_default_listener 351 | elif sc.event_type_str == 'Configuration': 352 | event_handler.columns = lis.CONFIGURATION_COLUMNS 353 | sc.listener = lis.configuration_default_listener 354 | else: 355 | raise Exception(f'No default listener for {sc.event_type_str} event type') 356 | 357 | if not clib.dxf_attach_event_listener(sc.subscription, sc.listener, sc.u_data): 358 | process_last_error() 359 | 360 | def dxf_attach_custom_listener(SubscriptionClass sc, lis.FuncWrapper fw): 361 | """ 362 | Attaches custom listener 363 | 364 | Parameters 365 | ---------- 366 | sc: SubscriptionClass 367 | SubscriptionClass with information about subscription 368 | fw: FuncWrapper 369 | c function wrapped in FuncWrapper class with Cython 370 | """ 371 | if not sc.subscription: 372 | raise ValueError('Subscription is not valid') 373 | event_handler = sc.get_event_handler() 374 | if not event_handler: 375 | raise ValueError('Event handler is not defined!') 376 | 377 | sc.listener = fw.func 378 | if not clib.dxf_attach_event_listener(sc.subscription, sc.listener, sc.u_data): 379 | process_last_error() 380 | 381 | def dxf_detach_listener(SubscriptionClass sc): 382 | """ 383 | Detaches any listener 384 | 385 | Parameters 386 | ---------- 387 | sc: SubscriptionClass 388 | SubscriptionClass with information about subscription 389 | """ 390 | if not sc.subscription: 391 | raise ValueError('Subscription is not valid') 392 | if not clib.dxf_detach_event_listener(sc.subscription, sc.listener): 393 | process_last_error() 394 | 395 | def dxf_close_connection(ConnectionClass cc): 396 | """ 397 | Closes connection 398 | 399 | 400 | Parameters 401 | ---------- 402 | cc: ConnectionClass 403 | Variable with connection information 404 | """ 405 | if cc.connection: 406 | for dependant in cc.get_weakrefs(): 407 | dependant.__close() 408 | 409 | clib.dxf_close_connection(cc.connection) 410 | cc.connection = NULL 411 | 412 | def dxf_close_subscription(SubscriptionClass sc): 413 | """ 414 | Closes subscription 415 | 416 | Parameters 417 | ---------- 418 | sc: SubscriptionClass 419 | SubscriptionClass with information about subscription 420 | """ 421 | if sc.subscription: 422 | clib.dxf_close_subscription(sc.subscription) 423 | sc.subscription = NULL 424 | 425 | def dxf_get_current_connection_status(ConnectionClass cc, return_str: bool=True): 426 | """ 427 | Returns one of four possible statuses 428 | 429 | Parameters 430 | ---------- 431 | cc: ConnectionClass 432 | Variable with connection information 433 | return_str: bool 434 | When True returns connection status in string format, otherwise internal c representation as integer 435 | 436 | 437 | """ 438 | status_mapping = { 439 | 0: 'Not connected', 440 | 1: 'Connected', 441 | 2: 'Login required', 442 | 3: 'Connected and authorized' 443 | } 444 | 445 | cdef clib.dxf_connection_status_t status 446 | clib.dxf_get_current_connection_status(cc.connection, &status) 447 | result = status 448 | if return_str: 449 | result = status_mapping[status] 450 | 451 | return result 452 | 453 | def dxf_get_current_connected_address(ConnectionClass cc): 454 | """ 455 | Returns current connected address 456 | 457 | Parameters 458 | ---------- 459 | cc: ConnectionClass 460 | Variable with connection information 461 | 462 | Returns 463 | ------- 464 | address: str 465 | Current connected address 466 | """ 467 | if not cc.connection: 468 | raise ValueError('Connection is not valid') 469 | 470 | cdef char * address 471 | clib.dxf_get_current_connected_address(cc.connection, &address) 472 | return (address).decode('UTF-8') 473 | 474 | def dxf_initialize_logger(file_name: str, rewrite_file: bool, show_timezone_info: bool, verbose: bool): 475 | """ 476 | Initializes the internal logger. 477 | Various actions and events, including the errors, are being logged throughout the library. They may be stored 478 | into the file. 479 | 480 | Parameters 481 | ---------- 482 | file_name: str 483 | A full path to the file where the log is to be stored 484 | rewrite_file: bool 485 | A flag defining the file open mode if it's True then the log file will be rewritten 486 | show_timezone_info: bool 487 | A flag defining the time display option in the log file if it's True then the time will be displayed 488 | with the timezone suffix 489 | verbose: bool 490 | A flag defining the logging mode if it's True then the verbose logging will be enabled 491 | 492 | """ 493 | clib.dxf_initialize_logger(file_name.encode('UTF-8'), int(rewrite_file), int(show_timezone_info), int(verbose)) 494 | 495 | def dxf_get_subscription_event_types(SubscriptionClass sc, return_str: bool=True): 496 | """ 497 | Gets subscription event type 498 | 499 | Parameters 500 | ---------- 501 | sc: SubscriptionClass 502 | SubscriptionClass with information about subscription 503 | return_str: bool 504 | When True returns event type in string format, otherwise internal c representation as integer 505 | 506 | Returns 507 | ------- 508 | str or int 509 | Subscription type 510 | """ 511 | if not sc.subscription: 512 | raise ValueError('Invalid subscription') 513 | 514 | cdef int event_type 515 | 516 | et_mapping = { 517 | 1: 'Trade', 518 | 2: 'Quote', 519 | 4: 'Summary', 520 | 8: 'Profile', 521 | 16: 'Order', 522 | 32: 'TimeAndSale', 523 | 64: 'Candle', 524 | 128: 'TradeETH', 525 | 256: 'SpreadOrder', 526 | 512: 'Greeks', 527 | 1024: 'TheoPrice', 528 | 2048: 'Underlying', 529 | 4096: 'Series', 530 | 8192: 'Configuration', 531 | -16384: '' 532 | } 533 | 534 | clib.dxf_get_subscription_event_types (sc.subscription, &event_type) 535 | result = event_type 536 | if return_str: 537 | result = et_mapping[event_type] 538 | 539 | return result 540 | 541 | def dxf_get_symbols(SubscriptionClass sc): 542 | """ 543 | Retrieves the list of symbols currently added to the subscription. 544 | 545 | Parameters 546 | ---------- 547 | sc: SubscriptionClass 548 | SubscriptionClass with information about subscription 549 | 550 | Returns 551 | ------- 552 | list 553 | List of unicode strings of subscription symbols 554 | """ 555 | if not sc.subscription: 556 | raise ValueError('Invalid subscription') 557 | 558 | cdef dxf_const_string_t * symbols 559 | symbols_list = list() 560 | cdef int symbol_count 561 | cdef int i 562 | 563 | clib.dxf_get_symbols(sc.subscription, &symbols, &symbol_count) 564 | for i in range(symbol_count): 565 | symbols_list.append(unicode_from_dxf_const_string_t(symbols[i])) 566 | 567 | return symbols_list 568 | 569 | def dxf_remove_symbols(SubscriptionClass sc, symbols: Iterable[str]): 570 | """ 571 | Removes several symbols from the subscription 572 | 573 | Parameters 574 | ---------- 575 | sc: SubscriptionClass 576 | SubscriptionClass with information about subscription 577 | symbols: list 578 | List of symbols to remove 579 | """ 580 | if not sc.subscription: 581 | raise ValueError('Invalid subscription') 582 | 583 | for symbol in symbols: 584 | clib.dxf_remove_symbol(sc.subscription, dxf_const_string_t_from_unicode(symbol)) 585 | 586 | def dxf_clear_symbols(SubscriptionClass sc): 587 | """ 588 | Removes all symbols from the subscription 589 | 590 | Parameters 591 | ---------- 592 | sc: SubscriptionClass 593 | SubscriptionClass with information about subscription 594 | """ 595 | if not sc.subscription: 596 | raise ValueError('Invalid subscription') 597 | 598 | clib.dxf_clear_symbols(sc.subscription) 599 | -------------------------------------------------------------------------------- /dxfeed/core/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxFeed/dxfeed-python-api/4cd4950d4d40de642a38afa304811369d43c358c/dxfeed/core/__init__.py -------------------------------------------------------------------------------- /dxfeed/core/listeners/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxFeed/dxfeed-python-api/4cd4950d4d40de642a38afa304811369d43c358c/dxfeed/core/listeners/__init__.py -------------------------------------------------------------------------------- /dxfeed/core/listeners/listener.pxd: -------------------------------------------------------------------------------- 1 | from dxfeed.core.pxd_include.DXTypes cimport * 2 | from dxfeed.core.pxd_include.EventData cimport * 3 | 4 | 5 | cdef class FuncWrapper: 6 | cdef dxf_event_listener_t func 7 | @staticmethod 8 | cdef FuncWrapper make_from_ptr(dxf_event_listener_t f) 9 | 10 | cdef void trade_default_listener(int event_type, dxf_const_string_t symbol_name, 11 | const dxf_event_data_t* data, int data_count, void* user_data) nogil 12 | 13 | cdef void quote_default_listener(int event_type, dxf_const_string_t symbol_name, 14 | const dxf_event_data_t* data, int data_count, void* user_data) nogil 15 | 16 | cdef void summary_default_listener(int event_type, dxf_const_string_t symbol_name, 17 | const dxf_event_data_t* data, int data_count, void* user_data) nogil 18 | 19 | cdef void profile_default_listener(int event_type, dxf_const_string_t symbol_name, 20 | const dxf_event_data_t* data, int data_count, void* user_data) nogil 21 | 22 | cdef void time_and_sale_default_listener(int event_type, dxf_const_string_t symbol_name, 23 | const dxf_event_data_t* data, int data_count, void* user_data) nogil 24 | 25 | cdef void candle_default_listener(int event_type, dxf_const_string_t symbol_name, 26 | const dxf_event_data_t* data, int data_count, void* user_data) nogil 27 | 28 | cdef void order_default_listener(int event_type, dxf_const_string_t symbol_name, 29 | const dxf_event_data_t* data, int data_count, void* user_data) nogil 30 | 31 | cdef void greeks_default_listener(int event_type, dxf_const_string_t symbol_name, 32 | const dxf_event_data_t* data, int data_count, void* user_data) nogil 33 | 34 | cdef void theo_price_default_listener(int event_type, dxf_const_string_t symbol_name, 35 | const dxf_event_data_t* data, int data_count, void* user_data) nogil 36 | 37 | cdef void underlying_default_listener(int event_type, dxf_const_string_t symbol_name, 38 | const dxf_event_data_t* data, int data_count, void* user_data) nogil 39 | 40 | cdef void series_default_listener(int event_type, dxf_const_string_t symbol_name, 41 | const dxf_event_data_t* data, int data_count, void* user_data) nogil 42 | 43 | cdef void configuration_default_listener(int event_type, dxf_const_string_t symbol_name, 44 | const dxf_event_data_t* data, int data_count, void* user_data) nogil 45 | -------------------------------------------------------------------------------- /dxfeed/core/pxd_include/DXErrorCodes.pxd: -------------------------------------------------------------------------------- 1 | from dxfeed.core.pxd_include.DXTypes cimport * 2 | 3 | cdef extern from "": 4 | ctypedef enum dx_log_level_t: 5 | dx_ll_trace = -2, 6 | dx_ll_debug = -1, 7 | dx_ll_info = 0, 8 | dx_ll_warn = 1, 9 | dx_ll_error = 2 10 | 11 | cdef extern from "": 12 | ctypedef enum dx_error_code_t: 13 | # # /* common error codes */ 14 | 15 | dx_ec_success = 0 16 | 17 | dx_ec_error_subsystem_failure # # /* this code may be set if passed from another thread */ 18 | 19 | dx_ec_invalid_func_param # # /* the invalid param is submitted by client */ 20 | dx_ec_invalid_func_param_internal # # /* the invalid param is a result of internal error */ 21 | 22 | dx_ec_internal_assert_violation 23 | 24 | # # /* memory error codes */ 25 | 26 | dx_mec_insufficient_memory 27 | 28 | # # /* socket error codes */ 29 | 30 | dx_sec_socket_subsystem_init_failed, # # /* Win32-specific */ 31 | dx_sec_socket_subsystem_init_required, # # /* Win32-specific */ 32 | dx_sec_socket_subsystem_incompatible_version, # # /* Win32-specific */ 33 | dx_sec_connection_gracefully_closed 34 | dx_sec_network_is_down 35 | dx_sec_blocking_call_in_progress 36 | dx_sec_addr_family_not_supported 37 | dx_sec_no_sockets_available 38 | dx_sec_no_buffer_space_available 39 | dx_sec_proto_not_supported 40 | dx_sec_socket_type_proto_incompat 41 | dx_sec_socket_type_addrfam_incompat 42 | dx_sec_addr_already_in_use 43 | dx_sec_blocking_call_interrupted 44 | dx_sec_nonblocking_oper_pending 45 | dx_sec_addr_not_valid 46 | dx_sec_connection_refused 47 | dx_sec_invalid_ptr_arg 48 | dx_sec_invalid_arg 49 | dx_sec_sock_already_connected 50 | dx_sec_network_is_unreachable 51 | dx_sec_sock_oper_on_nonsocket 52 | dx_sec_connection_timed_out 53 | dx_sec_res_temporarily_unavail 54 | dx_sec_permission_denied 55 | dx_sec_network_dropped_connection 56 | dx_sec_socket_not_connected 57 | dx_sec_operation_not_supported 58 | dx_sec_socket_shutdown 59 | dx_sec_message_too_long 60 | dx_sec_no_route_to_host 61 | dx_sec_connection_aborted 62 | dx_sec_connection_reset 63 | dx_sec_persistent_temp_error 64 | dx_sec_unrecoverable_error 65 | dx_sec_not_enough_memory 66 | dx_sec_no_data_on_host 67 | dx_sec_host_not_found 68 | 69 | dx_sec_generic_error 70 | 71 | # /* thread error codes */ 72 | 73 | dx_tec_not_enough_sys_resources 74 | dx_tec_permission_denied 75 | dx_tec_invalid_res_operation 76 | dx_tec_invalid_resource_id 77 | dx_tec_deadlock_detected 78 | dx_tec_not_enough_memory 79 | dx_tec_resource_busy 80 | 81 | dx_tec_generic_error 82 | 83 | # /* network error codes */ 84 | 85 | dx_nec_invalid_port_value 86 | dx_nec_invalid_function_arg 87 | dx_nec_connection_closed 88 | dx_nec_open_connection_error 89 | dx_nec_unknown_codec 90 | 91 | # /* buffered I/O error codes */ 92 | 93 | dx_bioec_buffer_overflow 94 | dx_bioec_buffer_not_initialized 95 | dx_bioec_index_out_of_bounds 96 | dx_bioec_buffer_underflow 97 | 98 | # /* UTF error codes */ 99 | 100 | dx_utfec_bad_utf_data_format 101 | dx_utfec_bad_utf_data_format_server 102 | 103 | # /* penta codec error codes */ 104 | 105 | dx_pcec_reserved_bit_sequence 106 | dx_pcec_invalid_symbol_length 107 | dx_pcec_invalid_event_flag 108 | 109 | # /* event subscription error codes */ 110 | 111 | dx_esec_invalid_event_type 112 | dx_esec_invalid_subscr_id 113 | dx_esec_invalid_symbol_name 114 | dx_esec_invalid_listener 115 | 116 | # /* logger error codes */ 117 | 118 | dx_lec_failed_to_open_file 119 | 120 | # /* protocol message error codes */ 121 | 122 | dx_pmec_invalid_message_type 123 | 124 | # /* protocol error codes */ 125 | 126 | dx_pec_unexpected_message_type 127 | dx_pec_unexpected_message_type_internal 128 | dx_pec_descr_record_field_info_corrupted 129 | dx_pec_message_incomplete 130 | dx_pec_invalid_message_length 131 | dx_pec_server_message_not_supported 132 | dx_pec_invalid_symbol 133 | dx_pec_record_description_not_received 134 | dx_pec_record_field_type_not_supported 135 | dx_pec_record_info_corrupted 136 | dx_pec_unknown_record_name 137 | dx_pec_record_not_supported 138 | dx_pec_describe_protocol_message_corrupted 139 | dx_pec_unexpected_message_sequence_internal 140 | dx_pec_local_message_not_supported_by_server 141 | dx_pec_inconsistent_message_support 142 | dx_pec_authentication_error 143 | dx_pec_credentials_required 144 | 145 | # /* connection error codes */ 146 | 147 | dx_cec_invalid_connection_handle 148 | dx_cec_invalid_connection_handle_internal 149 | dx_cec_connection_context_not_initialized 150 | dx_cec_invalid_connection_context_subsystem_id 151 | 152 | # /* candle event error codes*/ 153 | 154 | dx_ceec_invalid_candle_period_value 155 | 156 | 157 | # /* snapshot error codes */ 158 | 159 | dx_ssec_invalid_snapshot_id 160 | dx_ssec_invalid_event_id 161 | dx_ssec_invalid_symbol 162 | dx_ssec_snapshot_exist 163 | dx_ssec_invalid_listener 164 | dx_ssec_unknown_state 165 | dx_ssec_duplicate_record 166 | 167 | # /* configuration record serialization deserialization error codes */ 168 | 169 | dx_csdec_protocol_error 170 | dx_csdec_unsupported_version 171 | 172 | # /* miscellaneous error codes */ 173 | 174 | # /* error code count */ 175 | # /* this MUST be the last element in the enumeration */ 176 | 177 | dx_ec_count 178 | 179 | 180 | # # /* -------------------------------------------------------------------------- */ 181 | # # /* 182 | # * Message description functions 183 | # */ 184 | # # /* -------------------------------------------------------------------------- */ 185 | 186 | cdef dxf_const_string_t dx_get_error_description (dx_error_code_t code) 187 | 188 | cdef dx_log_level_t dx_get_log_level(dx_error_code_t code) 189 | -------------------------------------------------------------------------------- /dxfeed/core/pxd_include/DXTypes.pxd: -------------------------------------------------------------------------------- 1 | # something about license 2 | 3 | #ifndef DX_TYPES_H_INCLUDED 4 | #define DX_TYPES_H_INCLUDED 5 | 6 | cdef extern from "": 7 | ctypedef int ERRORCODE 8 | ctypedef void* dxf_subscription_t 9 | ctypedef void* dxf_connection_t 10 | ctypedef void* dxf_candle_attributes_t 11 | ctypedef void* dxf_snapshot_t 12 | ctypedef void* dxf_price_level_book_t 13 | ctypedef void* dxf_regional_book_t 14 | 15 | #ifdef _WIN32 16 | 17 | #pxd_include 18 | from libc.stddef cimport wchar_t 19 | 20 | cdef extern from "": 21 | ctypedef unsigned char dxf_bool_t # 8 bit 22 | ctypedef char dxf_byte_t # 8 bit 23 | ctypedef unsigned char dxf_ubyte_t # 8 bit 24 | ctypedef wchar_t dxf_char_t # 16 bit 25 | #ctypedef unsigned wchar_t dx_unsigned_char_t # 16 bit 26 | ctypedef short int dxf_short_t # 16 bit 27 | ctypedef unsigned short int dxf_ushort_t # 16 bit 28 | ctypedef int dxf_int_t # 32 bit 29 | ctypedef unsigned int dxf_uint_t # 32 bit 30 | ctypedef float dxf_float_t # 32 bit 31 | ctypedef long long dxf_long_t # 64 bit 32 | ctypedef unsigned long long dxf_ulong_t # 64 bit 33 | ctypedef double dxf_double_t # 64 bit 34 | ctypedef int dxf_dayid_t 35 | 36 | ctypedef dxf_char_t* dxf_string_t 37 | ctypedef const dxf_char_t* dxf_const_string_t 38 | 39 | #else /* POSIX? */ 40 | 41 | #pxd_include 42 | #pxd_include 43 | 44 | # ctypedef unsigned char dxf_bool_t # 8 bit 45 | # ctypedef int8_t dxf_byte_t # 8 bit 46 | # ctypedef uint8_t dxf_ubyte_t # 8 bit 47 | # ctypedef wchar_t dxf_char_t # 16 bit 48 | # #ctypedef unsigned wchar_t dx_unsigned_char_t # 16 bit 49 | # ctypedef int16_t dxf_short_t # 16 bit 50 | # ctypedef uint16_t dxf_ushort_t # 16 bit 51 | # ctypedef int32_t dxf_int_t # 32 bit 52 | # ctypedef uint32_t dxf_uint_t # 32 bit 53 | # ctypedef float dxf_float_t # 32 bit 54 | # ctypedef int64_t dxf_long_t # 64 bit 55 | # ctypedef uint64_t dxf_ulong_t # 64 bit 56 | # ctypedef double dxf_double_t # 64 bit 57 | # ctypedef int32_t dxf_dayid_t 58 | # 59 | # ctypedef dxf_char_t* dxf_string_t 60 | # ctypedef const dxf_char_t* dxf_const_string_t 61 | 62 | #endif /* _WIN32/POSIX */ 63 | 64 | cdef extern from "": 65 | ctypedef dxf_uint_t dxf_event_flags_t 66 | 67 | ctypedef struct dxf_byte_array_t: 68 | dxf_byte_t* elements, 69 | int size, 70 | int capacity, 71 | 72 | ctypedef struct dxf_property_item_t: 73 | dxf_string_t key, 74 | dxf_string_t value, 75 | 76 | ctypedef enum dxf_connection_status_t: 77 | dxf_cs_not_connected = 0, 78 | dxf_cs_connected, 79 | dxf_cs_login_required, 80 | dxf_cs_authorized 81 | 82 | #endif /* DX_TYPES_H_INCLUDED */ -------------------------------------------------------------------------------- /dxfeed/core/pxd_include/RecordData.pxd: -------------------------------------------------------------------------------- 1 | # /* 2 | # * The contents of this file are subject to the Mozilla Public License Version 3 | # * 1.1 (the "License") you may not use this file except in compliance with 4 | # * the License. You may obtain a copy of the License at 5 | # * http://www.mozilla.org/MPL/ 6 | # * 7 | # * Software distributed under the License is distributed on an "AS IS" basis, 8 | # * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License 9 | # * for the specific language governing rights and limitations under the 10 | # * License. 11 | # * 12 | # * The Initial Developer of the Original Code is Devexperts LLC. 13 | # * Portions created by the Initial Developer are Copyright (C) 2010 14 | # * the Initial Developer. All Rights Reserved. 15 | # * 16 | # * Contributor(s): 17 | # * 18 | # */ 19 | # 20 | # /* 21 | # * Here we have what's related to the records we receive from the server. 22 | # */ 23 | 24 | #ifndef RECORD_DATA_H_INCLUDED 25 | #define RECORD_DATA_H_INCLUDED 26 | 27 | #include "DXTypes.h" 28 | from dxfeed.core.pxd_include.DXTypes cimport * 29 | # /* -------------------------------------------------------------------------- */ 30 | # /* 31 | # * Record type constants 32 | # */ 33 | # /* -------------------------------------------------------------------------- */ 34 | cdef extern from "": 35 | ctypedef enum dx_record_info_id_t: 36 | dx_rid_begin = 0, 37 | dx_rid_trade = dx_rid_begin, 38 | dx_rid_quote, 39 | dx_rid_summary, 40 | dx_rid_profile, 41 | dx_rid_market_maker, 42 | dx_rid_order, 43 | dx_rid_time_and_sale, 44 | dx_rid_candle, 45 | dx_rid_trade_eth, 46 | dx_rid_spread_order, 47 | dx_rid_greeks, 48 | dx_rid_theo_price, 49 | dx_rid_underlying, 50 | dx_rid_series, 51 | dx_rid_configuration, 52 | 53 | # /* add new values above this line */ 54 | 55 | dx_rid_count, 56 | dx_rid_invalid 57 | 58 | 59 | ctypedef dxf_int_t dx_record_id_t 60 | 61 | # /* Invalid or empty record id */ 62 | # ctypedef static const dx_record_id_t DX_RECORD_ID_INVALID = -1 63 | cdef dx_record_id_t DX_RECORD_ID_INVALID = -1 64 | 65 | # /* -------------------------------------------------------------------------- */ 66 | # /* 67 | # * Record structures 68 | # */ 69 | # /* -------------------------------------------------------------------------- */ 70 | 71 | ctypedef struct dx_trade_t: 72 | dxf_int_t time 73 | dxf_int_t sequence 74 | dxf_int_t time_nanos 75 | dxf_char_t exchange_code 76 | dxf_double_t price 77 | dxf_double_t size 78 | dxf_int_t tick 79 | dxf_double_t change 80 | dxf_dayid_t day_id; 81 | dxf_double_t day_volume 82 | dxf_double_t day_turnover 83 | dxf_int_t flags 84 | 85 | ctypedef dx_trade_t dx_trade_eth_t 86 | 87 | ctypedef struct dx_quote_t: 88 | dxf_int_t sequence 89 | dxf_int_t time_nanos 90 | dxf_int_t bid_time 91 | dxf_char_t bid_exchange_code 92 | dxf_double_t bid_price 93 | dxf_double_t bid_size 94 | dxf_int_t ask_time 95 | dxf_char_t ask_exchange_code 96 | dxf_double_t ask_price 97 | dxf_double_t ask_size 98 | 99 | ctypedef struct dx_summary_t: 100 | dxf_dayid_t day_id 101 | dxf_double_t day_open_price 102 | dxf_double_t day_high_price 103 | dxf_double_t day_low_price 104 | dxf_double_t day_close_price 105 | dxf_dayid_t prev_day_id 106 | dxf_double_t prev_day_close_price 107 | dxf_double_t prev_day_volume 108 | dxf_double_t open_interest 109 | dxf_int_t flags 110 | 111 | 112 | ctypedef struct dx_profile_t: 113 | dxf_double_t beta 114 | dxf_double_t eps 115 | dxf_double_t div_freq 116 | dxf_double_t exd_div_amount 117 | dxf_dayid_t exd_div_date 118 | dxf_double_t high_price_52 119 | dxf_double_t low_price_52 120 | dxf_double_t shares 121 | dxf_double_t free_float 122 | dxf_double_t high_limit_price 123 | dxf_double_t low_limit_price 124 | dxf_int_t halt_start_time 125 | dxf_int_t halt_end_time 126 | dxf_int_t flags 127 | dxf_const_string_t description 128 | dxf_const_string_t status_reason 129 | 130 | cdef extern from "": 131 | ctypedef struct dx_market_maker_t: 132 | dxf_char_t mm_exchange 133 | dxf_int_t mm_id 134 | dxf_int_t mmbid_time 135 | dxf_double_t mmbid_price 136 | dxf_double_t mmbid_size 137 | dxf_double_t mmbid_count 138 | dxf_int_t mmask_time 139 | dxf_double_t mmask_price 140 | dxf_double_t mmask_size 141 | dxf_double_t mmask_count 142 | 143 | ctypedef struct dx_order_t: 144 | dxf_int_t index 145 | dxf_int_t time 146 | dxf_int_t time_nanos 147 | dxf_int_t sequence 148 | dxf_long_t action_time; 149 | dxf_long_t order_id; 150 | dxf_long_t aux_order_id; 151 | dxf_double_t price 152 | dxf_double_t size 153 | dxf_double_t executed_size; 154 | dxf_double_t count 155 | dxf_int_t flags 156 | dxf_long_t trade_id; 157 | dxf_double_t trade_price; 158 | dxf_double_t trade_size; 159 | dxf_int_t mmid 160 | 161 | 162 | ctypedef struct dx_spread_order_t: 163 | dxf_int_t index 164 | dxf_int_t time 165 | dxf_int_t time_nanos 166 | dxf_int_t sequence 167 | dxf_long_t action_time; 168 | dxf_long_t order_id; 169 | dxf_long_t aux_order_id; 170 | dxf_double_t price 171 | dxf_double_t size 172 | dxf_double_t executed_size; 173 | dxf_double_t count 174 | dxf_int_t flags 175 | dxf_long_t trade_id; 176 | dxf_double_t trade_price; 177 | dxf_double_t trade_size; 178 | dxf_const_string_t spread_symbol 179 | 180 | 181 | ctypedef struct dx_time_and_sale_t: 182 | dxf_int_t time 183 | dxf_int_t sequence 184 | dxf_char_t exchange_code 185 | dxf_double_t price 186 | dxf_double_t size 187 | dxf_double_t bid_price 188 | dxf_double_t ask_price 189 | dxf_int_t exchange_sale_conditions 190 | dxf_int_t flags 191 | dxf_const_string_t buyer 192 | dxf_const_string_t seller 193 | 194 | ctypedef struct dx_candle_t: 195 | dxf_int_t time 196 | dxf_int_t sequence 197 | dxf_double_t count 198 | dxf_double_t open 199 | dxf_double_t high 200 | dxf_double_t low 201 | dxf_double_t close 202 | dxf_double_t volume 203 | dxf_double_t vwap 204 | dxf_double_t bid_volume 205 | dxf_double_t ask_volume 206 | dxf_double_t open_interest 207 | dxf_double_t imp_volatility 208 | 209 | ctypedef struct dx_greeks_t: 210 | dxf_int_t time 211 | dxf_int_t sequence 212 | dxf_double_t price 213 | dxf_double_t volatility 214 | dxf_double_t delta 215 | dxf_double_t gamma 216 | dxf_double_t theta 217 | dxf_double_t rho 218 | dxf_double_t vega 219 | 220 | ctypedef struct dx_theo_price_t: 221 | # // To have same record for record and event 222 | dxf_long_t time 223 | dxf_double_t price 224 | dxf_double_t underlying_price 225 | dxf_double_t delta 226 | dxf_double_t gamma 227 | dxf_double_t dividend 228 | dxf_double_t interest 229 | 230 | ctypedef struct dx_underlying_t: 231 | dxf_double_t volatility 232 | dxf_double_t front_volatility 233 | dxf_double_t back_volatility 234 | dxf_double_t call_volume; 235 | dxf_double_t put_volume; 236 | dxf_double_t put_call_ratio 237 | 238 | ctypedef struct dx_series_t: 239 | dxf_int_t index 240 | dxf_int_t time 241 | dxf_int_t sequence 242 | dxf_dayid_t expiration 243 | dxf_double_t volatility 244 | dxf_double_t call_volume; 245 | dxf_double_t put_volume; 246 | dxf_double_t put_call_ratio 247 | dxf_double_t forward_price 248 | dxf_double_t dividend 249 | dxf_double_t interest 250 | 251 | 252 | ctypedef struct dx_configuration_t: 253 | dxf_int_t version 254 | dxf_byte_array_t object 255 | 256 | #endif /* RECORD_DATA_H_INCLUDED */ -------------------------------------------------------------------------------- /dxfeed/core/pxd_include/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxFeed/dxfeed-python-api/4cd4950d4d40de642a38afa304811369d43c358c/dxfeed/core/pxd_include/__init__.py -------------------------------------------------------------------------------- /dxfeed/core/utils/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dxFeed/dxfeed-python-api/4cd4950d4d40de642a38afa304811369d43c358c/dxfeed/core/utils/__init__.py -------------------------------------------------------------------------------- /dxfeed/core/utils/data_class.py: -------------------------------------------------------------------------------- 1 | from collections import deque 2 | from typing import Any 3 | from threading import Lock 4 | 5 | 6 | class DequeWithLock(deque): 7 | """ 8 | Class that provides lock mechanism to deque from collections for append, copy and get operations 9 | """ 10 | def __init__(self, iterable=(), maxlen=None): 11 | super().__init__(iterable, maxlen) 12 | self.lock = Lock() 13 | 14 | def safe_append(self, data: Any): 15 | """ 16 | Method appends data while locked 17 | Parameters 18 | ---------- 19 | data: any 20 | Data to append 21 | """ 22 | try: 23 | self.lock.acquire() 24 | self.append(data) 25 | finally: 26 | self.lock.release() 27 | 28 | def safe_get(self, keep: bool=True): 29 | """ 30 | Method that pops all the data with subsequent clearing 31 | 32 | Parameters 33 | ---------- 34 | keep: bool 35 | When False clears internal collections.deque object after call. Default True. 36 | Returns 37 | ------- 38 | list_to_return: list 39 | List filled with data 40 | """ 41 | list_to_return = list() 42 | try: 43 | self.lock.acquire() 44 | list_to_return = self.copy() 45 | if not keep: 46 | self.clear() 47 | finally: 48 | self.lock.release() 49 | return list(list_to_return) 50 | -------------------------------------------------------------------------------- /dxfeed/core/utils/handler.pxd: -------------------------------------------------------------------------------- 1 | 2 | 3 | cdef class EventHandler: 4 | cdef public list columns 5 | 6 | cdef void cython_internal_update_method(self, event) nogil 7 | 8 | cdef class DefaultHandler(EventHandler): 9 | cdef object __data 10 | -------------------------------------------------------------------------------- /dxfeed/core/utils/handler.pyx: -------------------------------------------------------------------------------- 1 | from warnings import warn 2 | from dxfeed.core.utils.data_class import DequeWithLock as deque_wl 3 | import pandas as pd 4 | from typing import Iterable 5 | 6 | 7 | cdef class EventHandler: 8 | """ 9 | Master class for user defined event handlers. `update` method should be considered as abstract. 10 | 11 | Attributes 12 | ---------- 13 | columns: list 14 | After attaching listener the field contains one-word descriptors of the values in upcoming event the order 15 | coincides 16 | """ 17 | def __init__(self): 18 | self.columns = list() 19 | 20 | cdef void cython_internal_update_method(self, event) nogil: 21 | with gil: 22 | self.update(event) 23 | 24 | def update(self, event: Iterable): 25 | """ 26 | Method that is called, when event arrives to related Subscription. Currently (Cython version 0.29.17), 27 | abstract methods are not implemented, so this implementation is sort of stub method. 28 | 29 | Parameters 30 | ---------- 31 | event: list 32 | List of various data specific to certain event type 33 | """ 34 | warn(Warning('You have not implemented update method in your EventHandler, that is called, when event comes!')) 35 | 36 | cdef class DefaultHandler(EventHandler): 37 | def __init__(self, data_len: int=100000): 38 | """ 39 | The class implements one possible event handler, which is considered as default. This class just stores upcoming 40 | events in custom deque with thread lock and has methods to get data as list or as pandas.DataFrame. 41 | 42 | Attributes 43 | ---------- 44 | data_len: int 45 | The length of internal DequeWithLock object. Default is 100000. 46 | columns: list 47 | After attaching listener the field contains one-word descriptors of the values in upcoming event the order 48 | coincides. 49 | """ 50 | super().__init__() 51 | self.__data = deque_wl(maxlen=data_len) 52 | 53 | def update(self, event: Iterable): 54 | """ 55 | Utility method that is called by underlying Cython level when new event is received. Stores events in 56 | DequeWithLock. 57 | 58 | Parameters 59 | ---------- 60 | event: list 61 | List of various data specific to certain event type. 62 | """ 63 | self.__data.safe_append(*event) 64 | 65 | def get_list(self, keep: bool=True): 66 | """ 67 | Method to get data stored in DequeWithLock as list. 68 | 69 | Parameters 70 | ---------- 71 | keep: bool 72 | When False clears internal DequeWithLock object after call. Default True. 73 | 74 | Returns 75 | ------- 76 | data: list 77 | List of received events. 78 | """ 79 | data = self.__data.safe_get(keep=keep) 80 | return data 81 | 82 | def get_dataframe(self, keep: bool=True): 83 | """ 84 | Returns received data as pandas.DataFrame object. 85 | 86 | Warning 87 | ------- 88 | There are no restriction on attaching one event handler to several subscriptions. In this case, method will 89 | work correctly if different subscriptions have same event type. Otherwise error will occur. 90 | 91 | Parameters 92 | ---------- 93 | keep: bool 94 | When False clears internal DequeWithLock object after call. Default True. 95 | 96 | Returns 97 | ------- 98 | df: pandas.DataFrame 99 | Dataframe with received events. 100 | """ 101 | df_data = self.get_list(keep=keep) 102 | 103 | 104 | df = pd.DataFrame(df_data, columns=self.columns) 105 | time_columns = (column for column in df.columns 106 | if 'Time' in column 107 | and 'TimeNanos' not in column) 108 | for column in time_columns: 109 | df.loc[:, column] = df.loc[:, column].values.astype('datetime64[ms]') 110 | if 'TimeNanos' in df.columns and 'Time' in df.columns: 111 | df.loc[:, 'Time'] += df.loc[:, 'TimeNanos'].values.astype('timedelta64[ns]') 112 | df.drop(columns=['TimeNanos'], inplace=True) 113 | return df 114 | -------------------------------------------------------------------------------- /dxfeed/core/utils/helpers.pxd: -------------------------------------------------------------------------------- 1 | from dxfeed.core.pxd_include.DXTypes cimport dxf_const_string_t 2 | from cpython.ref cimport PyObject 3 | 4 | cdef extern from *: 5 | PyObject * PyUnicode_FromWideChar(dxf_const_string_t w, Py_ssize_t size) 6 | dxf_const_string_t PyUnicode_AsWideCharString(object, Py_ssize_t *) 7 | 8 | cdef object unicode_from_dxf_const_string_t(dxf_const_string_t wcs, int size=*) 9 | 10 | cdef dxf_const_string_t dxf_const_string_t_from_unicode(object symbol) -------------------------------------------------------------------------------- /dxfeed/core/utils/helpers.pyx: -------------------------------------------------------------------------------- 1 | import os 2 | from warnings import warn 3 | from dxfeed.core.pxd_include.DXErrorCodes cimport * 4 | from dxfeed.core.pxd_include.DXFeed cimport * 5 | from dxfeed.core.pxd_include.DXTypes cimport * 6 | from pathlib import Path 7 | import dxfeed 8 | 9 | cdef object unicode_from_dxf_const_string_t(dxf_const_string_t wcs, int size=-1): 10 | """ 11 | Cython function, callable from C to convert dxf_const_string_t(wchar_t) to unicode 12 | 13 | Parameters 14 | ---------- 15 | wcs: dxf_const_string_t, wchar_t 16 | C string 17 | size: int 18 | Number of characters. By default -1 19 | 20 | Returns 21 | ------- 22 | unicode 23 | Python unicode string 24 | """ 25 | if wcs == NULL: 26 | return '' 27 | ret_val = PyUnicode_FromWideChar(wcs, size) 28 | return ret_val 29 | 30 | cdef dxf_const_string_t dxf_const_string_t_from_unicode(object symbol): 31 | """ 32 | Cython function, callable from C that converts python unicode string to wchar_t. 33 | 34 | Parameters 35 | ---------- 36 | symbol: unicode 37 | Unicode string, usually ticker 38 | Returns 39 | ------- 40 | wchar_t 41 | Wide Char 42 | """ 43 | return PyUnicode_AsWideCharString(symbol, NULL) 44 | 45 | def event_type_convert(event_type: str): 46 | """ 47 | The function converts event type given as string to int, used in C dxfeed dxfeed 48 | 49 | Parameters 50 | ---------- 51 | event_type: str 52 | Event type: 'Trade', 'Quote', 'Summary', 'Profile', 'Order', 'TimeAndSale', 'Candle', 'TradeETH', 'SpreadOrder', 53 | 'Greeks', 'TheoPrice', 'Underlying', 'Series', 'Configuration' or '' 54 | Returns 55 | ------- 56 | : int 57 | Integer that corresponds to event type in C dxfeed dxfeed 58 | """ 59 | et_mapping = { 60 | 'Trade': 1, 61 | 'Quote': 2, 62 | 'Summary': 4, 63 | 'Profile': 8, 64 | 'Order': 16, 65 | 'TimeAndSale': 32, 66 | 'Candle': 64, 67 | 'TradeETH': 128, 68 | 'SpreadOrder': 256, 69 | 'Greeks': 512, 70 | 'TheoPrice': 1024, 71 | 'Underlying': 2048, 72 | 'Series': 4096, 73 | 'Configuration': 8192, 74 | '': -16384 75 | } 76 | try: 77 | return et_mapping[event_type] 78 | except KeyError: 79 | warn(f'No mapping for {event_type}, please choose one from {et_mapping.keys()}') 80 | return -16384 81 | 82 | def get_include(): 83 | """ 84 | Function returns paths to header files of dxfeed-c-api library. Used for writing custom listeners 85 | 86 | Returns 87 | ------- 88 | out_dir: list 89 | List of paths to header files 90 | """ 91 | out_dir = list() 92 | out_dir.append( 93 | str(os.path.realpath(Path(dxfeed.__file__).resolve().parent / 'dxfeed-c-api' / 'include'))) 94 | return out_dir 95 | -------------------------------------------------------------------------------- /dxfeed/wrappers/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /dxfeed/wrappers/class_utils.py: -------------------------------------------------------------------------------- 1 | from typing import Union, Iterable 2 | import collections 3 | from warnings import warn 4 | from datetime import datetime 5 | import pandas as pd 6 | 7 | 8 | def to_iterable(value: Union[str, Iterable[str]]) -> Iterable[str]: 9 | """ 10 | Function to correctly wrap string into iterable object 11 | 12 | Parameters 13 | ---------- 14 | value: str, iterable 15 | String or iterable object 16 | 17 | Returns 18 | ------- 19 | : iterable 20 | """ 21 | if isinstance(value, str): 22 | value = [value] 23 | if not isinstance(value, collections.abc.Iterable): 24 | raise TypeError(f'Expected string or iterable type, got {type(value)}') 25 | return value 26 | 27 | 28 | def handle_datetime(date_time: Union[str, datetime], fmt: str = '%Y-%m-%d %H:%M:%S.%f', exact_format: bool = True): 29 | """ 30 | Function to convert string of date and time to datetime object. 31 | 32 | Parameters 33 | ---------- 34 | date_time: str, datetime.datetime 35 | Datetime to convert 36 | fmt: str 37 | Format of expected datetime 38 | exact_format: bool 39 | If False no warning will be thrown in case of incomplete date_time parameter. Default - True 40 | 41 | Returns 42 | ------- 43 | date_time: datetime.datetime 44 | date_time argument converted to datetime.datetime object. 45 | """ 46 | if isinstance(date_time, str): 47 | try: 48 | date_time = datetime.strptime(date_time, fmt) 49 | except ValueError: 50 | try: 51 | date_time = pd.to_datetime(date_time, format=fmt, infer_datetime_format=True) 52 | if exact_format: 53 | warn(Warning(f'Datetime argument does not exactly match {fmt} format,' + 54 | ' date was parsed automatically as ' + 55 | date_time.strftime(format=fmt))) 56 | except ValueError: 57 | raise ValueError(f'Datetime should use {fmt} format!') 58 | if not isinstance(date_time, datetime): 59 | raise TypeError(f'Datetime has incorrect type of {type(date_time)}, should be string or datetime') 60 | return date_time 61 | -------------------------------------------------------------------------------- /dxfeed/wrappers/endpoint.py: -------------------------------------------------------------------------------- 1 | from dxfeed.core import DXFeedPy as dxp 2 | from dxfeed.wrappers.subscription import Subscription 3 | from datetime import datetime 4 | from typing import Union 5 | 6 | 7 | class Endpoint(object): 8 | """ 9 | Class for connection management. After successful creation Instance will be connected to server 10 | with provided credentials 11 | 12 | Attributes 13 | ---------- 14 | connection_status: str 15 | Status of current connection 16 | address: str 17 | Current connection endpoint address 18 | 19 | """ 20 | def __init__(self, connection_address: str = 'demo.dxfeed.com:7300', connect: bool = True): 21 | """ 22 | Parameters 23 | ---------- 24 | connection_address: str 25 | One of possible connection addresses: 26 | 27 | - the single address: `host:port` or just `host` 28 | - address with credentials: `host:port[username=xxx,password=yyy]` 29 | - multiple addresses: `(host1:port1)(host2)(host3:port3[username=xxx,password=yyy])` 30 | 31 | Default: demo.dxfeed.com:7300 32 | connect: bool 33 | When True `connect` method is called during instance creation. Default - True 34 | """ 35 | self.__con_address = connection_address 36 | self.__connection = dxp.ConnectionClass() 37 | if connect: 38 | self.connect() 39 | 40 | def __del__(self): 41 | self.close_connection() 42 | 43 | @property 44 | def connection_status(self): 45 | return dxp.dxf_get_current_connection_status(self.__connection, return_str=True) 46 | 47 | @property 48 | def address(self): 49 | return self.__con_address 50 | 51 | def connect(self, reconnect: bool = True): 52 | """ 53 | Creates connection. If connection status differs from "Not connected" and `reconnect` is False, does nothing 54 | 55 | Parameters 56 | ---------- 57 | reconnect: bool 58 | When True closes previous connection. Default - True 59 | 60 | Returns 61 | ------- 62 | self: Endpoint 63 | """ 64 | if reconnect: 65 | dxp.dxf_close_connection(self.__connection) 66 | 67 | con_status = dxp.dxf_get_current_connection_status(self.__connection, return_str=True) 68 | 69 | if con_status == 'Not connected': 70 | self.__connection = dxp.dxf_create_connection(self.address) 71 | 72 | return self 73 | 74 | def create_subscription(self, event_type: str, date_time: Union[str, datetime] = None): 75 | """ 76 | Method creates certain event type subscription and returns Subscription class 77 | 78 | Parameters 79 | ---------- 80 | event_type: str 81 | One of possible event types: 'Trade', 'Quote', 'Summary', 'Profile', 'Order', 'TimeAndSale', 'Candle', 82 | 'TradeETH', 'SpreadOrder', 'Greeks', 'TheoPrice', 'Underlying', 'Series', 'Configuration' or '' 83 | date_time: str or datetime.datetime 84 | If present timed subscription will be created (conflated stream). For sting date format is following: 85 | %Y-%m-%d %H:%M:%S.%f. If None - stream subscription will be created. Default - None. 86 | 87 | Note 88 | ---- 89 | Some event types (e.g. Candle) support only timed subscription. 90 | 91 | Returns 92 | ------- 93 | subscription: Subscription 94 | Subscription class related to current connection 95 | """ 96 | con_status = dxp.dxf_get_current_connection_status(self.__connection, return_str=False) 97 | if con_status == 0 or con_status == 2: 98 | raise ValueError('Connection is not established') 99 | subscription = Subscription(connection=self.__connection, 100 | event_type=event_type, 101 | date_time=date_time) 102 | return subscription 103 | 104 | def close_connection(self): 105 | """ 106 | Method to close connections and all related subscriptions. 107 | """ 108 | dxp.dxf_close_connection(self.__connection) 109 | -------------------------------------------------------------------------------- /dxfeed/wrappers/subscription.py: -------------------------------------------------------------------------------- 1 | from typing import Iterable, Union, Optional 2 | from datetime import datetime 3 | from warnings import simplefilter 4 | 5 | from dxfeed.core import DXFeedPy as dxp 6 | from dxfeed.core.utils.handler import DefaultHandler 7 | import dxfeed.wrappers.class_utils as cu 8 | 9 | 10 | class Subscription(object): 11 | """ 12 | Class for subscription management. Recommended to be created only via create_subscription method in Endpoint class. 13 | Also stores incoming events 14 | 15 | Attributes 16 | ---------- 17 | event_type: str 18 | One of possible event types: 'Trade', 'Quote', 'Summary', 'Profile', 'Order', 'TimeAndSale', 'Candle', 19 | 'TradeETH', 'SpreadOrder', 'Greeks', 'TheoPrice', 'Underlying', 'Series', 'Configuration' or '' 20 | symbols: Iterable 21 | Symbols of current subscription. 22 | 23 | Note 24 | ---- 25 | Some event types (e.g. Candle) support only timed subscription. 26 | 27 | """ 28 | def __init__(self, connection, event_type: str, date_time: Union[str, datetime], exact_format: bool = True): 29 | """ 30 | 31 | Parameters 32 | ---------- 33 | connection: dxfeed.core.DXFeedPy.ConnectionClass 34 | Core class written in cython, that handle connection related details on the low level 35 | event_type: str 36 | One of possible event types: 'Trade', 'Quote', 'Summary', 'Profile', 'Order', 'TimeAndSale', 'Candle', 37 | 'TradeETH', 'SpreadOrder', 'Greeks', 'TheoPrice', 'Underlying', 'Series', 'Configuration' or '' 38 | date_time: str or datetime.datetime 39 | If present timed subscription will be created (conflated stream). For sting date format is following: 40 | %Y-%m-%d %H:%M:%S.%f. If None - stream subscription will be created (non-conflated). Default - None. 41 | exact_format: bool 42 | If False no warning will be thrown in case of incomplete date_time parameter. Default - True 43 | """ 44 | self.__event_type = event_type 45 | if date_time is None: 46 | self.__sub = dxp.dxf_create_subscription(cc=connection, 47 | event_type=event_type) 48 | else: 49 | date_time = cu.handle_datetime(date_time, fmt='%Y-%m-%d %H:%M:%S.%f', exact_format=exact_format) 50 | timestamp = int(date_time.timestamp() * 1000) 51 | self.__sub = dxp.dxf_create_subscription_timed(cc=connection, 52 | event_type=event_type, 53 | time=timestamp) 54 | 55 | def __del__(self): 56 | self.close_subscription() 57 | 58 | @property 59 | def event_type(self): 60 | return self.__event_type 61 | 62 | @property 63 | def symbols(self): 64 | return dxp.dxf_get_symbols(self.__sub) 65 | 66 | def add_symbols(self, symbols: Union[str, Iterable[str]]): 67 | """ 68 | Method to add symbol. Supports addition of one symbol as a string as well as list of symbols. 69 | If no event handler was set, DefaultHandler will be initialized. 70 | 71 | Parameters 72 | ---------- 73 | symbols: str, Iterable 74 | Symbols to add. Previously added and remained symbols are ignored on C-API level 75 | 76 | Returns 77 | ------- 78 | self: Subscription 79 | """ 80 | self._attach_default_listener() 81 | dxp.dxf_add_symbols(sc=self.__sub, symbols=cu.to_iterable(symbols)) 82 | return self 83 | 84 | def remove_symbols(self, symbols: Optional[Union[str, Iterable[str]]] = None): 85 | """ 86 | Method removes symbols from subscription. If no symbols provided removes all symbols 87 | 88 | Parameters 89 | ---------- 90 | symbols: str, Iterable 91 | One ticker or list of tickers to remove from subscription 92 | 93 | Returns 94 | ------- 95 | self: Subscription 96 | """ 97 | if symbols: 98 | dxp.dxf_remove_symbols(self.__sub, symbols=cu.to_iterable(symbols)) 99 | else: 100 | dxp.dxf_clear_symbols(self.__sub) 101 | return self 102 | 103 | def close_subscription(self): 104 | """ 105 | Method to close subscription. All received data will remain in the object. 106 | """ 107 | dxp.dxf_close_subscription(sc=self.__sub) 108 | 109 | def _attach_default_listener(self): 110 | """ 111 | Method to attach default listener. If event handler was not previously set, DefaultHandler will be initialized. 112 | 113 | Returns 114 | ------- 115 | self: Subscription 116 | """ 117 | if not self.get_event_handler(): 118 | self.set_event_handler(DefaultHandler()) 119 | simplefilter(action='ignore', category=FutureWarning) 120 | 121 | dxp.dxf_attach_listener(self.__sub) 122 | 123 | simplefilter(action='default', category=FutureWarning) 124 | 125 | return self 126 | 127 | def _detach_listener(self): 128 | """ 129 | Removes listener so new events won't be received 130 | 131 | Returns 132 | ------- 133 | self: Subscription 134 | """ 135 | dxp.dxf_detach_listener(self.__sub) 136 | return self 137 | 138 | def get_event_handler(self): 139 | """ 140 | Method to get event handler. If no handlers passed previously returns None. 141 | 142 | Returns 143 | ------- 144 | handler: EventHandler 145 | """ 146 | return self.__sub.get_event_handler() 147 | 148 | def set_event_handler(self, handler: dxp.EventHandler): 149 | """ 150 | Method to set the handler. 151 | 152 | Parameters 153 | ---------- 154 | handler: EventHandler 155 | Event handler with `update` method defined. 156 | 157 | Returns 158 | ------- 159 | self: Subscription 160 | """ 161 | self.__sub.set_event_handler(handler) 162 | return self 163 | -------------------------------------------------------------------------------- /examples/AllSubscriptionsExample.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Quick start: All Subscriptions Example" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "This tutorial aims to show all available subscriptions. You will find a link to the dxfeed knowledge base for each event type.\n", 15 | "\n", 16 | "Summary table about all event types is here: https://kb.dxfeed.com/display/DS/Model+of+Event+Publishing" 17 | ] 18 | }, 19 | { 20 | "cell_type": "markdown", 21 | "metadata": {}, 22 | "source": [ 23 | "### Import package" 24 | ] 25 | }, 26 | { 27 | "cell_type": "code", 28 | "execution_count": null, 29 | "metadata": {}, 30 | "outputs": [], 31 | "source": [ 32 | "import dxfeed as dx\n", 33 | "from datetime import datetime\n", 34 | "from dateutil.relativedelta import relativedelta" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "### Configure and create connection with Endpoint class\n", 42 | "Create instance of Endpoint class which will connect provided address. " 43 | ] 44 | }, 45 | { 46 | "cell_type": "code", 47 | "execution_count": null, 48 | "metadata": {}, 49 | "outputs": [], 50 | "source": [ 51 | "symbols = ['AAPL', 'MSFT']\n", 52 | "date_time = datetime.now() - relativedelta(days=3)\n", 53 | "endpoint = dx.Endpoint('demo.dxfeed.com:7300')" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "### Trade\n", 61 | "Details: https://kb.dxfeed.com/display/DS/dxFeed+API+Market+Events#dxFeedAPIMarketEvents-Trades" 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": null, 67 | "metadata": {}, 68 | "outputs": [], 69 | "source": [ 70 | "trade_sub = endpoint.create_subscription('Trade').add_symbols(symbols)" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": null, 76 | "metadata": {}, 77 | "outputs": [], 78 | "source": [ 79 | "trade_sub.get_event_handler().get_dataframe().head(3)" 80 | ] 81 | }, 82 | { 83 | "cell_type": "markdown", 84 | "metadata": {}, 85 | "source": [ 86 | "### Quote\n", 87 | "Details: https://kb.dxfeed.com/display/DS/dxFeed+API+Market+Events#dxFeedAPIMarketEvents-Quote" 88 | ] 89 | }, 90 | { 91 | "cell_type": "code", 92 | "execution_count": null, 93 | "metadata": {}, 94 | "outputs": [], 95 | "source": [ 96 | "quote_sub = endpoint.create_subscription('Quote').add_symbols(symbols)" 97 | ] 98 | }, 99 | { 100 | "cell_type": "code", 101 | "execution_count": null, 102 | "metadata": {}, 103 | "outputs": [], 104 | "source": [ 105 | "quote_sub.get_event_handler().get_dataframe().head(3)" 106 | ] 107 | }, 108 | { 109 | "cell_type": "markdown", 110 | "metadata": {}, 111 | "source": [ 112 | "### Summary\n", 113 | "Details: https://kb.dxfeed.com/display/DS/dxFeed+API+Market+Events#dxFeedAPIMarketEvents-Summary" 114 | ] 115 | }, 116 | { 117 | "cell_type": "code", 118 | "execution_count": null, 119 | "metadata": {}, 120 | "outputs": [], 121 | "source": [ 122 | "summary_sub = endpoint.create_subscription('Summary').add_symbols(symbols)" 123 | ] 124 | }, 125 | { 126 | "cell_type": "code", 127 | "execution_count": null, 128 | "metadata": {}, 129 | "outputs": [], 130 | "source": [ 131 | "summary_sub.get_event_handler().get_dataframe().head(3)" 132 | ] 133 | }, 134 | { 135 | "cell_type": "markdown", 136 | "metadata": {}, 137 | "source": [ 138 | "### Profile\n", 139 | "Details: https://kb.dxfeed.com/display/DS/dxFeed+API+Market+Events#dxFeedAPIMarketEvents-Meta" 140 | ] 141 | }, 142 | { 143 | "cell_type": "code", 144 | "execution_count": null, 145 | "metadata": {}, 146 | "outputs": [], 147 | "source": [ 148 | "profile_sub = endpoint.create_subscription('Profile').add_symbols(symbols)" 149 | ] 150 | }, 151 | { 152 | "cell_type": "code", 153 | "execution_count": null, 154 | "metadata": {}, 155 | "outputs": [], 156 | "source": [ 157 | "profile_sub.get_event_handler().get_dataframe().head(3)" 158 | ] 159 | }, 160 | { 161 | "cell_type": "markdown", 162 | "metadata": {}, 163 | "source": [ 164 | "### TimeAndSale\n", 165 | "Details: https://kb.dxfeed.com/display/DS/dxFeed+API+Market+Events#dxFeedAPIMarketEvents-TimeAndSale" 166 | ] 167 | }, 168 | { 169 | "cell_type": "code", 170 | "execution_count": null, 171 | "metadata": {}, 172 | "outputs": [], 173 | "source": [ 174 | "tns_sub = endpoint.create_subscription('TimeAndSale').add_symbols(symbols)" 175 | ] 176 | }, 177 | { 178 | "cell_type": "code", 179 | "execution_count": null, 180 | "metadata": {}, 181 | "outputs": [], 182 | "source": [ 183 | "tns_sub.get_event_handler().get_dataframe().head(3)" 184 | ] 185 | }, 186 | { 187 | "cell_type": "markdown", 188 | "metadata": {}, 189 | "source": [ 190 | "### Candle\n", 191 | "Details: https://kb.dxfeed.com/display/DS/Charting+Overview#ChartingOverview-Candletypes\n", 192 | "\n", 193 | "For Candle event type along with base symbol, you should specify an aggregation period. You can also set price type. More details: https://kb.dxfeed.com/display/DS/REST+API#RESTAPI-Candlesymbols." 194 | ] 195 | }, 196 | { 197 | "cell_type": "code", 198 | "execution_count": null, 199 | "metadata": {}, 200 | "outputs": [], 201 | "source": [ 202 | "aggregated_symbols = [symbol + '{=d}' for symbol in symbols]\n", 203 | "print(aggregated_symbols)" 204 | ] 205 | }, 206 | { 207 | "cell_type": "code", 208 | "execution_count": null, 209 | "metadata": {}, 210 | "outputs": [], 211 | "source": [ 212 | "candle_sub = endpoint.create_subscription('Candle', date_time=date_time).add_symbols(aggregated_symbols)" 213 | ] 214 | }, 215 | { 216 | "cell_type": "code", 217 | "execution_count": null, 218 | "metadata": {}, 219 | "outputs": [], 220 | "source": [ 221 | "candle_sub.get_event_handler().get_dataframe().head(3)" 222 | ] 223 | }, 224 | { 225 | "cell_type": "markdown", 226 | "metadata": {}, 227 | "source": [ 228 | "### Order\n", 229 | "Details: https://kb.dxfeed.com/display/DS/dxFeed+API+Market+Events#dxFeedAPIMarketEvents-Orders" 230 | ] 231 | }, 232 | { 233 | "cell_type": "code", 234 | "execution_count": null, 235 | "metadata": {}, 236 | "outputs": [], 237 | "source": [ 238 | "order_sub = endpoint.create_subscription('Order').add_symbols(symbols)" 239 | ] 240 | }, 241 | { 242 | "cell_type": "code", 243 | "execution_count": null, 244 | "metadata": {}, 245 | "outputs": [], 246 | "source": [ 247 | "order_sub.get_event_handler().get_dataframe().head(3)" 248 | ] 249 | }, 250 | { 251 | "cell_type": "markdown", 252 | "metadata": {}, 253 | "source": [ 254 | "### Underlying\n", 255 | "Details: https://kb.dxfeed.com/display/DS/dxFeed+API+Market+Events#dxFeedAPIMarketEvents-Underlying" 256 | ] 257 | }, 258 | { 259 | "cell_type": "code", 260 | "execution_count": null, 261 | "metadata": {}, 262 | "outputs": [], 263 | "source": [ 264 | "underlying_sub = endpoint.create_subscription('Underlying').add_symbols(symbols)" 265 | ] 266 | }, 267 | { 268 | "cell_type": "code", 269 | "execution_count": null, 270 | "metadata": {}, 271 | "outputs": [], 272 | "source": [ 273 | "underlying_sub.get_event_handler().get_dataframe().head(3)" 274 | ] 275 | }, 276 | { 277 | "cell_type": "markdown", 278 | "metadata": {}, 279 | "source": [ 280 | "### Series\n", 281 | "Snapshot of computed values that are available for all option series for a given underlying symbol based on the option prices on the market.\n", 282 | "List of properties: https://docs.dxfeed.com/dxfeed/api/com/dxfeed/event/option/Series.html" 283 | ] 284 | }, 285 | { 286 | "cell_type": "code", 287 | "execution_count": null, 288 | "metadata": {}, 289 | "outputs": [], 290 | "source": [ 291 | "series_sub = endpoint.create_subscription('Series').add_symbols(symbols)" 292 | ] 293 | }, 294 | { 295 | "cell_type": "code", 296 | "execution_count": null, 297 | "metadata": {}, 298 | "outputs": [], 299 | "source": [ 300 | "series_sub.get_event_handler().get_dataframe().head(3)" 301 | ] 302 | }, 303 | { 304 | "cell_type": "markdown", 305 | "metadata": {}, 306 | "source": [ 307 | "### Option related event types (Greeks, TheoPrice)\n", 308 | "Following event types (Greeks, TheoPrice) deal with options. One of the properties of options is expiration, which affects the symbol name in DxFeed symbology. To make this example long-living, we will get a relevant option symbol from the DxFeed IPF service.\n", 309 | "\n", 310 | "IPF stands for Instrument Profile Format for a normalized view of dxFeed instrument metadata. \n", 311 | "You can read more about it at kb.dxfeed.com:\n", 312 | "https://kb.dxfeed.com/display/DS/Instrument+Profile+Format+Overview\n", 313 | "\n", 314 | "https://kb.dxfeed.com/display/DS/How+to+Request+IPF" 315 | ] 316 | }, 317 | { 318 | "cell_type": "code", 319 | "execution_count": null, 320 | "metadata": {}, 321 | "outputs": [], 322 | "source": [ 323 | "import requests\n", 324 | "url = 'https://tools.dxfeed.com/ipf?TYPE=OPTION&UNDERLYING=AAPL&limit=1'\n", 325 | "r = requests.get(url, auth=('demo', 'demo'))\n", 326 | "option_symbol = r.text.split('\\n')[1].split(',')[1]\n", 327 | "print(option_symbol)" 328 | ] 329 | }, 330 | { 331 | "cell_type": "markdown", 332 | "metadata": {}, 333 | "source": [ 334 | "### Greeks\n", 335 | "Details: https://kb.dxfeed.com/display/DS/dxFeed+API+Market+Events#dxFeedAPIMarketEvents-Greeks" 336 | ] 337 | }, 338 | { 339 | "cell_type": "code", 340 | "execution_count": null, 341 | "metadata": {}, 342 | "outputs": [], 343 | "source": [ 344 | "greek_sub = endpoint.create_subscription('Greeks').add_symbols([option_symbol])" 345 | ] 346 | }, 347 | { 348 | "cell_type": "code", 349 | "execution_count": null, 350 | "metadata": {}, 351 | "outputs": [], 352 | "source": [ 353 | "greek_sub.get_event_handler().get_dataframe().head(3)" 354 | ] 355 | }, 356 | { 357 | "cell_type": "markdown", 358 | "metadata": {}, 359 | "source": [ 360 | "### TheoPrice\n", 361 | "Details: https://kb.dxfeed.com/display/DS/dxFeed+API+Market+Events#dxFeedAPIMarketEvents-TheoPrice" 362 | ] 363 | }, 364 | { 365 | "cell_type": "code", 366 | "execution_count": null, 367 | "metadata": {}, 368 | "outputs": [], 369 | "source": [ 370 | "theo_sub = endpoint.create_subscription('TheoPrice').add_symbols([option_symbol])" 371 | ] 372 | }, 373 | { 374 | "cell_type": "code", 375 | "execution_count": null, 376 | "metadata": {}, 377 | "outputs": [], 378 | "source": [ 379 | "theo_sub.get_event_handler().get_dataframe().head(3)" 380 | ] 381 | }, 382 | { 383 | "cell_type": "markdown", 384 | "metadata": {}, 385 | "source": [ 386 | "### Close connection" 387 | ] 388 | }, 389 | { 390 | "cell_type": "code", 391 | "execution_count": null, 392 | "metadata": {}, 393 | "outputs": [], 394 | "source": [ 395 | "endpoint.close_connection()\n", 396 | "print(f'Connection status: {endpoint.connection_status}')" 397 | ] 398 | } 399 | ], 400 | "metadata": { 401 | "kernelspec": { 402 | "display_name": "Python 3", 403 | "language": "python", 404 | "name": "python3" 405 | }, 406 | "language_info": { 407 | "codemirror_mode": { 408 | "name": "ipython", 409 | "version": 3 410 | }, 411 | "file_extension": ".py", 412 | "mimetype": "text/x-python", 413 | "name": "python", 414 | "nbconvert_exporter": "python", 415 | "pygments_lexer": "ipython3", 416 | "version": "3.7.3" 417 | } 418 | }, 419 | "nbformat": 4, 420 | "nbformat_minor": 4 421 | } 422 | -------------------------------------------------------------------------------- /examples/CustomHandlerExample.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Quick start: Custom Handler Example\n", 8 | "\n", 9 | "Here we will look into custom event handler implementation. Unlike the Basic Usage example, we will not pay much attention to anything behind the handler.\n", 10 | "\n", 11 | "Handler is a special object inherited from dxfeed.EventHandler class. It is attached to the Subscription. When an event comes, internal structures call the update method of your handler with event as a parameter. event is a list with data specific to each event type. For example, for the Trade event type: 'Symbol', 'Price', 'ExchangeCode', 'Size', 'Tick', 'Change', 'DayVolume', 'Time', 'IsETH'. More info here: https://kb.dxfeed.com/display/DS/dxFeed+API+Market+Events\n", 12 | "\n", 13 | "After adding symbols or attaching a default listener (what is actually done implicitly in the first case) list of one-word descriptions of event data is stored in columns field of your handler object, attached to Subscription.\n", 14 | "\n", 15 | "In this example, we will implement the event handler that prints price and volume changes for candle ask events. It also prints average volume change for the last 10 ask events.\n" 16 | ] 17 | }, 18 | { 19 | "cell_type": "markdown", 20 | "metadata": {}, 21 | "source": [ 22 | "### Import package" 23 | ] 24 | }, 25 | { 26 | "cell_type": "code", 27 | "execution_count": null, 28 | "metadata": {}, 29 | "outputs": [], 30 | "source": [ 31 | "import dxfeed as dx\n", 32 | "from dxfeed.core.utils.data_class import DequeWithLock # custom deque with thread lock\n", 33 | "from datetime import datetime # for timed subscription\n", 34 | "from dateutil.relativedelta import relativedelta\n", 35 | "import numpy as np" 36 | ] 37 | }, 38 | { 39 | "cell_type": "markdown", 40 | "metadata": {}, 41 | "source": [ 42 | "### Configure and create connection with Endpoint class\n", 43 | "Create instance of Endpoint class which will connect provided address. " 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "endpoint = dx.Endpoint('demo.dxfeed.com:7300')" 53 | ] 54 | }, 55 | { 56 | "cell_type": "markdown", 57 | "metadata": {}, 58 | "source": [ 59 | "### Configure and create subscription\n", 60 | "You should specify event type. For timed subscription (conflated stream) you should also provide time to start subscription from." 61 | ] 62 | }, 63 | { 64 | "cell_type": "code", 65 | "execution_count": null, 66 | "metadata": {}, 67 | "outputs": [], 68 | "source": [ 69 | "candle_sub = endpoint.create_subscription('Candle', date_time=datetime.now() - relativedelta(days=3))" 70 | ] 71 | }, 72 | { 73 | "cell_type": "code", 74 | "execution_count": null, 75 | "metadata": {}, 76 | "outputs": [], 77 | "source": [ 78 | "class DiffHandler(dx.EventHandler):\n", 79 | " def __init__(self):\n", 80 | " self.__prev_open = None\n", 81 | " self.__prev_high = None\n", 82 | " self.__prev_low = None\n", 83 | " self.__prev_close = None\n", 84 | " self.__prev_volume = None\n", 85 | " self.volume_changes = DequeWithLock()\n", 86 | " self.counter = 0\n", 87 | " \n", 88 | " def update(self, events):\n", 89 | " for event in events:\n", 90 | " if not np.isnan(event.ask_volume): # AskVolume not nan\n", 91 | " self.counter += 1\n", 92 | " print(f'Symbol: {event.symbol}')\n", 93 | " if self.counter == 1:\n", 94 | " self.__prev_open = event.open\n", 95 | " self.__prev_high = event.high\n", 96 | " self.__prev_low = event.low\n", 97 | " self.__prev_close = event.close\n", 98 | " self.__prev_volume = event.ask_volume\n", 99 | " print('First event processed')\n", 100 | " print('-------------------')\n", 101 | " else:\n", 102 | " print(f'Open changed by: {event.open - self.__prev_open}')\n", 103 | " self.__prev_open = event.open\n", 104 | " print(f'High changed by: {event.high - self.__prev_high}')\n", 105 | " self.__prev_high = event.high\n", 106 | " print(f'Open changed by: {event.low - self.__prev_low}')\n", 107 | " self.__prev_low = event.low\n", 108 | " print(f'Close changed by: {event.close - self.__prev_close}')\n", 109 | " self.__prev_close = event.close\n", 110 | " # Volume logic\n", 111 | " vol_change = event.ask_volume - self.__prev_volume\n", 112 | " self.volume_changes.safe_append(vol_change)\n", 113 | " print(f'Volume changed by: {vol_change}, from {self.__prev_volume}, to {event.ask_volume}')\n", 114 | " self.__prev_volume = event.ask_volume\n", 115 | " print(f'Ask events prcessed: {self.counter}')\n", 116 | " print('-------------------')\n", 117 | " if self.counter % 10 == 0:\n", 118 | " print(f'Average volume change for 10 past ask events is: {sum(self.volume_changes) / len(self.volume_changes)}')\n", 119 | " self.volume_changes.clear()\n", 120 | " print('-------------------')" 121 | ] 122 | }, 123 | { 124 | "cell_type": "markdown", 125 | "metadata": {}, 126 | "source": [ 127 | "For Candle event type along with base symbol, you should specify an aggregation period. You can also set price type. More details: https://kb.dxfeed.com/display/DS/REST+API#RESTAPI-Candlesymbols" 128 | ] 129 | }, 130 | { 131 | "cell_type": "code", 132 | "execution_count": null, 133 | "metadata": {}, 134 | "outputs": [], 135 | "source": [ 136 | "handler = DiffHandler()\n", 137 | "candle_sub.set_event_handler(handler).add_symbols(['AAPL{=d}']);" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "metadata": {}, 143 | "source": [ 144 | "### Close subscription" 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": null, 150 | "metadata": {}, 151 | "outputs": [], 152 | "source": [ 153 | "candle_sub.close_subscription()" 154 | ] 155 | }, 156 | { 157 | "cell_type": "markdown", 158 | "metadata": {}, 159 | "source": [ 160 | "### Close connection" 161 | ] 162 | }, 163 | { 164 | "cell_type": "code", 165 | "execution_count": null, 166 | "metadata": {}, 167 | "outputs": [], 168 | "source": [ 169 | "endpoint.close_connection()\n", 170 | "print(f'Connection status: {endpoint.connection_status}')" 171 | ] 172 | } 173 | ], 174 | "metadata": { 175 | "kernelspec": { 176 | "display_name": "Python 3", 177 | "language": "python", 178 | "name": "python3" 179 | }, 180 | "language_info": { 181 | "codemirror_mode": { 182 | "name": "ipython", 183 | "version": 3 184 | }, 185 | "file_extension": ".py", 186 | "mimetype": "text/x-python", 187 | "name": "python", 188 | "nbconvert_exporter": "python", 189 | "pygments_lexer": "ipython3", 190 | "version": "3.7.3" 191 | } 192 | }, 193 | "nbformat": 4, 194 | "nbformat_minor": 4 195 | } 196 | -------------------------------------------------------------------------------- /examples/Low_level_API/BasicAndTimedSubscription.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Basic usage: Create basic and timed subscription\n", 8 | "\n", 9 | "This tutorial is about the Cython level of the package, which connects Python and C API. Though high-level Python classes provide the necessary functionality safely and conveniently, the user is not restricted to use low-level functions. " 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "### Import core functions\n", 17 | "Here we deal with low level C styled api, so the import is slightly differ." 18 | ] 19 | }, 20 | { 21 | "cell_type": "code", 22 | "execution_count": null, 23 | "metadata": {}, 24 | "outputs": [], 25 | "source": [ 26 | "from dxfeed.core import DXFeedPy as dxc\n", 27 | "from dxfeed.core.utils.handler import DefaultHandler\n", 28 | "from datetime import datetime # for timed suscription\n", 29 | "from dateutil.relativedelta import relativedelta" 30 | ] 31 | }, 32 | { 33 | "cell_type": "markdown", 34 | "metadata": {}, 35 | "source": [ 36 | "### Create connection\n", 37 | "There are two ways at the moment to create connection: with token or with specifying connection address. Here we use the latter for simplicity. " 38 | ] 39 | }, 40 | { 41 | "cell_type": "code", 42 | "execution_count": null, 43 | "metadata": {}, 44 | "outputs": [], 45 | "source": [ 46 | "con = dxc.dxf_create_connection('demo.dxfeed.com:7300')" 47 | ] 48 | }, 49 | { 50 | "cell_type": "markdown", 51 | "metadata": {}, 52 | "source": [ 53 | "### Create subscription\n", 54 | "There are two types of subscriptions: ordinary for delivering stream data as-is and timed for conflated data. Except type of subscription you should provide type of events you want to get. Note: some event types, e.g. Candle, support only timed subscription." 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": null, 60 | "metadata": {}, 61 | "outputs": [], 62 | "source": [ 63 | "sub = dxc.dxf_create_subscription(con, 'Trade')\n", 64 | "sub_timed = dxc.dxf_create_subscription_timed(con, 'Candle', int((datetime.now() - relativedelta(days=3)).timestamp()))" 65 | ] 66 | }, 67 | { 68 | "cell_type": "markdown", 69 | "metadata": {}, 70 | "source": [ 71 | "### Attach event handler\n", 72 | "To process incoming data you have to define define `update(event)` method in your EventHandler child class. Or you may use DefaultHandler which stores upcoming data in deque of the length 100k. In this example we choose the latter." 73 | ] 74 | }, 75 | { 76 | "cell_type": "code", 77 | "execution_count": null, 78 | "metadata": {}, 79 | "outputs": [], 80 | "source": [ 81 | "trade_handler = DefaultHandler()\n", 82 | "candle_handler = DefaultHandler()" 83 | ] 84 | }, 85 | { 86 | "cell_type": "code", 87 | "execution_count": null, 88 | "metadata": {}, 89 | "outputs": [], 90 | "source": [ 91 | "sub.set_event_handler(trade_handler)\n", 92 | "sub_timed.set_event_handler(candle_handler)" 93 | ] 94 | }, 95 | { 96 | "cell_type": "markdown", 97 | "metadata": {}, 98 | "source": [ 99 | "### Attach listener\n", 100 | "A special function that processes incoming on the C level events should be initialized. There are default ones for each event type." 101 | ] 102 | }, 103 | { 104 | "cell_type": "code", 105 | "execution_count": null, 106 | "metadata": {}, 107 | "outputs": [], 108 | "source": [ 109 | "dxc.dxf_attach_listener(sub)\n", 110 | "dxc.dxf_attach_listener(sub_timed)" 111 | ] 112 | }, 113 | { 114 | "cell_type": "markdown", 115 | "metadata": {}, 116 | "source": [ 117 | "### Add tickers\n", 118 | "Symbols that will be processed should be defined. For Candle event type along with base symbol, you should specify an aggregation period. You can also set price type. More details: https://kb.dxfeed.com/display/DS/REST+API#RESTAPI-Candlesymbols." 119 | ] 120 | }, 121 | { 122 | "cell_type": "code", 123 | "execution_count": null, 124 | "metadata": {}, 125 | "outputs": [], 126 | "source": [ 127 | "dxc.dxf_add_symbols(sub, ['AAPL', 'MSFT'])\n", 128 | "dxc.dxf_add_symbols(sub_timed, ['AAPL{=d}', 'MSFT{=d}'])" 129 | ] 130 | }, 131 | { 132 | "cell_type": "markdown", 133 | "metadata": {}, 134 | "source": [ 135 | "### Access data\n", 136 | "The DefaultHandler class has `get_list()` and `get_dataframe()` methods to access the data." 137 | ] 138 | }, 139 | { 140 | "cell_type": "code", 141 | "execution_count": null, 142 | "metadata": {}, 143 | "outputs": [], 144 | "source": [ 145 | "trade_handler.get_list()[-5:]" 146 | ] 147 | }, 148 | { 149 | "cell_type": "code", 150 | "execution_count": null, 151 | "metadata": {}, 152 | "outputs": [], 153 | "source": [ 154 | "candle_handler.get_dataframe().head(3)" 155 | ] 156 | }, 157 | { 158 | "cell_type": "markdown", 159 | "metadata": {}, 160 | "source": [ 161 | "### Detach listener\n", 162 | "When you are no longer interested in recieving data detach the listener" 163 | ] 164 | }, 165 | { 166 | "cell_type": "code", 167 | "execution_count": null, 168 | "metadata": {}, 169 | "outputs": [], 170 | "source": [ 171 | "dxc.dxf_detach_listener(sub)\n", 172 | "dxc.dxf_detach_listener(sub_timed)" 173 | ] 174 | }, 175 | { 176 | "cell_type": "markdown", 177 | "metadata": {}, 178 | "source": [ 179 | "### Close connection" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": null, 185 | "metadata": {}, 186 | "outputs": [], 187 | "source": [ 188 | "dxc.dxf_close_connection(con)" 189 | ] 190 | }, 191 | { 192 | "cell_type": "markdown", 193 | "metadata": {}, 194 | "source": [ 195 | "### Transform data to pandas DataFrame" 196 | ] 197 | }, 198 | { 199 | "cell_type": "code", 200 | "execution_count": null, 201 | "metadata": {}, 202 | "outputs": [], 203 | "source": [ 204 | "df1 = trade_handler.get_dataframe()\n", 205 | "df1.head(3)" 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": null, 211 | "metadata": {}, 212 | "outputs": [], 213 | "source": [ 214 | "df2 = candle_handler.get_dataframe()\n", 215 | "df2.head(3)" 216 | ] 217 | } 218 | ], 219 | "metadata": { 220 | "kernelspec": { 221 | "display_name": "Python 3", 222 | "language": "python", 223 | "name": "python3" 224 | }, 225 | "language_info": { 226 | "codemirror_mode": { 227 | "name": "ipython", 228 | "version": 3 229 | }, 230 | "file_extension": ".py", 231 | "mimetype": "text/x-python", 232 | "name": "python", 233 | "nbconvert_exporter": "python", 234 | "pygments_lexer": "ipython3", 235 | "version": "3.7.3" 236 | } 237 | }, 238 | "nbformat": 4, 239 | "nbformat_minor": 4 240 | } 241 | -------------------------------------------------------------------------------- /examples/Low_level_API/CustomListenerExample.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Custom listener\n", 8 | "\n", 9 | "Since dxfeed v0.4.0, there is another way to implement the logic for processing incoming events (see Custom Event Handler example). \n", 10 | "\n", 11 | "This tutorial is for cases that are not covered by previous tutorials.\n", 12 | "\n", 13 | "**Note:** this notebook uses built-in Jupyter magic functions (details: https://ipython.readthedocs.io/en/7.15.0/interactive/magics.html) which start with `%` or `%%` sign. These function allows us to use only Jupyter Notebook for the demostration. You are free to use any IDE/editor without magics." 14 | ] 15 | }, 16 | { 17 | "cell_type": "markdown", 18 | "metadata": {}, 19 | "source": [ 20 | "### Pipeline\n", 21 | "1. Create cython package\n", 22 | "2. Build\n", 23 | "3. Install\n", 24 | "4. Import\n", 25 | "5. Use!" 26 | ] 27 | }, 28 | { 29 | "cell_type": "markdown", 30 | "metadata": {}, 31 | "source": [ 32 | "### Create cython package\n", 33 | "**First of all let's create special directory for custom listener package**" 34 | ] 35 | }, 36 | { 37 | "cell_type": "code", 38 | "execution_count": null, 39 | "metadata": {}, 40 | "outputs": [], 41 | "source": [ 42 | "%%bash\n", 43 | "rm custom_listener -rf\n", 44 | "mkdir custom_listener" 45 | ] 46 | }, 47 | { 48 | "cell_type": "code", 49 | "execution_count": null, 50 | "metadata": {}, 51 | "outputs": [], 52 | "source": [ 53 | "%cd custom_listener" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "#### Create .pyx file with the whole logic\n", 61 | "\n", 62 | "Here we will write listener for Trade event type that will store only price and ticker" 63 | ] 64 | }, 65 | { 66 | "cell_type": "code", 67 | "execution_count": null, 68 | "metadata": {}, 69 | "outputs": [], 70 | "source": [ 71 | "%%writefile cust.pyx\n", 72 | "from dxfeed.core.listeners.listener cimport *\n", 73 | "from dxfeed.core.utils.helpers cimport unicode_from_dxf_const_string_t\n", 74 | "from dxfeed.core.utils.handler cimport EventHandler\n", 75 | "\n", 76 | "cdef void trade_custom_listener(int event_type,\n", 77 | " dxf_const_string_t symbol_name,\n", 78 | " const dxf_event_data_t*data,\n", 79 | " int data_count, void*user_data) nogil:\n", 80 | " cdef dxf_trade_t*trades = data\n", 81 | " with gil:\n", 82 | " py_data = user_data\n", 83 | "\n", 84 | " for i in range(data_count):\n", 85 | " py_data.cython_internal_update_method([unicode_from_dxf_const_string_t(symbol_name),\n", 86 | " trades[i].price])\n", 87 | "\n", 88 | "tc = FuncWrapper.make_from_ptr(trade_custom_listener)" 89 | ] 90 | }, 91 | { 92 | "cell_type": "code", 93 | "execution_count": null, 94 | "metadata": {}, 95 | "outputs": [], 96 | "source": [ 97 | "!ls" 98 | ] 99 | }, 100 | { 101 | "cell_type": "markdown", 102 | "metadata": {}, 103 | "source": [ 104 | "- Line 2 imports all type definitions and function wrapper from installed dxfeed package\n", 105 | "- Line 3 imports helper function `unicode_from_dxf_const_string_t`\n", 106 | "- Line 4 import EventHandler class\n", 107 | "- Lines 6-16 stand for listener logic\n", 108 | "- nogil and with gil in lines 9 and 11 are important to prevent data corruption. More details [stackoverflow](https://stackoverflow.com/questions/57805481/)\n", 109 | "- Line 10 converts the data to trades data structure. **It is important to know what data structure has each event. This information can be found in EventData.pxd in the dxfeed package folder**\n", 110 | "- Line 12 stands for casting user data which is provided by subscription\n", 111 | "- Lines 14-16 we just append price and symbol to subscription dict\n", 112 | "- Line 18, here we wrap function to have access to it from python" 113 | ] 114 | }, 115 | { 116 | "cell_type": "markdown", 117 | "metadata": {}, 118 | "source": [ 119 | "#### Create setup.py to build the binary file" 120 | ] 121 | }, 122 | { 123 | "cell_type": "code", 124 | "execution_count": null, 125 | "metadata": {}, 126 | "outputs": [], 127 | "source": [ 128 | "%%writefile setup.py\n", 129 | "from Cython.Build import cythonize\n", 130 | "from setuptools import setup, Extension\n", 131 | "from dxfeed.core.utils.helpers import get_include\n", 132 | "\n", 133 | "ext = Extension(name=\"cust\",\n", 134 | " sources=[\"cust.pyx\"],\n", 135 | " include_dirs=get_include()\n", 136 | " )\n", 137 | "\n", 138 | "setup(\n", 139 | " ext_modules=cythonize([ext], language_level=3)\n", 140 | ")" 141 | ] 142 | }, 143 | { 144 | "cell_type": "markdown", 145 | "metadata": {}, 146 | "source": [ 147 | "- Line 4 imports dxfeed to get access to `get_include` function, which provide paths to .pxd and .h header files" 148 | ] 149 | }, 150 | { 151 | "cell_type": "markdown", 152 | "metadata": {}, 153 | "source": [ 154 | "#### Build the binary file" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": null, 160 | "metadata": {}, 161 | "outputs": [], 162 | "source": [ 163 | "!python setup.py build_ext --inplace" 164 | ] 165 | }, 166 | { 167 | "cell_type": "code", 168 | "execution_count": null, 169 | "metadata": {}, 170 | "outputs": [], 171 | "source": [ 172 | "!ls" 173 | ] 174 | }, 175 | { 176 | "cell_type": "markdown", 177 | "metadata": {}, 178 | "source": [ 179 | "### Import necessary modules" 180 | ] 181 | }, 182 | { 183 | "cell_type": "code", 184 | "execution_count": null, 185 | "metadata": {}, 186 | "outputs": [], 187 | "source": [ 188 | "import cust\n", 189 | "from dxfeed.core import DXFeedPy as dxc\n", 190 | "from dxfeed.core.utils.handler import EventHandler" 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": {}, 196 | "source": [ 197 | "### Create Custom Event Handler\n", 198 | "See Custom Event Handler tutorial for more details" 199 | ] 200 | }, 201 | { 202 | "cell_type": "code", 203 | "execution_count": null, 204 | "metadata": {}, 205 | "outputs": [], 206 | "source": [ 207 | "class CustomHandler(EventHandler):\n", 208 | " def __init__(self):\n", 209 | " self.data = list()\n", 210 | " \n", 211 | " def update(self, event):\n", 212 | " self.data.append(event)\n", 213 | " \n", 214 | " def get_data(self):\n", 215 | " return self.data" 216 | ] 217 | }, 218 | { 219 | "cell_type": "code", 220 | "execution_count": null, 221 | "metadata": {}, 222 | "outputs": [], 223 | "source": [ 224 | "con = dxc.dxf_create_connection()\n", 225 | "sub = dxc.dxf_create_subscription(con, 'Trade')" 226 | ] 227 | }, 228 | { 229 | "cell_type": "markdown", 230 | "metadata": {}, 231 | "source": [ 232 | "Attach custom handler" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": null, 238 | "metadata": {}, 239 | "outputs": [], 240 | "source": [ 241 | "handler = CustomHandler()\n", 242 | "handler.columns = ['Symbol', 'Price']\n", 243 | "sub.set_event_handler(handler)" 244 | ] 245 | }, 246 | { 247 | "cell_type": "markdown", 248 | "metadata": {}, 249 | "source": [ 250 | "Attach custom listener" 251 | ] 252 | }, 253 | { 254 | "cell_type": "code", 255 | "execution_count": null, 256 | "metadata": {}, 257 | "outputs": [], 258 | "source": [ 259 | "dxc.dxf_attach_custom_listener(sub, cust.tc)\n", 260 | "dxc.dxf_add_symbols(sub, ['AAPL', 'MSFT'])" 261 | ] 262 | }, 263 | { 264 | "cell_type": "markdown", 265 | "metadata": {}, 266 | "source": [ 267 | "Get data" 268 | ] 269 | }, 270 | { 271 | "cell_type": "code", 272 | "execution_count": null, 273 | "metadata": {}, 274 | "outputs": [], 275 | "source": [ 276 | "handler.get_data()[-3:]" 277 | ] 278 | }, 279 | { 280 | "cell_type": "code", 281 | "execution_count": null, 282 | "metadata": {}, 283 | "outputs": [], 284 | "source": [ 285 | "dxc.dxf_detach_listener(sub)" 286 | ] 287 | } 288 | ], 289 | "metadata": { 290 | "kernelspec": { 291 | "display_name": "Python 3", 292 | "language": "python", 293 | "name": "python3" 294 | }, 295 | "language_info": { 296 | "codemirror_mode": { 297 | "name": "ipython", 298 | "version": 3 299 | }, 300 | "file_extension": ".py", 301 | "mimetype": "text/x-python", 302 | "name": "python", 303 | "nbconvert_exporter": "python", 304 | "pygments_lexer": "ipython3", 305 | "version": "3.7.3" 306 | } 307 | }, 308 | "nbformat": 4, 309 | "nbformat_minor": 2 310 | } 311 | -------------------------------------------------------------------------------- /examples/SubscriptionExample.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "## Quick start: Subscription Example" 8 | ] 9 | }, 10 | { 11 | "cell_type": "markdown", 12 | "metadata": {}, 13 | "source": [ 14 | "There are three levels in the dxfeed package. The lowest is the C API library, the highest is Python wrapper classes. Cython level in the middle aims to connect these two. Here we are going to look into Python level.\n", 15 | "\n", 16 | "Python level, in its turn, mainly consists of three class types:\n", 17 | "\n", 18 | "1. Endpoint\n", 19 | "2. Subscription\n", 20 | "3. EventHandler\n", 21 | "\n", 22 | "\n", 23 | "The **Endpoint** is responsible for connection management and creating dependent classes, for\n", 24 | "example Subscription. One Endpoint may have several different\n", 25 | "Subscriptions, but each Subscription is related to one Endpoint.\n", 26 | "\n", 27 | "**Subscription** class sets the type of subscription (stream or timed), the type of\n", 28 | "events (e.g. Trade, Candle), etc.\n", 29 | "\n", 30 | "After you specified the data you want to receive, you have to specify\n", 31 | "how to process upcoming events. This is where the **EventHandler** class and\n", 32 | "its children come into play. Every time an event arrives Cython event\n", 33 | "listener will call ``self.update(event)`` method. You have to inherit\n", 34 | "from the EventHandler class and redefine the update method. Or you may\n", 35 | "use DefaultHandler which stores upcoming data in deque of the length\n", 36 | "100k." 37 | ] 38 | }, 39 | { 40 | "cell_type": "markdown", 41 | "metadata": {}, 42 | "source": [ 43 | "### Import package" 44 | ] 45 | }, 46 | { 47 | "cell_type": "code", 48 | "execution_count": null, 49 | "metadata": {}, 50 | "outputs": [], 51 | "source": [ 52 | "import dxfeed as dx\n", 53 | "from datetime import datetime # for timed subscription" 54 | ] 55 | }, 56 | { 57 | "cell_type": "markdown", 58 | "metadata": {}, 59 | "source": [ 60 | "### Configure and create connection with Endpoint class\n", 61 | "Create instance of Endpoint class which will connect provided address. " 62 | ] 63 | }, 64 | { 65 | "cell_type": "code", 66 | "execution_count": null, 67 | "metadata": {}, 68 | "outputs": [], 69 | "source": [ 70 | "endpoint = dx.Endpoint('demo.dxfeed.com:7300')" 71 | ] 72 | }, 73 | { 74 | "cell_type": "markdown", 75 | "metadata": {}, 76 | "source": [ 77 | "Endpoint instance contains information about the connection, e.g. connection address or status" 78 | ] 79 | }, 80 | { 81 | "cell_type": "code", 82 | "execution_count": null, 83 | "metadata": {}, 84 | "outputs": [], 85 | "source": [ 86 | "print(f'Connected address: {endpoint.address}')\n", 87 | "print(f'Connection status: {endpoint.connection_status}')" 88 | ] 89 | }, 90 | { 91 | "cell_type": "markdown", 92 | "metadata": {}, 93 | "source": [ 94 | "### Configure and create subscription\n", 95 | "You should specify event type. For timed subscription (conflated stream) you should also provide time to start subscription from." 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": null, 101 | "metadata": {}, 102 | "outputs": [], 103 | "source": [ 104 | "trade_sub = endpoint.create_subscription('Trade')" 105 | ] 106 | }, 107 | { 108 | "cell_type": "markdown", 109 | "metadata": {}, 110 | "source": [ 111 | "**Set event handler** - class that process incoming events. Here we use default one" 112 | ] 113 | }, 114 | { 115 | "cell_type": "code", 116 | "execution_count": null, 117 | "metadata": {}, 118 | "outputs": [], 119 | "source": [ 120 | "trade_handler = dx.DefaultHandler()\n", 121 | "trade_sub.set_event_handler(trade_handler);" 122 | ] 123 | }, 124 | { 125 | "cell_type": "markdown", 126 | "metadata": {}, 127 | "source": [ 128 | "**Add tikers** you want to recieve events for" 129 | ] 130 | }, 131 | { 132 | "cell_type": "code", 133 | "execution_count": null, 134 | "metadata": {}, 135 | "outputs": [], 136 | "source": [ 137 | "trade_sub = trade_sub.add_symbols(['C', 'IBM'])" 138 | ] 139 | }, 140 | { 141 | "cell_type": "markdown", 142 | "metadata": {}, 143 | "source": [ 144 | "For timed subscription you should provide either datetime object or string. String might be incomlete, in this case you will get warning with how your provided date parsed automatically. For Candle event type along with base symbol, you should specify an aggregation period. You can also set price type. More details: https://kb.dxfeed.com/display/DS/REST+API#RESTAPI-Candlesymbols." 145 | ] 146 | }, 147 | { 148 | "cell_type": "code", 149 | "execution_count": null, 150 | "metadata": {}, 151 | "outputs": [], 152 | "source": [ 153 | "tns_sub = endpoint.create_subscription('TimeAndSale', date_time=datetime.now()) \\\n", 154 | " .add_symbols(['AMZN'])" 155 | ] 156 | }, 157 | { 158 | "cell_type": "code", 159 | "execution_count": null, 160 | "metadata": {}, 161 | "outputs": [], 162 | "source": [ 163 | "candle_sub = endpoint.create_subscription('Candle', date_time='2020-04-16 13:05')\n", 164 | "candle_sub = candle_sub.add_symbols(['AAPL{=d}', 'MSFT{=d}'])" 165 | ] 166 | }, 167 | { 168 | "cell_type": "markdown", 169 | "metadata": {}, 170 | "source": [ 171 | "**Note**\n", 172 | "Two previous subscriptions attached DefaultHandler implicitly. To retrieve instances just call `get_event_handler()` method." 173 | ] 174 | }, 175 | { 176 | "cell_type": "code", 177 | "execution_count": null, 178 | "metadata": {}, 179 | "outputs": [], 180 | "source": [ 181 | "tns_handler = tns_sub.get_event_handler()\n", 182 | "candle_handler = candle_sub.get_event_handler()" 183 | ] 184 | }, 185 | { 186 | "cell_type": "markdown", 187 | "metadata": {}, 188 | "source": [ 189 | "#### Subscription instance properties" 190 | ] 191 | }, 192 | { 193 | "cell_type": "code", 194 | "execution_count": null, 195 | "metadata": {}, 196 | "outputs": [], 197 | "source": [ 198 | "print(f'TimeAndSale subscription event type: {tns_sub.event_type}')\n", 199 | "print(f'Candle subscription event type: {candle_sub.event_type}')\n", 200 | "print(f'Candle subscription symbols: {candle_sub.symbols}')" 201 | ] 202 | }, 203 | { 204 | "cell_type": "markdown", 205 | "metadata": {}, 206 | "source": [ 207 | "### Access data from DefaultHandler instance\n", 208 | "You can get colums, list or dataframe.\n", 209 | "You are also allowed to write handler that stores no data." 210 | ] 211 | }, 212 | { 213 | "cell_type": "code", 214 | "execution_count": null, 215 | "metadata": { 216 | "scrolled": true 217 | }, 218 | "outputs": [], 219 | "source": [ 220 | "print(f'Trade columns: {trade_handler.columns}')\n", 221 | "print(f'Candle columns: {candle_handler.columns}')" 222 | ] 223 | }, 224 | { 225 | "cell_type": "code", 226 | "execution_count": null, 227 | "metadata": { 228 | "scrolled": true 229 | }, 230 | "outputs": [], 231 | "source": [ 232 | "candle_handler.get_list()[-5:]" 233 | ] 234 | }, 235 | { 236 | "cell_type": "code", 237 | "execution_count": null, 238 | "metadata": {}, 239 | "outputs": [], 240 | "source": [ 241 | "candle_handler.get_dataframe().head(3)" 242 | ] 243 | }, 244 | { 245 | "cell_type": "markdown", 246 | "metadata": {}, 247 | "source": [ 248 | "### Close subscription" 249 | ] 250 | }, 251 | { 252 | "cell_type": "code", 253 | "execution_count": null, 254 | "metadata": {}, 255 | "outputs": [], 256 | "source": [ 257 | "trade_sub.close_subscription()\n", 258 | "tns_sub.close_subscription()\n", 259 | "candle_sub.close_subscription()" 260 | ] 261 | }, 262 | { 263 | "cell_type": "markdown", 264 | "metadata": {}, 265 | "source": [ 266 | "### Close connection" 267 | ] 268 | }, 269 | { 270 | "cell_type": "code", 271 | "execution_count": null, 272 | "metadata": {}, 273 | "outputs": [], 274 | "source": [ 275 | "endpoint.close_connection()\n", 276 | "print(f'Connection status: {endpoint.connection_status}')" 277 | ] 278 | } 279 | ], 280 | "metadata": { 281 | "kernelspec": { 282 | "display_name": "Python 3", 283 | "language": "python", 284 | "name": "python3" 285 | }, 286 | "language_info": { 287 | "codemirror_mode": { 288 | "name": "ipython", 289 | "version": 3 290 | }, 291 | "file_extension": ".py", 292 | "mimetype": "text/x-python", 293 | "name": "python", 294 | "nbconvert_exporter": "python", 295 | "pygments_lexer": "ipython3", 296 | "version": "3.7.3" 297 | } 298 | }, 299 | "nbformat": 4, 300 | "nbformat_minor": 4 301 | } 302 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = 'dxfeed' 3 | version = '0.5.3' 4 | description = 'DXFeed Python API via C API' 5 | authors = ['Index Management Team '] 6 | build = 'build.py' 7 | license = 'MPL-2.0' 8 | readme = 'README.md' 9 | classifiers = ['Development Status :: 4 - Beta', 10 | 'Operating System :: Microsoft :: Windows', 11 | 'Operating System :: POSIX :: Linux', 12 | 'Operating System :: MacOS', 13 | 'Programming Language :: Cython', 14 | 'Programming Language :: Python :: 3 :: Only', 15 | 'Programming Language :: Python :: 3.6', 16 | 'Programming Language :: Python :: 3.7', 17 | 'Programming Language :: Python :: 3.8', 18 | 'Programming Language :: Python :: 3.9', 19 | 'Topic :: Office/Business :: Financial', 20 | 'License :: OSI Approved :: Mozilla Public License 2.0 (MPL 2.0)', 21 | 'Natural Language :: English'] 22 | 23 | include = ['LICENSE'] 24 | exclude = ['dxfeed/tmp*'] 25 | 26 | [tool.poetry.urls] 27 | 'Source code' = 'https://github.com/dxFeed/dxfeed-python-api' 28 | 'Documentation' = 'https://dxfeed.readthedocs.io/en/latest/' 29 | 30 | [tool.poetry.dependencies] 31 | python = '^3.6' 32 | pandas = [{version = '>=1.0,!=1.1.5,<1.2', python = '>=3.6.1,<3.7'}, {version = '^1.0.0', python = '^3.7'}] 33 | toml = '^0.10.2' 34 | # The following dependencies are used for docs generation when installed as extras 35 | # (e.g. pip install nornir[docs]) 36 | # Currently they have to be specified as non-dev dependencies with optional = true 37 | # Refer to: https://github.com/sdispater/poetry/issues/129 38 | # Issue to watch for: https://github.com/python-poetry/poetry/issues/1644 39 | # Disable jupiter from pyproject.toml, because the `poetry install` is extremely with this dependency 40 | #jupyter = { version = '^1.0.0', optional = true } 41 | cython = { version = '^0.29.14', optional = true } 42 | 43 | [tool.poetry.dev-dependencies] 44 | cython = '^0.29.14' 45 | taskipy = '^1.8.1' 46 | pytest = '^6.2.4' 47 | sphinx = '^4.1.2' 48 | sphinx_rtd_theme = '^0.5.2' 49 | pygments = '^2.10' 50 | 51 | [tool.poetry.extras] 52 | docs = ['jupyter', 'cython'] 53 | 54 | [tool.taskipy.tasks] 55 | clear = 'python clear.py' 56 | build = 'python build.py && poetry build' 57 | build_inplace = 'python -c "from build import *; build_extensions()"' 58 | html_docs = 'make html -C docs' 59 | test = 'pytest tests' 60 | post_build = 'task clear' 61 | post_test = 'task clear' 62 | 63 | [build-system] 64 | requires = ['poetry_core>=1.0.0', 'setuptools>=57.4.0', 'cython==0.29.37', 'toml>=0.10.2'] 65 | build-backend = 'poetry.core.masonry.api' 66 | 67 | [build.native-dependencies] 68 | dxfeed_c_api = '8.3.0' 69 | # The URL template for zipped dxFeed C API bundle. Parameters used: 70 | # {version} - The C API version 71 | # {os} - The target OS 72 | dxfeed_c_api_bundle_url_template = 'https://github.com/dxFeed/dxfeed-c-api/releases/download/{version}/dxfeed-c-api-{version}-{os}-no-tls.zip' 73 | -------------------------------------------------------------------------------- /tests/conftest.py: -------------------------------------------------------------------------------- 1 | import sys 2 | from pathlib import Path 3 | sys.path.append(str(Path(__file__).parents[1])) 4 | from build import build_extensions 5 | 6 | 7 | def pytest_sessionstart(session): 8 | build_extensions() 9 | -------------------------------------------------------------------------------- /tests/test_class_utils.py: -------------------------------------------------------------------------------- 1 | from dxfeed.wrappers import class_utils as cu 2 | import pytest 3 | from datetime import datetime 4 | 5 | 6 | class ValueStorage(object): 7 | dt_formats = ['%Y-%m-%d %H:%M:%S.%f', '%Y-%m-%d %H:%M:%S', '%Y/%m/%d %H:%M:%S'] 8 | base_format = '%Y-%m-%d %H:%M:%S.%f' 9 | dt = datetime(2007, 12, 6, 16, 29, 43, 79043) 10 | 11 | 12 | def test_to_iterable_of_strings_with_string(): 13 | symbol = 'AAPL' 14 | actual_symbol = cu.to_iterable(symbol) 15 | assert actual_symbol == [symbol] 16 | 17 | 18 | def test_to_iterable_of_strings_with_iterable(): 19 | symbols = ('AAPL', 'MSFT') 20 | actual_symbols = cu.to_iterable(symbols) 21 | assert actual_symbols == symbols 22 | 23 | 24 | @pytest.mark.xfail 25 | def test_to_iterable_of_strings_with_incorrect_type(): 26 | cu.to_iterable(123) 27 | 28 | 29 | @pytest.mark.parametrize('dt_fmt', ValueStorage.dt_formats) 30 | def test_handle_datetime_from_datetime(dt_fmt): 31 | date_time = datetime.now() 32 | actual_datetime = cu.handle_datetime(date_time, fmt=dt_fmt) 33 | assert date_time == actual_datetime 34 | 35 | 36 | @pytest.mark.parametrize('dt_fmt', ValueStorage.dt_formats) 37 | def test_handle_datetime_with_complete_string_format(dt_fmt): 38 | date_time = ValueStorage.dt.strftime(format=dt_fmt) 39 | expected_date_time = datetime.strptime(date_time, dt_fmt) 40 | actual_datetime = cu.handle_datetime(expected_date_time, fmt=dt_fmt) 41 | assert expected_date_time == actual_datetime 42 | 43 | 44 | @pytest.mark.filterwarnings('ignore::Warning') 45 | @pytest.mark.parametrize('dt_fmt', ValueStorage.dt_formats) 46 | def test_handle_datetime_with_no_seconds_string_format(dt_fmt): 47 | fmt = dt_fmt[:dt_fmt.rfind('%S')-1] 48 | date_time = ValueStorage.dt.strftime(fmt) 49 | actual_datetime = cu.handle_datetime(date_time, fmt=dt_fmt) 50 | expected_datetime = ValueStorage.dt.replace(second=0, microsecond=0) 51 | assert actual_datetime == expected_datetime 52 | 53 | 54 | @pytest.mark.filterwarnings('ignore::Warning') 55 | @pytest.mark.parametrize('dt_fmt', ValueStorage.dt_formats) 56 | def test_handle_datetime_with_no_mins_string_format(dt_fmt): 57 | fmt = dt_fmt[:dt_fmt.rfind('%M')-1] 58 | date_time = ValueStorage.dt.strftime(fmt) 59 | actual_datetime = cu.handle_datetime(date_time, fmt=dt_fmt) 60 | expected_datetime = ValueStorage.dt.replace(minute=0, second=0, microsecond=0) 61 | assert actual_datetime == expected_datetime 62 | 63 | 64 | @pytest.mark.filterwarnings('ignore::Warning') 65 | @pytest.mark.parametrize('dt_fmt', ValueStorage.dt_formats) 66 | def test_handle_datetime_with_no_hours_string_format(dt_fmt): 67 | fmt = dt_fmt[:dt_fmt.rfind('%H')-1] 68 | date_time = ValueStorage.dt.strftime(fmt) 69 | actual_datetime = cu.handle_datetime(date_time, fmt=dt_fmt) 70 | expected_datetime = ValueStorage.dt.replace(hour=0, minute=0, second=0, microsecond=0) 71 | assert actual_datetime == expected_datetime 72 | 73 | 74 | @pytest.mark.xfail 75 | def test_handle_datetime_wrong_type(): 76 | cu.handle_datetime(20201010) 77 | -------------------------------------------------------------------------------- /tests/test_dxfeedpy.py: -------------------------------------------------------------------------------- 1 | import dxfeed.core.DXFeedPy as dxc 2 | from dxfeed.core.utils.handler import DefaultHandler 3 | import pytest 4 | 5 | 6 | class ValueStorage(object): # config 7 | demo_address = 'demo.dxfeed.com:7300' 8 | event_types = ['Trade', 'Quote', 'Summary', 'Profile', 'Order', 'TimeAndSale', 'Candle', 'TradeETH', 'SpreadOrder', 9 | 'Greeks', 'TheoPrice', 'Underlying', 'Series', 'Configuration'] 10 | 11 | 12 | @pytest.fixture(scope='function') 13 | def connection(): 14 | # Setup 15 | con = dxc.dxf_create_connection(ValueStorage.demo_address) 16 | yield con # pass var to test and test itself 17 | # Teardown 18 | dxc.dxf_close_connection(con) 19 | 20 | 21 | def test_connection_status(connection): 22 | exp_status = 'Connected' 23 | act_status = dxc.dxf_get_current_connection_status(connection) 24 | assert exp_status == act_status 25 | 26 | 27 | def test_connection_address(connection): 28 | exp_address = ValueStorage.demo_address 29 | act_address = dxc.dxf_get_current_connected_address(connection) 30 | assert exp_address == act_address 31 | 32 | 33 | @pytest.mark.xfail() 34 | def test_fail_connection_to_wrong_address(): 35 | dxc.dxf_create_connection(address='8.8.8.8') 36 | 37 | 38 | @pytest.mark.xfail() 39 | def test_fail_create_subscription_with_no_connection(): 40 | dxc.dxf_create_subscription() 41 | 42 | 43 | @pytest.mark.xfail() 44 | def test_fail_to_use_subscription_without_connection(connection): 45 | sub = dxc.dxf_create_subscription(cc=connection, event_type='Trade') 46 | dxc.dxf_close_connection(connection) 47 | dxc.dxf_add_symbols(sc=sub, symbols=['AAPL']) 48 | 49 | 50 | @pytest.mark.parametrize('sub_type', ValueStorage.event_types) 51 | def test_subscription_on_correct_types(connection, sub_type): 52 | sub = dxc.dxf_create_subscription(cc=connection, event_type=sub_type) 53 | act_sub_type = dxc.dxf_get_subscription_event_types(sc=sub) 54 | dxc.dxf_close_subscription(sub) 55 | assert act_sub_type == sub_type 56 | 57 | 58 | @pytest.mark.parametrize('sub_type', ValueStorage.event_types) 59 | def test_subscription_timed_on_correct_types(connection, sub_type): 60 | sub = dxc.dxf_create_subscription_timed(cc=connection, event_type=sub_type, time=0) 61 | act_sub_type = dxc.dxf_get_subscription_event_types(sc=sub) 62 | dxc.dxf_close_subscription(sub) 63 | assert act_sub_type == sub_type 64 | 65 | 66 | @pytest.mark.xfail() 67 | def test_subscription_fail_on_incorrect_time_type(connection): 68 | dxc.dxf_create_subscription_timed(cc=connection, event_type='Trade', time=5.2) 69 | 70 | 71 | @pytest.mark.xfail() 72 | def test_subscription_fail_on_incorrect_time_value(connection): 73 | dxc.dxf_create_subscription_timed(cc=connection, event_type='Trade', time=-7) 74 | 75 | 76 | @pytest.mark.xfail() 77 | def test_subscription_fail_on_incorrect_type(connection): 78 | dxc.dxf_create_subscription(cc=connection, event_type='TradeQuote') 79 | 80 | 81 | @pytest.mark.xfail() 82 | def test_subscription_fail_on_incorrect_type(connection): 83 | dxc.dxf_create_subscription_timed(cc=connection, event_type='TradeQuote', time=0) 84 | 85 | 86 | 87 | def test_symbol_addition(connection): 88 | symbols = ['AAPL', 'GOOG'] 89 | sub = dxc.dxf_create_subscription(cc=connection, event_type='Trade') 90 | dxc.dxf_add_symbols(sc=sub, symbols=symbols) 91 | actual_symbols = dxc.dxf_get_symbols(sc=sub) 92 | dxc.dxf_close_subscription(sub) 93 | assert set(symbols) == set(actual_symbols) 94 | 95 | 96 | def test_symbol_deletion(connection): 97 | symbols = ['AAPL', 'GOOG'] 98 | sub = dxc.dxf_create_subscription(cc=connection, event_type='Trade') 99 | dxc.dxf_add_symbols(sc=sub, symbols=symbols) 100 | dxc.dxf_remove_symbols(sc=sub, symbols=['AAPL']) 101 | actual_symbols = dxc.dxf_get_symbols(sc=sub) 102 | dxc.dxf_close_subscription(sub) 103 | assert ['GOOG'] == actual_symbols 104 | 105 | 106 | @pytest.mark.filterwarnings('ignore::UserWarning') 107 | def test_wrong_symbol_types_ignored(connection): 108 | symbols = ['AAPL', 'GOOG'] 109 | sub = dxc.dxf_create_subscription(cc=connection, event_type='Trade') 110 | dxc.dxf_add_symbols(sc=sub, symbols=symbols + [1, 5.0, [], True, {}, ()]) 111 | actual_symbols = dxc.dxf_get_symbols(sc=sub) 112 | dxc.dxf_close_subscription(sub) 113 | assert set(symbols) == set(actual_symbols) 114 | 115 | 116 | def test_symbol_clearing(connection): 117 | symbols = ['AAPL', 'GOOG'] 118 | sub = dxc.dxf_create_subscription(cc=connection, event_type='Trade') 119 | dxc.dxf_add_symbols(sc=sub, symbols=symbols) 120 | dxc.dxf_clear_symbols(sc=sub) 121 | actual_symbols = dxc.dxf_get_symbols(sc=sub) 122 | dxc.dxf_close_subscription(sub) 123 | assert actual_symbols == [] 124 | 125 | 126 | @pytest.mark.parametrize('sub_type', ValueStorage.event_types) 127 | def test_default_event_handler(connection, sub_type): 128 | sub = dxc.dxf_create_subscription(cc=connection, event_type=sub_type) 129 | sub.set_event_handler(DefaultHandler()) 130 | assert isinstance(sub.get_event_handler(), DefaultHandler) 131 | 132 | 133 | def test_weakref(): 134 | con = dxc.ConnectionClass() 135 | sub = dxc.SubscriptionClass() 136 | con.add_weakref(sub) 137 | assert con.get_weakrefs()[0] is sub 138 | 139 | 140 | @pytest.mark.xfail 141 | def test_weakref_fail_on_incorrect_type(): 142 | con = dxc.ConnectionClass() 143 | obj = list() 144 | con.add_weakref(obj) 145 | 146 | 147 | @pytest.mark.filterwarnings('ignore::Warning') 148 | def test_double_event_handler_attachment(connection): 149 | handler1 = DefaultHandler() 150 | handler2 = DefaultHandler() 151 | sub = dxc.dxf_create_subscription(cc=connection, event_type='Trade') 152 | sub.set_event_handler(handler1) 153 | dxc.dxf_attach_listener(sub) 154 | symbols = ['AAPL', 'MSFT'] 155 | dxc.dxf_add_symbols(sub, symbols) 156 | sub.set_event_handler(handler2) 157 | assert sub.get_event_handler() is handler2 158 | assert set(dxc.dxf_get_symbols(sub)) == set(symbols) 159 | -------------------------------------------------------------------------------- /tests/test_endpoint_class.py: -------------------------------------------------------------------------------- 1 | import dxfeed as dx 2 | import pytest 3 | 4 | 5 | class ValueStorage(object): # config 6 | demo_address = 'demo.dxfeed.com:7300' 7 | 8 | 9 | @pytest.fixture(scope='function') 10 | def dxfeed(): 11 | dxf = dx.Endpoint(connection_address=ValueStorage.demo_address) 12 | yield dxf 13 | dxf.close_connection() 14 | 15 | 16 | def test_endpoint_no_arguments(): 17 | dxf = dx.Endpoint() 18 | assert 'Connected' == dxf.connection_status 19 | 20 | 21 | def test_connection_status_property(dxfeed): 22 | assert 'Connected' == dxfeed.connection_status 23 | 24 | 25 | def test_create_endpoint_without_connection(): 26 | dxf = dx.Endpoint(connect=False) 27 | assert 'Not connected' == dxf.connection_status 28 | 29 | 30 | def test_address_property(dxfeed): 31 | assert dxfeed.address == ValueStorage.demo_address 32 | 33 | 34 | def test_closing_connection(dxfeed): 35 | dxfeed.close_connection() 36 | assert 'Not connected' == dxfeed.connection_status 37 | 38 | 39 | @pytest.mark.xfail() 40 | def test_connection_fail_on_incorrect_address(): 41 | dxfeed = dx.Endpoint(connection_address='8.8.8.8') 42 | -------------------------------------------------------------------------------- /tests/test_subscription_class.py: -------------------------------------------------------------------------------- 1 | import dxfeed as dx 2 | import pytest 3 | from datetime import datetime 4 | 5 | 6 | class ValueStorage(object): # config 7 | demo_address = 'demo.dxfeed.com:7300' 8 | event_types = ['Trade', 'Quote', 'Summary', 'Profile', 'Order', 'TimeAndSale', 'Candle', 'TradeETH', 'SpreadOrder', 9 | 'Greeks', 'TheoPrice', 'Underlying', 'Series', 'Configuration'] 10 | symbols = ['AAPL', 'MSFT'] 11 | date_time = datetime.now() 12 | 13 | 14 | @pytest.fixture(scope='function') 15 | def endpoint(): 16 | dxf = dx.Endpoint(connection_address=ValueStorage.demo_address) 17 | yield dxf 18 | dxf.close_connection() 19 | 20 | 21 | @pytest.fixture(scope='function', params=ValueStorage.event_types) 22 | def subscription(endpoint, request): 23 | sub = endpoint.create_subscription(event_type=request.param) 24 | yield sub 25 | sub.close_subscription() 26 | 27 | 28 | @pytest.fixture(scope='function', params=ValueStorage.event_types) 29 | def subscription_timed(endpoint, request): 30 | sub = endpoint.create_subscription(event_type=request.param, date_time=ValueStorage.date_time) 31 | yield sub 32 | sub.close_subscription() 33 | 34 | 35 | def test_subscription_event_type_property(subscription): 36 | assert subscription.event_type in ValueStorage.event_types 37 | 38 | 39 | def test_subscription_timed_event_type_property(subscription_timed): 40 | assert subscription_timed.event_type in ValueStorage.event_types 41 | 42 | 43 | def test_subscription_event_type_property(subscription): 44 | subscription = subscription.add_symbols(ValueStorage.symbols) 45 | assert set(subscription.symbols) == set(ValueStorage.symbols) 46 | 47 | 48 | def test_subscription_timed_event_type_property(subscription_timed): 49 | subscription = subscription_timed.add_symbols(ValueStorage.symbols) 50 | assert set(subscription.symbols) == set(ValueStorage.symbols) 51 | 52 | 53 | def test_remove_symbol(subscription): 54 | subscription = subscription.add_symbols(ValueStorage.symbols) \ 55 | .remove_symbols([ValueStorage.symbols[0]]) 56 | assert set(subscription.symbols) == set(ValueStorage.symbols[1:]) 57 | 58 | 59 | def test_remove_symbol_timed(subscription_timed): 60 | subscription_timed = subscription_timed.add_symbols(ValueStorage.symbols) \ 61 | .remove_symbols([ValueStorage.symbols[0]]) 62 | assert set(subscription_timed.symbols) == set(ValueStorage.symbols[1:]) 63 | 64 | 65 | def test_remove_all_symbols(subscription): 66 | subscription = subscription.add_symbols(ValueStorage.symbols) \ 67 | .remove_symbols() 68 | assert subscription.symbols == [] 69 | 70 | 71 | def test_remove_all_symbols_timed(subscription_timed): 72 | subscription_timed = subscription_timed.add_symbols(ValueStorage.symbols) \ 73 | .remove_symbols() 74 | assert subscription_timed.symbols == [] 75 | 76 | 77 | def test_default_event_handler(endpoint): 78 | sub = endpoint.create_subscription(event_type='Trade').add_symbols(ValueStorage.symbols) 79 | assert isinstance(sub.get_event_handler(), dx.DefaultHandler) 80 | 81 | 82 | def test_default_event_handler_timed(endpoint): 83 | sub = endpoint.create_subscription(event_type='Trade', date_time=ValueStorage.date_time).add_symbols(ValueStorage.symbols) 84 | assert isinstance(sub.get_event_handler(), dx.DefaultHandler) 85 | 86 | 87 | def test_event_handler_setter(endpoint): 88 | handler = dx.EventHandler() 89 | sub = endpoint.create_subscription(event_type='Trade').set_event_handler(handler) 90 | assert sub.get_event_handler() is handler 91 | 92 | 93 | def test_event_handler_setter_timed(endpoint): 94 | handler = dx.EventHandler() 95 | sub = endpoint.create_subscription(event_type='Trade', date_time=ValueStorage.date_time).set_event_handler(handler) 96 | assert sub.get_event_handler() is handler 97 | --------------------------------------------------------------------------------