Source code for krakipy.krakipy

# This file is part of krakipy.
#
#MIT LICENSE
#
#Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
#associated documentation files (the “Software”), to deal in the Software without restriction, including
#without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
#of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
#
#The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
#
#THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
#NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
#IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
#ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.


from pandas import to_datetime, DataFrame, Series, concat, json_normalize
from torpy.http.adapter import TorHttpAdapter
from datetime import datetime, timedelta
from requests import Session, HTTPError
from base64 import b64encode, b64decode
from torpy.client import TorClient
from urllib.parse import urlencode
from hashlib import sha256, sha512
from time import time, sleep
from functools import wraps 
from pyotp import TOTP
from hmac import new

from . import version

def callratelimiter(increment):
    def call(func):
        @wraps(func)
        def retry_decorator(*args, **kwargs):
            self = args[0]
            self._update_api_counter()
            try_number = 1
            while self.api_counter < self.limit-1:
                try:
                    if self.use_tor:
                        if self.counter % self.tor_refresh == 0:
                            self.session.new()
                    self.api_counter += increment
                    result = func(*args, **kwargs)
                    return result
                except HTTPError as err:
                    print(f"Attempt: {try_number:_>3}")
                    try_number += 1
                    sleep(self.retry * increment)
                    self._update_api_counter()
                    continue
            raise CallRateLimitError(f"Call rate limiter exceeded: counter={self.api_counter} limit={self.limit}. Please wait!")
        return retry_decorator
    return call


