├── .github ├── CODEOWNERS ├── ISSUE_TEMPLATE │ ├── question.md │ ├── feature_request.md │ └── bug_report.md ├── dependabot.yaml ├── codecov.yml ├── pull_request_template.md ├── SECURITY.md ├── release.yaml ├── workflows │ ├── _pre_commit.yaml │ ├── _build_doc.yaml │ ├── _pypi_publish.yaml │ ├── _pypi_test_publish.yaml │ ├── dependabot_auto_merge.yaml │ ├── _test_spot_public.yaml │ ├── _test_futures_public.yaml │ ├── scorecard.yml │ ├── _codeql.yaml │ ├── _codecov.yaml │ ├── _build.yaml │ ├── _test_spot_private.yaml │ └── _test_futures_private.yaml ├── self-review.md ├── CONTRIBUTING.md └── CODE_OF_CONDUCT.md ├── requirements-dev.txt ├── tests ├── __init__.py ├── cli │ ├── __init__.py │ ├── basic.sh │ └── conftest.py ├── futures │ ├── __init__.py │ ├── helper.py │ ├── test_futures_funding.py │ ├── conftest.py │ ├── test_futures_base_api.py │ └── test_futures_user.py └── spot │ ├── __init__.py │ ├── test_spot_websocket_internals.py │ ├── test_xstocks_basic.py │ ├── test_spot_earn.py │ ├── helper.py │ ├── conftest.py │ ├── test_spot_market.py │ └── test_spot_orderbook.py ├── doc ├── requirements.txt ├── Makefile ├── 08_about │ └── license.rst ├── 05_futures │ ├── websockets.rst │ └── rest.rst ├── 06_exceptions │ └── exceptions.rst ├── 04_spot │ ├── websockets.rst │ └── rest.rst ├── 03_examples │ ├── rest_examples │ │ ├── spot_ws_examples.rst │ │ ├── futures_ws_examples.rst │ │ ├── xstocks_rest_examples.rst │ │ ├── spot_rest_examples.rst │ │ └── futures_rest_examples.rst │ ├── trading_bot_templates │ │ ├── spot_orderbook.rst │ │ ├── spot_bot_templates.rst │ │ └── futures_bot_template.rst │ ├── trading_bot_templates.rst │ ├── rest_example_usage.rst │ └── command_line_interface.rst ├── index.rst ├── 02_getting_started │ └── getting_started.rst ├── 07_issues.rst ├── conf.py ├── links.rst └── 01_introduction.rst ├── MANIFEST.in ├── setup.py ├── CITATION ├── examples ├── README.md ├── xstocks_examples.py ├── spot_orderbook.py ├── futures_ws_examples.py ├── spot_ws_examples.py ├── spot_examples.py └── futures_examples.py ├── src └── kraken │ ├── __init__.py │ ├── utils │ ├── __init__.py │ └── utils.py │ ├── futures │ └── __init__.py │ └── spot │ └── __init__.py ├── .readthedocs.yaml ├── .gitignore ├── .pre-commit-config.yaml ├── del.txt └── Makefile /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @btschwertfeger 2 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | proxy.py 2 | pytest 3 | pytest-asyncio 4 | pytest-cov 5 | pytest-mock 6 | pytest-retry 7 | pytest-timeout 8 | pytest-xdist 9 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/question.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: General question 3 | about: Help us to understand your findings 4 | title: "" 5 | labels: "Question" 6 | assignees: "" 7 | --- 8 | 9 | There are no stupid questions. 10 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | # This file is required for the CI/CD codecov workflow. 8 | -------------------------------------------------------------------------------- /tests/cli/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2024 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | # This file is required for collecting coverage information. 8 | -------------------------------------------------------------------------------- /tests/futures/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | # This file is required for the CI/CD codecov workflow. 8 | -------------------------------------------------------------------------------- /tests/spot/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | # This file is required for collecting coverage information. 8 | -------------------------------------------------------------------------------- /.github/dependabot.yaml: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | version: 2 3 | updates: 4 | - package-ecosystem: github-actions 5 | directory: / 6 | schedule: 7 | interval: monthly 8 | groups: 9 | github-actions: 10 | dependency-type: production 11 | -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | # sphinx-rtd-theme 2 | aiohttp 3 | asyncio>=3.4 4 | click~=8.1 5 | cloup~=3.0 6 | furo 7 | ipython 8 | nbsphinx 9 | orjson 10 | requests~=2.32 11 | setuptools_scm 12 | sphinx<8.2.0 # doesn't work with nbsphinx 13 | sphinx-click 14 | websockets>=14.1 15 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | 8 | include README.md LICENSE pyproject.toml 9 | 10 | graft src/kraken 11 | prune .cache 12 | prune .github 13 | prune doc 14 | prune examples 15 | prune tests 16 | prune venv 17 | prune .venv 18 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | # This file is is required for the documentation build process. 8 | 9 | import setuptools_scm # pylint: disable=unused-import # noqa: F401 10 | from setuptools import setup 11 | 12 | setup() 13 | -------------------------------------------------------------------------------- /CITATION: -------------------------------------------------------------------------------- 1 | cff-version: 1.2.0 2 | message: "If you use this software, please cite it as below." 3 | authors: 4 | - family-names: "Schwertfeger" 5 | given-names: "Benjamin Thomas" 6 | orcid: "https://orcid.org/0000-0001-7664-8434" 7 | title: "python-kraken-sdk" 8 | doi: 10.5281/zenodo.7653259 9 | url: "https://github.com/btschwertfeger/python-kraken-sdk" 10 | keywords: ["Python", "Kraken", "Crypto", "Exchange", "API", "SDK"] 11 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # python-kraken-sdk usage examples 2 | 3 | This directory contains several examples demonstrating how to use the 4 | `python-kraken-sdk`. These examples cover various functionalities such as Spot 5 | trading, Futures trading, and market data retrieval using both REST and 6 | WebSocket APIs. 7 | 8 | Ideal examples of successful running trading algorithms based on the 9 | python-kraken-sdk are listed below: 10 | 11 | - https://github.com/btschwertfeger/infinity-grid 12 | - https://github.com/btschwertfeger/kraken-rebalance-bot 13 | - https://github.com/btschwertfeger/python-kraken-sdk/network/dependents 14 | 15 | For more detailed and up-to-date usage, refer to the unit tests provided in the 16 | main package. 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: "" 5 | labels: "Feature" 6 | assignees: "" 7 | --- 8 | 9 | **Is your feature request related to a problem? Please describe.** 10 | 11 | A clear and concise description of what the problem is. Ex. I'm always 12 | frustrated when [...] 13 | 14 | **Describe the solution you'd like** 15 | 16 | A clear and concise description of what you want to happen. 17 | 18 | **Describe alternatives you've considered** 19 | 20 | A clear and concise description of any alternative solutions or features you've 21 | considered. 22 | 23 | **Additional context** 24 | 25 | Add any other context or screenshots about the feature request here. 26 | -------------------------------------------------------------------------------- /src/kraken/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # https://github.com/btschwertfeger 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | -------------------------------------------------------------------------------- /.github/codecov.yml: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | coverage: 3 | ## Status checks in PR 4 | ## 5 | status: 6 | project: 7 | default: 8 | informational: true 9 | ## Target coverage is the last one 10 | ## 11 | target: auto 12 | ## this allows a 2% drop from the previous base commit coverage 13 | ## 14 | threshold: 2% 15 | patch: 16 | default: 17 | informational: true 18 | 19 | comment: 20 | layout: "reach, diff, flags, files" 21 | behavior: default 22 | require_changes: true # if false: post the comment even if coverage doesn't change 23 | require_base: no # [yes :: must have a base report to post] 24 | require_head: yes # [yes :: must have a head report to post] 25 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | A short summary into the pull request including the reason for that change. 4 | 5 | ## Changes / Features 6 | 7 | What has being changed - provide code snippets, examples, screenshots, lists - 8 | any kind of information hat helps to understand the update. 9 | 10 | **Make sure to address all topics of the [self-review 11 | checklist](https://github.com/btschwertfeger/python-kraken-sdk/blob/master/.github/self-review.md).** 12 | 13 | Closes: Don't forget to link an existing issue for your change. If there is no 14 | open issue for your change - create one to make it more likely that his update 15 | will be accepted: 16 | [python-kraken-sdk/issues/new/choose](https://github.com/btschwertfeger/python-kraken-sdk/issues/new/choose). 17 | -------------------------------------------------------------------------------- /.readthedocs.yaml: -------------------------------------------------------------------------------- 1 | # .readthedocs.yaml 2 | # Read the Docs configuration file 3 | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details 4 | 5 | # Required 6 | version: 2 7 | 8 | # Set the version of Python and other tools you might need 9 | build: 10 | os: ubuntu-22.04 11 | tools: 12 | python: "3.11" 13 | apt_packages: 14 | - pandoc 15 | 16 | # Build documentation in the doc/ directory with Sphinx 17 | sphinx: 18 | configuration: doc/conf.py 19 | 20 | # If using Sphinx, optionally build your docs in additional formats such as PDF 21 | formats: 22 | - pdf 23 | 24 | # Optionally declare the Python requirements required to build your docs 25 | python: 26 | install: 27 | - requirements: doc/requirements.txt 28 | - method: pip 29 | path: . 30 | -------------------------------------------------------------------------------- /doc/Makefile: -------------------------------------------------------------------------------- 1 | # -*- mode: make; coding: utf-8 -*- 2 | #!make 3 | # Minimal makefile for Sphinx documentation 4 | # 5 | 6 | # You can set these variables from the command line, and also 7 | # from the environment for the first two. 8 | SPHINXOPTS ?= 9 | SPHINXBUILD ?= sphinx-build 10 | SOURCEDIR = . 11 | BUILDDIR = _build 12 | 13 | # Put it first so that "make" without argument is like "make help". 14 | help: 15 | @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 16 | 17 | .PHONY: help Makefile 18 | 19 | # Catch-all target: route all unknown targets to Sphinx using the new 20 | # "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). 21 | %: Makefile 22 | @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) 23 | rm $(SOURCEDIR)/03_examples/market_client_example.ipynb 24 | -------------------------------------------------------------------------------- /doc/08_about/license.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | .. _section-license: 20 | 21 | License 22 | ======= 23 | 24 | .. include:: ../../LICENSE 25 | -------------------------------------------------------------------------------- /src/kraken/utils/__init__.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # 3 | # Copyright (C) 2024 Benjamin Thomas Schwertfeger 4 | # https://github.com/btschwertfeger 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | """Module providing utility functions.""" 20 | 21 | from kraken.utils.utils import deprecated 22 | 23 | __all__ = ["deprecated"] 24 | -------------------------------------------------------------------------------- /.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 | **Describe the bug** 10 | A clear and concise description of what the bug is. 11 | 12 | **To Reproduce** 13 | Steps to reproduce the behavior: 14 | 15 | 1. Import '...' 16 | 2. Set inputs '....' 17 | 3. Call method '....' 18 | 4. See error 19 | 20 | **Expected behavior** 21 | A clear and concise description of what you expected to happen. 22 | 23 | **Screenshots and code snippets** 24 | If applicable, add screenshots or code snippets to help explain your problem. 25 | 26 | **Environment (please complete the following information):** 27 | 28 | - OS: [e.g. MacOS Ventura xxx] 29 | - Python [e.g. 3.10, 3.11] 30 | - python-kraken-sdk [e.g. v3.1.2] 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/SECURITY.md: -------------------------------------------------------------------------------- 1 | # Security Policy 2 | 3 | ## Reporting Security Vulnerabilities 4 | 5 | At python-kraken-sdk, we take security seriously. We welcome and encourage 6 | responsible disclosure of security vulnerabilities. If you believe you've found 7 | a security vulnerability within our project, please report it immediately. 8 | 9 | ## How to Report a Security Vulnerability 10 | 11 | To report a security vulnerability, please send an email to 12 | [contact@b-schwertfeger.de](mailto:contact@b-schwertfeger.de) with a detailed 13 | description of the vulnerability. We kindly request that you refrain from 14 | disclosing the vulnerability publicly until we have had an opportunity to 15 | address it. 16 | 17 | ## Our Commitment 18 | 19 | We are committed to promptly addressing and resolving any security 20 | vulnerabilities reported to us. We will investigate all reports and take 21 | appropriate action to protect the security of our users and their data. 22 | -------------------------------------------------------------------------------- /doc/05_futures/websockets.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | Futures Websockets 20 | ================== 21 | 22 | .. autoclass:: kraken.futures.FuturesWSClient 23 | :members: 24 | :show-inheritance: 25 | :inherited-members: 26 | -------------------------------------------------------------------------------- /doc/06_exceptions/exceptions.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | Custom Exceptions 20 | ================= 21 | 22 | The `python-kraken-sdk`_ provides lots of custom exceptions which are listed 23 | below. 24 | 25 | .. automodule:: kraken.exceptions 26 | :members: 27 | :show-inheritance: 28 | :inherited-members: 29 | -------------------------------------------------------------------------------- /doc/04_spot/websockets.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | Spot Websockets 20 | =============== 21 | 22 | .. autoclass:: kraken.spot.SpotWSClient 23 | :members: 24 | :show-inheritance: 25 | :inherited-members: 26 | 27 | .. autoclass:: kraken.spot.SpotOrderBookClient 28 | :members: 29 | :show-inheritance: 30 | :inherited-members: 31 | -------------------------------------------------------------------------------- /.github/release.yaml: -------------------------------------------------------------------------------- 1 | # -*- mode: yaml; coding: utf-8 -*- 2 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 3 | # All rights reserved. 4 | # https://github.com/btschwertfeger 5 | # 6 | # Config file to auto-generate release notes based on 7 | # https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes 8 | 9 | changelog: 10 | exclude: 11 | # labels: 12 | # - ignore-for-release 13 | # authors: 14 | # - john-doe 15 | categories: 16 | - title: Breaking Changes ⚠️ 17 | labels: 18 | - Breaking 19 | - breaking 20 | - title: Implemented Enhancements 🎉 21 | labels: 22 | - Feature 23 | - enhancement 24 | - title: Fixed Bugs 🪲 25 | labels: 26 | - Bug 27 | - bug 28 | - title: Other Changes 📝 29 | labels: 30 | - "*" 31 | exclude: 32 | labels: 33 | - dependencies 34 | - github_actions 35 | - title: Dependencies 👒 36 | labels: 37 | - dependencies 38 | - github_actions 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Important to ignore for setuptools_scm 2 | _version.py 3 | .vscode/ 4 | 5 | # Byte-compiled / optimized / DLL files 6 | __pycache__/ 7 | *.py[cod] 8 | *$py.class 9 | 10 | # C extensions 11 | *.so 12 | 13 | # Distribution / packaging 14 | .Python 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | pip-wheel-metadata/ 28 | share/python-wheels/ 29 | *.egg-info/ 30 | .installed.cfg 31 | *.egg 32 | *.whl 33 | MANIFEST 34 | 35 | # Unit test / coverage reports 36 | .coverage 37 | .coverage.* 38 | .cache 39 | .ruff_cache 40 | coverage.xml 41 | pytest.xml 42 | *.cover 43 | *.py,cover 44 | .pytest_cache/ 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Sphinx documentation 51 | doc/_build/ 52 | 53 | # Jupyter Notebook 54 | .ipynb_checkpoints 55 | 56 | # Environments 57 | .env 58 | .venv 59 | env/ 60 | venv/ 61 | ENV/ 62 | env.bak/ 63 | venv.bak/ 64 | 65 | # mkdocs documentation 66 | /site 67 | 68 | # mypy 69 | .mypy_cache/ 70 | .dmypy.json 71 | dmypy.json 72 | mypy.xml 73 | 74 | # misc 75 | del*.py 76 | del/ 77 | todo.md 78 | .DS_Store 79 | *.csv 80 | *.log 81 | *.zip 82 | uv.lock 83 | -------------------------------------------------------------------------------- /.github/workflows/_pre_commit.yaml: -------------------------------------------------------------------------------- 1 | # -*- mode: yaml; coding: utf-8 -*- 2 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 3 | # All rights reserved. 4 | # https://github.com/btschwertfeger 5 | # 6 | # Template workflow to run pre-commit. 7 | # 8 | 9 | name: Pre-Commit 10 | 11 | on: 12 | workflow_call: 13 | 14 | permissions: 15 | contents: read 16 | 17 | jobs: 18 | Pre-Commit: 19 | runs-on: ubuntu-latest 20 | steps: 21 | - name: Harden Runner 22 | uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 23 | with: 24 | disable-sudo: true 25 | egress-policy: audit 26 | # allowed-endpoints: > 27 | # api.github.com:443 28 | # files.pythonhosted.org:443 29 | # github.com:443 30 | # proxy.golang.org:443 31 | # pypi.org:443 32 | # registry.npmjs.org:443 33 | - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 34 | - uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 35 | with: 36 | python-version: "3.11" 37 | - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 38 | -------------------------------------------------------------------------------- /src/kraken/futures/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # https://github.com/btschwertfeger 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # pylint: disable=unused-import 19 | 20 | """This module provides the Kraken Futures clients""" 21 | 22 | from kraken.base_api import FuturesAsyncClient 23 | from kraken.futures.funding import Funding 24 | from kraken.futures.market import Market 25 | from kraken.futures.trade import Trade 26 | from kraken.futures.user import User 27 | from kraken.futures.ws_client import FuturesWSClient 28 | 29 | __all__ = [ 30 | "Funding", 31 | "FuturesAsyncClient", 32 | "FuturesWSClient", 33 | "Market", 34 | "Trade", 35 | "User", 36 | ] 37 | -------------------------------------------------------------------------------- /doc/03_examples/rest_examples/spot_ws_examples.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | Spot Websocket 20 | -------------- 21 | 22 | The examples presented below serve to demonstrate the usage of the Spot 23 | websocket clients provided by `python-kraken-sdk`_ to access `Kraken`_'s 24 | Websocket API v2. 25 | 26 | For questions, feedback, additions, suggestions for improvement or problems 27 | `python-kraken-sdk/discussions`_ or `python-kraken-sdk/issues`_ may be helpful. 28 | 29 | .. literalinclude:: ../../../examples/spot_ws_examples.py 30 | :language: python 31 | :linenos: 32 | :caption: Example access and usage for Kraken Spot Websocket API 33 | -------------------------------------------------------------------------------- /doc/03_examples/rest_examples/futures_ws_examples.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | Futures Websocket 20 | ----------------- 21 | 22 | The examples presented below serve to demonstrate the usage of the Futures 23 | websocket clients provided by `python-kraken-sdk`_ to access `Kraken`_'s 24 | Websocket API. 25 | 26 | For questions, feedback, additions, suggestions for improvement or problems 27 | `python-kraken-sdk/discussions`_ or `python-kraken-sdk/issues`_ may be helpful. 28 | 29 | .. literalinclude:: ../../../examples/futures_ws_examples.py 30 | :language: python 31 | :linenos: 32 | :caption: Example access and usage for Kraken Futures Websocket API 33 | -------------------------------------------------------------------------------- /doc/03_examples/trading_bot_templates/spot_orderbook.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | Maintain a valid Spot Orderbook 20 | ------------------------------- 21 | 22 | The following examples demonstrate how to use the python-kraken-sdk to retrieve 23 | valid realtime orderbooks. The current implementation of the 24 | :class:`kraken.spot.SpotOrderBookClient` uses the websocket API v2. 25 | 26 | .. literalinclude:: ../../../examples/spot_orderbook.py 27 | :language: python 28 | :linenos: 29 | :caption: Sample on how to maintain a valid orderbook w/ websocket API 30 | 31 | References: 32 | - https://gist.github.com/btschwertfeger/6eea0eeff193f7cd1b262cfce4f0eb51 33 | -------------------------------------------------------------------------------- /src/kraken/spot/__init__.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # https://github.com/btschwertfeger 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # pylint: disable=unused-import,cyclic-import 19 | 20 | """Module that provides the Spot REST clients.""" 21 | 22 | from kraken.base_api import SpotAsyncClient, SpotClient 23 | from kraken.spot.earn import Earn 24 | from kraken.spot.funding import Funding 25 | from kraken.spot.market import Market 26 | from kraken.spot.orderbook import SpotOrderBookClient 27 | from kraken.spot.trade import Trade 28 | from kraken.spot.user import User 29 | from kraken.spot.ws_client import SpotWSClient 30 | 31 | __all__ = [ 32 | "Earn", 33 | "Funding", 34 | "SpotWSClient", 35 | "Market", 36 | "SpotOrderBookClient", 37 | "SpotClient", 38 | "SpotAsyncClient", 39 | "Trade", 40 | "User", 41 | ] 42 | -------------------------------------------------------------------------------- /doc/03_examples/trading_bot_templates/spot_bot_templates.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | Spot Trading Bot Templates 20 | -------------------------- 21 | 22 | The templates presented below serve as starting points for the development of 23 | a trading algorithms for Spot trading on the crypto asset exchange `Kraken`_ 24 | using the `python-kraken-sdk`_. 25 | 26 | The trading strategy can be implemented using the ``TradingBot`` class. This 27 | class has access to all REST clients and receives all messages that are sent by 28 | the subscribed websocket feeds via the ``on_message`` function. 29 | 30 | .. literalinclude:: ../../../examples/spot_trading_bot_template.py 31 | :language: python 32 | :linenos: 33 | :caption: Template to build a trading bot using the Kraken Spot Websocket API v2 34 | -------------------------------------------------------------------------------- /doc/03_examples/trading_bot_templates/futures_bot_template.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | Futures Trading Bot Template 20 | ---------------------------- 21 | 22 | The template presented below serves as a starting point for the development of 23 | a trading algorithm for trading futures contracts on the crypto asset exchange 24 | `Kraken`_ using the `python-kraken-sdk`_. 25 | 26 | The trading strategy can be implemented in the ``TradingBot`` class. This class 27 | has access to all REST clients and receives all messages that are sent via the 28 | subscribed websocket feeds via the ``on_message`` function. 29 | 30 | .. literalinclude:: ../../../examples/futures_trading_bot_template.py 31 | :language: python 32 | :linenos: 33 | :caption: Template to build a trading bot using the Kraken Futures Websocket API 34 | -------------------------------------------------------------------------------- /doc/03_examples/rest_examples/xstocks_rest_examples.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | .. _section-xstocks-rest-examples: 20 | 21 | xStocks REST 22 | ------------ 23 | 24 | The examples presented below serve to demonstrate the usage of the Spot 25 | REST clients provided by `python-kraken-sdk`_ to access `Kraken`_'s REST API for 26 | trading xStocks. 27 | 28 | For questions, feedback, additions, suggestions for improvement or problems 29 | `python-kraken-sdk/discussions`_ or `python-kraken-sdk/issues`_ may be helpful. 30 | 31 | See https://docs.kraken.com/api/docs/guides/global-intro for information about 32 | the available endpoints and their usage. 33 | 34 | .. literalinclude:: ../../../examples/xstocks_examples.py 35 | :language: python 36 | :linenos: 37 | :caption: Example usage of Spot REST client for xStocks 38 | -------------------------------------------------------------------------------- /doc/05_futures/rest.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | .. _section-futures-rest-clients: 20 | 21 | Futures REST Clients 22 | ==================== 23 | 24 | .. autoclass:: kraken.base_api.FuturesClient 25 | :members: 26 | :show-inheritance: 27 | :inherited-members: 28 | 29 | .. autoclass:: kraken.base_api.FuturesAsyncClient 30 | :members: 31 | :show-inheritance: 32 | :inherited-members: 33 | 34 | .. autoclass:: kraken.futures.User 35 | :members: 36 | :show-inheritance: 37 | :inherited-members: 38 | 39 | .. autoclass:: kraken.futures.Market 40 | :members: 41 | :show-inheritance: 42 | :inherited-members: 43 | 44 | .. autoclass:: kraken.futures.Trade 45 | :members: 46 | :show-inheritance: 47 | :inherited-members: 48 | 49 | .. autoclass:: kraken.futures.Funding 50 | :members: 51 | :show-inheritance: 52 | :inherited-members: 53 | -------------------------------------------------------------------------------- /tests/cli/basic.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # -*- mode: python; coding: utf-8 -*- 3 | # 4 | # Copyright (C) 2024 Benjamin Thomas Schwertfeger 5 | # All rights reserved. 6 | # https://github.com/btschwertfeger 7 | # 8 | # Test basic CLI functionality 9 | 10 | set -e 11 | 12 | run_test() { 13 | local description="$1" 14 | shift 15 | if "$@" > /dev/null 2>&1; then 16 | echo "${description}:: SUCCESS" 17 | else 18 | echo "${description}:: FAILED" 19 | return 1 20 | fi 21 | } 22 | 23 | run_test "spot_public_full_url" kraken spot https://api.kraken.com/0/public/Time 24 | run_test "spot_public_path_only" kraken spot /0/public/Time 25 | 26 | run_test "spot_private_balance_full_url" kraken spot -X POST https://api.kraken.com/0/private/Balance 27 | run_test "spot_private_balance_path_only" kraken spot -X POST /0/private/Balance 28 | 29 | run_test "spot_private_trade_balance_with_data_full_url" kraken spot -X POST https://api.kraken.com/0/private/TradeBalance -d '{"asset": "DOT"}' 30 | run_test "spot_private_trade_balance_with_data_path_only" kraken spot -X POST /0/private/TradeBalance -d '{"asset": "DOT"}' 31 | 32 | run_test "futures_public_charts_full_url" kraken futures https://futures.kraken.com/api/charts/v1/spot/PI_XBTUSD/1d 33 | run_test "futures_public_charts_path_only" kraken futures /api/charts/v1/spot/PI_XBTUSD/1d 34 | 35 | run_test "futures_private_openpositions" kraken futures https://futures.kraken.com/derivatives/api/v3/openpositions 36 | # run_test "futures_private_editorder" kraken futures -X POST https://futures.kraken.com/derivatives/api/v3/editorder -d '{"cliOrdID": "12345", "limitPrice": 10}' 37 | -------------------------------------------------------------------------------- /.github/workflows/_build_doc.yaml: -------------------------------------------------------------------------------- 1 | # -*- mode: yaml; coding: utf-8 -*- 2 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 3 | # All rights reserved. 4 | # https://github.com/btschwertfeger 5 | # 6 | # Template workflow to build documentation. 7 | # 8 | 9 | name: Build Doc 10 | 11 | on: 12 | workflow_call: 13 | inputs: 14 | os: 15 | type: string 16 | required: true 17 | python-version: 18 | type: string 19 | required: true 20 | 21 | permissions: 22 | contents: read 23 | 24 | jobs: 25 | Build: 26 | runs-on: ${{ inputs.os }} 27 | steps: 28 | - name: Harden Runner 29 | uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 30 | with: 31 | egress-policy: audit 32 | 33 | - name: Checkout repository 34 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 35 | 36 | - name: Set up Python ${{ inputs.python-version }} 37 | uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 38 | with: 39 | python-version: ${{ inputs.python-version }} 40 | 41 | - name: Install uv 42 | uses: astral-sh/setup-uv@v7 43 | 44 | - name: Install dependencies 45 | run: | 46 | sudo apt update 47 | DEBIAN_FRONTEND=noninteractive sudo apt install -y pandoc 48 | uv venv 49 | source .venv/bin/activate 50 | echo ${GITHUB_WORKSPACE}/.venv/bin >> $GITHUB_PATH 51 | uv pip install -r doc/requirements.txt 52 | uv pip install . 53 | 54 | - name: Build the documentation 55 | run: make doc 56 | -------------------------------------------------------------------------------- /doc/04_spot/rest.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | .. _section-spot-rest-clients: 20 | 21 | Spot REST Clients 22 | ================== 23 | 24 | .. autoclass:: kraken.base_api.SpotClient 25 | :members: 26 | :show-inheritance: 27 | :inherited-members: 28 | 29 | .. autoclass:: kraken.base_api.SpotAsyncClient 30 | :members: 31 | :show-inheritance: 32 | :inherited-members: 33 | 34 | .. autoclass:: kraken.spot.User 35 | :members: 36 | :show-inheritance: 37 | :inherited-members: 38 | 39 | .. autoclass:: kraken.spot.Market 40 | :members: 41 | :show-inheritance: 42 | :inherited-members: 43 | 44 | .. autoclass:: kraken.spot.Trade 45 | :members: 46 | :show-inheritance: 47 | :inherited-members: 48 | 49 | .. autoclass:: kraken.spot.Funding 50 | :members: 51 | :show-inheritance: 52 | :inherited-members: 53 | 54 | .. autoclass:: kraken.spot.Earn 55 | :members: 56 | :show-inheritance: 57 | :inherited-members: 58 | -------------------------------------------------------------------------------- /.github/self-review.md: -------------------------------------------------------------------------------- 1 | # Self review checklist ✅ 2 | 3 | Before creating a pull request you should check the following requirements that 4 | must be addressed before a PR will be accepted. 5 | 6 | - [ ] **All** pre-commit hooks must run through - successfully. 7 | - [ ] Make sure that the changes confirm the coding style of the 8 | [python-kraken-sdk](https://github.com/btschwertfeger/python-kraken-sdk). 9 | Most issues will be resolve through the pre-commit hooks. 10 | - [ ] Also take care to follow the community guidelines and the [Code of 11 | Conduct](./CODE_OF_CONDUCT.md). 12 | - [ ] Self-review your changes to detect typos, syntax errors, and any kind of 13 | unwanted behavior. 14 | - [ ] If you changed the source code you have to **ensure that all unit tests 15 | run through**. If you added a new function you also have to **write a 16 | test** for that. Also make sure to **follow the doc string style** of the 17 | package and **provide at least one working example** within a function doc 18 | string. This is important since doc strings will be reflected within the 19 | documentation. 20 | - [ ] Take your time to prepare your code before creating a PR. A good PR will 21 | save a lot of time for everyone. 22 | - [ ] There are several workflows/actions within this repository. Relevant 23 | workflows must be run successfully within your fork. Actions must be 24 | enabled within the fork, so that workflows can run within the context of a 25 | PR. [cicd.yaml](./workflows/cicd.yaml) can be used to run all actions at 26 | once - but requires having API keys for Spot, Futures and the Futures demo 27 | environment. 28 | -------------------------------------------------------------------------------- /doc/03_examples/trading_bot_templates.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | .. _section-trading-bot-templates: 20 | 21 | Trading Bot Templates 22 | ===================== 23 | 24 | The `python-kraken-sdk`_ is perfectly suited to develop automated trading 25 | algorithms. To facilitate the start here directly for Spot trading as well as 26 | for the Futures enthusiasts, templates are provided which can serve as the basis 27 | for dynamic trading algorithms. In both cases websockets are used to capture the 28 | live data. In addition REST clients are integrated, so that their endpoints can 29 | also be reached at any time. 30 | 31 | For questions, feedback, additions, suggestions or problems 32 | `python-kraken-sdk/discussions`_ or `python-kraken-sdk/issues`_ may be helpful. 33 | 34 | .. toctree:: 35 | :maxdepth: 2 36 | 37 | trading_bot_templates/spot_bot_templates.rst 38 | trading_bot_templates/spot_orderbook.rst 39 | trading_bot_templates/futures_bot_template.rst 40 | -------------------------------------------------------------------------------- /doc/index.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | .. Python Kraken SDK documentation master file, created by 19 | sphinx-quickstart on Sun Apr, 2 11:32:02 2023. 20 | 21 | Welcome to python-kraken-sdk's documentation! 22 | ============================================= 23 | 24 | |GitHub badge| |License badge| |PyVersions badge| |Downloads badge| 25 | |CI/CD badge| |codecov badge| |Typing badge| 26 | |OSSF Scorecard| |OSSF Best Practices| 27 | |Release date badge| |Release version badge| |DOI badge| 28 | 29 | .. toctree:: 30 | :caption: Contents: 31 | :maxdepth: 2 32 | 33 | 01_introduction.rst 34 | 02_getting_started/getting_started.rst 35 | 03_examples/command_line_interface.rst 36 | 03_examples/rest_example_usage.rst 37 | 03_examples/trading_bot_templates.rst 38 | 04_spot/rest.rst 39 | 04_spot/websockets.rst 40 | 05_futures/rest.rst 41 | 05_futures/websockets.rst 42 | 06_exceptions/exceptions.rst 43 | 07_issues.rst 44 | 08_about/license.rst 45 | 46 | Indices and tables 47 | ================== 48 | 49 | * :ref:`genindex` 50 | -------------------------------------------------------------------------------- /doc/03_examples/rest_example_usage.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | .. _section-examples: 20 | 21 | Usage Examples 22 | ============== 23 | 24 | The python-kraken-sdk provides lots of functions to easily access most of the 25 | REST and websocket endpoints of the Kraken Crypto Asset Exchange API. Since 26 | these endpoints and their parameters may change, all implemented endpoints are 27 | tested on a regular basis. 28 | 29 | The repository of the `python-kraken-sdk`_ provides some example scripts that 30 | demonstrate some of the implemented methods. Please see the sections listed 31 | below. 32 | 33 | Projects that use the SDK are listed below: 34 | 35 | * https://github.com/btschwertfeger/infinity-grid 36 | * https://github.com/btschwertfeger/kraken-rebalance-bot 37 | 38 | .. toctree:: 39 | :maxdepth: 2 40 | 41 | rest_examples/spot_rest_examples.rst 42 | rest_examples/spot_ws_examples.rst 43 | rest_examples/xstocks_rest_examples.rst 44 | market_client_example.ipynb 45 | rest_examples/futures_ws_examples.rst 46 | rest_examples/futures_rest_examples.rst 47 | -------------------------------------------------------------------------------- /doc/02_getting_started/getting_started.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | Getting Started 20 | =============== 21 | 22 | 1. Install the Python module 23 | 24 | .. code-block:: bash 25 | 26 | python3 -m pip install python-kraken-sdk 27 | 28 | 29 | 2. Register at Kraken and generate API Keys 30 | 31 | - Spot Trading: https://pro.kraken.com/app/settings/api 32 | - Futures Trading: https://futures.kraken.com/trade/settings/api 33 | - Futures Sandbox: https://demo-futures.kraken.com/settings/api 34 | 35 | 3. Start using the provided example scripts 36 | 37 | Examples can be found within the repository of the `python-kraken-sdk`_ and 38 | obviously in this documentation. See the :ref:`section-examples` section for 39 | basic usage examples and :ref:`section-trading-bot-templates` to get impressed 40 | by orderbook clients, as well as boilerplates for trading bots using the SDK. 41 | 42 | 4. Error handling 43 | 44 | If any unexpected behavior occurs, please check **your API permissions**, 45 | **rate limits**, update the `python-kraken-sdk`_, see the 46 | :ref:`section-troubleshooting` section, and if the error persists please open an 47 | issue at `python-kraken-sdk/issues`_. 48 | -------------------------------------------------------------------------------- /.github/workflows/_pypi_publish.yaml: -------------------------------------------------------------------------------- 1 | # -*- mode: yaml; coding: utf-8 -*- 2 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 3 | # All rights reserved. 4 | # https://github.com/btschwertfeger 5 | # 6 | # Template workflow to build the project and publish 7 | # the package to PyPI. 8 | # 9 | 10 | name: PyPI Publish 11 | 12 | on: 13 | workflow_call: 14 | secrets: 15 | API_TOKEN: 16 | required: true 17 | 18 | permissions: read-all 19 | 20 | jobs: 21 | publish-to-pypi: 22 | name: Publish Python distribution to PyPI 23 | runs-on: ubuntu-latest 24 | permissions: 25 | id-token: write # IMPORTANT: this permission is mandatory for OIDC publishing 26 | environment: 27 | name: pypi 28 | url: https://pypi.org/p/python-kraken-sdk 29 | steps: 30 | - name: Harden Runner 31 | uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 32 | with: 33 | disable-sudo: true 34 | egress-policy: block 35 | allowed-endpoints: > 36 | api.github.com:443 37 | fulcio.sigstore.dev 38 | ghcr.io 39 | github.com:443 40 | pkg-containers.githubusercontent.com:443 41 | pypi.org 42 | rekor.sigstore.dev 43 | tuf-repo-cdn.sigstore.dev 44 | upload.pypi.org 45 | uploads.github.com:443 46 | 47 | - name: Download all the distributions 48 | uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 49 | with: 50 | name: python-package-distributions 51 | path: dist/ 52 | 53 | - name: Publish package distributions to PyPI 54 | uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1 55 | with: 56 | password: ${{ secrets.API_TOKEN }} 57 | repository-url: https://upload.pypi.org/legacy/ 58 | -------------------------------------------------------------------------------- /.github/workflows/_pypi_test_publish.yaml: -------------------------------------------------------------------------------- 1 | # -*- mode: yaml; coding: utf-8 -*- 2 | # Copyright (C) 2024 Benjamin Thomas Schwertfeger 3 | # All rights reserved. 4 | # https://github.com/btschwertfeger 5 | # 6 | # Template workflow to build the project and publish 7 | # the package to test PyPI. 8 | # 9 | 10 | name: PyPI Publish 11 | 12 | on: 13 | workflow_call: 14 | secrets: 15 | API_TOKEN: 16 | required: true 17 | 18 | permissions: read-all 19 | 20 | jobs: 21 | publish-to-test-pypi: 22 | name: Publish Python distribution to PyPI 23 | runs-on: ubuntu-latest 24 | permissions: 25 | id-token: write # IMPORTANT: this permission is mandatory for OIDC publishing 26 | environment: 27 | name: testpypi 28 | url: https://test.pypi.org/p/python-kraken-sdk 29 | steps: 30 | - name: Harden Runner 31 | uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 32 | with: 33 | disable-sudo: true 34 | egress-policy: block 35 | allowed-endpoints: > 36 | api.github.com:443 37 | fulcio.sigstore.dev 38 | ghcr.io 39 | github.com:443 40 | pkg-containers.githubusercontent.com:443 41 | rekor.sigstore.dev 42 | test.pypi.org 43 | tuf-repo-cdn.sigstore.dev 44 | uploads.github.com:443 45 | 46 | - name: Download all the distributions 47 | uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 48 | with: 49 | name: python-package-distributions 50 | path: dist/ 51 | 52 | - name: Publish package distributions to Test PyPI 53 | uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # release/v1 54 | with: 55 | password: ${{ secrets.API_TOKEN }} 56 | repository-url: https://test.pypi.org/legacy/ 57 | -------------------------------------------------------------------------------- /.github/workflows/dependabot_auto_merge.yaml: -------------------------------------------------------------------------------- 1 | # -*- mode: yaml; coding: utf-8 -*- 2 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 3 | # All rights reserved. 4 | # https://github.com/btschwertfeger 5 | # 6 | # Workflow that approves and merges all pull requests from the dependabot[bot] 7 | # author. 8 | # 9 | # Original source (May, 2024): 10 | # - https://blog.somewhatabstract.com/2021/10/11/setting-up-dependabot-with-github-actions-to-approve-and-merge/ 11 | 12 | name: Dependabot auto-merge 13 | on: pull_request_target 14 | 15 | permissions: 16 | pull-requests: write 17 | contents: write 18 | 19 | jobs: 20 | dependabot: 21 | runs-on: ubuntu-latest 22 | if: ${{ github.actor == 'dependabot[bot]' }} 23 | steps: 24 | - name: Harden Runner 25 | uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 26 | with: 27 | disable-sudo: true 28 | egress-policy: block 29 | allowed-endpoints: > 30 | api.github.com:443 31 | github.com:443 32 | 33 | - name: Dependabot metadata 34 | id: dependabot-metadata 35 | uses: dependabot/fetch-metadata@08eff52bf64351f401fb50d4972fa95b9f2c2d1b # v2.4.0 36 | with: 37 | github-token: "${{ secrets.GITHUB_TOKEN }}" 38 | 39 | - name: Approve a PR 40 | if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }} 41 | run: gh pr review --approve "$PR_URL" 42 | env: 43 | PR_URL: ${{ github.event.pull_request.html_url }} 44 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 45 | 46 | - name: Enable auto-merge for Dependabot PRs 47 | if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }} 48 | run: gh pr merge --auto --squash "$PR_URL" 49 | env: 50 | PR_URL: ${{ github.event.pull_request.html_url }} 51 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 52 | -------------------------------------------------------------------------------- /tests/cli/conftest.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2024 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | 8 | """Module implementing fixtures for testing""" 9 | 10 | from __future__ import annotations 11 | 12 | import os 13 | from typing import Generator 14 | 15 | import pytest 16 | from click.testing import CliRunner 17 | 18 | 19 | @pytest.fixture 20 | def cli_runner() -> CliRunner: 21 | """Provide a cli-runner for testing the CLI""" 22 | return CliRunner() 23 | 24 | 25 | @pytest.fixture 26 | def with_spot_secrets() -> Generator: 27 | """Setup some environment variables for th CLI tests""" 28 | 29 | if not all( 30 | ( 31 | spot_api_key := os.getenv("SPOT_API_KEY"), 32 | spot_secret_key := os.getenv("SPOT_SECRET_KEY"), 33 | ), 34 | ): 35 | pytest.fail("No API keys provided for CLI tests!") 36 | 37 | os.environ["KRAKEN_SPOT_API_KEY"] = spot_api_key 38 | os.environ["KRAKEN_SPOT_SECRET_KEY"] = spot_secret_key 39 | 40 | yield 41 | 42 | for var in ("KRAKEN_SPOT_API_KEY", "KRAKEN_SPOT_SECRET_KEY"): 43 | if os.getenv(var): 44 | del os.environ[var] 45 | 46 | 47 | @pytest.fixture 48 | def with_futures_secrets() -> Generator: 49 | """Setup some environment variables for the CLI tests""" 50 | 51 | if not all( 52 | ( 53 | futures_api_key := os.getenv("FUTURES_API_KEY"), 54 | futures_secret_key := os.getenv("FUTURES_SECRET_KEY"), 55 | ), 56 | ): 57 | pytest.fail("No API keys provided for CLI tests!") 58 | 59 | os.environ["KRAKEN_FUTURES_API_KEY"] = futures_api_key 60 | os.environ["KRAKEN_FUTURES_SECRET_KEY"] = futures_secret_key 61 | 62 | yield 63 | 64 | for var in ( 65 | "KRAKEN_FUTURES_API_KEY", 66 | "KRAKEN_FUTURES_SECRET_KEY", 67 | ): 68 | if os.getenv(var): 69 | del os.environ[var] 70 | -------------------------------------------------------------------------------- /tests/spot/test_spot_websocket_internals.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | 8 | """Checks the internals.""" 9 | 10 | from __future__ import annotations 11 | 12 | from asyncio import run as asyncio_run 13 | from asyncio import sleep as async_sleep 14 | from typing import Self 15 | 16 | import pytest 17 | 18 | from kraken.spot.websocket import SpotWSClientBase 19 | 20 | 21 | @pytest.mark.spot 22 | @pytest.mark.spot_websocket 23 | class TestSpotWebSocketInternals: 24 | """Test class for Spot WebSocket internals.""" 25 | 26 | def test_ws_base_client_context_manager(self: Self) -> None: 27 | """ 28 | Checks that the KrakenSpotWSClientBase can be instantiated as context 29 | manager. 30 | """ 31 | 32 | async def check_it() -> None: 33 | class TestClient(SpotWSClientBase): 34 | async def on_message(self: TestClient, message: dict) -> None: 35 | if message == {"error": "yes"}: 36 | raise ValueError("Test Error") 37 | 38 | with TestClient(no_public=True) as client: 39 | with pytest.raises(ValueError, match=r"Test Error"): 40 | await client.on_message(message={"error": "yes"}) 41 | await async_sleep(5) 42 | 43 | asyncio_run(check_it()) 44 | 45 | def test_ws_base_client_on_message_no_callback( 46 | self: Self, 47 | caplog: pytest.LogCaptureFixture, 48 | ) -> None: 49 | """ 50 | Checks that the KrakenSpotWSClientBase logs a message when no callback 51 | was defined. 52 | """ 53 | 54 | async def run() -> None: 55 | client = SpotWSClientBase(no_public=True) 56 | await client.on_message({"event": "testing"}) 57 | 58 | asyncio_run(run()) 59 | assert "Received message but no callback is defined!" in caplog.text 60 | -------------------------------------------------------------------------------- /doc/07_issues.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | Known Issues 20 | ============ 21 | 22 | Issues listed here: `python-kraken-sdk/issues`_ 23 | 24 | Futures Trading 25 | --------------- 26 | 27 | - The Kraken API returns 500 - INTERNAL_SERVER_ERROR for some endpoints if 28 | ``order_id`` or ``orderId``, ``cliOrdId`` seems to work in all cases. 29 | - Kraken's API doesn't seem to know the ``trailing_stop`` order type and raises 30 | an error if this type is part of an order. This order type is documented here 31 | https://docs.kraken.com/api/docs/futures-api/trading/send-order 32 | 33 | .. code-block:: python 34 | :linenos: 35 | :caption: ``trailing_stop`` order type not working in Kraken Futures 36 | 37 | from kraken.futures import Trade 38 | 39 | Trade(key="api-key", secret="secret-key").create_order( 40 | orderType="trailing_stop", 41 | size=10, 42 | side="buy", 43 | symbol="PI_XBTUSD", 44 | limitPrice=12000, 45 | triggerSignal="mark", 46 | trailingStopDeviationUnit="PERCENT", 47 | trailingStopMaxDeviation=10, 48 | ) 49 | """ Output: 50 | { 51 | "status": "BAD_REQUEST", 52 | "result": "error", 53 | "errors": [{ 54 | "code": 11, 55 | "message": "invalid order type" 56 | }], 57 | "serverTime":"2023-04-07T19:26:41.299Z" 58 | } 59 | """" 60 | -------------------------------------------------------------------------------- /src/kraken/utils/utils.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # https://github.com/btschwertfeger 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | """Module implementing utility functions used across the package""" 20 | 21 | from __future__ import annotations 22 | 23 | import warnings 24 | from enum import Enum, auto 25 | from functools import wraps 26 | from typing import TYPE_CHECKING, Any 27 | 28 | if TYPE_CHECKING: 29 | from collections.abc import Callable 30 | 31 | 32 | class WSState(Enum): 33 | """Enum class representing the state of the WebSocket connection""" 34 | 35 | INIT = auto() # Initial state 36 | CONNECTING = auto() # Connection is being established 37 | CONNECTED = auto() # Connection is established 38 | RECONNECTING = auto() # Connection is being re-established 39 | CANCELLING = auto() # Connection is being cancelled 40 | ERRORHANDLING = auto() # Error is being handled 41 | ERROR = auto() # Error occurred 42 | CLOSED = auto() # Connection is closed 43 | 44 | 45 | def deprecated(message: str) -> Callable: 46 | """ 47 | Function used as decorator to mark decorated functions as deprecated with a 48 | custom message. 49 | """ 50 | 51 | def decorator(func: Callable) -> Callable: 52 | @wraps(func) 53 | def wrapper( 54 | *args: Any | None, 55 | **kwargs: Any | None, 56 | ) -> Any | None: # noqa: ANN401 57 | warnings.warn( 58 | f"{message}", 59 | category=DeprecationWarning, 60 | stacklevel=2, 61 | ) 62 | return func(*args, **kwargs) 63 | 64 | return wrapper 65 | 66 | return decorator 67 | 68 | 69 | __all__ = ["deprecated"] 70 | -------------------------------------------------------------------------------- /tests/futures/helper.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | 8 | from __future__ import annotations 9 | 10 | import logging 11 | from pathlib import Path 12 | 13 | from kraken.futures import FuturesWSClient 14 | 15 | CACHE_DIR: Path = Path(__file__).resolve().parent.parent.parent / ".cache" / "tests" 16 | CACHE_DIR.mkdir(parents=True, exist_ok=True) 17 | 18 | 19 | def is_success(value: object | dict | set | tuple | list | str | float | None) -> bool: 20 | """ 21 | Returns true if result is success, even if the order may not exist - but kraken received the correct request. 22 | """ 23 | return ( 24 | isinstance(value, dict) and "result" in value and value["result"] == "success" 25 | ) 26 | 27 | 28 | def is_not_error( 29 | value: object | dict | set | tuple | list | str | float | None, 30 | ) -> bool: 31 | """Returns true if result is not error""" 32 | return isinstance(value, dict) and "error" not in value 33 | 34 | 35 | class FuturesWebsocketClientTestWrapper(FuturesWSClient): 36 | """ 37 | Class that creates an instance to test the FuturesWSClient. 38 | 39 | It writes the messages to the log and a file. The log is used 40 | within the tests, the log file is for local debugging. 41 | """ 42 | 43 | LOG: logging.Logger = logging.getLogger(__name__) 44 | 45 | def __init__( 46 | self: FuturesWebsocketClientTestWrapper, 47 | key: str = "", 48 | secret: str = "", 49 | ) -> None: 50 | super().__init__(key=key, secret=secret, callback=self.on_message) 51 | self.LOG.setLevel(logging.INFO) 52 | 53 | async def on_message( 54 | self: FuturesWebsocketClientTestWrapper, 55 | message: list | dict, 56 | ) -> None: 57 | """ 58 | This is the callback function that must be implemented 59 | to handle custom websocket messages. 60 | """ 61 | self.LOG.info(message) # the log is read within the tests 62 | 63 | log: str = "" 64 | try: 65 | with Path(CACHE_DIR / "futures_ws.log").open( 66 | mode="r", 67 | encoding="utf-8", 68 | ) as logfile: 69 | log = logfile.read() 70 | except FileNotFoundError: 71 | pass 72 | 73 | with Path(CACHE_DIR / "futures_ws.log").open( 74 | mode="w", 75 | encoding="utf-8", 76 | ) as logfile: 77 | logfile.write(f"{log}\n{message}") 78 | -------------------------------------------------------------------------------- /doc/03_examples/command_line_interface.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2024 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | .. _section-command-line-interface-examples: 20 | 21 | Command-line Interface 22 | ---------------------- 23 | 24 | The python-kraken-sdk provides a command-line interface to access the Kraken API 25 | using basic instructions while performing authentication tasks in the 26 | background. The Spot and Futures APIs are accessible and follow the pattern 27 | ``kraken {spot,futures} [OPTIONS] URL``. All endpoints of the Kraken Spot and 28 | Futurs API can be accessed like that. See examples below. 29 | 30 | .. code-block:: bash 31 | :linenos: 32 | :caption: Command-line Interface Examples 33 | 34 | # get server time 35 | kraken spot https://api.kraken.com/0/public/Time 36 | {'unixtime': 1716707589, 'rfc1123': 'Sun, 26 May 24 07:13:09 +0000'} 37 | 38 | # get user's balances 39 | kraken spot --api-key= --secret-key= -X POST https://api.kraken.com/0/private/Balance 40 | {'ATOM': '17.28229999', 'BCH': '0.0000077100', 'ZUSD': '1000.0000'} 41 | 42 | # get user's trade balances 43 | kraken spot --api-key= --secret-key= -X POST https://api.kraken.com/0/private/TradeBalance --data '{"asset": "DOT"}' 44 | {'eb': '2.8987347115', 'tb': '1.1694303513', 'm': '0.0000000000', 'uv': '0', 'n': '0.0000000000', 'c': '0.0000000000', 'v': '0.0000000000', 'e': '1.1694303513', 'mf': '1.1694303513'} 45 | 46 | # get 1D candles for a futures instrument 47 | kraken futures https://futures.kraken.com/api/charts/v1/spot/PI_XBTUSD/1d 48 | {'candles': [{'time': 1625616000000, 'open': '34557.84000000000', 'high': '34803.20000000000', 'low': '33816.32000000000', 'close': '33880.22000000000', 'volume': '0' ... 49 | 50 | # get user's open futures positions 51 | kraken futures --api-key= --secret-key= https://futures.kraken.com/derivatives/api/v3/openpositions 52 | {'result': 'success', 'openPositions': [], 'serverTime': '2024-05-26T07:15:38.91Z'} 53 | 54 | .. click:: kraken.cli:cli 55 | :prog: kraken 56 | :nested: full 57 | -------------------------------------------------------------------------------- /tests/futures/test_futures_funding.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | 8 | """Module that implements the unit tests for the Futures funding client.""" 9 | 10 | from typing import Self 11 | 12 | import pytest 13 | 14 | from kraken.futures import Funding 15 | 16 | from .helper import is_success 17 | 18 | 19 | @pytest.mark.futures 20 | @pytest.mark.futures_auth 21 | @pytest.mark.futures_funding 22 | class TestFuturesFunding: 23 | def test_get_historical_funding_rates( 24 | self: Self, 25 | futures_demo_funding: Funding, 26 | ) -> None: 27 | """ 28 | Checks the ``get_historical_funding_rates`` function. 29 | """ 30 | assert is_success( 31 | futures_demo_funding.get_historical_funding_rates(symbol="PF_XBTUSD"), 32 | ) 33 | 34 | @pytest.mark.skip(reason="Tests do not have withdraw permission") 35 | def test_initiate_wallet_transfer( 36 | self: Self, 37 | futures_demo_funding: Funding, 38 | ) -> None: 39 | """ 40 | Checks the ``initiate_wallet_transfer`` function - skipped since 41 | a transfer in testing is not desired. 42 | """ 43 | # accounts must exist.. 44 | # print(futures_demo_funding.initiate_wallet_transfer( 45 | # amount=200, fromAccount='Futures Wallet', toAccount='Spot Wallet', unit='XBT' 46 | # )) 47 | 48 | @pytest.mark.skip(reason="Tests do not have withdraw permission") 49 | def test_initiate_subccount_transfer( 50 | self: Self, 51 | futures_demo_funding: Funding, 52 | ) -> None: 53 | """ 54 | Checks the ``initiate_subaccount_transfer`` function. 55 | """ 56 | # print(futures_demo_funding.initiate_subaccount_transfer( 57 | # amount=200, 58 | # fromAccount='The wallet (cash or margin account) from which funds should be debited', 59 | # fromUser='The user account (this or a sub account) from which funds should be debited', 60 | # toAccount='The wallet (cash or margin account) to which funds should be credited', 61 | # toUser='The user account (this or a sub account) to which funds should be credited', 62 | # unit='XBT', 63 | # )) 64 | 65 | @pytest.mark.skip(reason="Tests do not have withdraw permission") 66 | def test_initiate_withdrawal_to_spot_wallet( 67 | self: Self, 68 | futures_demo_funding: Funding, 69 | ) -> None: 70 | """ 71 | Checks the ``initiate_withdrawal_to_spot_wallet`` function. 72 | """ 73 | # print(futures_demo_funding.initiate_withdrawal_to_spot_wallet( 74 | # amount=200, 75 | # currency='XBT', 76 | # )) 77 | -------------------------------------------------------------------------------- /examples/xstocks_examples.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python3 2 | # -*- mode: python; coding: utf-8 -*- 3 | # 4 | # Copyright (C) 2025 Benjamin Thomas Schwertfeger 5 | # All rights reserved. 6 | # https://github.com/btschwertfeger 7 | # 8 | 9 | """ 10 | Module that implements *some* examples for the Kraken Spot REST clients usage 11 | with focus on xStocks. 12 | 13 | NOTE: The xStocks feature is not available globally. Please checkout Kraken's 14 | documentation to get to know the availability zones. 15 | """ 16 | 17 | import logging 18 | import os 19 | 20 | from kraken.spot import SpotClient, Trade 21 | 22 | logging.basicConfig( 23 | format="%(asctime)s %(module)s,line: %(lineno)d %(levelname)8s | %(message)s", 24 | datefmt="%Y/%m/%d %H:%M:%S", 25 | level=logging.INFO, 26 | ) 27 | logging.getLogger("requests").setLevel(logging.WARNING) 28 | logging.getLogger("urllib3").setLevel(logging.WARNING) 29 | 30 | 31 | key = os.getenv("SPOT_API_KEY") 32 | secret = os.getenv("SPOT_SECRET_KEY") 33 | 34 | CLIENT = SpotClient(key=key, secret=secret) 35 | 36 | 37 | def list_all_xstocks() -> None: 38 | """List all available xStocks on Kraken.""" 39 | print( 40 | CLIENT.request( 41 | "GET", 42 | "/0/public/AssetPairs", 43 | params={"aclass_base": "tokenized_asset"}, # <- important! 44 | auth=False, 45 | ), 46 | ) 47 | 48 | 49 | def create_xstock_order() -> None: 50 | """Create a test order for an xStock (validate mode, not placing actual order).""" 51 | print( 52 | CLIENT.request( 53 | "POST", 54 | "/0/private/AddOrder", 55 | params={ 56 | "type": "buy", 57 | "volume": "1", 58 | "ordertype": "limit", 59 | "pair": "AAPLxUSD", 60 | "price": "100.0", 61 | "validate": True, 62 | "asset_class": "tokenized_asset", # <- important 63 | }, 64 | ), 65 | ) 66 | 67 | 68 | def create_xstock_order_alternative() -> None: 69 | """Create a test order for an xStock (validate mode, not placing actual order).""" 70 | trade = Trade(key=key, secret=secret) 71 | 72 | print( 73 | trade.create_order( 74 | pair="AAPLxUSD", 75 | side="buy", 76 | ordertype="limit", 77 | volume="1", 78 | price="100.0", 79 | validate=True, 80 | extra_params={"asset_class": "tokenized_asset"}, 81 | ), 82 | ) 83 | 84 | 85 | def main() -> None: 86 | """Uncomment the examples you want to run:""" 87 | # NOTE: These are only examples that show how to use the clients, there are 88 | # many other functions available in the clients. 89 | list_all_xstocks() 90 | # create_xstock_order() 91 | # create_xstock_order_alternative() 92 | 93 | 94 | if __name__ == "__main__": 95 | main() 96 | -------------------------------------------------------------------------------- /.github/workflows/_test_spot_public.yaml: -------------------------------------------------------------------------------- 1 | # -*- mode: yaml; coding: utf-8 -*- 2 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 3 | # All rights reserved. 4 | # https://github.com/btschwertfeger 5 | # 6 | # Template workflow to test the public Spot endpoints 7 | # of the python-kraken-sdk. 8 | # Runs tests for: 9 | # * Spot REST clients 10 | # 11 | 12 | name: Test Spot 13 | 14 | on: 15 | workflow_call: 16 | inputs: 17 | os: 18 | type: string 19 | required: true 20 | python-version: 21 | type: string 22 | required: true 23 | 24 | permissions: 25 | contents: read 26 | 27 | jobs: 28 | Test-Spot: 29 | name: Test ${{ inputs.os }} ${{ inputs.python-version }} 30 | runs-on: ${{ inputs.os }} 31 | timeout-minutes: 5 32 | steps: 33 | - name: Harden Runner 34 | uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 35 | with: 36 | disable-sudo: true 37 | egress-policy: block 38 | allowed-endpoints: > 39 | api.github.com:443 40 | api.kraken.com:443 41 | files.pythonhosted.org:443 42 | github.com:443 43 | objects.githubusercontent.com:443 44 | pypi.org:443 45 | release-assets.githubusercontent.com:443 46 | ws.kraken.com:443 47 | 48 | - name: Checkout repository 49 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 50 | 51 | - name: Download the built wheel 52 | uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 53 | with: 54 | name: python-package-distributions 55 | path: dist/ 56 | 57 | - name: Set up Python ${{ inputs.python-version }} 58 | uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 59 | with: 60 | python-version: ${{ inputs.python-version }} 61 | 62 | - name: Install uv 63 | uses: astral-sh/setup-uv@v7 64 | 65 | - name: Install package (Linux or macOS) 66 | if: runner.os != 'Windows' 67 | run: | 68 | uv venv 69 | source .venv/bin/activate 70 | echo ${GITHUB_WORKSPACE}/.venv/bin >> $GITHUB_PATH 71 | uv pip install "$(find dist -name '*.whl' | head -1)" -r requirements-dev.txt 72 | 73 | - name: Install package (Windows) 74 | if: runner.os == 'Windows' 75 | run: | 76 | uv venv 77 | .venv\Scripts\activate.ps1 78 | echo "$env:GITHUB_WORKSPACE\.venv\Scripts" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 79 | $wheel = (Get-ChildItem -Path dist -Filter "*.whl" | Select-Object -First 1).FullName 80 | uv pip install $wheel -r requirements-dev.txt 81 | 82 | - name: Testing Spot REST endpoints 83 | run: pytest -vv -x -n auto -m "spot and not spot_auth and not spot_websocket" tests 84 | 85 | - name: Testing Spot websocket endpoints 86 | run: pytest -vv -x -n auto -m "spot and not spot_auth and spot_websocket" tests 87 | -------------------------------------------------------------------------------- /.github/workflows/_test_futures_public.yaml: -------------------------------------------------------------------------------- 1 | # -*- mode: yaml; coding: utf-8 -*- 2 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 3 | # All rights reserved. 4 | # https://github.com/btschwertfeger 5 | # 6 | # Template workflow to test the public Futures endpoints 7 | # of the python-kraken-sdk. 8 | # Runs tests for: 9 | # * Futures REST clients 10 | # 11 | 12 | name: Test Futures 13 | 14 | on: 15 | workflow_call: 16 | inputs: 17 | os: 18 | type: string 19 | required: true 20 | python-version: 21 | type: string 22 | required: true 23 | 24 | permissions: 25 | contents: read 26 | 27 | jobs: 28 | Test-Futures: 29 | name: Test ${{ inputs.os }} ${{ inputs.python-version }} 30 | runs-on: ${{ inputs.os }} 31 | timeout-minutes: 5 32 | steps: 33 | - name: Harden Runner 34 | uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 35 | with: 36 | disable-sudo: true 37 | egress-policy: block 38 | allowed-endpoints: > 39 | api.github.com:443 40 | files.pythonhosted.org:443 41 | futures.kraken.com:443 42 | github.com:443 43 | objects.githubusercontent.com:443 44 | release-assets.githubusercontent.com:443 45 | pypi.org:443 46 | 47 | - name: Checkout repository 48 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 49 | 50 | - name: Download the built wheel 51 | uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 52 | with: 53 | name: python-package-distributions 54 | path: dist/ 55 | 56 | - name: Set up Python ${{ inputs.python-version }} 57 | uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 58 | with: 59 | python-version: ${{ inputs.python-version }} 60 | 61 | - name: Install uv 62 | uses: astral-sh/setup-uv@v7 63 | 64 | - name: Install package (Linux or macOS) 65 | if: runner.os != 'Windows' 66 | run: | 67 | uv venv 68 | source .venv/bin/activate 69 | echo ${GITHUB_WORKSPACE}/.venv/bin >> $GITHUB_PATH 70 | uv pip install "$(find dist -name '*.whl' | head -1)" -r requirements-dev.txt 71 | 72 | - name: Install package (Windows) 73 | if: runner.os == 'Windows' 74 | run: | 75 | uv venv 76 | .venv\Scripts\activate.ps1 77 | echo "$env:GITHUB_WORKSPACE\.venv\Scripts" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 78 | $wheel = (Get-ChildItem -Path dist -Filter "*.whl" | Select-Object -First 1).FullName 79 | uv pip install $wheel -r requirements-dev.txt 80 | 81 | - name: Testing Futures REST endpoints 82 | run: pytest -vv -x -n auto -m "futures and not futures_auth and not futures_websocket" tests 83 | 84 | - name: Testing Futures websocket endpoints 85 | run: pytest -vv -x -n auto -m "futures and not futures_auth and futures_websocket" tests 86 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | # -*- mode: yaml; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | 8 | repos: 9 | - repo: https://github.com/astral-sh/ruff-pre-commit 10 | rev: v0.14.6 11 | hooks: 12 | - id: ruff-check 13 | args: 14 | - --preview 15 | - --fix 16 | - --exit-non-zero-on-fix 17 | # Formatter disabled since it does not work together with COM812 18 | # - id: ruff-format 19 | # args: 20 | # - --preview 21 | # - --exit-non-zero-on-fix 22 | - repo: https://github.com/pre-commit/mirrors-mypy 23 | rev: v1.18.2 24 | hooks: 25 | - id: mypy 26 | name: mypy 27 | additional_dependencies: ["types-requests"] 28 | pass_filenames: false 29 | args: 30 | - --config-file=pyproject.toml 31 | - --install-types 32 | - --non-interactive 33 | - repo: https://github.com/gitleaks/gitleaks 34 | rev: v8.29.1 35 | hooks: 36 | - id: gitleaks 37 | - repo: https://github.com/codespell-project/codespell 38 | rev: v2.4.1 39 | hooks: 40 | - id: codespell 41 | additional_dependencies: [tomli] 42 | - repo: https://github.com/pre-commit/pre-commit-hooks 43 | rev: v6.0.0 44 | hooks: 45 | - id: check-yaml 46 | - id: check-ast 47 | - id: check-json 48 | - id: check-toml 49 | - id: check-docstring-first 50 | - id: check-case-conflict 51 | - id: check-merge-conflict 52 | - id: check-added-large-files 53 | args: [--maxkb=500] 54 | - id: check-executables-have-shebangs 55 | - id: trailing-whitespace 56 | - id: fix-byte-order-marker 57 | - id: mixed-line-ending 58 | - id: requirements-txt-fixer 59 | - id: end-of-file-fixer 60 | - id: detect-private-key 61 | - repo: https://github.com/pre-commit/pygrep-hooks 62 | rev: v1.10.0 63 | hooks: 64 | - id: python-use-type-annotations 65 | - id: python-check-blanket-noqa 66 | - id: python-check-blanket-type-ignore 67 | - id: python-check-mock-methods 68 | - id: python-no-eval 69 | - id: python-no-log-warn 70 | - id: rst-backticks 71 | - id: rst-directive-colons 72 | - id: rst-inline-touching-normal 73 | - id: text-unicode-replacement-char 74 | - repo: https://github.com/psf/black 75 | rev: 25.11.0 76 | hooks: 77 | - id: black 78 | - repo: https://github.com/rbubley/mirrors-prettier 79 | rev: v3.6.2 80 | hooks: 81 | - id: prettier 82 | - repo: https://github.com/PyCQA/isort # TODO: remove as soon as ruff is stable 83 | rev: 7.0.0 84 | hooks: 85 | - id: isort 86 | args: 87 | - --profile=black 88 | - repo: https://github.com/PyCQA/bandit 89 | rev: 1.9.1 90 | hooks: 91 | - id: bandit 92 | exclude: "^tests/.*|examples/.*" 93 | - repo: https://github.com/yunojuno/pre-commit-xenon 94 | rev: v0.1 95 | hooks: 96 | - id: xenon 97 | args: 98 | - --max-average=A 99 | - --max-modules=A 100 | - --max-absolute=C 101 | - --exclude=src/kraken/spot/ws_client.py 102 | -------------------------------------------------------------------------------- /tests/spot/test_xstocks_basic.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2025 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | 8 | """Module that implements the unit tests for the Spot xStocks features.""" 9 | 10 | from typing import ClassVar, Self 11 | 12 | import pytest 13 | 14 | from kraken.exceptions import KrakenPermissionDeniedError 15 | from kraken.spot import Market, SpotClient, Trade 16 | 17 | 18 | @pytest.mark.spot 19 | @pytest.mark.xstocks 20 | @pytest.mark.skip(reason="xStocks is only available in certain regions!") 21 | class TestSpotXStocks: 22 | """Test class for Spot xStocks client functionality.""" 23 | 24 | SAVE_ORDER: ClassVar[dict[str, str]] = { 25 | "pair": "AAPLxUSD", 26 | "ordertype": "limit", 27 | "volume": "1", 28 | "price": "100.0", 29 | "validate": True, 30 | } 31 | 32 | def test_get_asset_pairs_market_client( 33 | self: Self, 34 | xstocks_market_client: Market, 35 | ) -> None: 36 | """ 37 | Checks the ``get_asset_pairs`` endpoint by performing a valid request 38 | and validating that the response does not contain the error key. 39 | """ 40 | # internal exception handling would catch any errors 41 | result = xstocks_market_client.get_asset_pairs( 42 | extra_params={"aclass_base": "tokenized_asset"}, 43 | ) 44 | assert isinstance(result, dict), result 45 | 46 | def test_get_asset_pairs_spot_client( 47 | self: Self, 48 | xstocks_client: SpotClient, 49 | ) -> None: 50 | """ 51 | Checks the ``get_asset_pairs`` endpoint by performing a valid request 52 | and validating that the response does not contain the error key. 53 | """ 54 | # internal exception handling would catch any errors 55 | result = xstocks_client.request( 56 | "GET", 57 | "/0/public/AssetPairs", 58 | params={"aclass_base": "tokenized_asset"}, 59 | auth=False, 60 | ) 61 | assert isinstance(result, dict), result 62 | 63 | @pytest.mark.spot_auth 64 | def test_create_order_trade_client( 65 | self: Self, 66 | xstocks_trade_client: Trade, 67 | ) -> None: 68 | """Checks if the endpoint is basically available.""" 69 | with pytest.raises( 70 | KrakenPermissionDeniedError, 71 | match=r".*API key doesn't have permission to make this request.*", 72 | ): 73 | xstocks_trade_client.create_order( 74 | **self.SAVE_ORDER, 75 | side="buy", 76 | extra_params={"asset_class": "tokenized_asset"}, 77 | ) 78 | 79 | @pytest.mark.spot_auth 80 | def test_create_order_spot_client( 81 | self: Self, 82 | xstocks_client: SpotClient, 83 | ) -> None: 84 | """Checks if the endpoint is basically available.""" 85 | with pytest.raises( 86 | KrakenPermissionDeniedError, 87 | match=r".*API key doesn't have permission to make this request.*", 88 | ): 89 | xstocks_client.request( 90 | "POST", 91 | "/0/private/AddOrder", 92 | params={"type": "buy", "asset_class": "tokenized_asset"} 93 | | self.SAVE_ORDER, 94 | ) 95 | -------------------------------------------------------------------------------- /.github/workflows/scorecard.yml: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # This workflow uses actions that are not certified by GitHub. They are provided 3 | # by a third-party and are governed by separate terms of service, privacy 4 | # policy, and support documentation. 5 | 6 | name: Scorecard supply-chain security 7 | 8 | on: 9 | # For Branch-Protection check. Only the default branch is supported. See 10 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection 11 | branch_protection_rule: 12 | # To guarantee Maintained check is occasionally updated. See 13 | # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained 14 | schedule: 15 | - cron: "29 19 * * 5" 16 | push: 17 | branches: ["master"] 18 | 19 | # Declare default permissions as read only. 20 | permissions: read-all 21 | 22 | jobs: 23 | analysis: 24 | name: Scorecard analysis 25 | runs-on: ubuntu-latest 26 | permissions: 27 | # Needed to upload the results to code-scanning dashboard. 28 | security-events: write 29 | # Needed to publish results and get a badge (see publish_results below). 30 | id-token: write 31 | # Uncomment the permissions below if installing in a private repository. 32 | # contents: read 33 | # actions: read 34 | 35 | steps: 36 | - name: Harden Runner 37 | uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 38 | with: 39 | egress-policy: audit 40 | 41 | - name: "Checkout code" 42 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 43 | with: 44 | persist-credentials: false 45 | 46 | - name: "Run analysis" 47 | uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 48 | with: 49 | results_file: results.sarif 50 | results_format: sarif 51 | # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: 52 | # - you want to enable the Branch-Protection check on a *public* repository, or 53 | # - you are installing Scorecard on a *private* repository 54 | # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. 55 | # repo_token: ${{ secrets.SCORECARD_TOKEN }} 56 | 57 | # Public repositories: 58 | # - Publish results to OpenSSF REST API for easy access by consumers 59 | # - Allows the repository to include the Scorecard badge. 60 | # - See https://github.com/ossf/scorecard-action#publishing-results. 61 | # For private repositories: 62 | # - `publish_results` will always be set to `false`, regardless 63 | # of the value entered here. 64 | publish_results: true 65 | 66 | # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF 67 | # format to the repository Actions tab. 68 | - name: "Upload artifact" 69 | uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 70 | with: 71 | name: SARIF file 72 | path: results.sarif 73 | retention-days: 5 74 | 75 | # Upload the results to GitHub's code scanning dashboard. 76 | - name: "Upload to code-scanning" 77 | uses: github/codeql-action/upload-sarif@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5 78 | with: 79 | sarif_file: results.sarif 80 | -------------------------------------------------------------------------------- /doc/03_examples/rest_examples/spot_rest_examples.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | .. _section-spot-rest-examples: 20 | 21 | Spot REST 22 | --------- 23 | 24 | The examples presented below serve to demonstrate the usage of the Spot 25 | REST clients provided by `python-kraken-sdk`_ to access `Kraken`_'s REST API. 26 | 27 | For questions, feedback, additions, suggestions for improvement or problems 28 | `python-kraken-sdk/discussions`_ or `python-kraken-sdk/issues`_ may be helpful. 29 | 30 | See https://docs.kraken.com/api/docs/guides/global-intro for information about 31 | the available endpoints and their usage. 32 | 33 | The Spot client provides access to all un-and authenticated endpoints of 34 | Kraken's Spot API. 35 | 36 | .. code-block:: python 37 | :linenos: 38 | :caption: Example: Spot Client Usage (1) 39 | 40 | from kraken.spot import SpotClient 41 | 42 | client = SpotClient(key="", secret="") 43 | print(client.request("POST", "/0/private/Balance")) 44 | 45 | The async Spot client allows for asynchronous access to Kraken's Spot API 46 | endpoints. Below are two examples demonstrating its usage. 47 | 48 | Using SpotAsyncClient without a context manager; In this example, the client is 49 | manually closed after the request is made. 50 | 51 | .. code-block:: python 52 | :linenos: 53 | :caption: Example: Spot Client Usage (2) 54 | 55 | import asyncio 56 | from kraken.spot import SpotAsyncClient 57 | 58 | async def main(): 59 | client = SpotAsyncClient(key="", secret="") 60 | try: 61 | response = await client.request("POST", "/0/private/Balance") 62 | print(response) 63 | finally: 64 | await client.async_close() 65 | 66 | asyncio.run(main()) 67 | 68 | Using SpotAsyncClient as context manager; This example demonstrates the use of 69 | the context manager, which ensures the client is automatically closed after the 70 | request is completed. 71 | 72 | .. code-block:: python 73 | :linenos: 74 | :caption: Example: Spot Client Usage (3) 75 | 76 | import asyncio 77 | from kraken.spot import SpotAsyncClient 78 | 79 | async def main(): 80 | async with SpotAsyncClient( 81 | key="", secret="" 82 | ) as client: 83 | response = await client.request("POST", "/0/private/Balance") 84 | print(response) 85 | 86 | asyncio.run(main()) 87 | 88 | The following legacy examples are not maintained on a regular basis. They serve 89 | only for demonstration purposes - make sure to checkout the documentation of the 90 | individual functions. 91 | 92 | .. literalinclude:: ../../../examples/spot_examples.py 93 | :language: python 94 | :linenos: 95 | :caption: Example usage of Spot REST clients 96 | -------------------------------------------------------------------------------- /.github/workflows/_codeql.yaml: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # For most projects, this workflow file will not need changing; you simply need 3 | # to commit it to your repository. 4 | # 5 | 6 | name: CodeQL 7 | 8 | on: 9 | workflow_call: 10 | 11 | # Don't change this permissions. These must match those of the analyze job. 12 | permissions: 13 | actions: read 14 | contents: read 15 | security-events: write 16 | 17 | jobs: 18 | analyze: 19 | name: Analyze 20 | runs-on: ubuntu-latest 21 | permissions: 22 | actions: read 23 | contents: read 24 | security-events: write 25 | 26 | strategy: 27 | fail-fast: false 28 | matrix: 29 | language: ["python"] 30 | # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python', 'ruby' ] 31 | # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support 32 | 33 | steps: 34 | - name: Harden Runner 35 | uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 36 | with: 37 | egress-policy: audit 38 | disable-sudo: true 39 | allowed-endpoints: > 40 | api.github.com:443 41 | api.securityscorecards.dev 42 | github.com:443 43 | uploads.github.com:443 44 | 45 | - name: Checkout repository 46 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 47 | 48 | - name: "Dependency Review" 49 | if: github.event_name == 'pull_request' 50 | uses: actions/dependency-review-action@40c09b7dc99638e5ddb0bfd91c1673effc064d8a # v4.8.1 51 | 52 | # Initializes the CodeQL tools for scanning. 53 | - name: Initialize CodeQL 54 | uses: github/codeql-action/init@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5 55 | with: 56 | languages: python 57 | # If you wish to specify custom queries, you can do so here or in a config file. 58 | # By default, queries listed here will override any specified in a config file. 59 | # Prefix the list here with "+" to use these queries and those in the config file. 60 | 61 | # Details on CodeQL's query packs refer to : https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs 62 | queries: security-extended,security-and-quality 63 | 64 | # Autobuild attempts to build any compiled languages (C/C++, C#, Go, or Java). 65 | # If this step fails, then you should remove it and run the build manually (see below) 66 | - name: Autobuild 67 | uses: github/codeql-action/autobuild@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5 68 | 69 | # ℹ️ Command-line programs to run using the OS shell. 70 | # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun 71 | 72 | # If the Autobuild fails above, remove it and uncomment the following three lines. 73 | # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. 74 | 75 | # - run: | 76 | # echo "Run, Build Application using script" 77 | # ./location_of_script_within_repo/buildscript.sh 78 | 79 | - name: Perform CodeQL Analysis 80 | uses: github/codeql-action/analyze@0499de31b99561a6d14a36a5f662c2a54f91beee # v3.29.5 81 | with: 82 | category: "/language:python" 83 | -------------------------------------------------------------------------------- /doc/03_examples/rest_examples/futures_rest_examples.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | .. _section-futures-rest-examples: 20 | 21 | Futures REST 22 | ------------ 23 | 24 | The examples presented below serve to demonstrate the usage of the Futures 25 | REST clients provided by `python-kraken-sdk`_ to access `Kraken`_'s REST API. 26 | 27 | For questions, feedback, additions, suggestions for improvement or problems 28 | `python-kraken-sdk/discussions`_ or `python-kraken-sdk/issues`_ may be helpful. 29 | 30 | See https://docs.kraken.com/api/docs/guides/global-intro for information about 31 | the available endpoints and their usage. 32 | 33 | The Futures client provides access to all un-and authenticated endpoints of 34 | Kraken's Futures API. 35 | 36 | .. code-block:: python 37 | :linenos: 38 | :caption: Example: Spot Client Usage (1) 39 | 40 | from kraken.futures import FuturesClient 41 | 42 | client = FuturesClient(key="", secret="") 43 | print(client.request("GET", "/derivatives/api/v3/accounts")) 44 | 45 | The async Futures client allows for asynchronous access to Kraken's Futures 46 | endpoints. Below are two examples demonstrating its usage. 47 | 48 | Using FuturesAsyncClient without a context manager; In this example, the client 49 | is manually closed after the request is made. 50 | 51 | .. code-block:: python 52 | :linenos: 53 | :caption: Example: Spot Client Usage (2) 54 | 55 | import asyncio 56 | from kraken.futures import FuturesAsyncClient 57 | 58 | async def main(): 59 | client = FuturesAsyncClient(key="", secret="") 60 | try: 61 | response = await client.request("GET", "/derivatives/api/v3/accounts") 62 | print(response) 63 | finally: 64 | await client.async_close() 65 | 66 | asyncio.run(main()) 67 | 68 | Using FuturesAsyncClient as context manager; This example demonstrates the use 69 | of the context manager, which ensures the client is automatically closed after 70 | the request is completed. 71 | 72 | .. code-block:: python 73 | :linenos: 74 | :caption: Example: Spot Client Usage (3) 75 | 76 | import asyncio 77 | from kraken.futures import FuturesAsyncClient 78 | 79 | async def main(): 80 | async with FuturesAsyncClient( 81 | key="", secret="" 82 | ) as client: 83 | response = await client.request("GET", "/derivatives/api/v3/accounts") 84 | print(response) 85 | 86 | asyncio.run(main()) 87 | 88 | The following legacy examples are not maintained on a regular basis. They serve 89 | only for demonstration purposes - make sure to checkout the documentation of the 90 | individual functions. 91 | 92 | .. literalinclude:: ../../../examples/futures_examples.py 93 | :language: python 94 | :linenos: 95 | :caption: Example usage of Futures REST clients 96 | -------------------------------------------------------------------------------- /tests/spot/test_spot_earn.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | 8 | """Module that implements the unit tests for the Spot Earn client.""" 9 | 10 | from typing import Any, Self 11 | 12 | import pytest 13 | 14 | from kraken.spot import Earn 15 | 16 | from .helper import is_not_error 17 | 18 | 19 | @pytest.mark.spot 20 | @pytest.mark.spot_auth 21 | @pytest.mark.spot_earn 22 | @pytest.mark.skip(reason="Tests do not have earn permission") 23 | class TestSpotEarn: 24 | """Test class for Spot Earn client functionality.""" 25 | 26 | TEST_AMOUNT = "1" 27 | TEST_STRATEGY_ID = "ESRFUO3-Q62XD-WIOIL7" 28 | TEST_ASSET = "DOT" 29 | 30 | def _assert_successful_operation(self: Self, result: Any) -> None: 31 | """Helper method to assert successful operations for allocation/deallocation.""" 32 | assert isinstance(result, bool) 33 | 34 | def _assert_successful_query(self: Self, result: Any) -> None: 35 | """Helper method to assert successful query operations.""" 36 | assert is_not_error(result) 37 | 38 | def test_allocate_earn_funds(self: Self, spot_auth_earn: Earn) -> None: 39 | """ 40 | Checks if the response of the ``allocate_earn_funds`` is of 41 | type bool which mean that the request was successful. 42 | """ 43 | result = spot_auth_earn.allocate_earn_funds( 44 | amount=self.TEST_AMOUNT, 45 | strategy_id=self.TEST_STRATEGY_ID, 46 | ) 47 | self._assert_successful_operation(result) 48 | 49 | def test_deallocate_earn_funds(self: Self, spot_auth_earn: Earn) -> None: 50 | """ 51 | Checks if the response of the ``deallocate_earn_funds`` is of 52 | type bool which mean that the request was successful. 53 | """ 54 | result = spot_auth_earn.deallocate_earn_funds( 55 | amount=self.TEST_AMOUNT, 56 | strategy_id=self.TEST_STRATEGY_ID, 57 | ) 58 | self._assert_successful_operation(result) 59 | 60 | def test_get_allocation_status(self: Self, spot_auth_earn: Earn) -> None: 61 | """ 62 | Checks if the response of the ``get_allocation_status`` does not contain a 63 | named error which mean that the request was successful. 64 | """ 65 | result = spot_auth_earn.get_allocation_status( 66 | strategy_id=self.TEST_STRATEGY_ID, 67 | ) 68 | self._assert_successful_query(result) 69 | 70 | def test_get_deallocation_status(self: Self, spot_auth_earn: Earn) -> None: 71 | """ 72 | Checks if the response of the ``get_deallocation_status`` does not contain a 73 | named error which mean that the request was successful. 74 | """ 75 | result = spot_auth_earn.get_deallocation_status( 76 | strategy_id=self.TEST_STRATEGY_ID, 77 | ) 78 | self._assert_successful_query(result) 79 | 80 | def test_list_earn_strategies(self: Self, spot_auth_earn: Earn) -> None: 81 | """ 82 | Checks if the response of the ``list_earn_strategies`` does not contain a 83 | named error which mean that the request was successful. 84 | """ 85 | result = spot_auth_earn.list_earn_strategies(asset=self.TEST_ASSET) 86 | self._assert_successful_query(result) 87 | 88 | def test_list_earn_allocations(self: Self, spot_auth_earn: Earn) -> None: 89 | """ 90 | Checks if the response of the ``list_earn_allocations`` does not contain a 91 | named error which mean that the request was successful. 92 | """ 93 | result = spot_auth_earn.list_earn_allocations(asset=self.TEST_ASSET) 94 | self._assert_successful_query(result) 95 | -------------------------------------------------------------------------------- /.github/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Welcome to python-kraken-sdk contributing guide 2 | 3 | Thank you for investing your time in contributing to the 4 | [python-kraken-sdk](https://github.com/btschwertfeger/python-kraken-sdk/) 5 | project! 🔥 6 | 7 | Read our [Code of Conduct](./CODE_OF_CONDUCT.md) to keep our community 8 | approachable and respectable. 9 | 10 | If you have any questions, comments, suggestions or general topics, please feel 11 | free to open or join a thread at 12 | [python-kraken-sdk/discussions](https://github.com/btschwertfeger/python-kraken-sdk/discussions). 13 | We are looking forward to nice conversations, strategy exchange and an knowledge 14 | transfer! 15 | 16 | ## Getting started 🚀 17 | 18 | In this guide you will get an overview of the contribution workflow from opening 19 | an issue, creating a PR, reviewing, and merging the PR. 20 | 21 | ### Issues 22 | 23 | #### Create a new issue 24 | 25 | If you have an issue that's not yet listed in the troubleshooting section of 26 | [README.md](https://github.com/btschwertfeger/python-kraken-sdk#readme) or in 27 | [documentation](https://python-kraken-sdk.readthedocs.io/en/stable), feel free 28 | to create a new 29 | [issue](https://github.com/btschwertfeger/python-kraken-sdk/issues) if there is 30 | no similar one listed among the existing ones. Also, for any features that are 31 | missing, this is the right place to request them. 32 | 33 | #### Solve an issue 34 | 35 | Scan through our [existing issues](https://github.com/btschwertfeger/python-kraken-sdk/issues) 36 | to find one that interests you. If the future brings more and more features or 37 | issues - you can also filter for specific `labels`. 38 | 39 | ### Make Changes 40 | 41 | 1. Fork the repository and clone your fork locally. 42 | 43 | ```bash 44 | git clone https://github.com//python-kraken-sdk.git 45 | ``` 46 | 47 | 2. Install the provided [pre-commit](https://pre-commit.com/) hooks within the 48 | repository and make sure that all hooks run through, before pushing changes. 49 | 50 | ```bash 51 | cd python-kraken-sdk 52 | python-kraken-sdk~$: pre-commit install 53 | ``` 54 | 55 | 3. Create a new branch and start implementing your changes. 56 | 57 | In the project provides a `Makefile` which offers many shortcuts to execute 58 | different commands. For example, you can use `make test` to run all unit 59 | tests or `make build` to build the package. `make dev` installs the 60 | python-kraken-sdk in editable state into the current environment. However, 61 | for development it is recommended to set up a virtual environment first. 62 | 63 | ### Commit your updates 🎬 64 | 65 | Once you're happy or reached some minor goal - commit the changes. Please take 66 | care to address **all** requirements of the [self-review 67 | checklist](./.github/self-review.md) before creating a PR to speed up the review 68 | process. ⚡️ 69 | 70 | ### Pull Request 71 | 72 | When you're finished with the changes, create a pull request. 73 | 74 | - All checks of the [self-review checklist](./.github/self-review.md) must be 75 | addressed. 76 | - Don't forget to link PR to an issue or create one to link if there is no 77 | existing issue. 78 | - You may asked for changes to be made before a PR can be merged, either using 79 | _suggested changes_ or pull request _comments_. You can make any other changes 80 | in your fork, then commit them to your branch. 81 | - As you update your PR and apply changes, mark each conversation as resolved. 82 | 83 | ### Your PR is merged! 🏅 84 | 85 | Great! We're happy and proud of any contribution made on this project. So you 86 | may want to start to work on the next issue? 🔥 87 | 88 | --- 89 | 90 | This file is based on the 91 | [CONTRIBUTING.md](https://github.com/github/docs/blob/v1.0.1/CONTRIBUTING.md) 92 | file provided by [GitHub Docs](https://github.com/github/docs). 93 | -------------------------------------------------------------------------------- /del.txt: -------------------------------------------------------------------------------- 1 | affine==2.4.0 2 | alabaster==0.7.12 3 | appdirs==1.4.4 4 | asciitree==0.3.3 5 | attrs==23.2.0 6 | Babel==2.10.3 7 | bcc==0.29.1 8 | bcrypt==3.2.2 9 | beautifulsoup4==4.12.3 10 | blinker==1.7.0 11 | blosc==1.11.1 12 | Bottleneck==1.3.5 13 | Brlapi==0.8.5 14 | Brotli==1.1.0 15 | cdo==1.6.0 16 | certifi==2023.11.17 17 | cftime==1.6.3 18 | chardet==5.2.0 19 | click==8.1.6 20 | click-plugins==1.1.1 21 | cligj==0.7.2 22 | cloud-init==25.2 23 | cloudpickle==3.0.0 24 | colorama==0.4.6 25 | colorlog==6.8.0 26 | command-not-found==0.3 27 | configobj==5.0.8 28 | contourpy==1.0.7 29 | cryptography==41.0.7 30 | cssselect==1.2.0 31 | cupshelpers==1.0 32 | cycler==0.11.0 33 | dask==2023.12.1+dfsg 34 | dbus-python==1.3.2 35 | decorator==5.1.1 36 | defer==1.0.6 37 | defusedxml==0.7.1 38 | distributed==0+unknown 39 | distro==1.9.0 40 | distro-info==1.7+build1 41 | dnspython==2.6.1 42 | docutils==0.20.1 43 | dropbox==11.36.2 44 | duplicity==2.1.4 45 | et-xmlfile==1.0.1 46 | fasteners==0.18 47 | fonttools==4.46.0 48 | freetype-py==2.4.0 49 | fs==2.4.16 50 | fsspec==2024.2.0 51 | fusepy==3.0.1 52 | gpg==1.18.0 53 | gyp==0.1 54 | h5netcdf==1.3.0 55 | h5py==3.10.0 56 | h5py._debian_h5py_serial==3.10.0 57 | html5lib==1.1 58 | httplib2==0.20.4 59 | idna==3.6 60 | imagesize==1.4.1 61 | importlib-metadata==4.12.0 62 | iniconfig==1.1.1 63 | jaraco.classes==3.2.1 64 | jeepney==0.8.0 65 | Jinja2==3.1.2 66 | jsonpatch==1.32 67 | jsonpointer==2.0 68 | jsonschema==4.10.3 69 | keyring==24.3.1 70 | kiwisolver==0.0.0 71 | language-selector==0.1 72 | launchpadlib==1.11.0 73 | lazr.restfulclient==0.14.6 74 | lazr.uri==1.0.6 75 | libarchive-c==2.9 76 | libevdev==0.5 77 | locket==1.0.0 78 | louis==3.29.0 79 | lxml==5.2.1 80 | lz4==4.0.2+dfsg 81 | Mako==1.3.2.dev0 82 | Markdown==3.5.2 83 | markdown-it-py==3.0.0 84 | MarkupSafe==2.1.5 85 | matplotlib==3.6.3 86 | mdurl==0.1.2 87 | monotonic==1.6 88 | more-itertools==10.2.0 89 | mpmath==0.0.0 90 | msgpack==1.0.3 91 | netaddr==0.8.0 92 | netCDF4==1.6.5 93 | numcodecs==0.12.1+ds 94 | numexpr==2.9.0 95 | numpy==1.26.4 96 | oauthlib==3.2.2 97 | odfpy==1.4.2 98 | olefile==0.46 99 | openpyxl==3.1.2 100 | packaging==24.0 101 | pandas==2.1.4+dfsg 102 | paramiko==2.12.0 103 | partd==1.4.1 104 | pexpect==4.9.0 105 | pillow==10.2.0 106 | pluggy==1.4.0 107 | ply==3.11 108 | psutil==5.9.8 109 | ptyprocess==0.7.0 110 | py==1.11.0 111 | py-cpuinfo==9.0.0 112 | pycairo==1.25.1 113 | pycups==2.0.1 114 | pydata-sphinx-theme==0.7.2 115 | pygit2==1.14.1 116 | Pygments==2.17.2 117 | PyGObject==3.48.2 118 | PyJWT==2.7.0 119 | PyNaCl==1.5.0 120 | pyparsing==3.1.1 121 | pyrsistent==0.20.0 122 | pyserial==3.5 123 | pytest==7.4.4 124 | python-apt==2.7.7+ubuntu5 125 | python-dateutil==2.8.2 126 | python-debian==0.1.49+ubuntu2 127 | pytz==2024.1 128 | pyudev==0.24.0 129 | pyxdg==0.28 130 | PyYAML==6.0.1 131 | pyzmq==24.0.1 132 | rasterio==1.3.9 133 | reportlab==4.1.0 134 | requests==2.31.0 135 | rich==13.7.1 136 | rlPyCairo==0.3.0 137 | roman==3.3 138 | SciPy==1.11.4 139 | screen-resolution-extra==0.0.0 140 | SecretStorage==3.3.3 141 | setuptools==68.1.2 142 | Shredder===2.9.0 Odd Olm 143 | six==1.16.0 144 | snowballstemmer==2.2.0 145 | snuggs==1.4.7 146 | sortedcontainers==2.4.0 147 | soupsieve==2.5 148 | Sphinx==7.2.6 149 | sphinx-copybutton==0.4.0 150 | stone==3.3.1 151 | sympy==1.12 152 | systemd-python==235 153 | tables==3.9.2 154 | tblib==3.0.0 155 | toolz==0.12.0 156 | tornado==6.4 157 | typing_extensions==4.10.0 158 | ubuntu-drivers-common==0.0.0 159 | ubuntu-pro-client==8001 160 | ufoLib2==0.16.0 161 | ufw==0.36.2 162 | unattended-upgrades==0.1 163 | unicodedata2==15.1.0 164 | urllib3==2.0.7 165 | usb-creator==0.3.16 166 | wadllib==1.3.6 167 | webencodings==0.5.1 168 | wheel==0.42.0 169 | xarray==999 170 | xdg==5 171 | xkit==0.0.0 172 | zarr==2.17.0 173 | zict==3.0.0 174 | zipp==1.0.0 175 | -------------------------------------------------------------------------------- /.github/workflows/_codecov.yaml: -------------------------------------------------------------------------------- 1 | # -*- mode: yaml; coding: utf-8 -*- 2 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 3 | # All rights reserved. 4 | # https://github.com/btschwertfeger 5 | # 6 | # Template workflow to build the project for a specific os 7 | # and Python version, run the tests and upload the results to codecov. 8 | # 9 | 10 | name: CodeCov 11 | 12 | on: 13 | workflow_call: 14 | inputs: 15 | os: 16 | type: string 17 | required: true 18 | python-version: 19 | type: string 20 | required: true 21 | secrets: 22 | CODECOV_TOKEN: 23 | required: true 24 | SPOT_API_KEY: 25 | required: true 26 | SPOT_SECRET_KEY: 27 | required: true 28 | XSTOCKS_API_KEY: 29 | required: true 30 | XSTOCKS_SECRET_KEY: 31 | required: true 32 | FUTURES_API_KEY: 33 | required: true 34 | FUTURES_SECRET_KEY: 35 | required: true 36 | FUTURES_SANDBOX_KEY: 37 | required: true 38 | FUTURES_SANDBOX_SECRET: 39 | required: true 40 | 41 | permissions: 42 | contents: read 43 | 44 | jobs: 45 | CodeCov: 46 | name: Coverage 47 | runs-on: ${{ inputs.os }} 48 | timeout-minutes: 30 49 | env: 50 | OS: ${{ inputs.os }} 51 | PYTHON: ${{ inputs.python-version }} 52 | 53 | steps: 54 | - name: Harden Runner 55 | uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 56 | with: 57 | disable-sudo: true 58 | egress-policy: audit 59 | 60 | - name: Checkout repository 61 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 62 | 63 | - name: Download the built wheel 64 | uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 65 | with: 66 | name: python-package-distributions 67 | path: dist/ 68 | 69 | - name: Set up Python ${{ inputs.python-version }} 70 | uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 71 | with: 72 | python-version: ${{ inputs.python-version }} 73 | 74 | - name: Install uv 75 | uses: astral-sh/setup-uv@v7 76 | 77 | - name: Install package 78 | run: | 79 | uv venv 80 | source .venv/bin/activate 81 | echo ${GITHUB_WORKSPACE}/.venv/bin >> $GITHUB_PATH 82 | uv pip install "$(find dist -name '*.whl' | head -1)" -r requirements-dev.txt 83 | 84 | - name: Generate coverage report 85 | env: 86 | SPOT_API_KEY: ${{ secrets.SPOT_API_KEY }} 87 | SPOT_SECRET_KEY: ${{ secrets.SPOT_SECRET_KEY }} 88 | XSTOCKS_API_KEY: ${{ secrets.XSTOCKS_API_KEY }} 89 | XSTOCKS_SECRET_KEY: ${{ secrets.XSTOCKS_SECRET_KEY }} 90 | FUTURES_API_KEY: ${{ secrets.FUTURES_API_KEY }} 91 | FUTURES_SECRET_KEY: ${{ secrets.FUTURES_SECRET_KEY }} 92 | FUTURES_SANDBOX_KEY: ${{ secrets.FUTURES_SANDBOX_KEY }} 93 | FUTURES_SANDBOX_SECRET: ${{ secrets.FUTURES_SANDBOX_SECRET }} 94 | run: pytest -vv -x --cov=kraken --cov-report=xml:coverage.xml --cov-report=term tests 95 | 96 | - name: Export coverage report 97 | uses: actions/upload-artifact@v5 98 | with: 99 | name: coverage 100 | path: coverage.xml 101 | 102 | - name: Upload coverage to Codecov 103 | uses: codecov/codecov-action@5a1091511ad55cbe89839c7260b706298ca349f7 # v5.5.1 104 | with: 105 | token: ${{ secrets.CODECOV_TOKEN }} 106 | files: coverage.xml 107 | env_vars: OS,PYTHON 108 | fail_ci_if_error: false 109 | flags: unittests 110 | name: codecov-umbrella 111 | verbose: true 112 | -------------------------------------------------------------------------------- /.github/workflows/_build.yaml: -------------------------------------------------------------------------------- 1 | # -*- mode: yaml; coding: utf-8 -*- 2 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 3 | # All rights reserved. 4 | # https://github.com/btschwertfeger 5 | # 6 | # Template workflow to build the project for a specific os 7 | # and Python version. 8 | # 9 | 10 | name: Build 11 | 12 | on: 13 | workflow_call: 14 | inputs: 15 | os: 16 | type: string 17 | required: true 18 | python-version: 19 | type: string 20 | required: true 21 | 22 | permissions: 23 | contents: read 24 | 25 | jobs: 26 | Build: 27 | runs-on: ${{ inputs.os }} 28 | steps: 29 | - name: Harden Runner 30 | uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 31 | with: 32 | disable-sudo: true 33 | egress-policy: block 34 | allowed-endpoints: > 35 | api.github.com:443 36 | files.pythonhosted.org:443 37 | github.com:443 38 | objects.githubusercontent.com:443 39 | pypi.org:443 40 | release-assets.githubusercontent.com:443 41 | 42 | - name: Checkout repository 43 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 44 | with: 45 | fetch-depth: 0 # IMPORTANT: otherwise the current tag does not get fetched and the build version gets worse 46 | 47 | - name: Set up Python ${{ inputs.python-version }} 48 | uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 49 | with: 50 | python-version: ${{ inputs.python-version }} 51 | 52 | - name: Install uv 53 | uses: astral-sh/setup-uv@v7 54 | 55 | - name: Check git status (not Windows) 56 | if: runner.os != 'Windows' 57 | run: | 58 | if [[ -z "$(git status --porcelain)" ]]; then 59 | echo "No changes found." 60 | else 61 | echo "Changes detected. Please commit or discard changes before publishing." 62 | git status --porcelain 63 | exit 1 64 | fi 65 | 66 | - name: Check git status (Windows) 67 | if: runner.os == 'Windows' 68 | run: | 69 | if (-not (git status --porcelain)) { 70 | Write-Output "No changes found." 71 | } else { 72 | Write-Output "Changes detected. Please commit or discard changes before publishing." 73 | git status --porcelain 74 | exit 1 75 | } 76 | 77 | - name: Build Linux 78 | if: runner.os == 'linux' 79 | run: | 80 | uv build . 81 | uv tool install dist/python_kraken_sdk*.whl 82 | uv run kraken --version 83 | 84 | - name: Build macOS 85 | if: runner.os == 'macOS' 86 | run: | 87 | uv build . 88 | uv tool install dist/python_kraken_sdk*.whl 89 | uv run kraken --version 90 | 91 | - name: Build Windows 92 | if: runner.os == 'Windows' 93 | run: | 94 | uv build . 95 | try { 96 | $WHEEL = Get-ChildItem -Path ./dist -Filter "python_kraken_sdk*.whl" -ErrorAction Stop 97 | uv tool install $WHEEL# 98 | uv run kraken --version 99 | } catch { 100 | Write-Error "Error: .whl file not found in .\dist." 101 | exit 1 102 | } 103 | 104 | - name: Store the distribution packages 105 | uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0 106 | # upload artifacts with the oldest supported version 107 | if: runner.os == 'linux' && inputs.python-version == '3.11' 108 | with: 109 | name: python-package-distributions 110 | path: dist/ 111 | -------------------------------------------------------------------------------- /doc/conf.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # https://github.com/btschwertfeger 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | # 19 | # Configuration file for the Sphinx documentation builder. 20 | # 21 | # For the full list of built-in configuration values, see the documentation: 22 | # https://www.sphinx-doc.org/en/master/usage/configuration.html 23 | # pylint: disable=invalid-name 24 | 25 | """ 26 | This module is the configuration for the Sphinx documentation building process. 27 | """ 28 | 29 | import sys 30 | from pathlib import Path 31 | from shutil import copyfile 32 | from typing import Any 33 | 34 | # -- Project information ----------------------------------------------------- 35 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information 36 | 37 | project = "python-kraken-sdk" 38 | copyright = "2023, Benjamin Thomas Schwertfeger" # noqa: A001 # pylint: disable=redefined-builtin 39 | author = "Benjamin Thomas Schwertfeger" 40 | 41 | # Add the package to sys.path: 42 | sys.path.insert(0, str(Path("..").resolve() / "src")) 43 | 44 | rst_epilog = "" 45 | # Read link all targets from file 46 | with Path("links.rst").open(encoding="utf-8") as f: 47 | rst_epilog += f.read() 48 | 49 | 50 | def setup(app: Any) -> None: # noqa: ARG001,ANN401 51 | """Setup function to modify doc building""" 52 | copyfile( 53 | Path("..") / "examples" / "market_client_example.ipynb", 54 | Path("03_examples") / "market_client_example.ipynb", 55 | ) 56 | 57 | 58 | # -- General configuration --------------------------------------------------- 59 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration 60 | 61 | extensions = [ 62 | "nbsphinx", 63 | "sphinx_click", 64 | "sphinx.ext.autodoc", 65 | "sphinx.ext.doctest", 66 | "sphinx.ext.intersphinx", 67 | "sphinx.ext.coverage", 68 | "sphinx.ext.napoleon", 69 | "sphinx.ext.autosectionlabel", 70 | "IPython.sphinxext.ipython_console_highlighting", 71 | ] 72 | 73 | templates_path = ["_templates"] 74 | exclude_patterns = [ 75 | "_build", 76 | "Thumbs.db", 77 | ".DS_Store", 78 | "links.rst", 79 | "**.ipynb_checkpoints", 80 | ] 81 | 82 | # -- Options for HTML output ------------------------------------------------- 83 | # https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output 84 | 85 | html_theme = "furo" # "sphinx_book_theme" # "sphinx_rtd_theme" 86 | # html_static_path = ["_static"] # for images 87 | html_context = { 88 | "display_github": True, 89 | "github_user": "btschwertfeger", 90 | "github_repo": "python-kraken-sdk", 91 | "github_version": "master/doc/", 92 | } 93 | html_theme_options = { 94 | # "sidebar_hide_name": True, 95 | "light_css_variables": { 96 | "color-brand-primary": "#336790", 97 | "color-brand-content": "#336790", 98 | }, 99 | "dark_css_variables": { 100 | "color-brand-primary": "#E5B62F", 101 | "color-brand-content": "#E5B62F", 102 | }, 103 | "source_repository": "https://github.com/btschwertfeger/python-kraken-sdk/", 104 | "source_branch": "master", 105 | "source_directory": "doc/", 106 | } 107 | -------------------------------------------------------------------------------- /.github/workflows/_test_spot_private.yaml: -------------------------------------------------------------------------------- 1 | # -*- mode: yaml; coding: utf-8 -*- 2 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 3 | # All rights reserved. 4 | # https://github.com/btschwertfeger 5 | # 6 | # Template workflow to test the private Spot endpoints of the 7 | # python-kraken-sdk. 8 | # 9 | # Runs tests for: 10 | # * Spot REST clients 11 | # * Spot websocket clients 12 | # 13 | 14 | name: Test Spot 15 | 16 | on: 17 | workflow_call: 18 | inputs: 19 | os: 20 | type: string 21 | required: true 22 | python-version: 23 | type: string 24 | required: true 25 | secrets: 26 | SPOT_API_KEY: 27 | required: true 28 | SPOT_SECRET_KEY: 29 | required: true 30 | XSTOCKS_API_KEY: 31 | required: true 32 | XSTOCKS_SECRET_KEY: 33 | required: true 34 | 35 | permissions: 36 | contents: read 37 | 38 | jobs: 39 | Test-Spot: 40 | name: Test ${{ inputs.os }} ${{ inputs.python-version }} 41 | runs-on: ${{ inputs.os }} 42 | timeout-minutes: 10 43 | concurrency: 44 | group: test_spot_private 45 | cancel-in-progress: true 46 | steps: 47 | - name: Harden Runner 48 | uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 49 | with: 50 | disable-sudo: true 51 | egress-policy: block 52 | allowed-endpoints: > 53 | api.github.com:443 54 | api.kraken.com:443 55 | files.pythonhosted.org:443 56 | github.com:443 57 | objects.githubusercontent.com:443 58 | pypi.org:443 59 | release-assets.githubusercontent.com:443 60 | ws-auth.kraken.com:443 61 | ws.kraken.com:443 62 | 63 | - name: Checkout repository 64 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 65 | 66 | - name: Download the built wheel 67 | uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 68 | with: 69 | name: python-package-distributions 70 | path: dist/ 71 | 72 | - name: Set up Python ${{ inputs.python-version }} 73 | uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 74 | with: 75 | python-version: ${{ inputs.python-version }} 76 | 77 | - name: Install uv 78 | uses: astral-sh/setup-uv@v7 79 | 80 | - name: Install package (Linux or macOS) 81 | if: runner.os != 'Windows' 82 | run: | 83 | uv venv 84 | source .venv/bin/activate 85 | echo ${GITHUB_WORKSPACE}/.venv/bin >> $GITHUB_PATH 86 | uv pip install "$(find dist -name '*.whl' | head -1)" -r requirements-dev.txt 87 | 88 | - name: Install package (Windows) 89 | if: runner.os == 'Windows' 90 | run: | 91 | uv venv 92 | .venv\Scripts\activate.ps1 93 | echo "$env:GITHUB_WORKSPACE\.venv\Scripts" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 94 | $wheel = (Get-ChildItem -Path dist -Filter "*.whl" | Select-Object -First 1).FullName 95 | uv pip install $wheel -r requirements-dev.txt 96 | 97 | - name: Testing Spot REST endpoints 98 | env: 99 | SPOT_API_KEY: ${{ secrets.SPOT_API_KEY }} 100 | SPOT_SECRET_KEY: ${{ secrets.SPOT_SECRET_KEY }} 101 | XSTOCKS_API_KEY: ${{ secrets.XSTOCKS_API_KEY }} 102 | XSTOCKS_SECRET_KEY: ${{ secrets.XSTOCKS_SECRET_KEY }} 103 | run: pytest -vv -x -m "spot and spot_auth and not spot_websocket" tests 104 | 105 | - name: Testing Spot websocket client 106 | env: 107 | SPOT_API_KEY: ${{ secrets.SPOT_API_KEY }} 108 | SPOT_SECRET_KEY: ${{ secrets.SPOT_SECRET_KEY }} 109 | run: pytest -vv -x -m "spot and spot_auth and spot_websocket" tests 110 | -------------------------------------------------------------------------------- /tests/spot/helper.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | 8 | """ 9 | Module that implements the unit tests for the Kraken Spot Websocket API v2 10 | client. 11 | """ 12 | 13 | from __future__ import annotations 14 | 15 | import json 16 | import logging 17 | from pathlib import Path 18 | 19 | from kraken.spot import SpotOrderBookClient, SpotWSClient 20 | 21 | FIXTURE_DIR: Path = Path(__file__).resolve().parent / "fixture" 22 | CACHE_DIR: Path = Path(__file__).resolve().parent.parent.parent / ".cache" / "tests" 23 | CACHE_DIR.mkdir(parents=True, exist_ok=True) 24 | 25 | 26 | def is_not_error( 27 | value: object | dict | set | tuple | list | str | float | None, 28 | ) -> bool: 29 | """Returns True if 'error' as key not in dict.""" 30 | return isinstance(value, dict) and "error" not in value 31 | 32 | 33 | class SpotWebsocketClientTestWrapper(SpotWSClient): 34 | """ 35 | Class that creates an instance to test the SpotWSClient. 36 | 37 | It writes the messages to the log and a file. The log is used 38 | within the tests, the log file is for local debugging. 39 | """ 40 | 41 | LOG: logging.Logger = logging.getLogger(__name__) 42 | 43 | def __init__( 44 | self: SpotWebsocketClientTestWrapper, 45 | key: str = "", 46 | secret: str = "", 47 | **kwargs: dict | str | float | bool | None, 48 | ) -> None: 49 | super().__init__(key=key, secret=secret, callback=self.on_message, **kwargs) 50 | self.LOG.setLevel(logging.INFO) 51 | fh = logging.FileHandler(filename=CACHE_DIR / "spot_ws-v2.log", mode="a") 52 | fh.setLevel(logging.INFO) 53 | self.LOG.addHandler(fh) 54 | 55 | async def on_message(self: SpotWebsocketClientTestWrapper, message: dict) -> None: 56 | """ 57 | This is the callback function that must be implemented 58 | to handle custom websocket messages. 59 | """ 60 | self.LOG.info(json.dumps(message)) # the log is read within the tests 61 | 62 | 63 | class SpotOrderBookClientWrapper(SpotOrderBookClient): 64 | """ 65 | This class is used for testing the Spot SpotOrderBookClient. 66 | 67 | It writes the messages to the log and a file. The log is used 68 | within the tests, the log file is for local debugging. 69 | """ 70 | 71 | LOG: logging.Logger = logging.getLogger(__name__) 72 | 73 | def __init__(self: SpotOrderBookClientWrapper) -> None: 74 | super().__init__() 75 | self.LOG.setLevel(logging.INFO) 76 | 77 | async def on_message(self: SpotOrderBookClientWrapper, message: dict) -> None: 78 | self.ensure_log(message) 79 | await super().on_message(message=message) 80 | 81 | async def on_book_update( 82 | self: SpotOrderBookClientWrapper, 83 | pair: str, 84 | message: dict, 85 | ) -> None: 86 | """ 87 | This is the callback function that must be implemented 88 | to handle custom websocket messages. 89 | """ 90 | self.ensure_log((pair, message)) 91 | 92 | @classmethod 93 | def ensure_log(cls, content: dict | list) -> None: 94 | """ 95 | Ensures that the messages are logged. 96 | Into a file for debugging and general to the log 97 | to read the logs within the unit tests. 98 | """ 99 | cls.LOG.info(json.dumps(content)) 100 | 101 | log: str = "" 102 | try: 103 | with Path(CACHE_DIR / "spot_orderbook-2.log").open( 104 | mode="r", 105 | encoding="utf-8", 106 | ) as logfile: 107 | log = logfile.read() 108 | except FileNotFoundError: 109 | pass 110 | 111 | with Path(CACHE_DIR / "spot_orderbook.log").open( 112 | mode="a", 113 | encoding="utf-8", 114 | ) as logfile: 115 | logfile.write(f"{log}\n{json.dumps(content)}") 116 | -------------------------------------------------------------------------------- /.github/workflows/_test_futures_private.yaml: -------------------------------------------------------------------------------- 1 | # -*- mode: yaml; coding: utf-8 -*- 2 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 3 | # All rights reserved. 4 | # https://github.com/btschwertfeger 5 | # 6 | # Template workflow to test the private Futures endpoints 7 | # of the python-kraken-sdk. 8 | # Runs tests for: 9 | # * Futures REST clients 10 | # * Futures websocket client 11 | # 12 | 13 | name: Test Futures 14 | 15 | on: 16 | workflow_call: 17 | inputs: 18 | os: 19 | type: string 20 | required: true 21 | python-version: 22 | type: string 23 | required: true 24 | secrets: 25 | FUTURES_API_KEY: 26 | required: true 27 | FUTURES_SECRET_KEY: 28 | required: true 29 | FUTURES_SANDBOX_KEY: 30 | required: true 31 | FUTURES_SANDBOX_SECRET: 32 | required: true 33 | 34 | permissions: 35 | contents: read 36 | 37 | jobs: 38 | Test-Futures: 39 | name: Test ${{ inputs.os }} ${{ inputs.python-version }} 40 | runs-on: ${{ inputs.os }} 41 | timeout-minutes: 7 42 | concurrency: 43 | group: test_futures_private 44 | cancel-in-progress: true 45 | steps: 46 | - name: Harden Runner 47 | uses: step-security/harden-runner@f4a75cfd619ee5ce8d5b864b0d183aff3c69b55a # v2.13.1 48 | with: 49 | disable-sudo: true 50 | egress-policy: block 51 | allowed-endpoints: > 52 | api.github.com:443 53 | demo-futures.kraken.com:443 54 | files.pythonhosted.org:443 55 | futures.kraken.com:443 56 | github.com:443 57 | objects.githubusercontent.com:443 58 | pypi.org:443 59 | release-assets.githubusercontent.com:443 60 | 61 | - name: Checkout repository 62 | uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 63 | 64 | - name: Download the built wheel 65 | uses: actions/download-artifact@018cc2cf5baa6db3ef3c5f8a56943fffe632ef53 # v6.0.0 66 | with: 67 | name: python-package-distributions 68 | path: dist/ 69 | 70 | - name: Set up Python ${{ inputs.python-version }} 71 | uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 72 | with: 73 | python-version: ${{ inputs.python-version }} 74 | 75 | - name: Install uv 76 | uses: astral-sh/setup-uv@v7 77 | 78 | - name: Install package (Linux or macOS) 79 | if: runner.os != 'Windows' 80 | run: | 81 | uv venv 82 | source .venv/bin/activate 83 | echo ${GITHUB_WORKSPACE}/.venv/bin >> $GITHUB_PATH 84 | uv pip install "$(find dist -name '*.whl' | head -1)" -r requirements-dev.txt 85 | 86 | - name: Install package (Windows) 87 | if: runner.os == 'Windows' 88 | run: | 89 | uv venv 90 | .venv\Scripts\activate.ps1 91 | echo "$env:GITHUB_WORKSPACE\.venv\Scripts" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append 92 | $wheel = (Get-ChildItem -Path dist -Filter "*.whl" | Select-Object -First 1).FullName 93 | uv pip install $wheel -r requirements-dev.txt 94 | 95 | - name: Testing Futures REST endpoints 96 | env: 97 | FUTURES_API_KEY: ${{ secrets.FUTURES_API_KEY }} 98 | FUTURES_SECRET_KEY: ${{ secrets.FUTURES_SECRET_KEY }} 99 | FUTURES_SANDBOX_KEY: ${{ secrets.FUTURES_SANDBOX_KEY }} 100 | FUTURES_SANDBOX_SECRET: ${{ secrets.FUTURES_SANDBOX_SECRET }} 101 | run: pytest -vv -x -m "futures and futures_auth and not futures_websocket" tests 102 | 103 | - name: Testing Futures websocket client 104 | env: 105 | FUTURES_API_KEY: ${{ secrets.FUTURES_API_KEY }} 106 | FUTURES_SECRET_KEY: ${{ secrets.FUTURES_SECRET_KEY }} 107 | FUTURES_SANDBOX_KEY: ${{ secrets.FUTURES_SANDBOX_KEY }} 108 | FUTURES_SANDBOX_SECRET: ${{ secrets.FUTURES_SANDBOX_SECRET }} 109 | run: pytest -vv -x -m "futures and futures_auth and futures_websocket" tests 110 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # -*- mode: make; coding: utf-8 -*- 2 | #!make 3 | # 4 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 5 | # All rights reserved. 6 | # https://github.com/btschwertfeger 7 | # 8 | 9 | UV ?= uv 10 | PYTHON := python 11 | PYTEST := $(UV) run pytest 12 | PYTEST_OPTS := -vv --junit-xml=pytest.xml 13 | PYTEST_COV_OPTS := $(PYTEST_OPTS) --cov=kraken --cov-report=xml:coverage.xml --cov-report=term-missing --cov-report=html 14 | TEST_DIR := tests 15 | 16 | ## ======= M A K E F I L E - T A R G E T S ===================================== 17 | ## help Show this help message 18 | ## 19 | .PHONY: help 20 | help: 21 | @grep "^##" Makefile | sed -e "s/##//" 22 | 23 | ## ======= B U I L D I N G ===================================================== 24 | ## build Builds the package 25 | ## 26 | .PHONY: build 27 | build: check-uv 28 | $(UV) build . 29 | 30 | ## rebuild Rebuild the package 31 | ## 32 | .PHONY: rebuild 33 | rebuild: clean build 34 | 35 | ## doc Build the documentation 36 | ## 37 | .PHONY: doc 38 | doc: 39 | cd doc && make html 40 | 41 | ## ======= I N S T A L L A T I O N ============================================= 42 | ## install Install the package 43 | ## 44 | .PHONY: install 45 | install: check-uv 46 | $(UV) pip install . 47 | 48 | ## dev Installs the extended package in edit mode 49 | ## 50 | .PHONY: dev 51 | dev: check-uv 52 | $(UV) pip install -e . -r requirements-dev.txt -r doc/requirements.txt 53 | 54 | ## ======= T E S T I N G ======================================================= 55 | ## test Run the unit tests 56 | ## 57 | .PHONY: test 58 | test: 59 | @rm .cache/tests/*.log || true 60 | ./tests/cli/basic.sh 61 | $(PYTEST) $(PYTEST_OPTS) $(TEST_DIR) 62 | 63 | .PHONY: tests 64 | tests: test 65 | 66 | ## retest Run tests that failed in the last run 67 | ## 68 | .PHONY: retest 69 | retest: 70 | @rm .cache/tests/*.log || true 71 | $(PYTEST) $(PYTEST_OPTS) --lf $(TEST_DIR) 72 | 73 | ## wip Run tests marked as 'wip' 74 | ## 75 | .PHONY: wip 76 | wip: 77 | @rm .cache/tests/*.log || true 78 | $(PYTEST) -m "wip" -vv $(TEST_DIR) 79 | 80 | ## coverage Run all tests and generate the coverage report 81 | ## 82 | .PHONY: coverage 83 | coverage: 84 | @rm .cache/tests/*.log || true 85 | ./tests/cli/basic.sh 86 | $(PYTEST) $(PYTEST_COV_OPTS) $(TEST_DIR) 87 | 88 | ## doctest Run the documentation related tests 89 | ## 90 | .PHONY: doctest 91 | doctest: 92 | cd docs && make doctest 93 | 94 | ## ======= M I S C E L A N I O U S ============================================= 95 | ## prek Run the pre-commit targets 96 | ## 97 | .PHONY: prek 98 | prek: 99 | @prek run -a 100 | 101 | ## ruff Run ruff without fix 102 | ## 103 | .PHONY: ruff 104 | ruff: 105 | $(UVX) ruff check --preview . 106 | 107 | ## ruff-fix Run ruff with fix 108 | ## 109 | .PHONY: ruff-fix 110 | ruff-fix: 111 | $(UVX) ruff check --fix --preview . 112 | 113 | ## changelog Generate the changelog 114 | ## 115 | .PHONY: changelog 116 | changelog: 117 | docker run -it --rm \ 118 | -v $(PWD):/usr/local/src/your-app \ 119 | githubchangeloggenerator/github-changelog-generator \ 120 | --user btschwertfeger \ 121 | --project python-kraken-sdk \ 122 | --token $(GHTOKEN) \ 123 | --breaking-labels Breaking \ 124 | --no-issues \ 125 | --no-issues-wo-labels \ 126 | --enhancement-labels Feature,enhancement \ 127 | --release-branch master \ 128 | --pr-label "Uncategorized merged pull requests:" 129 | 130 | ## clean Clean the workspace 131 | ## 132 | .PHONY: clean 133 | clean: 134 | rm -rf .cache \ 135 | .vscode \ 136 | dist/ \ 137 | doc/_build \ 138 | src/python_kraken_sdk.egg-info \ 139 | build/ 140 | 141 | rm -f .coverage \ 142 | *.csv \ 143 | *.log \ 144 | *.zip \ 145 | coverage.xml \ 146 | src/kraken/_version.py \ 147 | mypy.xml \ 148 | pytest.xml \ 149 | tests/*.zip 150 | 151 | find tests -name "__pycache__" | xargs rm -rf 152 | find src -name "__pycache__" | xargs rm -rf 153 | find examples -name "__pycache__" | xargs rm -rf 154 | 155 | ## check-uv Check if uv is installed 156 | ## 157 | .PHONY: check-uv 158 | check-uv: 159 | @if ! command -v $(UV) >/dev/null; then \ 160 | echo "Error: uv is not installed. Please visit https://github.com/astral-sh/uv for installation instructions."; \ 161 | exit 1; \ 162 | fi 163 | -------------------------------------------------------------------------------- /tests/spot/conftest.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | 8 | """ 9 | Module providing fixtures used for the unit tests regarding the Kraken Spot API. 10 | """ 11 | 12 | import os 13 | 14 | import pytest 15 | 16 | from kraken.spot import Earn, Funding, Market, SpotClient, Trade, User 17 | 18 | SPOT_API_KEY: str = os.getenv("SPOT_API_KEY") 19 | SPOT_SECRET_KEY: str = os.getenv("SPOT_SECRET_KEY") 20 | XSTOCKS_API_KEY: str = os.getenv("XSTOCKS_API_KEY") 21 | XSTOCKS_SECRET_KEY: str = os.getenv("XSTOCKS_SECRET_KEY") 22 | XSTOCKS_API_URL: str = os.getenv( 23 | "XSTOCKS_API_URL", 24 | "https://api.vip.uat.lobster.kraken.com", 25 | ) 26 | 27 | 28 | # ============================================================================== 29 | # Spot Crypto 30 | @pytest.fixture(scope="session") 31 | def spot_api_key() -> str: 32 | """Returns the Kraken Spot API Key for testing.""" 33 | return SPOT_API_KEY 34 | 35 | 36 | @pytest.fixture(scope="session") 37 | def spot_secret_key() -> str: 38 | """Returns the Kraken Spot API secret for testing.""" 39 | return SPOT_SECRET_KEY 40 | 41 | 42 | @pytest.fixture(scope="session") 43 | def spot_auth_user() -> User: 44 | """ 45 | Fixture providing an authenticated Spot user client. 46 | """ 47 | return User(key=SPOT_API_KEY, secret=SPOT_SECRET_KEY) 48 | 49 | 50 | @pytest.fixture(scope="session") 51 | def spot_market() -> Market: 52 | """ 53 | Fixture providing an unauthenticated Spot market client. 54 | """ 55 | return Market() 56 | 57 | 58 | @pytest.fixture(scope="session") 59 | def spot_auth_market() -> Market: 60 | """ 61 | Fixture providing an authenticated Spot market client. 62 | """ 63 | return Market(key=SPOT_API_KEY, secret=SPOT_SECRET_KEY) 64 | 65 | 66 | @pytest.fixture(scope="session") 67 | def spot_trade() -> Trade: 68 | """ 69 | Fixture providing an unauthenticated Spot trade client. 70 | """ 71 | return Trade() 72 | 73 | 74 | @pytest.fixture(scope="session") 75 | def spot_auth_trade() -> Trade: 76 | """ 77 | Fixture providing an authenticated Spot trade client. 78 | """ 79 | return Trade(key=SPOT_API_KEY, secret=SPOT_SECRET_KEY) 80 | 81 | 82 | @pytest.fixture(scope="session") 83 | def spot_earn() -> Earn: 84 | """ 85 | Fixture providing an unauthenticated Spot earn client. 86 | """ 87 | return Earn() 88 | 89 | 90 | @pytest.fixture(scope="session") 91 | def spot_auth_earn() -> Earn: 92 | """ 93 | Fixture providing an authenticated Spot earn client. 94 | """ 95 | raise ValueError("Do not use the authenticated Spot earn client for testing!") 96 | 97 | 98 | @pytest.fixture(scope="session") 99 | def spot_auth_funding() -> Funding: 100 | """ 101 | Fixture providing an authenticated Spot funding client. 102 | """ 103 | return Funding(key=SPOT_API_KEY, secret=SPOT_SECRET_KEY) 104 | 105 | 106 | # ============================================================================== 107 | # Spot xStocks 108 | @pytest.fixture(scope="session") 109 | def xstocks_api_key() -> str: 110 | """Returns the Kraken xStocks API Key for testing.""" 111 | return XSTOCKS_API_KEY 112 | 113 | 114 | @pytest.fixture(scope="session") 115 | def xstocks_secret_key() -> str: 116 | """Returns the Kraken xStocks API secret for testing.""" 117 | return XSTOCKS_SECRET_KEY 118 | 119 | 120 | @pytest.fixture(scope="session") 121 | def xstocks_client() -> SpotClient: 122 | """ 123 | Fixture providing an authenticated Spot client. 124 | """ 125 | return SpotClient( 126 | key=XSTOCKS_API_KEY, 127 | secret=XSTOCKS_SECRET_KEY, 128 | url=XSTOCKS_API_URL, 129 | ) 130 | 131 | 132 | @pytest.fixture(scope="session") 133 | def xstocks_market_client() -> Market: 134 | """ 135 | Fixture providing an authenticated Spot client. 136 | """ 137 | return Market(key=XSTOCKS_API_KEY, secret=XSTOCKS_SECRET_KEY, url=XSTOCKS_API_URL) 138 | 139 | 140 | @pytest.fixture(scope="session") 141 | def xstocks_trade_client() -> Trade: 142 | """ 143 | Fixture providing an authenticated Spot client. 144 | """ 145 | return Trade(key=XSTOCKS_API_KEY, secret=XSTOCKS_SECRET_KEY, url=XSTOCKS_API_URL) 146 | -------------------------------------------------------------------------------- /doc/links.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | .. LINKS 19 | 20 | .. _Kraken: https://kraken.com 21 | .. _python-kraken-sdk: https://github.com/btschwertfeger/python-kraken-sdk 22 | .. _python-kraken-sdk/issues: https://github.com/btschwertfeger/python-kraken-sdk/issues 23 | .. _python-kraken-sdk/discussions: https://github.com/btschwertfeger/python-kraken-sdk/discussions 24 | 25 | .. BADGES 26 | 27 | .. |GitHub badge| image:: https://badgen.net/badge/icon/github?icon=github&label 28 | :target: https://github.com/btschwertfeger/python-kraken-sdk 29 | :alt: GitHub 30 | .. |License badge| image:: https://img.shields.io/badge/License-Apache_2.0-blue.svg 31 | :target: https://opensource.org/licenses/Apache-2.0 32 | :alt: License 33 | .. |PyVersions badge| image:: https://img.shields.io/badge/python-3.11+-blue.svg 34 | :target: https://github.com/btschwertfeger/python-kraken-sdk 35 | :alt: Supported Python Versions 36 | .. |Downloads badge| image:: https://static.pepy.tech/personalized-badge/python-kraken-sdk?period=total&units=abbreviation&left_color=grey&right_color=orange&left_text=downloads 37 | :target: https://pepy.tech/project/python-kraken-sdk 38 | :alt: Downloads 39 | .. |Typing badge| image:: https://img.shields.io/badge/typing-mypy-informational 40 | :target: https://mypy-lang.org/ 41 | :alt: Typing: mypy 42 | .. |CI/CD badge| image:: https://github.com/btschwertfeger/Python-Kraken-SDK/actions/workflows/cicd.yaml/badge.svg?branch=master 43 | :target: https://github.com/btschwertfeger/Python-Kraken-SDK/actions/workflows/cicd.yaml 44 | :alt: CI/CD 45 | .. |codecov badge| image:: https://codecov.io/gh/btschwertfeger/Python-Kraken-SDK/branch/master/badge.svg 46 | :target: https://app.codecov.io/gh/btschwertfeger/Python-Kraken-SDK 47 | :alt: Coverage 48 | .. |Release date badge| image:: https://shields.io/github/release-date/btschwertfeger/python-kraken-sdk 49 | :target: https://github.com/btschwertfeger/Python-Kraken-SDK 50 | :alt: Release date 51 | .. |Release version badge| image:: https://img.shields.io/pypi/v/python-kraken-sdk 52 | :target: https://pypi.org/project/python-kraken-sdk/ 53 | :alt: Latest Version 54 | .. |DOI badge| image:: https://zenodo.org/badge/510751854.svg 55 | :target: https://zenodo.org/badge/latestdoi/510751854 56 | :alt: DOI 57 | .. |Docs latest| image:: https://readthedocs.org/projects/python-kraken-sdk/badge/?version=latest 58 | :target: https://python-kraken-sdk.readthedocs.io/en/latest 59 | :alt: Documentation status latest 60 | .. |Docs stable| image:: https://readthedocs.org/projects/python-kraken-sdk/badge/?version=stable 61 | :target: https://python-kraken-sdk.readthedocs.io/en/stable 62 | :alt: Documentation status stable 63 | 64 | .. |OSSF Scorecard| image:: https://img.shields.io/ossf-scorecard/github.com/btschwertfeger/python-kraken-sdk?label=openssf%20scorecard&style=flat 65 | :target: https://securityscorecards.dev/viewer/?uri=github.com/btschwertfeger/python-kraken-sdk 66 | :alt: OSSF Scorecard 67 | 68 | .. |OSSF Best Practices| image:: https://www.bestpractices.dev/projects/8673/badge 69 | :target: https://www.bestpractices.dev/projects/8673 70 | :alt: OSSF Best Practices 71 | -------------------------------------------------------------------------------- /examples/spot_orderbook.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python3 2 | # -*- mode: python; coding: utf-8 -*- 3 | # 4 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 5 | # All rights reserved. 6 | # https://github.com/btschwertfeger 7 | # 8 | 9 | """ 10 | 11 | **For websocket API v2** 12 | 13 | This module provides an example on how to use the Spot Orderbook client of the 14 | python-kraken-sdk (https://github.com/btschwertfeger/python-kraken-sdk) to 15 | retrieve and maintain a valid Spot order book for (a) specific asset pair(s). 16 | It can be run directly without any credentials if the python-kraken-sdk is 17 | installed. 18 | 19 | python3 -m pip install python-kraken-sdk 20 | 21 | The output when running this snippet looks like the following table and updates 22 | the book as soon as Kraken sent any order book update. 23 | 24 | Bid Volume Ask Volume 25 | 27076.00000 (8.28552127) 27076.10000 (2.85897056) 26 | 27075.90000 (3.75748052) 27077.30000 (0.57243521) 27 | 27074.40000 (0.57249652) 27080.80000 (0.00100000) 28 | 27072.90000 (0.01200917) 27081.00000 (0.00012345) 29 | 27072.80000 (0.25000000) 27081.70000 (0.30000000) 30 | 27072.30000 (4.89735970) 27082.70000 (0.05539777) 31 | 27072.20000 (2.65896716) 27082.80000 (0.00400000) 32 | 27072.10000 (2.77037635) 27082.90000 (0.57231684) 33 | 27072.00000 (0.81770000) 27083.00000 (0.38934000) 34 | 27071.50000 (0.07194657) 27083.80000 (2.76918992) 35 | 36 | This can be the basis of an order book based trading strategy where realtime 37 | data and fast price movements are considered. 38 | """ 39 | 40 | from __future__ import annotations 41 | 42 | import asyncio 43 | import logging 44 | from typing import Any 45 | 46 | from kraken.spot import SpotOrderBookClient 47 | 48 | logging.basicConfig( 49 | format="%(asctime)s %(module)s,line: %(lineno)d %(levelname)8s | %(message)s", 50 | datefmt="%Y/%m/%d %H:%M:%S", 51 | level=logging.INFO, 52 | ) 53 | logging.getLogger().setLevel(logging.INFO) 54 | logging.getLogger("requests").setLevel(logging.WARNING) 55 | 56 | 57 | class Orderbook(SpotOrderBookClient): 58 | """ 59 | This is a wrapper class that is used to overload the :func:`on_book_update` 60 | function. It can also be used as a base for trading strategy. Since the 61 | :class:`kraken.spot.SpotOrderBookClient` is derived from 62 | :class:`kraken.spot.SpotWSClient` it can also be used to access the 63 | :func:`subscribe` function and any other provided utility. 64 | """ 65 | 66 | async def on_book_update( 67 | self: Orderbook, 68 | pair: str, 69 | message: list, # noqa: ARG002 70 | ) -> None: 71 | """ 72 | This function is called every time the order book of ``pair`` gets 73 | updated. 74 | 75 | The ``pair`` parameter can be used to access the updated order book as 76 | shown in the function body below. 77 | 78 | :param pair: The currency pair of the updated order book 79 | :type pair: str 80 | :param message: The message sent by Kraken (not needed in most cases) 81 | :type message: list 82 | """ 83 | book: dict[str, Any] = self.get(pair=pair) 84 | bid: list[tuple[str, str]] = list(book["bid"].items()) 85 | ask: list[tuple[str, str]] = list(book["ask"].items()) 86 | 87 | print("Bid Volume\t\t Ask Volume") 88 | for level in range(self.depth): 89 | print( 90 | f"{bid[level][0]} ({bid[level][1][0]}) \t {ask[level][0]} ({ask[level][1][0]})", 91 | ) 92 | 93 | assert book["valid"] # ensure that the checksum is valid 94 | # … the client will automatically resubscribe to a book feed if the 95 | # checksum is not valid. The user must not do anything for that, but 96 | # will get informed. 97 | 98 | 99 | async def main() -> None: 100 | """ 101 | Here we depth of the order book and also a pair. We could 102 | subscribe to multiple pairs, but for simplicity only XBT/USD is chosen. 103 | 104 | The Orderbook class can be instantiated, which receives the order 105 | book-related messages, after we subscribed to the book feed. 106 | 107 | Finally we need some "game loop" - so we create a while loop 108 | that runs as long as there is no error. 109 | """ 110 | 111 | async with Orderbook(depth=10) as orderbook: 112 | await orderbook.add_book( 113 | pairs=["BTC/USD"], # we can also subscribe to more currency pairs 114 | ) 115 | 116 | while not orderbook.exception_occur: 117 | await asyncio.sleep(10) 118 | 119 | 120 | if __name__ == "__main__": 121 | try: 122 | asyncio.run(main()) 123 | except KeyboardInterrupt: 124 | print("KeyboardInterrupt!") 125 | -------------------------------------------------------------------------------- /tests/futures/conftest.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | 8 | import os 9 | 10 | import pytest 11 | 12 | from kraken.futures import Funding, Market, Trade, User 13 | 14 | FUTURES_API_KEY: str = os.getenv("FUTURES_API_KEY") 15 | FUTURES_SECRET_KEY: str = os.getenv("FUTURES_SECRET_KEY") 16 | FUTURES_SANDBOX_KEY: str = os.getenv("FUTURES_SANDBOX_KEY") 17 | FUTURES_SANDBOX_SECRET_KEY: str = os.getenv("FUTURES_SANDBOX_SECRET") 18 | FUTURES_EXTENDED_TIMEOUT: int = 30 19 | 20 | 21 | @pytest.fixture(scope="session") 22 | def futures_api_key() -> str: 23 | """Returns the Futures API key""" 24 | return FUTURES_API_KEY 25 | 26 | 27 | @pytest.fixture(scope="session") 28 | def futures_secret_key() -> str: 29 | """Returns the Futures API secret key""" 30 | return FUTURES_SECRET_KEY 31 | 32 | 33 | @pytest.fixture(scope="session") 34 | def futures_market() -> Market: 35 | """ 36 | Fixture providing an unauthenticated Futures Market client 37 | """ 38 | market: Market = Market() 39 | market.TIMEOUT = FUTURES_EXTENDED_TIMEOUT 40 | return market 41 | 42 | 43 | @pytest.fixture(scope="session") 44 | def futures_auth_market() -> Market: 45 | """ 46 | Fixture providing an authenticated Futures Market client. 47 | """ 48 | market: Market = Market(key=FUTURES_API_KEY, secret=FUTURES_SECRET_KEY) 49 | market.TIMEOUT = FUTURES_EXTENDED_TIMEOUT 50 | return market 51 | 52 | 53 | @pytest.fixture(scope="session") 54 | def futures_demo_market() -> Market: 55 | """ 56 | Fixture providing an authenticated Futures Market client that 57 | uses the demo/sandbox environment. 58 | """ 59 | market: Market = Market( 60 | key=FUTURES_SANDBOX_KEY, 61 | secret=FUTURES_SANDBOX_SECRET_KEY, 62 | sandbox=True, 63 | ) 64 | market.TIMEOUT = FUTURES_EXTENDED_TIMEOUT 65 | return market 66 | 67 | 68 | @pytest.fixture(scope="session") 69 | def futures_user() -> User: 70 | """ 71 | Fixture providing an unauthenticated Futures User client. 72 | """ 73 | user: User = User() 74 | user.TIMEOUT = FUTURES_EXTENDED_TIMEOUT 75 | return user 76 | 77 | 78 | @pytest.fixture(scope="session") 79 | def futures_auth_user() -> User: 80 | """ 81 | Fixture providing an authenticated Futures User client. 82 | """ 83 | user: User = User(key=FUTURES_API_KEY, secret=FUTURES_SECRET_KEY) 84 | User.TIMEOUT = FUTURES_EXTENDED_TIMEOUT 85 | return user 86 | 87 | 88 | @pytest.fixture(scope="session") 89 | def futures_demo_user() -> User: 90 | """ 91 | Fixture providing an authenticated Futures User client that 92 | uses the demo/sandbox environment. 93 | """ 94 | user: User = User( 95 | key=FUTURES_SANDBOX_KEY, 96 | secret=FUTURES_SANDBOX_SECRET_KEY, 97 | sandbox=True, 98 | ) 99 | User.TIMEOUT = FUTURES_EXTENDED_TIMEOUT 100 | return user 101 | 102 | 103 | @pytest.fixture(scope="session") 104 | def futures_trade() -> Trade: 105 | """ 106 | Fixture providing an unauthenticated Futures Trade client. 107 | """ 108 | trade: Trade = Trade() 109 | trade.TIMEOUT = FUTURES_EXTENDED_TIMEOUT 110 | return trade 111 | 112 | 113 | @pytest.fixture(scope="session") 114 | def futures_auth_trade() -> Trade: 115 | """ 116 | Fixture providing an authenticated Futures Trade client. 117 | """ 118 | trade: Trade = Trade(key=FUTURES_API_KEY, secret=FUTURES_SECRET_KEY) 119 | trade.TIMEOUT = FUTURES_EXTENDED_TIMEOUT 120 | return trade 121 | 122 | 123 | @pytest.fixture(scope="session") 124 | def futures_demo_trade() -> Trade: 125 | """ 126 | Fixture providing an authenticated Futures Trade client that 127 | uses the demo/sandbox environment. 128 | """ 129 | trade: Trade = Trade( 130 | key=FUTURES_SANDBOX_KEY, 131 | secret=FUTURES_SANDBOX_SECRET_KEY, 132 | sandbox=True, 133 | ) 134 | trade.TIMEOUT = FUTURES_EXTENDED_TIMEOUT 135 | return trade 136 | 137 | 138 | @pytest.fixture(scope="session") 139 | def futures_funding() -> Funding: 140 | """ 141 | Fixture providing an unauthenticated Futures Funding client. 142 | """ 143 | funding: Funding = Funding() 144 | funding.TIMEOUT = FUTURES_EXTENDED_TIMEOUT 145 | return funding 146 | 147 | 148 | @pytest.fixture(scope="session") 149 | def futures_auth_funding() -> Funding: 150 | """ 151 | Fixture providing an authenticated Futures Funding client. 152 | """ 153 | funding: Funding = Funding(key=FUTURES_API_KEY, secret=FUTURES_SECRET_KEY) 154 | funding.TIMEOUT = FUTURES_EXTENDED_TIMEOUT 155 | return funding 156 | 157 | 158 | @pytest.fixture(scope="session") 159 | def futures_demo_funding() -> Funding: 160 | """ 161 | Fixture providing an authenticated Futures Funding client that 162 | uses the demo/sandbox environment. 163 | """ 164 | funding: Funding = Funding( 165 | key=FUTURES_SANDBOX_KEY, 166 | secret=FUTURES_SANDBOX_SECRET_KEY, 167 | sandbox=True, 168 | ) 169 | funding.TIMEOUT = FUTURES_EXTENDED_TIMEOUT 170 | return funding 171 | -------------------------------------------------------------------------------- /examples/futures_ws_examples.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python3 2 | # -*- mode: python; coding: utf-8 -*- 3 | # 4 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 5 | # All rights reserved. 6 | # https://github.com/btschwertfeger 7 | # 8 | 9 | """ 10 | Module that provides an example usage for the Kraken Futures websocket client. 11 | """ 12 | 13 | from __future__ import annotations 14 | 15 | import asyncio 16 | import logging 17 | import os 18 | import time 19 | 20 | from kraken.futures import FuturesWSClient 21 | 22 | logging.basicConfig( 23 | format="%(asctime)s %(module)s,line: %(lineno)d %(levelname)8s | %(message)s", 24 | datefmt="%Y/%m/%d %H:%M:%S", 25 | level=logging.INFO, 26 | ) 27 | logging.getLogger("requests").setLevel(logging.WARNING) 28 | logging.getLogger("urllib3").setLevel(logging.WARNING) 29 | LOG: logging.Logger = logging.getLogger(__name__) 30 | 31 | clients = [] 32 | 33 | 34 | # Custom client 35 | class Client(FuturesWSClient): 36 | """Can be used to create a custom trading strategy""" 37 | 38 | async def on_message(self: Client, message: list | dict) -> None: 39 | """Receives the websocket messages""" 40 | LOG.info(message) 41 | # … apply your trading strategy in this class 42 | # … you can also combine this with the Futures REST clients 43 | 44 | 45 | async def main() -> None: 46 | """Create a client and subscribe to channels/feeds""" 47 | 48 | key = os.getenv("FUTURES_API_KEY") 49 | secret = os.getenv("FUTURES_SECRET_KEY") 50 | 51 | try: 52 | # _____Public_Websocket_Feeds___________________ 53 | client = Client() 54 | clients.append(client) 55 | await client.start() 56 | # print(client.get_available_public_subscription_feeds()) 57 | 58 | products = ["PI_XBTUSD", "PF_SOLUSD"] 59 | # subscribe to a public websocket feed 60 | await client.subscribe(feed="ticker", products=products) 61 | await client.subscribe(feed="book", products=products) 62 | # await client.subscribe(feed='trade', products=products) 63 | # await client.subscribe(feed='ticker_lite', products=products) 64 | # await client.subscribe(feed='heartbeat') 65 | # time.sleep(2) 66 | 67 | # unsubscribe from a websocket feed 68 | time.sleep(2) # in case subscribe is not done yet 69 | # await client.unsubscribe(feed='ticker', products=['PI_XBTUSD']) 70 | await client.unsubscribe(feed="ticker", products=["PF_XBTUSD"]) 71 | await client.unsubscribe(feed="book", products=products) 72 | # ... 73 | 74 | # _____Private_Websocket_Feeds_________________ 75 | if key and secret: 76 | client_auth = Client(key=key, secret=secret) 77 | clients.append(client_auth) 78 | await client_auth.start() 79 | # print(client_auth.get_available_private_subscription_feeds()) 80 | 81 | # subscribe to a private/authenticated websocket feed 82 | await client_auth.subscribe(feed="fills") 83 | await client_auth.subscribe(feed="open_positions") 84 | # await client_auth.subscribe(feed='open_orders') 85 | # await client_auth.subscribe(feed='open_orders_verbose') 86 | # await client_auth.subscribe(feed='deposits_withdrawals') 87 | # await client_auth.subscribe(feed='account_balances_and_margins') 88 | # await client_auth.subscribe(feed='balances') 89 | # await client_auth.subscribe(feed='account_log') 90 | # await client_auth.subscribe(feed='notifications_auth') 91 | 92 | # authenticated clients can also subscribe to public feeds 93 | # await client_auth.subscribe(feed='ticker', products=['PI_XBTUSD', 'PF_ETHUSD']) 94 | 95 | # time.sleep(1) 96 | # unsubscribe from a private/authenticated websocket feed 97 | await client_auth.unsubscribe(feed="fills") 98 | await client_auth.unsubscribe(feed="open_positions") 99 | # ... 100 | 101 | while not client.exception_occur: # and not client_auth.exception_occur: 102 | await asyncio.sleep(6) 103 | finally: 104 | # Close the sessions properly. 105 | for _client in clients: 106 | await _client.close() 107 | 108 | 109 | if __name__ == "__main__": 110 | asyncio.run(main()) 111 | # the websocket client will send {'event': 'asyncio.CancelledError'} via on_message 112 | # so you can handle the behavior/next actions individually within you strategy 113 | 114 | # ============================================================ 115 | # Alternative - as ContextManager: 116 | 117 | # from kraken.futures import KrakenFuturesWSClient 118 | # import asyncio 119 | 120 | # async def on_message(message): 121 | # print(message) 122 | 123 | # async def main() -> None: 124 | # async with KrakenFuturesWSClient(callback=on_message) as session: 125 | # await session.subscribe(feed="ticker", products=["PF_XBTUSD"]) 126 | # while True: 127 | # await asyncio.sleep(6) 128 | 129 | # if __name__ == "__main__": 130 | # try: 131 | # asyncio.run(main()) 132 | # except KeyboardInterrupt: 133 | # pass 134 | -------------------------------------------------------------------------------- /examples/spot_ws_examples.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python3 2 | # -*- mode: python; coding: utf-8 -*- 3 | # 4 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 5 | # All rights reserved. 6 | # https://github.com/btschwertfeger 7 | # 8 | 9 | """ 10 | Module that provides an example usage for the KrakenSpotWebsocketClient. 11 | It uses the Kraken Websocket API v2. 12 | """ 13 | 14 | from __future__ import annotations 15 | 16 | import asyncio 17 | import logging 18 | import os 19 | 20 | from kraken.spot import SpotWSClient 21 | 22 | logging.basicConfig( 23 | format="%(asctime)s %(module)s,line: %(lineno)d %(levelname)8s | %(message)s", 24 | datefmt="%Y/%m/%d %H:%M:%S", 25 | level=logging.INFO, 26 | ) 27 | logging.getLogger("requests").setLevel(logging.WARNING) 28 | logging.getLogger("urllib3").setLevel(logging.WARNING) 29 | 30 | clients = [] 31 | 32 | 33 | class Client(SpotWSClient): 34 | """Can be used to create a custom trading strategy""" 35 | 36 | async def on_message(self: Client, message: dict) -> None: 37 | """Receives the websocket messages""" 38 | if message.get("method") == "pong" or message.get("channel") == "heartbeat": 39 | return 40 | 41 | print(message) 42 | # now you can access lots of methods, for example to create an order: 43 | # if self._is_auth: # only if the client is authenticated … 44 | # await self.send_message( 45 | # message={ 46 | # "method": "add_order", 47 | # "params": { 48 | # "limit_price": 1234.56, 49 | # "order_type": "limit", 50 | # "order_userref": 123456789, 51 | # "order_qty": 1.0, 52 | # "side": "buy", 53 | # "symbol": "BTC/USD", 54 | # "validate": True, 55 | # }, 56 | # } 57 | # ) 58 | # ... it is also possible to call regular REST endpoints 59 | # but using the websocket messages is more efficient. 60 | # You can also un-/subscribe here using self.subscribe/self.unsubscribe. 61 | 62 | 63 | async def main() -> None: 64 | key: str = os.getenv("SPOT_API_KEY") 65 | secret: str = os.getenv("SPOT_SECRET_KEY") 66 | 67 | try: 68 | # Public/unauthenticated websocket client 69 | client: Client = Client() # only use this one if you don't need private feeds 70 | clients.append(client) 71 | await client.start() 72 | # print(client.public_channel_names) # list public subscription names 73 | 74 | await client.subscribe( 75 | params={"channel": "ticker", "symbol": ["BTC/USD", "DOT/USD"]}, 76 | ) 77 | await client.subscribe( 78 | params={"channel": "book", "depth": 25, "symbol": ["BTC/USD"]}, 79 | ) 80 | # await client.subscribe(params={"channel": "ohlc", "symbol": ["BTC/USD"]}) 81 | await client.subscribe( 82 | params={ 83 | "channel": "ohlc", 84 | "interval": 15, 85 | "snapshot": False, 86 | "symbol": ["BTC/USD", "DOT/USD"], 87 | }, 88 | ) 89 | await client.subscribe(params={"channel": "trade", "symbol": ["BTC/USD"]}) 90 | 91 | # wait because unsubscribing is faster than unsubscribing ... (just for that example) 92 | await asyncio.sleep(3) 93 | # print(client.active_public_subscriptions) # … to list active subscriptions 94 | await client.unsubscribe( 95 | params={"channel": "ticker", "symbol": ["BTC/USD", "DOT/USD"]}, 96 | ) 97 | # ... 98 | 99 | if key and secret: 100 | # Per default, the authenticated client starts two websocket connections, 101 | # one for authenticated and one for public messages. If there is no need 102 | # for a public connection, it can be disabled using the ``no_public`` 103 | # parameter. 104 | client_auth = Client(key=key, secret=secret, no_public=True) 105 | clients.append(client_auth) 106 | await client_auth.start() 107 | # print(client_auth.private_channel_names) # … list private channel names 108 | # when using the authenticated client, you can also subscribe to public feeds 109 | await client_auth.subscribe(params={"channel": "executions"}) 110 | 111 | await asyncio.sleep(5) 112 | await client_auth.unsubscribe(params={"channel": "executions"}) 113 | 114 | while not client.exception_occur: # and not client_auth.exception_occur: 115 | await asyncio.sleep(6) 116 | finally: 117 | # Stop the sessions properly. 118 | for _client in clients: 119 | await _client.close() 120 | 121 | 122 | if __name__ == "__main__": 123 | asyncio.run(main()) 124 | 125 | # ============================================================ 126 | # Alternative - as ContextManager: 127 | 128 | # from kraken.spot import SpotWSClient 129 | # import asyncio 130 | 131 | 132 | # async def on_message(message: dict) -> None: 133 | # print(message) 134 | 135 | 136 | # async def main() -> None: 137 | # async with SpotWSClient(callback=on_message) as session: 138 | # await session.subscribe(params={"channel": "ticker", "symbol": ["BTC/USD"]}) 139 | 140 | # while True: 141 | # await asyncio.sleep(6) 142 | 143 | 144 | # if __name__ == "__main__": 145 | # try: 146 | # asyncio.run(main()) 147 | # except KeyboardInterrupt: 148 | # pass 149 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socioeconomic status, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | - Demonstrating empathy and kindness toward other people 21 | - Being respectful of differing opinions, viewpoints, and experiences 22 | - Giving and gracefully accepting constructive feedback 23 | - Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | - Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | - The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | - Trolling, insulting or derogatory comments, and personal or political attacks 33 | - Public or private harassment 34 | - Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | - Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement. 63 | All complaints will be reviewed and investigated promptly and fairly. 64 | 65 | All community leaders are obligated to respect the privacy and security of the 66 | reporter of any incident. 67 | 68 | ## Enforcement Guidelines 69 | 70 | Community leaders will follow these Community Impact Guidelines in determining 71 | the consequences for any action they deem in violation of this Code of Conduct: 72 | 73 | ### 1. Correction 74 | 75 | **Community Impact**: Use of inappropriate language or other behavior deemed 76 | unprofessional or unwelcome in the community. 77 | 78 | **Consequence**: A private, written warning from community leaders, providing 79 | clarity around the nature of the violation and an explanation of why the 80 | behavior was inappropriate. A public apology may be requested. 81 | 82 | ### 2. Warning 83 | 84 | **Community Impact**: A violation through a single incident or series 85 | of actions. 86 | 87 | **Consequence**: A warning with consequences for continued behavior. No 88 | interaction with the people involved, including unsolicited interaction with 89 | those enforcing the Code of Conduct, for a specified period of time. This 90 | includes avoiding interactions in community spaces as well as external channels 91 | like social media. Violating these terms may lead to a temporary or permanent 92 | ban. 93 | 94 | ### 3. Temporary Ban 95 | 96 | **Community Impact**: A serious violation of community standards, including 97 | sustained inappropriate behavior. 98 | 99 | **Consequence**: A temporary ban from any sort of interaction or public 100 | communication with the community for a specified period of time. No public or 101 | private interaction with the people involved, including unsolicited interaction 102 | with those enforcing the Code of Conduct, is allowed during this period. 103 | Violating these terms may lead to a permanent ban. 104 | 105 | ### 4. Permanent Ban 106 | 107 | **Community Impact**: Demonstrating a pattern of violation of community 108 | standards, including sustained inappropriate behavior, harassment of an 109 | individual, or aggression toward or disparagement of classes of individuals. 110 | 111 | **Consequence**: A permanent ban from any sort of public interaction within 112 | the community. 113 | 114 | ## Attribution 115 | 116 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 117 | version 2.0, available at 118 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 119 | 120 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 121 | enforcement ladder](https://github.com/mozilla/diversity). 122 | 123 | [homepage]: https://www.contributor-covenant.org 124 | 125 | For answers to common questions about this code of conduct, see the FAQ at 126 | https://www.contributor-covenant.org/faq. Translations are available at 127 | https://www.contributor-covenant.org/translations. 128 | -------------------------------------------------------------------------------- /doc/01_introduction.rst: -------------------------------------------------------------------------------- 1 | .. -*- mode: rst; coding: utf-8 -*- 2 | .. 3 | .. Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | .. https://github.com/btschwertfeger 5 | .. 6 | .. Licensed under the Apache License, Version 2.0 (the "License"); 7 | .. you may not use this file except in compliance with the License. 8 | .. You may obtain a copy of the License at 9 | .. 10 | .. http://www.apache.org/licenses/LICENSE-2.0 11 | .. 12 | .. Unless required by applicable law or agreed to in writing, software 13 | .. distributed under the License is distributed on an "AS IS" BASIS, 14 | .. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | .. See the License for the specific language governing permissions and 16 | .. limitations under the License. 17 | .. 18 | 19 | python-kraken-sdk 20 | ================= 21 | 22 | |GitHub badge| |License badge| |PyVersions badge| |Downloads badge| 23 | |CI/CD badge| |codecov badge| |Typing badge| 24 | |OSSF Scorecard| |OSSF Best Practices| 25 | |Release date badge| |Release version badge| |DOI badge| 26 | 27 | 28 | **This is the documentation of the unofficial Python SDK to interact with the 29 | Kraken Crypto Asset Exchange.** 30 | 31 | *Payward Ltd. and Kraken are in no way associated with the authors of this 32 | package and documentation. Please note that this project is independent and not 33 | endorsed by Kraken or Payward Ltd. Users should be aware that they are using 34 | third-party software, and the authors of this project are not responsible for 35 | any issues, losses, or risks associated with its usage.* 36 | 37 | This documentation refers to the `python-kraken-sdk`_ and serves to simplify the 38 | application of trading strategies, in which as far as possible all interaction 39 | possibilities with the crypto asset exchange Kraken are implemented, tested 40 | and documented. 41 | 42 | - Gladly open an issue on GitHub on make if something is incorrect or missing 43 | (`python-kraken-sdk/issues`_). 44 | - The output in the examples may differ, as these are only intended as examples 45 | and may change in the future. 46 | - If a certain endpoint is not reachable, the function 47 | :func:`kraken.spot.SpotClient.request` or 48 | :func:`kraken.futures.FuturesClient.request`, 49 | which is also available in all derived REST clients, can be used to reach an 50 | endpoint with the appropriate parameters. Here private content can also be 51 | accessed, provided that either the base class or one of the clients has been 52 | initialized with valid credentials. 53 | - For Futures there is the websocket client 54 | :class:`kraken.futures.FuturesWSClient` and for Spot and xStocks 55 | :class:`kraken.spot.SpotWSClient`. 56 | 57 | 58 | ⚠️ Disclaimer 59 | ------------- 60 | 61 | There is no guarantee that this software will work flawlessly at this or later 62 | times. Of course, no responsibility is taken for possible profits or losses. 63 | This software probably has some errors in it, so use it at your own risk. Also 64 | no one should be motivated or tempted to invest assets in speculative forms of 65 | investment. By using this software you release the author(s) from any liability 66 | regarding the use of this software. 67 | 68 | 69 | 🚀 Features 70 | ----------- 71 | 72 | General: 73 | 74 | - Command-line interface 75 | - Access both public and private, REST and websocket endpoints 76 | - Responsive error handling and custom exceptions 77 | - Extensive examples 78 | - Tested using the `pytest `_ framework 79 | - Releases are permanently archived at `Zenodo `_ 80 | 81 | Available Clients: 82 | 83 | - Spot REST Clients - including xStocks capability 84 | - Spot Websocket Client (Websocket API v2) 85 | - Spot Orderbook Client (Websocket API v2) 86 | - Futures REST Clients 87 | - Futures Websocket Client 88 | 89 | Projects using this SDK: 90 | 91 | - https://github.com/btschwertfeger/infinity-grid 92 | - https://github.com/btschwertfeger/kraken-rebalance-bot 93 | - https://github.com/btschwertfeger/python-kraken-sdk/network/dependents 94 | 95 | 📌 Important Notice 96 | ------------------- 97 | 98 | **ONLY tagged releases are available at PyPI**. The content of the master branch 99 | may not match with the content of the latest release. - Please have a look at 100 | the release specific READMEs and changelogs. 101 | 102 | It is also recommended to **pin the used version** to avoid unexpected behavior 103 | on new releases. 104 | 105 | 106 | .. _section-troubleshooting: 107 | 108 | 💡 Troubleshooting 109 | ------------------ 110 | 111 | - Check if you downloaded and installed the **latest version** of the 112 | python-kraken-sdk. 113 | - Check the **permissions of your API keys** and the required permissions on the 114 | respective endpoints. 115 | - If you get some Cloudflare or **rate limit errors**, please check your 116 | `Kraken`_ Tier level and maybe apply for a higher rank if required. 117 | - **Use different API keys for different algorithms**, because the nonce 118 | calculation is based on timestamps and a sent nonce must always be the highest 119 | nonce ever sent of that API key. Having multiple algorithms using the same 120 | keys will result in invalid nonce errors. 121 | - Always keep an eye on https://status.kraken.com/ when encountering 122 | connectivity problems. 123 | - Feel free to open an issue at `python-kraken-sdk/issues`_. 124 | - The xStocks feature is not available globally. Please checkout Kraken's 125 | documentation to get to know the availability zones. 126 | 127 | 📜 References 128 | ------------- 129 | 130 | - https://python-kraken-sdk.readthedocs.io/en/stable 131 | - https://docs.kraken.com/api/ 132 | - https://docs.kraken.com/api/docs/guides/global-intro 133 | - https://support.kraken.com/hc/en-us/sections/360012894412-Futures-API 134 | -------------------------------------------------------------------------------- /tests/futures/test_futures_base_api.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | 8 | """Module that checks the general Futures Base API class.""" 9 | 10 | from asyncio import run 11 | from typing import Self 12 | from unittest import IsolatedAsyncioTestCase 13 | 14 | import pytest 15 | from proxy import TestCase 16 | 17 | from kraken.base_api import FuturesAsyncClient, FuturesClient 18 | from kraken.exceptions import KrakenRequiredArgumentMissingError 19 | from kraken.futures import Funding, Market, Trade, User 20 | 21 | from .helper import is_success 22 | 23 | 24 | @pytest.mark.futures 25 | class TestFuturesBaseAPI: 26 | """Test class for Futures Base API functionality.""" 27 | 28 | def test_KrakenFuturesBaseAPI_without_exception(self) -> None: 29 | """ 30 | Checks first if the expected error will be raised and then 31 | creates a new KrakenFuturesBaseAPI instance that does not raise 32 | the custom Kraken exceptions. This new instance then executes 33 | the same request and the returned response gets evaluated. 34 | """ 35 | with pytest.raises(KrakenRequiredArgumentMissingError): 36 | FuturesClient( 37 | key="fake", 38 | secret="fake", 39 | ).request(method="POST", uri="/derivatives/api/v3/sendorder", auth=True) 40 | 41 | result: dict = ( 42 | FuturesClient(key="fake", secret="fake", use_custom_exceptions=False) # type: ignore[union-attr] 43 | .request(method="POST", uri="/derivatives/api/v3/sendorder", auth=True) 44 | .json() 45 | ) 46 | 47 | assert result.get("result") == "error" 48 | assert result.get("error") == "requiredArgumentMissing" 49 | 50 | @pytest.mark.futures_auth 51 | def test_futures_rest_contextmanager( 52 | self, 53 | futures_market: Market, 54 | futures_auth_funding: Funding, 55 | futures_demo_trade: Trade, 56 | futures_auth_user: User, 57 | ) -> None: 58 | """ 59 | Checks if the clients can be used as context manager. 60 | """ 61 | with futures_market as market: 62 | assert isinstance(market.get_tick_types(), list) 63 | 64 | with futures_auth_funding as funding: 65 | assert is_success(funding.get_historical_funding_rates(symbol="PF_SOLUSD")) 66 | 67 | with futures_auth_user as user: 68 | assert is_success(user.get_wallets()) 69 | 70 | with futures_demo_trade as trade: 71 | assert is_success(trade.get_fills()) 72 | 73 | 74 | # ============================================================================== 75 | # Futures async client 76 | 77 | 78 | @pytest.mark.futures 79 | class TestFuturesBaseAPIAsync: 80 | """Test class for Futures Base API async functionality.""" 81 | 82 | def test_futures_async_rest_contextmanager(self: Self) -> None: 83 | """ 84 | Checks if the clients can be used as context manager. 85 | """ 86 | 87 | async def check() -> None: 88 | async with FuturesAsyncClient() as client: 89 | assert isinstance( 90 | await client.request( 91 | "GET", 92 | "/api/charts/v1/spot/PI_XBTUSD/1h", 93 | auth=False, 94 | post_params={"from": "1668989233", "to": "1668999233"}, 95 | ), 96 | dict, 97 | ) 98 | 99 | run(check()) 100 | 101 | @pytest.mark.futures_auth 102 | def test_futures_rest_async_client_post( 103 | self: Self, 104 | futures_api_key: str, 105 | futures_secret_key: str, 106 | ) -> None: 107 | """ 108 | Check the instantiation as well as a simple request using the async client. 109 | """ 110 | 111 | async def check() -> None: 112 | client = FuturesAsyncClient(futures_api_key, futures_secret_key) 113 | try: 114 | assert isinstance( 115 | await client.request( 116 | "POST", 117 | "/derivatives/api/v3/orders/status", 118 | post_params={ 119 | "orderIds": [ 120 | "bcaaefce-27a3-44b4-b13a-19df21e3f087", 121 | "685d5a1a-23eb-450c-bf17-1e4ab5c6fe8a", 122 | ], 123 | }, 124 | ), 125 | dict, 126 | ) 127 | finally: 128 | await client.close() 129 | 130 | run(check()) 131 | 132 | 133 | @pytest.mark.futures 134 | @pytest.mark.futures_market 135 | @pytest.mark.futures_market 136 | class TestProxyPyEmbedded(TestCase, IsolatedAsyncioTestCase): 137 | def get_proxy_str(self: Self) -> str: 138 | return f"http://127.0.0.1:{self.PROXY.flags.port}" 139 | 140 | def test_futures_rest_proxies(self) -> None: 141 | """ 142 | Checks if the clients can be used with a proxy. 143 | """ 144 | client = FuturesClient(proxy=self.get_proxy_str()) 145 | assert isinstance( 146 | client.request( 147 | "GET", 148 | "/api/charts/v1/spot/PI_XBTUSD/1h", 149 | auth=False, 150 | post_params={"from": "1668989233", "to": "1668999233"}, 151 | ), 152 | dict, 153 | ) 154 | 155 | @pytest.mark.asyncio 156 | async def test_futures_rest_proxies_async(self: Self) -> None: 157 | """ 158 | Checks if the async clients can be used with a proxy. 159 | """ 160 | client = FuturesAsyncClient(proxy=self.get_proxy_str()) 161 | res = await client.request( 162 | "GET", 163 | "/api/charts/v1/spot/PI_XBTUSD/1h", 164 | auth=False, 165 | post_params={"from": "1668989233", "to": "1668999233"}, 166 | ) 167 | assert isinstance(res, dict) 168 | -------------------------------------------------------------------------------- /tests/futures/test_futures_user.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | 8 | """Module that implements the unit tests for the Futures user client.""" 9 | 10 | import random 11 | import tempfile 12 | from pathlib import Path 13 | from typing import TYPE_CHECKING, Any, Self 14 | 15 | import pytest 16 | 17 | from kraken.futures import User 18 | 19 | from .helper import is_success 20 | 21 | if TYPE_CHECKING: 22 | import requests 23 | 24 | 25 | @pytest.mark.futures 26 | @pytest.mark.futures_auth 27 | @pytest.mark.futures_user 28 | class TestFuturesUser: 29 | """Test class for Futures User client functionality.""" 30 | 31 | TRADEABLE = "PF_SOLUSD" 32 | SINCE = 1668989233 33 | BEFORE = 1668999999 34 | SORT_ASC = "asc" 35 | SUBACCOUNT_UID = "778387bh61b-f990-4128-16a7-f819abc8" 36 | 37 | def _assert_successful_response(self: Self, result: Any) -> None: 38 | """Helper method to assert a successful response.""" 39 | assert is_success(result) 40 | 41 | def _assert_elements_in_result(self: Self, result: dict) -> None: 42 | """Helper method to assert 'elements' key is in result.""" 43 | assert isinstance(result, dict) 44 | assert "elements" in result 45 | 46 | def test_get_wallets(self: Self, futures_auth_user: User) -> None: 47 | """ 48 | Checks the ``get_wallets`` endpoint. 49 | """ 50 | self._assert_successful_response(futures_auth_user.get_wallets()) 51 | 52 | def test_get_subaccounts(self: Self, futures_auth_user: User) -> None: 53 | """ 54 | Checks the ``get_subaccounts`` endpoint. 55 | """ 56 | self._assert_successful_response(futures_auth_user.get_subaccounts()) 57 | 58 | def test_get_unwindqueue(self: Self, futures_auth_user: User) -> None: 59 | """ 60 | Checks the ``get_unwindqueue`` endpoint. 61 | """ 62 | self._assert_successful_response(futures_auth_user.get_unwind_queue()) 63 | 64 | def test_get_notifications(self: Self, futures_auth_user: User) -> None: 65 | """ 66 | Checks the ``get_notifications`` endpoint. 67 | """ 68 | self._assert_successful_response(futures_auth_user.get_notifications()) 69 | 70 | def test_get_account_log(self: Self, futures_auth_user: User) -> None: 71 | """ 72 | Checks the ``get_account_log`` endpoint. 73 | """ 74 | assert isinstance(futures_auth_user.get_account_log(), dict) 75 | assert isinstance( 76 | futures_auth_user.get_account_log(info="futures liquidation"), 77 | dict, 78 | ) 79 | 80 | def test_get_account_log_csv(self: Self, futures_auth_user: User) -> None: 81 | """ 82 | Checks the ``get_account_log_csv`` endpoint. 83 | """ 84 | response: requests.Response = futures_auth_user.get_account_log_csv() 85 | assert response.status_code in {200, "200"} 86 | 87 | with tempfile.TemporaryDirectory() as tmp_dir: 88 | file_path: Path = ( 89 | Path(tmp_dir) / f"account_log-{random.randint(0, 10000)}.csv" 90 | ) 91 | 92 | with file_path.open("wb") as file: 93 | for chunk in response.iter_content(chunk_size=512): 94 | if chunk: 95 | file.write(chunk) 96 | 97 | def test_get_execution_events(self: Self, futures_auth_user: User) -> None: 98 | """ 99 | Checks the ``get_execution_events`` endpoint. 100 | """ 101 | result: dict = futures_auth_user.get_execution_events( 102 | tradeable=self.TRADEABLE, 103 | since=self.SINCE, 104 | before=self.BEFORE, 105 | sort=self.SORT_ASC, 106 | ) 107 | self._assert_elements_in_result(result) 108 | 109 | def test_get_order_events(self: Self, futures_auth_user: User) -> None: 110 | """ 111 | Checks the ``get_order_events`` endpoint. 112 | """ 113 | result: dict = futures_auth_user.get_order_events( 114 | tradeable=self.TRADEABLE, 115 | since=self.SINCE, 116 | before=self.BEFORE, 117 | sort=self.SORT_ASC, 118 | ) 119 | self._assert_elements_in_result(result) 120 | 121 | def test_get_open_orders(self: Self, futures_auth_user: User) -> None: 122 | """ 123 | Checks the ``get_open_orders`` endpoint. 124 | """ 125 | self._assert_successful_response(futures_auth_user.get_open_orders()) 126 | 127 | def test_get_open_positions(self: Self, futures_auth_user: User) -> None: 128 | """ 129 | Checks the ``get_open_positions`` endpoint. 130 | """ 131 | self._assert_successful_response(futures_auth_user.get_open_positions()) 132 | 133 | def test_get_trigger_events(self: Self, futures_auth_user: User) -> None: 134 | """ 135 | Checks the ``get_trigger_events`` endpoint. 136 | """ 137 | result = futures_auth_user.get_trigger_events( 138 | tradeable=self.TRADEABLE, 139 | since=self.SINCE, 140 | before=self.BEFORE, 141 | sort=self.SORT_ASC, 142 | ) 143 | self._assert_elements_in_result(result) 144 | 145 | @pytest.mark.skip("Subaccount actions are only available for institutional clients") 146 | def test_check_trading_enabled_on_subaccount( 147 | self: Self, 148 | futures_auth_user: User, 149 | ) -> None: 150 | """ 151 | Checks the ``check_trading_enabled_on_subaccount`` function. 152 | 153 | Until now, subaccounts are only available for institutional clients, so this 154 | execution raises an error. This test will work correctly (hopefully) when 155 | Kraken enables subaccounts for pro trader. 156 | """ 157 | assert futures_auth_user.check_trading_enabled_on_subaccount( 158 | subaccountUid=self.SUBACCOUNT_UID, 159 | ) == {"tradingEnabled": False} 160 | 161 | @pytest.mark.skip("Subaccount actions are only available for institutional clients") 162 | def test_set_trading_on_subaccount(self: Self, futures_auth_user: User) -> None: 163 | """ 164 | Checks the ``set_trading_on_subaccount`` function. 165 | 166 | Until now, subaccounts are only available for institutional clients, so this 167 | execution raises an error. This test will work correctly (hopefully) when 168 | Kraken enables subaccounts for pro trader. 169 | """ 170 | assert futures_auth_user.set_trading_on_subaccount( 171 | subaccountUid=self.SUBACCOUNT_UID, 172 | trading_enabled=True, 173 | ) == {"tradingEnabled": True} 174 | -------------------------------------------------------------------------------- /tests/spot/test_spot_market.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | 8 | """Module that implements the unit tests for the Spot market client.""" 9 | 10 | from time import sleep 11 | from typing import Any, Self 12 | 13 | import pytest 14 | 15 | from kraken.spot import Market 16 | 17 | from .helper import is_not_error 18 | 19 | 20 | @pytest.mark.spot 21 | @pytest.mark.spot_market 22 | class TestSpotMarket: 23 | """Test class for Spot Market client functionality.""" 24 | 25 | TEST_PAIR_BTCUSD = "XBTUSD" 26 | TEST_PAIR_DOTUSD = "DOTUSD" 27 | TEST_PAIR_DOTEUR = "DOTEUR" 28 | TEST_ASSET_BTC = "XBT" 29 | TEST_ASSET_ETH = "ETH" 30 | TEST_ASSET_USD = "USD" 31 | TEST_INTERVAL = 240 32 | TEST_SINCE = "1616663618" 33 | TEST_COUNT = 2 34 | 35 | def _assert_not_error(self: Self, result: Any) -> None: 36 | """Helper method to assert responses without errors.""" 37 | assert is_not_error(result) 38 | 39 | def test_get_system_status(self: Self, spot_market: Market) -> None: 40 | """ 41 | Checks the ``get_system_status`` endpoint by performing a 42 | valid request and validating that the response does not 43 | contain the error key. 44 | """ 45 | self._assert_not_error(spot_market.get_system_status()) 46 | 47 | def test_get_assets(self: Self, spot_market: Market) -> None: 48 | """ 49 | Checks the ``get_assets`` endpoint by performing multiple 50 | requests with different parameters and 51 | validating that the response does not contain the error key. 52 | """ 53 | for params in ( 54 | {}, 55 | {"assets": self.TEST_ASSET_USD}, 56 | {"assets": [self.TEST_ASSET_USD]}, 57 | {"assets": [f"{self.TEST_ASSET_BTC},{self.TEST_ASSET_USD}"]}, 58 | { 59 | "assets": [self.TEST_ASSET_BTC, self.TEST_ASSET_USD], 60 | "aclass": "currency", 61 | }, 62 | ): 63 | self._assert_not_error(spot_market.get_assets(**params)) 64 | sleep(3) 65 | 66 | def test_get_asset_pairs(self: Self, spot_market: Market) -> None: 67 | """ 68 | Checks the ``get_asset_pairs`` endpoint by performing multiple 69 | requests with different parameters and validating that the response 70 | does not contain the error key. 71 | """ 72 | self._assert_not_error(spot_market.get_asset_pairs()) 73 | self._assert_not_error(spot_market.get_asset_pairs(pair=self.TEST_PAIR_BTCUSD)) 74 | self._assert_not_error( 75 | spot_market.get_asset_pairs( 76 | pair=[self.TEST_PAIR_DOTEUR, self.TEST_PAIR_BTCUSD], 77 | ), 78 | ) 79 | for i in ("info", "leverage", "fees", "margin"): 80 | self._assert_not_error( 81 | spot_market.get_asset_pairs(pair=self.TEST_PAIR_DOTEUR, info=i), 82 | ) 83 | break # there is not reason for requesting more, this is just for info 84 | sleep(3) 85 | 86 | def test_get_ticker(self: Self, spot_market: Market) -> None: 87 | """ 88 | Checks the ``get_ticker`` endpoint by performing multiple 89 | requests with different parameters and validating that the response 90 | does not contain the error key. 91 | """ 92 | self._assert_not_error(spot_market.get_ticker()) 93 | self._assert_not_error(spot_market.get_ticker(pair=self.TEST_PAIR_BTCUSD)) 94 | self._assert_not_error( 95 | spot_market.get_ticker(pair=[self.TEST_PAIR_DOTUSD, self.TEST_PAIR_BTCUSD]), 96 | ) 97 | 98 | def test_get_ohlc(self: Self, spot_market: Market) -> None: 99 | """ 100 | Checks the ``get_ohlc`` endpoint by performing multiple 101 | requests with different parameters and validating that the response 102 | does not contain the error key. 103 | """ 104 | self._assert_not_error(spot_market.get_ohlc(pair=self.TEST_PAIR_BTCUSD)) 105 | self._assert_not_error( 106 | spot_market.get_ohlc( 107 | pair=self.TEST_PAIR_BTCUSD, 108 | interval=self.TEST_INTERVAL, 109 | since=self.TEST_SINCE, 110 | ), 111 | ) 112 | 113 | def test_get_order_book(self: Self, spot_market: Market) -> None: 114 | """ 115 | Checks the ``get_order_book`` endpoint by performing multiple 116 | requests with different parameters and validating that the response 117 | does not contain the error key. 118 | """ 119 | self._assert_not_error(spot_market.get_order_book(pair=self.TEST_PAIR_BTCUSD)) 120 | self._assert_not_error( 121 | spot_market.get_order_book( 122 | pair=self.TEST_PAIR_BTCUSD, 123 | count=self.TEST_COUNT, 124 | ), 125 | ) 126 | 127 | def test_get_recent_trades(self: Self, spot_market: Market) -> None: 128 | """ 129 | Checks the ``get_recent_trades`` endpoint by performing multiple 130 | requests with different parameters and validating that the response 131 | does not contain the error key. 132 | """ 133 | self._assert_not_error( 134 | spot_market.get_recent_trades(pair=self.TEST_PAIR_BTCUSD), 135 | ) 136 | self._assert_not_error( 137 | spot_market.get_recent_trades( 138 | pair=self.TEST_PAIR_BTCUSD, 139 | since=self.TEST_SINCE, 140 | count=self.TEST_COUNT, 141 | ), 142 | ) 143 | 144 | def test_get_recent_spreads(self: Self, spot_market: Market) -> None: 145 | """ 146 | Checks the ``get_recent_spreads`` endpoint by performing multiple 147 | requests with different parameters and validating that the response 148 | does not contain the error key. 149 | """ 150 | self._assert_not_error( 151 | spot_market.get_recent_spreads(pair=self.TEST_PAIR_BTCUSD), 152 | ) 153 | self._assert_not_error( 154 | spot_market.get_recent_spreads( 155 | pair=self.TEST_PAIR_BTCUSD, 156 | since=self.TEST_SINCE, 157 | ), 158 | ) 159 | 160 | def test_extra_parameter(self: Self, spot_market: Market) -> None: 161 | """ 162 | Checks if the extra parameter can be used to overwrite an existing one. 163 | This also checks ensure_string for this parameter. 164 | """ 165 | result: dict = spot_market.get_assets( 166 | assets=self.TEST_ASSET_BTC, 167 | extra_params={"asset": self.TEST_ASSET_ETH}, 168 | ) 169 | assert self.TEST_ASSET_BTC not in result 170 | assert f"X{self.TEST_ASSET_ETH}" in result 171 | -------------------------------------------------------------------------------- /examples/spot_examples.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python3 2 | # -*- mode: python; coding: utf-8 -*- 3 | # 4 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 5 | # All rights reserved. 6 | # https://github.com/btschwertfeger 7 | # 8 | 9 | """ 10 | Module that implements *some* examples for the Kraken Spot REST clients usage. 11 | """ 12 | 13 | import logging 14 | import os 15 | import time 16 | from pathlib import Path 17 | 18 | from kraken.spot import Funding, Market, Trade, User 19 | 20 | logging.basicConfig( 21 | format="%(asctime)s %(module)s,line: %(lineno)d %(levelname)8s | %(message)s", 22 | datefmt="%Y/%m/%d %H:%M:%S", 23 | level=logging.INFO, 24 | ) 25 | logging.getLogger("requests").setLevel(logging.WARNING) 26 | logging.getLogger("urllib3").setLevel(logging.WARNING) 27 | 28 | 29 | key = os.getenv("SPOT_API_KEY") 30 | secret = os.getenv("SPOT_SECRET_KEY") 31 | 32 | 33 | def user_examples() -> None: 34 | """Example usage of the Spot User client""" 35 | # Usage of the User client to access private endpoints: 36 | user = User(key=key, secret=secret) 37 | 38 | print(user.get_account_balance()) 39 | print(user.get_trade_balance()) # asset="BTC" 40 | print(user.get_open_orders()) 41 | print(user.get_closed_orders()) 42 | print( 43 | user.get_orders_info( 44 | txid="OBQFM7-JNVKS-H3ULEH", # or txid="id1,id2,id3" or txid=["id1","id2"] 45 | ), 46 | ) 47 | print(user.get_trades_history()) 48 | time.sleep(3) # to avoid rate limit 49 | print(user.get_trades_info(txid="TCNTTR-QBEVO-E5H5UK")) 50 | print(user.get_open_positions()) # or txid="someid" 51 | print( 52 | user.get_ledgers_info(), # asset="BTC" or asset="BTC,EUR" or asset=["BTC","EUR"] 53 | ) 54 | print(user.get_ledgers(id_="LIORGR-33NXH-LBUS5Z")) 55 | print(user.get_trade_volume()) # pair="BTC/EUR" 56 | 57 | # Exporting a ledger and trade report can be useful for analysis or 58 | # record-keeping purposes: 59 | response = user.request_export_report( 60 | report="ledgers", # or report="trades" 61 | description="myLedgers1", 62 | format_="CSV", 63 | ) 64 | print(user.get_export_report_status(report="ledgers")) 65 | 66 | # save report to file 67 | response_data = user.retrieve_export(id_=response["id"]) 68 | with Path("myExport.zip").open("wb") as file: 69 | for chunk in response_data.iter_content(chunk_size=512): 70 | if chunk: 71 | file.write(chunk) 72 | 73 | print( 74 | user.delete_export_report(id_=response["id"], type_="delete"), 75 | ) 76 | 77 | 78 | def market_examples() -> None: 79 | """Example usage of the Spot Market client""" 80 | market = Market() 81 | 82 | print(market.get_assets(assets=["XBT"])) 83 | print(market.get_asset_pairs(pair=["DOTEUR"])) 84 | print(market.get_ticker(pair="XBTUSD")) 85 | print(market.get_ohlc(pair="XBTUSD", interval=5)) 86 | print(market.get_order_book(pair="XBTUSD", count=10)) 87 | print(market.get_recent_trades(pair="XBTUSD")) 88 | print(market.get_recent_spreads(pair="XBTUSD")) 89 | print(market.get_system_status()) 90 | time.sleep(2) 91 | 92 | 93 | def trade_examples() -> None: 94 | """Example usage of the Spot Trade client""" 95 | print( 96 | "Attention: Please check if you really want to execute trade functions." 97 | " Running them without caution may lead to unintended orders!", 98 | ) 99 | return 100 | trade = Trade(key=key, secret=secret) 101 | 102 | print( 103 | trade.create_order( 104 | ordertype="limit", 105 | side="buy", 106 | volume=1, 107 | pair="BTC/EUR", 108 | price=0.01, 109 | ), 110 | ) 111 | print( 112 | trade.create_order_batch( 113 | orders=[ 114 | { 115 | "close": { 116 | "ordertype": "stop-loss-limit", 117 | "price": 120, 118 | "price2": 110, 119 | }, 120 | "ordertype": "limit", 121 | "price": 140, 122 | "price2": 130, 123 | "timeinforce": "GTC", 124 | "type": "buy", 125 | "userref": "345dsdfddfgdsgdfgsfdsfsdf", 126 | "volume": 1000, 127 | }, 128 | { 129 | "ordertype": "limit", 130 | "price": 150, 131 | "timeinforce": "GTC", 132 | "type": "sell", 133 | "userref": "1dfgesggwe5t3", 134 | "volume": 123, 135 | }, 136 | ], 137 | pair="BTC/USD", 138 | validate=True, 139 | ), 140 | ) 141 | 142 | print( 143 | trade.edit_order(txid="sometxid", pair="BTC/EUR", volume=4.2, price=17000), 144 | ) 145 | time.sleep(2) 146 | 147 | print(trade.cancel_order(txid="O2JLFP-VYFIW-35ZAAE")) 148 | print(trade.cancel_all_orders()) 149 | print(trade.cancel_all_orders_after_x(timeout=6)) 150 | 151 | print( 152 | trade.cancel_order_batch( 153 | orders=[ 154 | "O2JLFP-VYFIW-35ZAAE", 155 | "O523KJ-DO4M2-KAT243", 156 | "OCDIAL-YC66C-DOF7HS", 157 | "OVFPZ2-DA2GV-VBFVVI", 158 | ], 159 | ), 160 | ) 161 | 162 | 163 | def funding_examples() -> None: 164 | """Example usage of the Funding client""" 165 | funding = Funding(key=key, secret=secret) 166 | print(funding.get_deposit_methods(asset="DOT")) 167 | # print(funding.get_deposit_address(asset="DOT", method="Polkadot")) 168 | # print(funding.get_recent_deposits_status(asset="DOT")) 169 | print( 170 | funding.get_withdrawal_info(asset="DOT", key="MyPolkadotWallet", amount="200"), 171 | ) 172 | 173 | print( 174 | "Attention: Please check if you really want to execute funding functions." 175 | " Running them without caution may lead to unintended withdrawals!", 176 | ) 177 | return 178 | time.sleep(2) # to avoid rate limit 179 | print(funding.withdraw_funds(asset="DOT", key="MyPolkadotWallet", amount=200)) 180 | print(funding.get_recent_withdraw_status(asset="DOT")) 181 | print(funding.cancel_withdraw(asset="DOT", refid="12345")) 182 | print( 183 | funding.wallet_transfer( 184 | asset="ETH", 185 | amount=0.100, 186 | from_="Spot Wallet", 187 | to_="Futures Wallet", 188 | ), 189 | ) 190 | 191 | 192 | def main() -> None: 193 | """Uncomment the examples you want to run:""" 194 | # NOTE: These are only examples that show how to use the clients, there are 195 | # many other functions available in the clients. 196 | 197 | # user_examples() 198 | # market_examples() 199 | # trade_examples() 200 | # funding_examples() 201 | 202 | 203 | if __name__ == "__main__": 204 | main() 205 | -------------------------------------------------------------------------------- /tests/spot/test_spot_orderbook.py: -------------------------------------------------------------------------------- 1 | # -*- mode: python; coding: utf-8 -*- 2 | # 3 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 4 | # All rights reserved. 5 | # https://github.com/btschwertfeger 6 | # 7 | 8 | """ 9 | Module that implements the unit tests regarding the Spot Orderbook client. 10 | """ 11 | 12 | from __future__ import annotations 13 | 14 | import asyncio 15 | import json 16 | from asyncio import sleep as async_sleep 17 | from collections import OrderedDict 18 | from typing import TYPE_CHECKING, Self 19 | from unittest import mock 20 | 21 | import pytest 22 | 23 | from kraken.spot import SpotOrderBookClient 24 | 25 | from .helper import FIXTURE_DIR, SpotOrderBookClientWrapper 26 | 27 | if TYPE_CHECKING: 28 | from pathlib import Path 29 | 30 | 31 | @pytest.mark.spot 32 | @pytest.mark.spot_websocket 33 | @pytest.mark.spot_orderbook 34 | class TestSpotOrderBook: 35 | """Test class for Spot Orderbook client functionality.""" 36 | 37 | TEST_PAIR_BTCUSD = "BTC/USD" 38 | TEST_PAIR_DOTUSD = "DOT/USD" 39 | TEST_PAIR_ETHUSD = "ETH/USD" 40 | TEST_PAIR_MATICUSD = "MATIC/USD" 41 | TEST_PAIR_BTCEUR = "BTC/EUR" 42 | TEST_DEPTH = 10 43 | 44 | def test_create_public_bot(self: Self, caplog: pytest.LogCaptureFixture) -> None: 45 | """ 46 | Checks if the websocket client can be instantiated. 47 | """ 48 | 49 | async def create_bot() -> None: 50 | async with SpotOrderBookClientWrapper() as orderbook: 51 | await async_sleep(10) 52 | assert orderbook.depth == self.TEST_DEPTH 53 | 54 | asyncio.run(create_bot()) 55 | 56 | for expected in ( 57 | 'channel": "status"', 58 | '"api_version": "v2"', 59 | '"system": "online",', 60 | '"type": "update"', 61 | ): 62 | assert expected in caplog.text 63 | 64 | def test_get_first(self) -> None: 65 | """ 66 | Checks the ``get_first`` method. 67 | """ 68 | assert ( 69 | float(10) 70 | == SpotOrderBookClientWrapper.get_first(("10", "5")) 71 | == SpotOrderBookClientWrapper.get_first((10, 5)) 72 | ) 73 | 74 | @mock.patch("kraken.spot.orderbook.SpotWSClient", return_value=None) 75 | @mock.patch( 76 | "kraken.spot.orderbook.SpotOrderBookClient.remove_book", 77 | return_value=mock.AsyncMock(), 78 | ) 79 | @mock.patch( 80 | "kraken.spot.orderbook.SpotOrderBookClient.add_book", 81 | return_value=mock.AsyncMock(), 82 | ) 83 | def test_passing_msg_and_validate_checksum( 84 | self: Self, 85 | mock_add_book: mock.MagicMock, # noqa: ARG002 86 | mock_remove_book: mock.MagicMock, # noqa: ARG002 87 | mock_ws_client: mock.MagicMock, # noqa: ARG002 88 | ) -> None: 89 | """ 90 | This function checks if the initial snapshot and the book updates are 91 | assigned correctly so that the checksum calculation can validate the 92 | assigned book updates and values. 93 | """ 94 | json_file_path: Path = FIXTURE_DIR / "orderbook-v2.json" 95 | with json_file_path.open("r", encoding="utf-8") as json_file: 96 | orderbook: dict = json.load(json_file) 97 | 98 | async def assign() -> None: 99 | client: SpotOrderBookClient = SpotOrderBookClient(depth=self.TEST_DEPTH) 100 | # starting the client is not necessary for this test 101 | 102 | await client.on_message(message=orderbook["init"]) 103 | assert client.get(pair=self.TEST_PAIR_BTCUSD)["valid"] 104 | 105 | for update in orderbook["updates"]: 106 | await client.on_message(message=update) 107 | assert client.get(pair=self.TEST_PAIR_BTCUSD)["valid"] 108 | 109 | bad_message: dict = { 110 | "channel": "book", 111 | "type": "update", 112 | "data": [ 113 | { 114 | "symbol": self.TEST_PAIR_BTCUSD, 115 | "bids": [{"price": 29430.3, "qty": 1.69289565}], 116 | "asks": [], 117 | "checksum": 2438868880, 118 | "timestamp": "2023-07-30T15:30:49.008834Z", 119 | }, 120 | ], 121 | } 122 | await client.on_message(message=bad_message) 123 | assert not client.get(pair=self.TEST_PAIR_BTCUSD)["valid"] 124 | 125 | asyncio.run(assign()) 126 | 127 | def test_add_book(self: Self, caplog: pytest.LogCaptureFixture) -> None: 128 | """ 129 | Checks if the orderbook client is able to add a book by subscribing. 130 | The logs are then checked for the expected results. 131 | """ 132 | 133 | async def execute_add_book() -> None: 134 | async with SpotOrderBookClientWrapper() as orderbook: 135 | await orderbook.add_book( 136 | pairs=[ 137 | self.TEST_PAIR_BTCUSD, 138 | self.TEST_PAIR_DOTUSD, 139 | self.TEST_PAIR_ETHUSD, 140 | self.TEST_PAIR_MATICUSD, 141 | self.TEST_PAIR_BTCEUR, 142 | ], 143 | ) 144 | await async_sleep(4) 145 | 146 | book: dict | None = orderbook.get(pair=self.TEST_PAIR_BTCUSD) 147 | assert isinstance(book, dict) 148 | 149 | assert all( 150 | key in book 151 | for key in ("ask", "bid", "valid", "price_decimals", "qty_decimals") 152 | ), book 153 | 154 | assert isinstance(book["ask"], OrderedDict) 155 | assert isinstance(book["bid"], OrderedDict) 156 | 157 | for ask, bid in zip(book["ask"], book["bid"], strict=True): 158 | assert isinstance(ask, str) 159 | assert isinstance(bid, str) 160 | 161 | asyncio.run(execute_add_book()) 162 | 163 | for expected in ( 164 | '{"method": "subscribe", "result": {"channel": "book", "depth": 10, ' 165 | '"snapshot": true, "symbol": "BTC/USD"}, "success": true, "time_in": ', 166 | '{"channel": "book", "type": "snapshot", "data": ' 167 | '[{"symbol": "BTC/USD", "bids": ', 168 | ): 169 | assert expected in caplog.text 170 | 171 | def test_remove_book(self: Self, caplog: pytest.LogCaptureFixture) -> None: 172 | """ 173 | Checks if the orderbook client is able to add a book by subscribing to a book 174 | and unsubscribing right after + validating using the logs. 175 | """ 176 | 177 | async def execute_remove_book() -> None: 178 | async with SpotOrderBookClientWrapper() as orderbook: 179 | await orderbook.add_book(pairs=[self.TEST_PAIR_BTCUSD]) 180 | await async_sleep(2) 181 | 182 | await orderbook.remove_book(pairs=[self.TEST_PAIR_BTCUSD]) 183 | await async_sleep(2) 184 | 185 | asyncio.run(execute_remove_book()) 186 | 187 | assert ( 188 | '{"method": "unsubscribe", "result": {"channel": "book", "depth": 10, "symbol": "BTC/USD"}, "success": true, "time_in":' 189 | in caplog.text 190 | ) 191 | -------------------------------------------------------------------------------- /examples/futures_examples.py: -------------------------------------------------------------------------------- 1 | # !/usr/bin/env python3 2 | # -*- mode: python; coding: utf-8 -*- 3 | # 4 | # Copyright (C) 2023 Benjamin Thomas Schwertfeger 5 | # All rights reserved. 6 | # https://github.com/btschwertfeger 7 | # 8 | 9 | """ 10 | Module that implements *some* examples for the Kraken Futures REST clients 11 | usage. 12 | """ 13 | 14 | import logging 15 | import os 16 | import time 17 | from pathlib import Path 18 | 19 | from kraken.futures import Funding, Market, Trade, User 20 | 21 | logging.basicConfig( 22 | format="%(asctime)s %(module)s,line: %(lineno)d %(levelname)8s | %(message)s", 23 | datefmt="%Y/%m/%d %H:%M:%S", 24 | level=logging.INFO, 25 | ) 26 | logging.getLogger("requests").setLevel(logging.WARNING) 27 | logging.getLogger("urllib3").setLevel(logging.WARNING) 28 | 29 | key = os.getenv("FUTURES_SANDBOX_KEY") 30 | secret = os.getenv("FUTURES_SANDBOX_SECRET") 31 | 32 | 33 | def market_examples() -> None: 34 | """Example Futures Market client usage""" 35 | 36 | # Usage of the Market client to access public endpoints: 37 | market = Market() 38 | print(market.get_tick_types()) 39 | print(market.get_tradeable_products(tick_type="trade")) 40 | print(market.get_resolutions(tick_type="trade", tradeable="PI_XBTUSD")) 41 | print( 42 | market.get_ohlc( 43 | tick_type="trade", 44 | symbol="PI_XBTUSD", 45 | resolution="5m", 46 | from_="1668989233", 47 | ), 48 | ) 49 | print(market.get_fee_schedules()) 50 | print( 51 | market.get_orderbook(symbol="fi_xbtusd_180615"), 52 | ) # might need adjustment of the symbol 53 | print(market.get_tickers()) 54 | print(market.get_instruments()) 55 | print(market.get_instruments_status()) 56 | print(market.get_instruments_status(instrument="PI_XBTUSD")) 57 | print(market.get_trade_history(symbol="PI_XBTUSD")) 58 | print(market.get_historical_funding_rates(symbol="PI_XBTUSD")) 59 | time.sleep(2) # Just to avoid rate limits 60 | 61 | # Usage of the Market client to access private endpoints: 62 | # (commented out to avoid accidental usage) 63 | priv_market = Market(key=key, secret=secret, sandbox=True) 64 | # print(priv_market.get_fee_schedules_vol()) 65 | print(priv_market.get_leverage_preference()) 66 | # print(priv_market.set_leverage_preference(symbol='PF_XBTUSD', maxLeverage=2)) # set max leverage 67 | # print(priv_market.set_leverage_preference(symbol='PF_XBTUSD')) # reset max leverage 68 | # print(priv_market.set_pnl_preference(symbol='PF_XBTUSD', pnlPreference='BTC')) 69 | 70 | # time.sleep(2) 71 | # print(priv_market.get_execution_events()) 72 | # print(market.get_public_execution_events(tradeable='PI_XBTUSD')) 73 | # print(market.get_public_order_events(tradeable='PI_XBTUSD')) 74 | # print(market.get_public_mark_price_events(tradeable='PI_XBTUSD')) 75 | # print(priv_market.get_order_events()) 76 | # print(priv_market.get_trigger_events()) 77 | 78 | 79 | def user_examples() -> None: 80 | """Example Futures User client usage""" 81 | # NOTE: This only works if you have set valid credentials for the the 82 | # Futures demo environment. Remove the `sandbox=True` argument to use 83 | # the production environment. 84 | # 85 | # Usage of the User client to access private endpoints: 86 | user = User(key=key, secret=secret, sandbox=True) 87 | print(user.get_wallets()) 88 | print(user.get_subaccounts()) 89 | print(user.get_unwind_queue()) 90 | print(user.get_notifications()) 91 | print(user.get_open_positions()) 92 | print(user.get_open_orders()) 93 | 94 | # You can retrieve the account log like so: 95 | print(user.get_account_log(before="1604937694000")) 96 | print(user.get_account_log(info="futures liquidation")) 97 | time.sleep(2) # Just to avoid rate limits 98 | 99 | response = user.get_account_log_csv() 100 | assert response.status_code in {200, "200"} 101 | with Path("account_log.csv").open("wb") as file: 102 | for chunk in response.iter_content(chunk_size=512): 103 | if chunk: 104 | file.write(chunk) 105 | 106 | 107 | def trade_examples() -> None: 108 | """Example Futures Trade client usage""" 109 | print( 110 | "Attention: Please check if you want to execute the trade endpoints!" 111 | " Check the script manually before running this example.", 112 | ) 113 | return 114 | # return 115 | # NOTE: This only works if you have set valid credentials for the the 116 | # Futures demo environment. Remove the `sandbox=True` argument to use 117 | # the production environment. 118 | trade = Trade(key=key, secret=secret, sandbox=True) 119 | print(trade.get_fills()) 120 | print(trade.get_fills(lastFillTime="2020-07-21T12:41:52.790Z")) 121 | print( 122 | trade.create_batch_order( 123 | batchorder_list=[ 124 | { 125 | "order": "send", 126 | "order_tag": "1", 127 | "orderType": "lmt", 128 | "symbol": "PI_XBTUSD", 129 | "side": "buy", 130 | "size": 1, 131 | "limitPrice": 1.00, 132 | }, 133 | { 134 | "order": "send", 135 | "order_tag": "2", 136 | "orderType": "stp", 137 | "symbol": "PI_XBTUSD", 138 | "side": "buy", 139 | "size": 1, 140 | "limitPrice": 2.00, 141 | "stopPrice": 3.00, 142 | }, 143 | { 144 | "order": "cancel", 145 | "order_id": "e35d61dd-8a30-4d5f-a574-b5593ef0c050", 146 | }, 147 | { 148 | "order": "cancel", 149 | "cliOrdId": 123456789, 150 | }, 151 | ], 152 | ), 153 | ) 154 | print(trade.cancel_all_orders()) 155 | print(trade.cancel_all_orders(symbol="pi_xbtusd")) 156 | print(trade.dead_mans_switch(timeout=60)) 157 | print(trade.dead_mans_switch(timeout=0)) # to deactivate 158 | print(trade.cancel_order(order_id="some order id")) 159 | print( 160 | trade.edit_order( 161 | orderId="some order id", 162 | size=300, 163 | limitPrice=401, 164 | stopPrice=350, 165 | ), 166 | ) 167 | print(trade.get_orders_status(orderIds=["orderid1", "orderid2"])) 168 | print( 169 | trade.create_order( 170 | orderType="lmt", 171 | side="buy", 172 | size=1, 173 | limitPrice=4, 174 | symbol="pf_bchusd", 175 | ), 176 | ) 177 | print( 178 | trade.create_order( 179 | orderType="take_profit", 180 | side="buy", 181 | size=1, 182 | symbol="pf_bchusd", 183 | stopPrice=100, 184 | triggerSignal="mark", 185 | ), 186 | ) 187 | 188 | 189 | def funding_examples() -> None: 190 | """Example Funding client usage""" 191 | funding = Funding(key=key, secret=secret, sandbox=True) 192 | print(funding.get_historical_funding_rates(symbol="PF_SOLUSD")) 193 | 194 | 195 | def main() -> None: 196 | """Uncomment the examples you want to run:""" 197 | # user_examples() 198 | # market_examples() 199 | # trade_examples() 200 | # funding_examples() 201 | 202 | 203 | if __name__ == "__main__": 204 | main() 205 | --------------------------------------------------------------------------------