Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve caching #102

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
70 changes: 54 additions & 16 deletions kraken/base_api/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import json
import time
import urllib.parse
from typing import Any, Dict, List, Optional, Type, Union
from typing import Any, Callable, Dict, List, Optional, Type, Union
from urllib.parse import urljoin
from uuid import uuid1

Expand All @@ -20,6 +20,59 @@
from kraken.exceptions import KrakenException


def defined(value: Any) -> bool:
"""Returns ``True`` if ``value`` is not ``None``"""
return value is not None


def ensure_string(parameter_name: str) -> Callable:
"""
This function is intended to be used as decorator
to ensure that a specific parameter is of type string.

.. code-block:: python
:linenos:
:caption: Example

@ensure_string("assets")
@lru_cache()
def get_assets(
self: "Market",
assets: Optional[Union[str, List[str]]] = None,
aclass: Optional[str] = None,
) -> dict:
# If the function was called using
# get_assets(assets=["BTC","USD","ETH"])
# there will be no error because of the non-hashable
# parameters, because the decorator transforms the
# list into: "BTC,USD,ETH"

:param parameter_name: The parameter name to transform into string
:type parameter_name: str
:return: The called function
:rtype: Callable
"""

def decorator(func: Callable) -> Callable:
def wrapper(*args: Any, **kwargs: Any) -> Any:
if parameter_name in kwargs:
value: Any = kwargs[parameter_name]
if isinstance(value, str) or value is None:
pass
elif isinstance(value, list):
kwargs[parameter_name] = ",".join(value)
else:
raise ValueError(
f"{parameter_name} cannot be {type(kwargs[parameter_name])}!"
)

return func(*args, **kwargs)

return wrapper

return decorator


class KrakenErrorHandler:
"""
Class that checks if the response of a request contains error messages and
Expand Down Expand Up @@ -313,21 +366,6 @@ def return_unique_id(self: "KrakenBaseSpotAPI") -> str:
"""
return "".join(str(uuid1()).split("-"))

def _to_str_list(self: "KrakenBaseSpotAPI", value: Union[str, list]) -> str:
"""
Converts a list to a comme separated string

:param value: The value to convert to e.g., ["XBT", "USD"] => "XBT,USD"
:type value: Union[str,dict]
:return: The content ov `value` as comma-separated string
:rtype: str
"""
if isinstance(value, str):
return value
if isinstance(value, list):
return ",".join(value)
raise ValueError("a must be type of str or list of strings")

def __enter__(self: "KrakenBaseSpotAPI") -> "KrakenBaseSpotAPI":
return self

Expand Down
8 changes: 4 additions & 4 deletions kraken/futures/funding/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,10 @@ class Funding(KrakenBaseFuturesAPI):

def __init__(
self,
key: Optional[str] = "",
secret: Optional[str] = "",
url: Optional[str] = "",
sandbox: Optional[bool] = False,
key: str = "",
secret: str = "",
url: str = "",
sandbox: bool = False,
) -> None:
super().__init__(key=key, secret=secret, url=url, sandbox=sandbox)

Expand Down
41 changes: 25 additions & 16 deletions kraken/futures/market/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from functools import lru_cache
from typing import List, Optional, Union

from ...base_api import KrakenBaseFuturesAPI
from ...base_api import KrakenBaseFuturesAPI, defined


class Market(KrakenBaseFuturesAPI):
Expand Down Expand Up @@ -47,10 +47,10 @@ class Market(KrakenBaseFuturesAPI):

def __init__(
self: "Market",
key: Optional[str] = "",
secret: Optional[str] = "",
url: Optional[str] = "",
sandbox: Optional[bool] = False,
key: str = "",
secret: str = "",
url: str = "",
sandbox: bool = False,
) -> None:
super().__init__(key=key, secret=secret, url=url, sandbox=sandbox)

