Skip to content

Commit

Permalink
Merge branch 'master' into 191-add-processbefore-parameter-to-krakenf…
Browse files Browse the repository at this point in the history
…uturestradecancel_orderedit_ordercreate_ordercreate_batch_order
  • Loading branch information
btschwertfeger committed Mar 8, 2024
2 parents dabcba5 + 3f53ec7 commit e3ed0d7
Show file tree
Hide file tree
Showing 36 changed files with 272 additions and 219 deletions.
37 changes: 22 additions & 15 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,23 @@

repos:
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.1.13
rev: v0.2.2
hooks:
- id: ruff
args:
- --preview
- --fix
- --exit-non-zero-on-fix
- repo: https://github.com/pycqa/flake8
rev: 6.1.0
rev: 7.0.0
hooks:
- id: flake8
args:
- --select=E9,F63,F7,F82
- --show-source
- --statistics
- repo: https://github.com/pycqa/pylint
rev: v3.0.2
rev: v3.0.3
hooks:
- id: pylint
name: pylint
Expand All @@ -31,16 +31,23 @@ repos:
- --rcfile=pyproject.toml
- -d=R0801 # ignore duplicate code
- -j=4
# - repo: https://github.com/pre-commit/mirrors-mypy # FIXME
# rev: v1.8.0
# hooks:
# - id: mypy
# name: mypy
# additional_dependencies: ["types-requests"]
# pass_filenames: false
# args:
# - --config-file=pyproject.toml
# - kraken
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.8.0
hooks:
- id: mypy
name: mypy
additional_dependencies: ["types-requests"]
pass_filenames: false
args:
- --config-file=pyproject.toml
- --install-types
- --non-interactive
- repo: https://github.com/codespell-project/codespell
rev: v2.2.6
hooks:
- id: codespell
additional_dependencies:
- tomli
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.5.0
hooks:
Expand Down Expand Up @@ -75,15 +82,15 @@ repos:
- id: rst-inline-touching-normal
- id: text-unicode-replacement-char
- repo: https://github.com/psf/black
rev: 23.7.0
rev: 24.2.0
hooks:
- id: black
- repo: https://github.com/pre-commit/mirrors-prettier
rev: v3.0.2
hooks:
- id: prettier
- repo: https://github.com/PyCQA/isort
rev: 5.12.0
rev: 5.13.2
hooks:
- id: isort
args:
Expand Down
3 changes: 3 additions & 0 deletions MANIFEST.in
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,6 @@ graft kraken
prune doc
prune examples
prune tests
prune venv
prune .github
prune .cache
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -316,7 +316,7 @@ The Kraken Futures API documentation can be found here:
## Futures REST API

As the Spot API, Kraken also offers a REST API for Futures. Examples on how to
use the python-kraken-sdk fot Futures are shown in
use the python-kraken-sdk for Futures are shown in
`examples/futures_examples.py` and listed in a shorter ways below.