[docs]class KeyNotSetError(Exception): """This Error indicates that you tried to do a private request but did not set the Kraken API Keys.""" pass
[docs]class KrakenAPIError(Exception): """This Error indicates that in the response to your query had an error message when it was recieved.""" pass
[docs]class CallRateLimitError(Exception): """This Error indicates that you sent to many requests in the last 20s.""" pass
class Dark_Session(object): def __init__(self, use_tor=False): self.use_tor = use_tor if use_tor: self._tor = TorClient() self.new() def new(self): if self.use_tor: self._guard = self._tor.get_guard() adapter = TorHttpAdapter(self._guard, 3, retries=0) self.session = Session() self.session.mount("http://", adapter) self.session.mount("https://", adapter) else: self.session = Session() def get_ip(self): return self.session.get("http://httpbin.org/ip").json()["origin"] def post(self, *args, **kwargs): return self.session.post(*args, **kwargs) def get(self, *args, **kwargs): return self.session.get(*args, **kwargs) def close(self): if self.session is not None: self.session.close() if self.use_tor: self._guard.close() self.session = None self._guard = None def _check_error(result): if len(result["error"]) > 0: raise KrakenAPIError(result["error"])
[docs]class KrakenAPI(object): """The KrakenAPI object stores the authentification information"""
[docs] def __init__(self, key="", secret_key="", use_2fa=None, use_tor=False, tor_refresh=5, retry=0.5, limit=20): """ Creates an object that can hold the authentification information. The keys are only needed to perform private queries :param key: The key to the Kraken API (optional) :type key: str :param secret_key: The secret key to the Kraken API (optional) :type secret_key: str :param use_2fa: Is used to pass the desired two factor authentification (optional) - None = no two factor authentification (default) - {"static password": your_static_2fa_password} = two factor authentification using a static password method. Example: use_2fa={"static password": "/&59s^wqUU=baQ~W"} - {"2FA App": your_2fa_app_setup_key} = two factor authentification using OTP passwords like the Google Authenticator App does it. Example: use_2fa={"2FA App": "E452ZYHEX22AXGKIFUGQVPXF"} :type use_2fa: None or dict :param use_tor: Weither or not to use the tor network for requests (optional) - False = use normal requests using the clearnet (default) - True = use tor requests using the darknet :type use_tor: bool :param tor_refresh: Amount of requests per session before the IP is changed (optional) default = 5 :type tor_refresh: int :param retry: Amount of time between retries in sec (optional) :type retry: float :param limit: The maximum amount of retries (optional) :type limit: int """ self.auth_method = None self._authentification = None if use_2fa: assert len(use_2fa) == 1, "There can only be one authentification method per API-token." authentification_methods = {"static password": self._auth_static_password, "2FA App": self._auth_2fa_app} types = authentification_methods.keys() method = list(use_2fa.keys())[0] assert method in types, f"The authentification method called {method} is not supported. Only {types} are supported." assert type(use_2fa[method]) == str, f"The 2FA pasword has to be a string. {use_2fa[method]} is not a string." self.auth_method = method self._authentification = {"method": authentification_methods[method], "password_2fa": use_2fa[method]} self._key = key self._secret = secret_key self.uri = "https://api.kraken.com" self.apiversion = "0" self.session = Dark_Session(use_tor) self.use_tor = use_tor if not use_tor: self.session.session.headers.update({"User-Agent": "krakipy/" + version.__version__ + " (+" + version.__url__ + ")"}) else: self.tor_refresh = tor_refresh self.response = None self.time_of_last_query = time() self.api_counter = 0 self.counter = 0 self.limit = limit self.retry = retry
def _auth_static_password(self): return self._authentification["password_2fa"] def _auth_2fa_app(self): return TOTP(self._authentification["password_2fa"]).now() def __enter__(self): return self def __exit__(self, exc_type, exc_val, exc_tb): if self.session is not None: self.session.close() def __del__(self): if self.session is not None: self.session.close() del self._key del self._secret del self._authentification def close(self): """ Closes the session """ if self.session is not None: self.session.close() def __str__(self): return f"""[{__class__.__name__}]\nVERSION: {self.apiversion}\nURI: {self.uri}\nAPI-Key: {"*" * len(self._key) if self._key else "-"}\nAPI-Secretkey: {"*" * len(self._secret) if self._secret else "-"}\nAPI-2FA-method: {self.auth_method}\nAPI-Counter: {self.api_counter}\nUsing Tor: {self.use_tor}\nRequest-Counter: {self.counter}\nRequest-Limit: {self.limit}\nRequest-Retry: {self.retry} s""" def _nonce(self): return int(1000*time()) def _sign(self, data, urlpath): encoded = (str(data["nonce"]) + urlencode(data)).encode() message = urlpath.encode() + sha256(encoded).digest() signature = new(b64decode(self._secret), message, sha512) return b64encode(signature.digest()).decode() def _query(self, urlpath, data, headers=None, timeout=None): if data is None: data = {} if headers is None: headers = {} self.response = self.session.post(self.uri + urlpath, data = data, headers = headers, timeout = timeout) self.counter += 1 if self.response.status_code not in (200, 201, 202): self.response.raise_for_status() try: result = self.response.json() except: result = {"result":self.response.content, "error": []} return result def _query_public(self, method, data=None, timeout=None): urlpath = "/" + self.apiversion + "/public/" + method return self._query(urlpath, data, timeout = timeout) def _query_private(self, method, data=None, timeout=None): if not self._key or not self._secret: raise KeyNotSetError("The Key and Secret-Key to the API need to be set to do private queries.") if data is None: data = {} urlpath = "/" + self.apiversion + "/private/" + method data["nonce"] = self._nonce() if self._authentification != None: data["otp"] = self._authentification["method"]() headers = {"API-Key": self._key, "API-Sign": self._sign(data, urlpath)} return self._query(urlpath, data, headers, timeout = timeout) def _do_public_request(self, action, **kwargs): kwargs = {key: value for key, value in kwargs.items() if value is not None} res = self._query_public(action, data = kwargs) _check_error(res) return res["result"] def _do_private_request(self, action, **kwargs): kwargs = {key: value for key, value in kwargs.items() if value is not None} res = self._query_private(action, data = kwargs) _check_error(res) return res["result"] #Public Market Data
[docs] @callratelimiter(1) def get_server_time(self): """ Public Market Data Returns The time of the Kraken API server. :returns: Rfc1123 and unixtime :rtype: (str, int) Example: KrakenAPI.get_server_time() -> ("Sun, 6 Dec 20 14:12:39 +0000", 1607263959) """ res = self._query_public("Time") _check_error(res) return res["result"]["rfc1123"], res["result"]["unixtime"]
[docs] @callratelimiter(1) def get_system_status(self): """ Public Market Data Returns the current system status or trading mode and a timestamp. :returns: The system status and timestamp :rtype: (str, str) Example: KrakenAPI.get_system_status() -> ("online", "2020-12-06T13:59:55Z") .. note:: Possible status values include: - "online" (operational, full trading available) - "cancel_only" (existing orders are cancelable, but new orders cannot be created) - "post_only" (existing orders are cancelable, and only new post limit orders can be submitted) - "maintenance" (system is offline for maintenance) """ res = self._query_public("SystemStatus") _check_error(res) return res["result"]["status"], res["result"]["timestamp"]
[docs] @callratelimiter(1) def get_asset_info(self, asset=None, aclass=None): """ Public Market Data Get information about the assets that are available for deposit, withdrawal, trading and staking. :param asset: Comma delimited list of assets to get info on (optional) - default = "all" :type asset: str :param aclass: Asset class (optional) - default = "currency" :type aclass: str :returns: DataFrame of asset names and their info :rtype: :py:attr:`pandas.DataFrame` """ info = DataFrame(self._do_public_request("Assets", asset=asset, aclass=aclass), index=["aclass", "altname", "decimals", "display_decimals"]).T.convert_dtypes() return info
[docs] @callratelimiter(1) def get_tradable_asset_pairs(self, pair=None, info=None): """ Public Market Data :param info: The info to retrieve (optional) - info = all info (default) - leverage = leverage info - fees = fees schedule - margin = margin info :type info: str :param pair: Comma delimited list of asset pairs to get info on (optional) - default = "all" :type pair: str :returns: DataFrame of pair names and their info :rtype: :py:attr:`pandas.DataFrame` """ res = self._do_public_request("AssetPairs", info=info, pair=pair) pairs = DataFrame(res, index=["altname", "wsname", "aclass_base", "base", "aclass_quote", "quote", "lot", "pair_decimals", "lot_decimals", "lot_multiplier", "leverage_buy", "leverage_sell", "fees", "fees_maker", "fee_volume_currency", "margin_call", "margin_stop", "ordermin"]).T.convert_dtypes() return pairs
[docs] @callratelimiter(1) def get_ticker_information(self, pair): """ Public Market Data :param pair: Comma delimited list of asset pairs to get info on :type pair: str :returns: DataFrame of pair names and their ticker info :rtype: :py:attr:`pandas.DataFrame` .. note:: Today"s prices start at midnight UTC """ return DataFrame(self._do_public_request("Ticker", pair=pair), index=["a", "b", "c", "h", "l", "o", "p", "t", "v"]).T.convert_dtypes()
[docs] @callratelimiter(2) def get_ohlc_data(self, pair, interval=1, since=None): """ Public Market Data :param pair: Asset pair to get OHLC data for :type pair: str :param interval: The time frame interval in minutes (optional): - 1 (default) = 1 minute - 5 = 5 minutes - 15 = 15 minutes - 30 = 30 minutes - 60 = 1 hour - 240 = 4 hours - 1440 = 1 day - 10080 = 1 week - 21600 = 15 days :type interval: int :param since: Return committed OHLC data since given id (optional. exclusive) :type since: int :returns: DataFrame of pair name and OHLC data :rtype: :py:attr:`pandas.DataFrame` .. note:: The last entry in the OHLC array is for the current, not-yet-committed frame and will always be present, regardless of the value of since. """ res = self._do_public_request("OHLC", pair=pair, interval=interval, since=since) ohlc = DataFrame(res[tuple(res.keys())[0]], columns=["time", "open", "high", "low", "close", "vwap", "volume", "count"], dtype="float") last = float(res["last"]) return ohlc, last
[docs] @callratelimiter(1) def get_order_book(self, pair, count=100): """ Public Market Data :param pair: Asset pair to get market depth for :type pair: str :param count: Maximum number of asks/bids (optional) - default = 100, Range: [1..500] :type count: int :returns: Ask and bid DataFrame of pair name and market depth :rtype: (:py:attr:`pandas.DataFrame`, :py:attr:`pandas.DataFrame`) """ res = self._do_public_request("Depth", pair=pair, count=count) cols = ["price", "volume", "time"] asks = DataFrame(res[pair]["asks"], columns=cols, dtype="float") bids = DataFrame(res[pair]["bids"], columns=cols, dtype="float") return asks, bids
[docs] @callratelimiter(2) def get_recent_trades(self, pair, since=None): """ Public Market Data Returns the last 1000 trades by default :param pair: Asset pair to get trade data for :type pair: str :param since: Return trade data since given id (optional. exclusive) :type since: int :returns: DataFrame of pair name and recent trade data and id to be used as since when polling for new trade data. :rtype: (:py:attr:`pandas.DataFrame`, int) """ res = self._do_public_request("Trades", pair=pair, since=since) trades = DataFrame(res[pair], columns=["price", "volume", "time", "buy_sell", "market_limit", "misc"], dtype="float") last = float(res["last"]) return trades, last
[docs] @callratelimiter(1) def get_recent_spreads(self, pair, since=None): """ Public Market Data :param pair: Asset pair to get spread data for :type pair: str :param since: Return trade data since given id (optional. exclusive) :type since: int :returns: DataFrame of pair name and recent spread data and id to be used as since when polling for new spread data :rtype: (:py:attr:`pandas.DataFrame`, int) """ res = self._do_public_request("Spread", pair=pair, since=since) spread = DataFrame(res[pair], columns=["time", "bid", "ask"], dtype="float") spread["spread"] = spread.ask - spread.bid last = float(res["last"]) return spread, last
#Private User Data
[docs] @callratelimiter(1) def get_account_balance(self): """ Private User Data Retrieve all cash balances, net of pending withdrawals. :returns: DataFrame of asset names and balance amount :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Funds permissions - Query** """ res = self._do_private_request("Balance") balance = DataFrame(res, index=["vol"], dtype="float").T return balance
[docs] @callratelimiter(1) def get_extended_balance(self): """ Private User Data Retrieve all extended account balances, including credits and held amounts. Balance available for trading is calculated as: available balance = balance + credit - credit_used - hold_trade :returns: DataFrame of asset names and balance amount :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Funds permissions - Query** """ res = self._do_private_request("BalanceEx") balance = DataFrame(res, dtype="float").T return balance
[docs] @callratelimiter(1) def get_credit_lines(self, rebased=True): """ Private User Data Retrieve all credit line details for VIPs with this functionality. :param rebased: Whether to display in terms underlying equity(True) or SPV tokens(False) (optional) - default = True :type rebased: bool :returns: Series of credit line overview and DataFrame of asset credit line details retrieved :rtype: (:py:attr:`pandas.Series`, :py:attr:`pandas.DataFrame`) API Key Permissions Required: **Funds permissions - Query** """ res = self._do_private_request("CreditLines", rebase_multiplier="rebased" if rebased else "base") limits_monitor = Series(res.pop("limits_monitor"), index=["total_credit_usd", "total_credit_used_usd", "total_collateral_value_usd", "equity_usd", "debt_to_equity"], dtype="float").fillna(0) asset_details = DataFrame(res.pop("asset_details"), dtype="float").fillna(0) return limits_monitor, asset_details
[docs] @callratelimiter(2) def get_trade_balance(self, asset="ZEUR"): """ Private User Data Retrieve a summary of collateral balances, margin position valuations, equity and margin level. :param asset: Base asset used to determine balance - default = "ZEUR" :type asset: str :returns: DataFrame of trade balance info :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Orders and trades - Query open orders & trades** """ res = self._do_private_request("TradeBalance", asset=asset) tradebalance = DataFrame(res, index=[asset], columns=["eb", "tb", "m", "n", "c", "v", "e", "mf", "ml"], dtype="float") return tradebalance
[docs] @callratelimiter(1) def get_open_orders(self, trades=False, userref=None): """ Private User Data Retrieve information about currently open orders. :param trades: Whether or not to include trades in output (optional) - default = False :type trades: bool :param userref: Restrict results to given user reference id (optional) :type userref: str :returns: DataFrame of open order info with txid as the index :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Orders and trades - Query open orders & trades** """ res = self._do_private_request("OpenOrders", trades=trades, userref=userref) openorders = DataFrame(res["open"], index=["cost", "descr", "expiretm", "fee", "limitprice", "misc", "oflags", "opentm", "price", "refid", "starttm", "status", "stopprice", "userref", "vol", "vol_exec"]).T.convert_dtypes() return openorders
[docs] @callratelimiter(1) def get_closed_orders(self, trades=False, userref=None, start=None, end=None, ofs=None, closetime=None): """ Private User Data Retrieve information about orders that have been closed (filled or cancelled). 50 results are returned at a time, the most recent by default. :param trades: Whether or not to include trades in output (optional) - default = False :type trades: bool :param userref: Restrict results to given user reference id (optional) :type userref: str :param start: Starting unix timestamp or order tx id of results (optional. exclusive) :type start: int or str :param end: Ending unix timestamp or order tx id of results (optional. inclusive) :type start: int or str :param ofs: The result offset :type ofs: int :param closetime: Which time to use (optional): - open - close - both (default) :type closetime: str :returns: DataFrame of of order info and amount of available order info matching criteria :rtype: (:py:attr:`pandas.DataFrame`, int) API Key Permissions Required: **Orders and trades - Query closed orders & trades** """ res = self._do_private_request("ClosedOrders", trades=trades, userref=userref, start=start, end=end, ofs=ofs, closetime=closetime) closed = DataFrame(res["closed"], index=["refid", "userref", "status", "reason", "opentm", "closetm", "starttm", "expiretm", "descr", "vol", "vol_exec", "cost", "fee", "price", "stopprice", "limitprice", "misc", "oflags", "trades"]).T.convert_dtypes() count = int(res["count"]) return closed, count
[docs] @callratelimiter(1) def query_orders_info(self, txid, trades=False, userref=None): """ Private User Data Retrieve information about specific orders. :param trades: Whether or not to include trades in output (optional) - default = False :type trades: bool :param userref: Restrict results to given user reference id (optional) :type userref: str :param txid: Comma delimited list of transaction ids to query info about (50 maximum) :type txid: str :returns: DataFrame of associative orders info :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Orders and trades - Query open orders & trades** or **Orders and trades - Query closed orders & trades**, depending on status of order """ res = self._do_private_request("QueryOrders", txid=txid, trades=trades, userref=userref) orders = DataFrame(res, index=["closetm", "cost", "descr", "expiretm", "fee", "limitprice", "misc", "oflags", "opentm", "price", "reason", "refid", "starttm", "status", "stopprice", "trades", "userref", "vol", "vol_exec"]).T.convert_dtypes() return orders
[docs] @callratelimiter(1) def get_order_amends(self, order_id): """ Private User Data Retrieves an audit trail of amend transactions on the specified order. The list is ordered by ascending amend timestamp. :param order_id: The Kraken order identifier for the amended order. :type order_id: str :returns: DataFrame of associative orders info :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Orders and trades - Query open orders & trades** or **Orders and trades - Query closed orders & trades**, depending on status of order """ res = self._do_private_request("OrderAmends", order_id=order_id) orders = DataFrame(res["amends"], index=["amend_id", "amend_type", "order_qty", "remaining_qty", "limit_price", "timestamp"]).convert_dtypes() return orders
[docs] @callratelimiter(2) def get_trades_history(self, trade_type="all", trades=False, start=None, end=None, ofs=None): """ Private User Data Retrieve information about trades/fills. 50 results are returned at a time, the most recent by default. :param trade_type: type of trade (optional): - all = all types (default) - any position = any position (open or closed) - closed position = positions that have been closed - closing position = any trade closing all or part of a position - no position = non-positional trades :type trade_type: str :param trades: Whether or not to include trades related to position in output (optional) - default = False :type trades: bool :param start: Starting unix timestamp or order tx id of results (optional. exclusive) :type start: int or str :param end: Ending unix timestamp or order tx id of results (optional. inclusive) :type start: int or str :param ofs: Result offset for pagination :type ofs: int :returns: DataFrame of trade info and the amount of available trades info matching criteria :rtype: (:py:attr:`pandas.DataFrame`, int) API Key Permissions Required: **Orders and trades - Query closed orders & trades** """ res = self._do_private_request("TradesHistory", trades=trades, start=start, end=end, ofs=ofs, type=trade_type) trades = DataFrame(res["trades"], index=["ordertxid", "postxid", "pair", "time", "type", "ordertype", "price", "cost", "fee", "vol", "margin", "misc"]).T.convert_dtypes() count = int(res["count"]) return trades, count
[docs] @callratelimiter(2) def query_trades_info(self, txid, trades=False): """ Private User Data Retrieve information about specific trades/fills. :param txid: Comma delimited list of transaction ids to query info about (20 maximum) :type txid: str :param trades: Whether or not to include trades related to position in output (optional) - default = False :type trades: bool :returns: DataFrame of associative trades info :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Orders and trades - Query closed orders & trades** """ res = self._do_private_request("QueryTrades", txid=txid, trades=trades) trades = DataFrame(res, index=["cost", "fee", "margin", "misc", "ordertxid", "ordertype", "pair", "postxid", "price", "time", "type", "vol"]).T.convert_dtypes() return trades
[docs] @callratelimiter(1) def get_open_positions(self, txid=None, docalcs=False, consolidation=None): """ Private User Data :param txid: Comma delimited list of transaction ids to restrict output to :type txid: str :param docalcs: Whether or not to include profit/loss calculations (optional) - default = False :type docalcs: bool :param consolidation: What to consolidate the positions data around (optional) - "market" = will consolidate positions based on market pair :type consolidation: str :returns: A DataFrame of open position info :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Orders and trades - Query open orders & trades** .. note:: Using the consolidation optional field will result in consolidated view of the data being returned. """ res = self._do_private_request("OpenPositions", txid=txid, docalcs=docalcs, consolidation=consolidation) pos = DataFrame(res, index=["ordertxid", "posstatus", "pair", "time", "type", "ordertype", "cost", "fee", "vol", "vol_closed", "margin", "value", "net", "terms", "rollovertm", "misc", "oflags"]).T.convert_dtypes() return pos
[docs] @callratelimiter(2) def get_ledgers_info(self, asset=None, aclass=None, selection_type="all", start=None, end=None, ofs=None): """ Private User Data Retrieve information about ledger entries. 50 results are returned at a time, the most recent by default. :param asset: Comma delimited list of assets to restrict output to (optional) - default = "all" :type asset: str :param aclass: Asset class (optional) - default = "currency" :type aclass: str :param selection_type: Type of trade (optional): - all (default) - deposit - withdrawal - trade - margin :type selection_type: str :param start: Starting unix timestamp or order tx id of results (optional. exclusive) :type start: int or str :param end: Ending unix timestamp or order tx id of results (optional. inclusive) :type start: int or str :param ofs: Result offset for pagination :type ofs: int :returns: DataFrame of associative ledgers info :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Data - Query ledger entries** """ res = self._do_private_request("Ledgers", aclass=aclass, asset=asset, type=selection_type, start=start, end=end, ofs=ofs) ledgers = DataFrame(res["ledger"], index=["refid", "time", "type", "subtype", "aclass", "asset", "amount", "fee", "balance"]).T.convert_dtypes() return ledgers
[docs] @callratelimiter(2) def query_ledgers(self, id, trades=False): """ Private User Data Retrieve information about specific ledger entries. :param id: Comma delimited list of ledger ids to query info about (20 maximum) :type id: str :param trades: Whether or not to include trades related to position in output :type trades: bool (optional.) - default = False :returns: DataFrame of associative ledgers info :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Data - Query ledger entries** """ res = self._do_private_request("QueryLedgers", id=id, trades=trades) ledgers = DataFrame(res, index=["aclass", "amount", "asset", "balance", "fee", "refid", "subtype", "time", "type"]).T.convert_dtypes() return ledgers
[docs] @callratelimiter(2) def get_trade_volume(self, pair): """ Private User Data :param pair: Comma delimited list of asset pairs to get fee info on (optional) :type pair: str :returns: The volume currency, current discount volume, DataFrame of fees and DataFrame of maker fees :rtype: (str, float, :py:attr:`pandas.DataFrame`, :py:attr:`pandas.DataFrame`) API Key Permissions Required: **Funds permissions - Query** ..note: If an asset pair is on a maker/taker fee schedule, the taker side is given in fees and maker side in fees_maker. For pairs not on maker/taker, they will only be given in fees. """ res = self._do_private_request("TradeVolume", pair=pair) currency = str(res["currency"]) volume = float(res["volume"]) fees = DataFrame(res.get("fees"), index=["fee", "maxfee", "minfee", "nextfee", "nextvolume", "tiervolume"]).T.convert_dtypes() fees_maker = DataFrame(res.get("fees_maker"), index=["fee", "maxfee", "minfee", "nextfee", "nextvolume", "tiervolume"]).T.convert_dtypes() return currency, volume, fees, fees_maker
[docs] def request_export_report(self, description, report, data_format="CSV", fields=None, asset=None, starttm=None, endtm=None): """ Private User Data Request export of trades or ledgers. :param description: Report description info :type description: str :param report: Report type - trades - ledgers :type report: str :param data_format: The data format - CSV (default) - TSV :type data_format: str :param fields: Comma delimited list of fields to include in report (optional). default = "all" trades: - ordertxid - time - ordertype - price - cost - fee - vol - margin - misc - ledgers ledgers: - refid - time - type - aclass - asset - amount - fee - vbalance :type fields: str :param asset: Comma delimited list of assets to get info on (optional) - default = "all" :type asset: str :param starttm: Report start unixtime (optional). default = one year before now :type starttm: int :param endtm: Report end unixtime (optional). default = now :type endtm: int :returns: Report id :rtype: str API Key Permissions Required: **Data - Export data** .. note:: Field options are based on report type. """ return str(self._do_private_request("AddExport", report=report, description=description, format=data_format, fields=fields, asset=asset, starttm=starttm, endtm=endtm)["id"])
[docs] @callratelimiter(1) def get_export_report_status(self, report): """ Private User Data :param report: Report type: - trades - ledgers :type report: str :returns: DataFrame of reports and their info :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Data - Export data** """ res = self._do_private_request("ExportStatus", report=report) status = DataFrame(res, columns=["id", "descr", "format", "report", "subtype", "status", "flags", "fields", "createdtm", "expiretm", "starttm", "completedtm", "datastarttm", "dataendtm", "aclass", "asset"]).convert_dtypes() return status
[docs] @callratelimiter(3) def retrieve_export_report(self, report_id, return_raw=False, dir=None): """ Private User Data :param report_id: Report id :type report_id: str :param return_raw: Weither or not the report is returned as raw binary (optional) - default = False :type return_raw: bool :param dir: If given a directory the report will be saved there as a zipfile (optional) :type dir: str :returns: None or the binary of the compressed report.zip file :rtype: None or raw binary API Key Permissions Required: **Data - Export data** """ assert not return_raw and dir is None, "At least one way the Data is returned must be specified" report = self._do_private_request("RetrieveExport", id=report_id) if dir is not None: with open("{}Report_{}.zip".format(dir, report_id), "wb") as f: f.write(report) if return_raw: return report return None
[docs] @callratelimiter(1) def delete_export_report(self, report_id, remove_type): """ Private User Data :param report_id: Report id :type report_id: str :param remove_type: Removal type - cancel - delete :type remove_type: str :returns: Returns remove type :rtype: dict API Key Permissions Required: **Data - Export data** .. note:: The delete remove type can only be used for a report that has already been processed. Use cancel for queued and processing statuses. """ return self._do_private_request("RemoveExport", id=report_id, type=remove_type)
#Private User Trading
[docs] def add_standard_order(self, pair, type, ordertype, volume, userref=None, cl_ord_id=None, displayvol=None, asset_class=None, price=None, price2=None, trigger="last", leverage=None, reduce_only=False, stptype="cancel-newest", oflags=None, timeinforce="GTC", starttm=0, expiretm=0, validate=True, close_ordertype=None, close_price=None, close_price2=None, deadline=None, trading_agreement="agree"): """ Private User Trading :param pair: Asset pair :type pair: str :param type: Type of order - buy - sell :type type: str :param ordertype: Order type: - market - limit (price = limit price) - iceberg - stop-loss (price = stop loss price) - take-profit (price = take profit price) - stop-loss-limit (price = stop loss trigger price, price2 = triggered limit price) - take-profit-limit (price = take profit trigger price, price2 = triggered limit price) - trailing-stop (price = trailing stop offset) - trailing-stop-limit (price = trailing stop offset, price2 = triggered limit offset) - settle-position :type ordertype: str :param volume: Order volume in lots :type volume: float or str :param userref: User reference id. 32-bit signed number. (optional) :type userref: str :param cl_ord_id: Client order id. Long UUID, short UUID or free ascii text. (optional) :type cl_ord_id: str :param displayvol: Used to edit an iceberg order, this is the visible order quantity in terms of the base asset. The rest of the order will be hidden, although the full volume can be filled at any time by any order of that size or larger that matches in the order book. displayvol can only be used with the limit order type, must be greater than 0, and less than volume. :type displayvol: float or str :param asset_class: Required on requests for non-crypto pairs, i.e. use tokenized_asset for xstocks. (optional) :type asset_class: str :param price: Price (optional. dependent upon ordertype) :type price: float or str :param price2: Secondary price (optional. dependent upon ordertype) :type price2: float or str :param trigger: Price signal used to trigger stop-loss, stop-loss-limit, take-profit, take-profit-limit, trailing-stop and trailing-stop-limit orders. (optional. default = "last") - index - last :type trigger: str :param leverage: Amount of leverage desired (optional. default = none) :type leverage: int or str :param reduce_only: If true, order will only reduce a currently open position, not increase it or open a new position. (optional. default = False) :type reduce_only: boolean :param stptype: Self trade prevention behavior definition (optional. default = "cancel-newest"): - cancel-newest = if self trade is triggered, arriving order will be canceled - cancel-oldest = if self trade is triggered, resting order will be canceled - cancel-both = if self trade is triggered, both arriving and resting orders will be canceled :type stptype: str :param oflags: Comma delimited list of order flags (optional): - viqc = volume in quote currency (not available for leveraged orders) - fcib = prefer fee in base currency - fciq = prefer fee in quote currency - post = post only order (available when ordertype = limit) :type oflags: str :param timeinforce: How long it should remain in the order book before being cancelled (optional. default = "GTC"): - GTC = (Good-'Til-Cancelled) - IOC = (Immediate-Or-Cancel) - GTD = (Good-'Til-Date) must also specify expiretm :type timeinforce: str :param starttm: Scheduled start time (optional): - 0 = now (default) - +<n> = schedule start time <n> seconds from now - <n> = unix timestamp of start time :type starttm: int :param expiretm: Expiration time (optional): - 0 = no expiration (default) - +<n> = expire <n> seconds from now - <n> = unix timestamp of expiration time :type expiretm: int :param deadline: RFC3339 timestamp (e.g. "2023-07-01T00:18:45Z") after which this order would be rejected. (optional) :type deadline: str :param validate: Validate inputs only. do not submit order (optional) :type validate: bool :param close_ordertype: Optional closing order to add to system when order gets filled: order type - limit - stop-loss - take-profit - stop-loss-limit - take-profit-limit :type close_ordertype: str :param close_price: Price :type close_price: float or str :param close_price2: Secondary price :type close_price2: float or str :returns: Dictionary of order description info :rtype: dict API Key Permissions Required: **Orders and trades - Create & modify orders** .. note:: - See Get tradable asset pairs for specifications on asset pair prices, lots, and leverage. - Prices can be preceded by +, -, or # to signify the price as a relative amount (with the exception of trailing stops, which are always relative). + adds the amount to the current offered price. - subtracts the amount from the current offered price. # will either add or subtract the amount to the current offered price, depending on the type and order type used. Relative prices can be suffixed with a % to signify the relative amount as a percentage of the offered price. - For orders using leverage, 0 can be used for the volume to auto-fill the volume needed to close out your position. - If you receive the error "EOrder:Trading agreement required", refer to your API key management page for further details. - Volume can be specified as 0 for closing margin orders to automatically fill the requisite quantity. """ if validate is False: validate = None volume = str(volume) price = str(price) if price else None price2 = str(price2) if price2 else None displayvol = str(displayvol) if displayvol else None leverage = str(leverage) if leverage else None close_price = str(close_price) if close_price else None close_price2 = str(close_price2) if close_price2 else None data = {arg: value for arg, value in locals().items() if arg != "self" and value is not None} if close_ordertype != None: data["close[ordertype]"] = data.pop("close_ordertype") if close_price != None: data["close[price]"] = data.pop("close_price") if close_price2 != None: data["close[price2]"] = data.pop("close_price2") res = self._query_private("AddOrder", data=data) _check_error(res) return str(res["result"])
[docs] def amend_order(self, txid=None, cl_ord_id=None, pair=None, order_qty=None, display_qty=None, limit_price=None, trigger_price=None, post_only=False, deadline=None): """ Private User Trading :param txid: Transaction id, either txid or cl_ord_id is required. :type txid: str :param cl_ord_id: Client order id, either txid or cl_ord_id is required. :type cl_ord_id: str :param pair: Asset pair :type pair: str :param order_qty: Order volume in lots :type order_qty: float or str :param display_qty: For iceberg orders only, it defines the new quantity to show in the book while the rest of order quantity remains hidden. Minimum value is 1 / 15 of remaining order quantity. :type display_qty: float or str :param limit_price: The new limit price restriction on the order (for order types that support limit price only). The relative pricing can be set by using the +, - prefixes and/or % suffix. :type limit_price: float or str :param trigger_price: The new trigger price to activate the order (for triggered order types only). The relative pricing can be set by using the +, - prefixes and/or % suffix. :type trigger_price: float or str :param post_only: An optional flag for limit_price amends. If True, the limit price change will be rejected if the order cannot be posted passively in the book. :type post_only: bool :param deadline: RFC3339 timestamp (e.g. "2023-07-01T00:18:45Z") after which this order would be rejected. (optional) :type deadline: str :returns: A successful amend request will return the unique Kraken amend identifier. :rtype: str API Key Permissions Required: **Orders and trades - Create & modify orders** """ assert (not txid and cl_ord_id) or (txid and not cl_ord_id), "Either txid or cl_ord_id is required." order_qty = str(order_qty) if order_qty else None display_qty = str(display_qty) if display_qty else None limit_price = str(limit_price) if limit_price else None trigger_price = str(trigger_price) if trigger_price else None post_only = str(post_only).lower() if post_only is not None else None data = {arg: value for arg, value in locals().items() if arg != "self" and value is not None} res = self._query_private("AmendOrder", data=data) _check_error(res) return str(res["result"]["amend_id"])
[docs] def cancel_order(self, txid): """ Private User Trading Cancel a particular open order (or set of open orders) by txid :param txid: Transaction id :type txid: str :returns: Number of orders canceled and weither order(s) is/are pending cancellation :rtype: (int, bool) API Key Permissions Required: **Orders and trades - Create & modify orders** and **Orders and trades - Cancel & close orders** """ data = self._do_private_request("CancelOrder", txid=txid) return int(data["count"]), data.get("pending")
[docs] def cancel_all_orders(self): """ Private User Trading Cancel all open orders :returns: Number of orders canceled :rtype: int API Key Permissions Required: **Orders and trades - Create & modify orders** and **Orders and trades - Cancel & close orders** """ data = self._do_private_request("CancelAll") return int(data["count"])
[docs] def cancel_all_orders_after(self, timeout): """ Private User Trading This method provides a "Dead Man"s Switch" mechanism to protect the client from network malfunction, extreme latency or unexpected matching engine downtime. The client can send a request with a timeout (in seconds), that will start a countdown timer which will cancel all client orders when the timer expires. The client has to keep sending new requests to push back the trigger time, or deactivate the mechanism by specifying a timeout of 0. If the timer expires, all orders are cancelled and then the timer remains disabled until the client provides a new (non-zero) timeout. The recommended use is to make a call every 15 to 30 seconds, providing a timeout of 60 seconds. This allows the client to keep the orders in place in case of a brief disconnection or transient delay, while keeping them safe in case of a network breakdown. It is also recommended to disable the timer ahead of regularly scheduled trading engine maintenance (if the timer is enabled, all orders will be cancelled when the trading engine comes back from downtime - planned or otherwise). :param timeout: Duration (in seconds) to set/extend the timer by :type timeout: int :returns: The timestamp when the request was recieved, The timestamp after which all orders will be cancelled, unless the timer is extended or disabled :rtype: str, str API Key Permissions Required: **Orders and trades - Create & modify orders** and **Orders and trades - Cancel & close orders** Example Return: KrakenAPI.cancel_all_orders_after(60) -> ("2021-03-24T17:41:56Z", "2021-03-24T17:42:56Z") """ res = self._do_private_request("CancelAllOrdersAfter", timeout=timeout) return res["currentTime"], res["triggertime"]
[docs] def get_websocket_token(self): """ Private User Trading An authentication token must be requested via this REST API endpoint in order to connect to and authenticate with our Websockets API. The token should be used within 15 minutes of creation, but it does not expire once a successful Websockets connection and private subscription has been made and is maintained. :returns: Websockets token and expiration time :rtype: dict API Key Permissions Required: **WebSocket interface - On** """ return self._do_private_request("GetWebSocketsToken")
[docs] def add_order_batch(self, orders): """ Private User Trading Sends a collection of orders (minimum of 2 and maximum 15) :param orders: List of order parameter dicts. :type orders: list of dict :returns: list transaction info dicts :rtype: list of dict API Key Permissions Required: **Orders and trades - Create & modify orders** and **Orders and trades - Cancel & close orders** """ res = self._do_private_request("AddOrderBatch", orders=orders) return res["orders"]
[docs] def cancel_order_batch(self, orders): """ Private User Trading Cancel multiple open orders by txid or userref (maximum 50 total unique IDs/references) :param orders: List of open order transaction IDs (txid) or user references (userref), up to a maximum of 50 total unique IDs/references. :type orders: list of str or int :returns: Number of orders canceled :rtype: int API Key Permissions Required: **Orders and trades - Create & modify orders** and **Orders and trades - Cancel & close orders** """ res = self._do_private_request("CancelOrderBatch", orders=orders) return int(res["count"])
#Private User Funding
[docs] @callratelimiter(1) def get_deposit_methods(self, asset): """ Private User Funding Retrieve methods available for depositing a particular asset. :param asset: Asset being deposited :type asset: str :returns: DataFrame of deposit methods :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Funds permissions - Query** and **Funds permissions - Deposit** """ res = self._do_private_request("DepositMethods", asset=asset) depo = DataFrame(res, columns=["method", "limit", "fee", "gen-address"]).convert_dtypes() return depo
[docs] @callratelimiter(1) def get_deposit_addresses(self, asset, method, new=False): """ Private User Funding Retrieve (or generate a new) deposit addresses for a particular asset and method. :param asset: Asset being deposited :type asset: str :param method: Name of the deopsit method :type method: str :param new: Whether to generate a new address (optional.) - default = False :type new: bool :returns: DataFrame of associative deposit addresses :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Funds permissions - Query** """ res = self._do_private_request("DepositAddresses", asset=asset, method=method, new=new) depo = DataFrame(res, columns=["address", "expiretm", "new"]).convert_dtypes() depo.fillna(False, inplace=True) return depo
[docs] @callratelimiter(1) def get_deposit_status(self, asset, method=None): """ Private User Funding Retrieve information about recent deposits made. :param asset: Asset being deposited :type asset: str :param method: Name of the deopsit method (optional) :type method: str :returns: DataFrame of deposit status information :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Funds permissions - Query** """ res = self._do_private_request("DepositStatus", asset=asset, method=method) depo_status = DataFrame(res, columns=["method", "aclass", "asset", "refid", "txid", "info", "amount", "fee", "time", "status"]).convert_dtypes().fillna(0.0) return depo_status
[docs] @callratelimiter(1) def get_withdrawal_methods(self, asset=None, aclass="currency", network=None): """ Private User Funding Retrieve a list of withdrawal methods available for the user. :param asset: Filter methods for specific asset (optional) :type asset: str :param aclass: Filter methods for specific asset class (optional) - default = "currency" :type aclass: str :param network: Filter methods for specific network (optional) :type network: str :returns: DataFrame of withdrawal methods :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Funds permissions - Query** and **Funds permissions - Withdraw** """ res = self._do_private_request("WithdrawMethods", asset=asset, aclass=aclass, network=network) wd = DataFrame(res, columns=["asset", "method", "network", "minimum"]).convert_dtypes() return wd
[docs] @callratelimiter(1) def get_withdrawal_addresses(self, asset=None, aclass="currency", method=None, key=None, verified=None): """ Private User Funding Retrieve a list of withdrawal addresses available for the user. :param asset: Filter addresses for specific asset (Optional) :type asset: str :param aclass: Filter addresses for specific asset class (Optional) :type aclass: str :param method: Filter addresses for specific method (Optional) :type method: str :param key: Find address for by withdrawal key name, as set up on your account (Optional) :type key: str :param verified: Filter by verification status of the withdrawal address. Withdrawal addresses successfully completing email confirmation will have a verification status of true. (Optional) :type verified: bool :returns: DataFrame of withdrawal addresses retrieved. :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Funds permissions - Query** and **Funds permissions - Withdraw** """ verified = str(verified).lower() if verified is not None else None res = self._do_private_request("WithdrawAddresses", asset=asset, aclass=aclass, method=method, key=key, verified=verified) wd = DataFrame(res, columns=["adress", "asset", "method", "key", "tag", "verified"]).convert_dtypes() return wd[["asset", "method", "key", "tag", "adress", "verified"]].fillna("")
[docs] @callratelimiter(1) def get_withdrawal_info(self, asset, key, amount): """ Private User Funding Retrieve fee information about potential withdrawals for a particular asset, key and amount. :param asset: Asset being withdrawn :type asset: str :param key: Withdrawal key name, as set up on your account :type key: str :param amount: Amount to withdraw :type amount: float :returns: DataFrame of associative withdrawal info :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Funds permissions - Query** and **Funds permissions - Withdraw** """ res = self._do_private_request("WithdrawInfo", asset=asset, key=key, amount=amount) wd = DataFrame(res, index=[asset], columns=["method", "limit", "amount", "fee"]).convert_dtypes() return wd
[docs] def withdraw_funds(self, asset, key, amount): """ Private User Funding :param asset: Asset being withdrawn :type asset: str :param key: Withdrawal key name, as set up on your account :type key: str :param amount: Amount to withdraw :type amount: float or str :returns: Reference id :rtype: str API Key Permissions Required: **Funds permissions - Withdraw** """ return str(self._do_private_request("Withdraw", asset=asset, key=key, amount=float(amount))["refid"])
[docs] @callratelimiter(1) def get_withdrawal_status(self, asset, method=None): """ Private User Funding Retrieve information about recently requests withdrawals. :param asset: Asset being withdrawn :type asset: str :param method: Name of the withdrawal method (optional) :type method: str :returns: DataFrame of withdrawal status information :rtype: :py:attr:`pandas.DataFrame` API Key Permissions Required: **Funds permissions - Withdraw** or **Data - Query ledger entries** """ res = self._do_private_request("WithdrawStatus", asset=asset, method=method) wd_status = DataFrame(res, columns=["method", "aclass", "asset", "refid", "txid", "info", "amount", "fee", "time", "status", "status-prop"]).convert_dtypes() return wd_status
[docs] @callratelimiter(1) def request_withdrawal_cancel(self, asset, refid): """ Private User Funding Cancel a recently requested withdrawal, if it has not already been successfully processed. :param asset: Asset being withdrawn :type asset: str :param refid: Withdrawal reference id :type refid: str :returns: True on success :rtype: bool API Key Permissions Required: **Funds permissions - Withdraw**, unless withdrawal is a **WalletTransfer**, then no permissions are required. .. note:: **Cancelation cannot be guaranteed.** This will put in a cancelation request. Depending upon how far along the withdrawal process is, it may not be possible to cancel the withdrawal. """ return bool(self._do_private_request("WithdrawCancel", asset=asset, refid=refid))
[docs] @callratelimiter(1) def wallet_transfer_to_futures(self, asset, amount): """ Private User Funding Transfer from Kraken spot wallet to Kraken Futures holding wallet. Note that a transfer in the other direction must be requested via the Kraken Futures API endpoint. :param asset: Asset being withdrawn :type asset: str :param amount: Amount to withdraw :type amount: float :returns: Reference id :rtype: str """ data = {"asset": asset, "from": "Spot Wallet", "to": "Futures Wallet", "amount": amount} res = self._query_private("WalletTransfer", data) _check_error(res) return str(res["result"]["refid"])
#Subaccounts
[docs] @callratelimiter(1) def create_subaccount(self, username, email): """ Private User Subaccounts Create a trading subaccount. Note: CreateSubaccount must be called using an API key from the master account. :param username: Username for the subaccount :type username: str :param email: Email address for the subaccount :type email: str :returns: Whether subaccount creation was successful or not. :rtype: bool """ return bool(self._do_private_request("CreateSubaccount", username=username, email=email))
[docs] @callratelimiter(1) def account_transfer(self, asset, amount, from_iiban, to_iiban, aclass="currency"): """ Private User Subaccounts Transfer funds to and from master and subaccounts. Note: AccountTransfer must be called using an API key from the master account. :param asset: Asset being transferred :type asset: str :param amount: Amount of asset to transfer :type amount: float or str :param from_iiban: IIBAN of the source account :type from_iiban: str :param to_iiban: IIBAN of the destination account :type to_iiban: str :param aclass: Specify the asset class of the asset being transferred. (Optional. Default = "currency") :type aclass: str :returns: Whether subaccount creation was successful or not. :rtype: bool """ data = {"asset": asset, "from": from_iiban, "to": to_iiban, "amount": amount, "aclass": aclass} res = self._query_private("AccountTransfer", data) _check_error(res) return res["result"]
#Transparency
[docs] @callratelimiter(1) def get_pre_trade_data(self, symbol): """ Public Market Data Returns the price levels in the order book with aggregated order quantities at each price level. The top 10 levels are returned for each trading pair. :param symbol: The of symbols for the currency pair. :type symbol: str :returns: Order book data of the pair and DataFrames of bids and asks :rtype: (dict, :py:attr:`pandas.DataFrame`, :py:attr:`pandas.DataFrame`) """ res = self._do_public_request("PreTrade", symbol=symbol) bids = DataFrame(res.pop("bids"), columns=["side", "price", "qty", "count", "publication_ts", "submission_ts"]).convert_dtypes() asks = DataFrame(res.pop("asks"), columns=["side", "price", "qty", "count", "publication_ts", "submission_ts"]).convert_dtypes() return res, bids, asks
[docs] @callratelimiter(1) def get_post_trade_data(self, symbol, from_ts=None, to_ts=None, count=1000): """ Public Market Data Returns a list of trades on the spot exchange. If no filter parameters are specified, the last n trades for all pairs are received. :param symbol: Filter the results to the currency pair. :type symbol: str :param from_ts: Filter the results to include the trades after this timestamp. (Optional) :type from_ts: str :param to_ts: Filter the results to include the trades before or at this timestamp. (Optional) :type to_ts: str :param count: The maximum number of trades to return in range 1-1000. (Optional. Default = 1000) :type count: int :returns: Order book data of the pair and DataFrames of trades :rtype: (dict, :py:attr:`pandas.DataFrame`) """ res = self._do_public_request("PostTrade", symbol=symbol, from_ts=from_ts, to_ts=to_ts, count=count) trades = DataFrame(res.pop("trades"), columns=["trade_id", "price", "quantity", "symbol", "description", "base_asset", "base_notation", "base_dti_code", "base_dti_short_name", "quote_asset", "quote_notation", "quote_dti_code", "quote_dti_short_name", "trade_venue", "trade_ts", "publication_venue", "publication_ts"]).convert_dtypes() return res, trades
def _update_api_counter(self): now = time() self.api_counter = max(0, self.api_counter - int(now - self.time_of_last_query)) self.time_of_last_query = now
[docs]def add_dtime(df): """ Extra Adds a new column "dtime" (datetime) from the column "time" (unixtime) :param df: A DataFrame with a column "time" in unixtime-format :type df: :py:attr:`pandas.DataFrame` :returns: df with a "dtime" column added :rtype: :py:attr:`pandas.DataFrame` """ df["dtime"] = to_datetime(df.time, unit="s") return df
[docs]def datetime_to_unixtime(dt): """ Extra Converts from datetime to unixtime :param dt: datetime object :type dt: :py:attr:`datetime.datetime` :returns: the unixtime of dt :rtype: int """ return int((dt - datetime(1970, 1, 1)).total_seconds())
[docs]def unixtime_to_datetime(ux): """ Extra Converts from unixtime to datetime :param ux: unixtime timestamp :type ux: int :returns: the datetime of ux :rtype: :py:attr:`datetime.datetime` """ return datetime(1970, 1, 1) + timedelta(0, ux)