Expand Down Expand Up @@ -115,9 +115,9 @@ def get_ohlc(
raise ValueError(f"resolution must be in {resolutions}")

params: dict = {}
if from_ is not None:
if defined(from_):
params["from"] = from_
if to is not None:
if defined(to):
params["to"] = to
return self._request( # type: ignore[return-value]
method="GET",
Expand All @@ -134,6 +134,8 @@ def get_tick_types(self: "Market") -> List[str]:

- https://docs.futures.kraken.com/#http-api-charts-ohlc-get-tick-types

This function uses caching. Run ``get_tick_types.cache_clear()`` to clear.

:return: List of available tick types
:rtype: List[str]

Expand All @@ -154,6 +156,8 @@ def get_tradeable_products(self: "Market", tick_type: str) -> List[str]:

- https://docs.futures.kraken.com/#http-api-charts-ohlc-get-tradeable-products

This function uses caching. Run ``get_tradeable_products.cache_clear()`` to clear.

:param tick_type: The kind of data, based on ``mark``, ``spot``, or ``trade``
:type tick_type: str
:return: List of tradeable assets
Expand All @@ -178,6 +182,8 @@ def get_resolutions(self: "Market", tick_type: str, tradeable: str) -> List[str]

- https://docs.futures.kraken.com/#http-api-charts-ohlc-get-resolutions

This function uses caching. Run ``get_resolutions.cache_clear()`` to clear.

:param tick_type: The kind of data, based on ``mark``, ``spot``, or ``trade``
:type tick_type: str
:param tick_type: The asset of interest
Expand All @@ -203,8 +209,11 @@ def get_fee_schedules(self: "Market") -> dict:
Retrieve information about the current fees

- https://docs.futures.kraken.com/#http-api-trading-v3-api-fee-schedules-get-fee-schedules

- https://support.kraken.com/hc/en-us/articles/360049269572-Fee-Schedules

This function uses caching. Run ``get_fee_schedules.cache_clear()`` to clear.

:return: Dictionary containing information about the fees for wide range of tradeable assets
:rtype: dict

Expand Down Expand Up @@ -304,7 +313,7 @@ def get_orderbook(self: "Market", symbol: Optional[str] = None) -> dict:
}
"""
params: dict = {}
if symbol is not None:
if defined(symbol):
params["symbol"] = symbol

return self._request( # type: ignore[return-value]
Expand Down Expand Up @@ -526,9 +535,9 @@ def get_trade_history(
]
"""
params: dict = {}
if symbol is not None:
if defined(symbol):
params["symbol"] = symbol
if lastTime is not None:
if defined(lastTime):
params["lastTime"] = lastTime
params.update(kwargs)
return self._request( # type: ignore[return-value]
Expand Down Expand Up @@ -637,7 +646,7 @@ def set_leverage_preference(
{'result': 'success', 'serverTime': '2023-04-04T05:59:49.576Z'}
"""
params: dict = {"symbol": symbol}
if maxLeverage is not None:
if defined(maxLeverage):
params["maxLeverage"] = maxLeverage

return self._request( # type: ignore[return-value]
Expand Down Expand Up @@ -736,15 +745,15 @@ def _get_historical_events(
:type auth: bool
"""
params: dict = {}
if before is not None:
if defined(before):
params["before"] = before
if continuation_token is not None:
if defined(continuation_token):
params["continuation_token"] = continuation_token
if since is not None:
if defined(since):
params["since"] = since
if sort is not None:
if defined(sort):
params["sort"] = sort
if tradeable is not None:
if defined(tradeable):
params["tradeable"] = tradeable
params.update(kwargs)
return self._request( # type: ignore[return-value]
Expand Down
48 changes: 24 additions & 24 deletions kraken/futures/trade/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from typing import List, Optional, Tuple, Union

from ...base_api import KrakenBaseFuturesAPI
from ...base_api import KrakenBaseFuturesAPI, defined


class Trade(KrakenBaseFuturesAPI):
Expand Down Expand Up @@ -46,10 +46,10 @@ class Trade(KrakenBaseFuturesAPI):

def __init__(
self: "Trade",
key: Optional[str] = "",
secret: Optional[str] = "",
url: Optional[str] = "",
sandbox: Optional[bool] = False,
key: str = "",
secret: str = "",
url: str = "",
sandbox: bool = False,
) -> None:
super().__init__(key=key, secret=secret, url=url, sandbox=sandbox)

Expand Down Expand Up @@ -93,7 +93,7 @@ def get_fills(self: "Trade", lastFillTime: Optional[str] = None) -> dict:
}
"""
query_params: dict = {}
if lastFillTime:
if defined(lastFillTime):
query_params["lastFillTime"] = lastFillTime
return self._request( # type: ignore[return-value]
method="GET",
Expand Down Expand Up @@ -248,7 +248,7 @@ def cancel_all_orders(self: "Trade", symbol: Optional[str] = None) -> dict:
}
"""
params: dict = {}
if symbol is not None:
if defined(symbol):
params["symbol"] = symbol
return self._request( # type: ignore[return-value]
method="POST",
Expand Down Expand Up @@ -331,9 +331,9 @@ def cancel_order(
"""

params: dict = {}
if order_id is not None:
if defined(order_id):
params["order_id"] = order_id
elif cliOrdId is not None:
elif defined(cliOrdId):
params["cliOrdId"] = cliOrdId
else:
raise ValueError("Either order_id or cliOrdId must be set!")
Expand Down Expand Up @@ -394,18 +394,18 @@ def edit_order(
}
"""
params: dict = {}
if orderId is not None:
if defined(orderId):
params["orderId"] = orderId
elif cliOrdId is not None:
elif defined(cliOrdId):
params["cliOrdId"] = cliOrdId
else:
raise ValueError("Either orderId or cliOrdId must be set!")

if limitPrice is not None:
if defined(limitPrice):
params["limitPrice"] = limitPrice
if size is not None:
if defined(size):
params["size"] = size
if stopPrice is not None:
if defined(stopPrice):
params["stopPrice"] = stopPrice

return self._request( # type: ignore[return-value]
Expand Down Expand Up @@ -448,9 +448,9 @@ def get_orders_status(
{'result': 'success', 'serverTime': '2023-04-04T17:27:29.667Z', 'orders': []}
"""
params = {}
if orderIds is not None:
if defined(orderIds):
params["orderIds"] = orderIds
elif cliOrdIds is not None:
elif defined(cliOrdIds):
params["cliOrdIds"] = cliOrdIds

return self._request( # type: ignore[return-value]
Expand Down Expand Up @@ -647,22 +647,22 @@ def create_order(
"size": size,
"symbol": symbol,
}
if cliOrdId is not None:
if defined(cliOrdId):
params["cliOrdId"] = cliOrdId
if limitPrice is not None:
if defined(limitPrice):
params["limitPrice"] = limitPrice
if reduceOnly is not None:
if defined(reduceOnly):
params["reduceOnly"] = reduceOnly
if stopPrice is not None:
if defined(stopPrice):
params["stopPrice"] = stopPrice
if triggerSignal is not None:
trigger_signals = ("mark", "spot", "last")
if defined(triggerSignal):
trigger_signals: tuple = ("mark", "spot", "last")
if triggerSignal not in trigger_signals:
raise ValueError(f"Trigger signal must be in [{trigger_signals}]!")
params["triggerSignal"] = triggerSignal
if trailingStopDeviationUnit is not None:
if defined(trailingStopDeviationUnit):
params["trailingStopDeviationUnit"] = trailingStopDeviationUnit
if trailingStopMaxDeviation is not None:
if defined(trailingStopMaxDeviation):
params["trailingStopMaxDeviation"] = trailingStopMaxDeviation

return self._request( # type: ignore[return-value]
Expand Down
Loading