```python
Expand Down
2 changes: 2 additions & 0 deletions doc/src/issues.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ Issues listed here: `python-kraken-sdk/issues`_
Futures Trading
---------------

- The Kraken API returns 500 - INTERNAL_SERVER_ERROR for some endpoints if
``order_id`` or ``orderId``, ``cliOrdId`` seems to work in all cases.
- Kraken's API doesn't seem to know the ``trailing_stop`` order type and raises
an error if this type is part of an order. This order type is documented here
https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-send-order
Expand Down
2 changes: 1 addition & 1 deletion examples/futures_trading_bot_template.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ async def __main(self: ManagedBot) -> None:
Instantiates the trading strategy/algorithm and subscribes to the
desired websocket feeds. Run the loop while no exception occur.
Thi variable `exception_occur` which is an attribute of the
The variable `exception_occur` which is an attribute of the
KrakenFuturesWSClient can be set individually but is also being set to
`True` if the websocket connection has some fatal error. This is used to
exit the asyncio loop - but you can also apply your own reconnect rules.
Expand Down
6 changes: 3 additions & 3 deletions examples/market_client_example.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@
"metadata": {},
"outputs": [],
"source": [
"market = Market() "
"market = Market()"
]
},
{
Expand Down Expand Up @@ -280,7 +280,7 @@
"# compute ema\n",
"df['ema21'] = df['close'].ewm(span=21, adjust=False, min_periods=21).mean()\n",
"df['ema50'] = df['close'].ewm(span=50, adjust=False, min_periods=50).mean()\n",
"df['ema200'] = df['close'].ewm(span=200, adjust=False, min_periods=200).mean() "
"df['ema200'] = df['close'].ewm(span=200, adjust=False, min_periods=200).mean()"
]
},
{
Expand Down Expand Up @@ -619,7 +619,7 @@
" df.at[row, 's2'] = np.NaN\n",
" df.at[row, 'r2'] = np.NaN\n",
"\n",
"# isnt that a beauty?:\n",
"# isn't that a beauty?:\n",
"df"
]
},
Expand Down
1 change: 1 addition & 0 deletions examples/spot_trading_bot_template_v1.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2023 Benjamin Thomas Schwertfeger
# GitHub: https://github.com/btschwertfeger
# ruff: noqa: RUF027

"""
Module that provides a template to build a Spot trading algorithm using the
Expand Down
2 changes: 1 addition & 1 deletion examples/spot_trading_bot_template_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# -*- coding: utf-8 -*-
# Copyright (C) 2023 Benjamin Thomas Schwertfeger
# GitHub: https://github.com/btschwertfeger

# ruff: noqa: RUF027
"""
Module that provides a template to build a Spot trading algorithm using the
python-kraken-sdk and Kraken Spot websocket API v2.
Expand Down
74 changes: 31 additions & 43 deletions kraken/base_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,9 @@
import hmac
import json
import time
import urllib.parse
from functools import wraps
from typing import Any, Callable, Optional, Type, TypeVar
from urllib.parse import urljoin
from typing import Any, Callable, Final, Optional, Type, TypeVar
from urllib.parse import urlencode, urljoin
from uuid import uuid1

import requests
Expand Down Expand Up @@ -183,7 +182,6 @@ class KrakenSpotBaseAPI:
"""

URL: str = "https://api.kraken.com"
API_V: str = "/0"
TIMEOUT: int = 10

def __init__(
Expand All @@ -198,9 +196,7 @@ def __init__(
if sandbox:
raise ValueError("Sandbox not available for Kraken Spot trading.")
if url:
self.url = url
else:
self.url = urljoin(self.URL, self.API_V)
self.URL = url

self.__key: str = key
self.__secret: str = secret
Expand Down Expand Up @@ -254,6 +250,9 @@ def _request( # noqa: PLR0913 # pylint: disable=too-many-arguments
:rtype: dict[str, Any] | list[str] | list[dict[str, Any]] |
requests.Response
"""
METHOD: str = method.upper()
URL: str = urljoin(self.URL, uri)

if not defined(params):
params = {}
if defined(extra_params):
Expand All @@ -263,12 +262,11 @@ def _request( # noqa: PLR0913 # pylint: disable=too-many-arguments
else extra_params
)

METHOD: str = method.upper()
if METHOD in {"GET", "DELETE"} and params:
data_json: str = "&".join(
[f"{key}={params[key]}" for key in sorted(params)],
)
uri += f"?{data_json}".replace(" ", "%20")
query_params: str = (
urlencode(params, doseq=True)
if METHOD in {"GET", "DELETE"} and params
else ""
)

TIMEOUT: int = self.TIMEOUT if timeout != 10 else timeout
HEADERS: dict = {}
Expand All @@ -286,26 +284,25 @@ def _request( # noqa: PLR0913 # pylint: disable=too-many-arguments
sign_data = json.dumps(params)
else:
content_type = "application/x-www-form-urlencoded; charset=utf-8"
sign_data = urllib.parse.urlencode(params)
sign_data = urlencode(params, doseq=True)

HEADERS.update(
{
"Content-Type": content_type,
"API-Key": self.__key,
"API-Sign": self._get_kraken_signature(
url_path=f"{self.API_V}{uri}",
url_path=f"{uri}{query_params}",
data=sign_data,
nonce=params["nonce"],
),
},
)

URL: str = f"{self.url}{uri}"
if METHOD in {"GET", "DELETE"}:
return self.__check_response_data(
response=self.__session.request(
method=METHOD,
url=URL,
url=f"{URL}?{query_params}",
headers=HEADERS,
timeout=TIMEOUT,
),
Expand Down Expand Up @@ -506,15 +503,14 @@ def _request( # noqa: PLR0913 # pylint: disable=too-many-arguments
This is used for example when requesting an export of the trade
history as .zip archive.
:type return_raw: bool, optional
:raise kraken.exceptions.KrakenException.*: If the response contains
:raise kraken.exceptions.*: If the response contains
errors
:return: The response
:rtype: dict[str, Any] | list[dict[str, Any]] | list[str] | requests.Response
"""
METHOD: str = method.upper()
METHOD: Final[str] = method.upper()
URL: Final[str] = urljoin(self.url, uri)

post_string: str = ""
listed_params: list[str]
if defined(extra_params):
extra_params = (
json.loads(extra_params)
Expand All @@ -524,22 +520,16 @@ def _request( # noqa: PLR0913 # pylint: disable=too-many-arguments
else:
extra_params = {}

if defined(post_params):
post_params |= extra_params
listed_params = [f"{key}={post_params[key]}" for key in sorted(post_params)]
post_string = "&".join(listed_params)
else:
if post_params is None:
post_params = {}
post_params |= extra_params

query_string: str = ""
if query_params is not None:
listed_params = [
f"{key}={query_params[key]}" for key in sorted(query_params)
]
query_string = "&".join(listed_params).replace(" ", "%20")
else:
query_params = {}
encoded_payload: Final[str] = urlencode(post_params, doseq=True)

# post_string: Final[str] = json.dumps(post_params) if post_params else ""
query_string = (
"" if query_params is None else urlencode(query_params, doseq=True)
)

TIMEOUT: int = self.TIMEOUT if timeout == 10 else timeout
HEADERS: dict = {}
Expand All @@ -554,19 +544,17 @@ def _request( # noqa: PLR0913 # pylint: disable=too-many-arguments
"APIKey": self.__key,
"Authent": self._get_kraken_futures_signature(
uri,
query_string + post_string,
query_string + encoded_payload,
nonce,
),
},
)

if METHOD in {"GET", "DELETE"}:
return self.__check_response_data(
response=self.__session.request(
method=METHOD,
url=f"{self.url}{uri}"
if not query_string
else f"{self.url}{uri}?{query_string}",
url=URL,
params=query_string,
headers=HEADERS,
timeout=TIMEOUT,
),
Expand All @@ -577,8 +565,8 @@ def _request( # noqa: PLR0913 # pylint: disable=too-many-arguments
return self.__check_response_data(
response=self.__session.request(
method=METHOD,
url=f"{self.url}{uri}",
params=str.encode(post_string),
url=URL,
params=encoded_payload,
headers=HEADERS,
timeout=TIMEOUT,
),
Expand All @@ -588,8 +576,8 @@ def _request( # noqa: PLR0913 # pylint: disable=too-many-arguments
return self.__check_response_data(
response=self.__session.request(
method=METHOD,
url=f"{self.url}{uri}?{post_string}",
data=str.encode(post_string),
url=URL,
data=encoded_payload,
headers=HEADERS,
timeout=TIMEOUT,
),
Expand Down
2 changes: 1 addition & 1 deletion kraken/futures/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,4 @@
from kraken.futures.user import User
from kraken.futures.ws_client import KrakenFuturesWSClient

__all__ = ["Funding", "Market", "Trade", "User", "KrakenFuturesWSClient"]
__all__ = ["Funding", "KrakenFuturesWSClient", "Market", "Trade", "User"]
2 changes: 1 addition & 1 deletion kraken/futures/trade.py
Original file line number Diff line number Diff line change
Expand Up @@ -492,7 +492,7 @@ def get_orders_status(
Requires at least the ``General API - Read Only`` permission in the API
key settings.
- https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-get-the-current-status-for-specific-orders
- https://docs.futures.kraken.com/#http-api-trading-v3-api-order-management-get-specific-orders-39-status
:param orderIds: The order ids to cancel
:type orderIds: str | list[str], optional
Expand Down
2 changes: 1 addition & 1 deletion kraken/futures/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -605,7 +605,7 @@ def get_order_events(
extra_params: Optional[dict] = None,
) -> dict:
"""
Retriev information about the user-specific order events including
Retrieve information about the user-specific order events including
opened, closed, filled, etc. The returned ``continuation_token`` can be
used to request more data.
Expand Down
2 changes: 1 addition & 1 deletion kraken/futures/websocket/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ async def __run(self: ConnectFuturesWebsocket, event: asyncio.Event) -> None:
self.__new_challenge = None
self.__last_challenge = None

async with websockets.connect( # pylint: disable=no-member
async with websockets.connect( # pylint: disable=no-member # noqa: PLR1702
f"wss://{self.__ws_endpoint}",
ping_interval=30,
) as socket:
Expand Down
8 changes: 3 additions & 5 deletions kraken/futures/ws_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,9 @@ def __init__(
self.__callback: Any = callback
self._conn: ConnectFuturesWebsocket = ConnectFuturesWebsocket(
client=self,
endpoint=url
if url
else self.DEMO_ENV_URL
if sandbox
else self.PROD_ENV_URL,
endpoint=(
url if url else self.DEMO_ENV_URL if sandbox else self.PROD_ENV_URL
),
callback=self.on_message,
)

Expand Down
Loading

0 comments on commit e3ed0d7

Please sign in to comment.