Execution·Exchange-Specific Execution·Beginner

Kraken Execution

Implement trade execution on Kraken using their REST API with proper nonce generation and management, rate limit compliance, and complete order lifecycle management for spot market trading with multiple supported order types and conditional close parameters.

executionorder-execution

Execute Trades on Kraken

1. Overview

This notebook demonstrates how to execute trades programmatically on Kraken using the krakenex library. It covers authentication, market data retrieval, fee lookup, order placement, and order management.

What You Will Learn

StepDescription
AuthenticationConnect to Kraken via API key and private key
Market DataFetch ticker prices and order book depth
Fee LookupRetrieve trading fee rates for a trading pair
Market OrderPlace an immediate buy/sell order
Limit OrderPlace a price-controlled buy/sell order
Order StatusQuery and display order fill details
Cancel OrderCancel an open limit order

Note: Kraken does not have a public testnet. Use validate=True in the order calls to perform a dry-run without actually submitting the order. Remove validate=True (or set it to False) to place a live order. Dummy Mode: Live API calls in this notebook are commented out and replaced with realistic dummy data so it can run end-to-end without a live connection or trading funds. Uncomment the real calls and remove the dummy assignments to run against a real account.

2. Dependency Imports

Install and import the required libraries. krakenex is a lightweight Python wrapper for the Kraken REST API.

[1]
# Install the Kraken client library if not already present.
!pip install krakenex --quiet
  Preparing metadata (setup.py) ... [?25l[?25hdone
  Building wheel for krakenex (setup.py) ... [?25l[?25hdone

The krakenex library is a Python wrapper for the Kraken REST API, making it easier to interact with the exchange. It handles aspects like API authentication, request signing, and response parsing.

[2]
import krakenex
import warnings

# Suppress all warnings for cleaner output in a demonstration context.
warnings.filterwarnings("ignore")

This cell imports the necessary krakenex library for interacting with Kraken and the warnings module. Warnings are suppressed here to keep the notebook output clean, which is often useful in a demonstration setting.

3. Authentication

3.1. create_client Function

Kraken uses an API key and a private key (base64-encoded). Both are generated in the Kraken account security settings. Public endpoints (market data) work without authentication; private endpoints (trading) require both keys.

[3]
def create_client(api_key: str, api_secret: str) -> krakenex.API:
    """Create and return an authenticated Kraken API client.

    Parameters
    ----------
    api_key    : Your Kraken API key.
    api_secret : Your Kraken private key (base64-encoded).

    Returns
    -------
    krakenex.API: Authenticated Kraken client instance.
    """
    # Initialise the client and assign credentials directly.
    client = krakenex.API()
    client.key    = api_key
    client.secret = api_secret
    return client

The create_client function is a utility to set up an authenticated connection to the Kraken API. It takes your API key and secret as arguments and returns a krakenex.API object, which is then used for all subsequent private API calls (like placing orders or checking balances). Public calls (like market data) don't strictly require authentication, but it's good practice to initialize the client this way for consistency.

[4]
# ── Replace with your own keys ──────────────────────────────────────────────
API_KEY    = "YOUR_KRAKEN_API_KEY"
API_SECRET = "YOUR_KRAKEN_PRIVATE_KEY"
# ────────────────────────────────────────────────────────────────────────────

# Create the authenticated Kraken client.
client = create_client(API_KEY, API_SECRET)
print("Kraken client created successfully.")
Kraken client created successfully.

This cell demonstrates how to use the create_client function. You need to replace the placeholder YOUR_KRAKEN_API_KEY and YOUR_KRAKEN_PRIVATE_KEY with your actual API credentials obtained from your Kraken account settings. The print statement confirms that the client has been initialized.

4. Market Data Functions

4.1. get_ticker_price Function

Fetches the current best bid and ask prices for a Kraken trading pair. Note that Kraken uses its own pair notation (e.g., XBTUSD for Bitcoin/USD).

[5]
def get_ticker_price(client: krakenex.API, pair: str) -> dict:
    """Return the best bid and ask price for a trading pair.

    Parameters
    ----------
    client : Authenticated Kraken API client.
    pair   : Kraken trading pair, e.g. 'XBTUSD'.

    Returns
    -------
    dict: Contains 'bid' and 'ask' as floats.
    """
    # Ticker endpoint returns a nested dict keyed by pair name.
    response = client.query_public("Ticker", {"pair": pair})
    data = list(response["result"].values())[0]
    return {
        "bid": float(data["b"][0]),   # b[0] = best bid price.
        "ask": float(data["a"][0]),   # a[0] = best ask price.
    }
[6]
# Fetch the current XBTUSD ticker (BTC/USD on Kraken).
# Commented out to run locally with dummy data
# ticker = get_ticker_price(client, "XBTUSD")

ticker = {"bid": 60000.00, "ask": 60005.00}  # Dummy data

print(f"XBTUSD — Bid: {ticker['bid']:,.2f} | Ask: {ticker['ask']:,.2f}")
XBTUSD — Bid: 60,000.00 | Ask: 60,005.00

4.2. get_order_book Function

Retrieves the top N levels of bids and asks for a given Kraken trading pair.

[7]
def get_order_book(client: krakenex.API, pair: str, depth: int = 5) -> dict:
    """Return the top N bid and ask levels from the order book.

    Parameters
    ----------
    client : Authenticated Kraken API client.
    pair   : Kraken trading pair, e.g. 'XBTUSD'.
    depth  : Number of price levels to retrieve (default: 5).

    Returns
    -------
    dict: Contains 'bids' and 'asks' as lists of [price, quantity].
    """
    # Depth endpoint returns 'bids' and 'asks' each as [price, volume, timestamp].
    response = client.query_public("Depth", {"pair": pair, "count": depth})
    data = list(response["result"].values())[0]
    return {
        "bids": [[float(b[0]), float(b[1])] for b in data["bids"][:depth]],
        "asks": [[float(a[0]), float(a[1])] for a in data["asks"][:depth]],
    }
[8]
# Display the top 5 levels of the XBTUSD order book.
# Commented out to run locally with dummy data
# book = get_order_book(client, "XBTUSD", depth=5)

book = {  # Dummy data
    "bids": [[59999.5, 0.512], [59999.0, 1.204], [59998.5, 0.880], [59998.0, 2.001], [59997.5, 0.330]],
    "asks": [[60000.5, 0.470], [60001.0, 1.150], [60001.5, 0.905], [60002.0, 1.760], [60002.5, 0.290]],
}

print("Top 5 Asks (sell side):")
for price, qty in reversed(book["asks"]):
    print(f"  {price:>12,.2f}  |  {qty:.6f} BTC")

print("-" * 35)
print("Top 5 Bids (buy side):")
for price, qty in book["bids"]:
    print(f"  {price:>12,.2f}  |  {qty:.6f} BTC")
Top 5 Asks (sell side):
     60,002.50  |  0.290000 BTC
     60,002.00  |  1.760000 BTC
     60,001.50  |  0.905000 BTC
     60,001.00  |  1.150000 BTC
     60,000.50  |  0.470000 BTC
-----------------------------------
Top 5 Bids (buy side):
     59,999.50  |  0.512000 BTC
     59,999.00  |  1.204000 BTC
     59,998.50  |  0.880000 BTC
     59,998.00  |  2.001000 BTC
     59,997.50  |  0.330000 BTC

5. Fee Information

5.1. get_trade_fee Function

Queries your account's current trading fee schedule for a given pair. Kraken fees decrease as your 30-day rolling volume increases.

[9]
def get_trade_fee(client: krakenex.API, pair: str) -> dict:
    """Return maker and taker fee rates for a trading pair.

    Parameters
    ----------
    client : Authenticated Kraken API client.
    pair   : Kraken trading pair, e.g. 'XBTUSD'.

    Returns
    -------
    dict: Contains 'maker_fee' and 'taker_fee' as floats.
    """
    # TradeVolume returns the account fee schedule; fees are in percent.
    response = client.query_private("TradeVolume", {"pair": pair, "fee-info": True})

    # Check for API errors in the response
    if response and response.get('error'):
        # Print the error message(s) from Kraken API
        for error_msg in response['error']:
            print(f"Kraken API Error: {error_msg}")
        # For graceful handling, return default fees
        return {
            "maker_fee": 0.16 / 100, # Default maker fee
            "taker_fee": 0.26 / 100, # Default taker fee
        }

    # If no errors, proceed to extract fees. Ensure 'result' key exists.
    if not response or not response.get('result'):
        print("Kraken API did not return 'result' data for TradeVolume. Returning default fees.")
        return {
            "maker_fee": 0.16 / 100, # Default maker fee
            "taker_fee": 0.26 / 100, # Default taker fee
        }

    fees = response["result"].get("fees", {})
    fees_maker = response["result"].get("fees_maker", {})

    # The existing logic for default values if 'fees' or 'fees_maker' are empty is good here
    taker_pct = float(list(fees.values())[0]["fee"])   if fees       else 0.26
    maker_pct = float(list(fees_maker.values())[0]["fee"]) if fees_maker else 0.16

    return {
        "maker_fee": maker_pct / 100,
        "taker_fee": taker_pct / 100,
    }
[10]
# Display fee rates for XBTUSD.
# Commented out to run locally with dummy data
# fees = get_trade_fee(client, "XBTUSD")

fees = {"maker_fee": 0.16 / 100, "taker_fee": 0.26 / 100}  # Dummy data (default Kraken tier)

print(f"XBTUSD Fees — Maker: {fees['maker_fee']*100:.4f}% | Taker: {fees['taker_fee']*100:.4f}%")
XBTUSD Fees — Maker: 0.1600% | Taker: 0.2600%

6. Order Execution Functions

6.1. place_market_order Function

Places a market order on Kraken. Executes immediately at the best available price. Pass validate=True for a dry-run that verifies the order without submitting it.

[11]
def place_market_order(
    client: krakenex.API, pair: str, side: str, volume: float, validate: bool = True
) -> dict:
    """Place a market order on Kraken.

    Parameters
    ----------
    client   : Authenticated Kraken API client.
    pair     : Kraken trading pair, e.g. 'XBTUSD'.
    side     : 'buy' or 'sell'.
    volume   : Order volume in base currency (e.g. BTC).
    validate : If True, performs a dry-run without submitting the order.

    Returns
    -------
    dict: Kraken AddOrder response (contains txid list when validate=False).
    """
    params = {
        "pair"    : pair,
        "type"    : side.lower(),
        "ordertype": "market",
        "volume"  : str(volume),
    }
    # validate flag instructs Kraken to check but not submit the order.
    if validate:
        params["validate"] = "true"

    response = client.query_private("AddOrder", params)
    return response["result"]
[12]
# Dry-run a market BUY for 0.001 BTC — validate=True means no real order.
# Commented out to run locally with dummy data
# market_order = place_market_order(
#     client, pair="XBTUSD", side="buy", volume=0.001, validate=True
# )

market_order = {  # Dummy data
    "descr": {"order": "buy 0.00100000 XBTUSD @ market"},
    "txid": "(validate=True, no txid)",
}

print("Market Order (Dry Run):")
print(f"  Description: {market_order.get('descr', {}).get('order', 'N/A')}")
print(f"  Tx IDs     : {market_order.get('txid', '(validate=True, no txid)')}")
Market Order (Dry Run):
  Description: buy 0.00100000 XBTUSD @ market
  Tx IDs     : (validate=True, no txid)

6.2. place_limit_order Function

Places a limit order on Kraken. The order rests in the book until the market reaches your price, or until you cancel it.

[13]
def place_limit_order(
    client: krakenex.API, pair: str, side: str, volume: float, price: float, validate: bool = True
) -> dict:
    """Place a limit order on Kraken.

    Parameters
    ----------
    client   : Authenticated Kraken API client.
    pair     : Kraken trading pair, e.g. 'XBTUSD'.
    side     : 'buy' or 'sell'.
    volume   : Order volume in base currency.
    price    : Limit price at which to execute.
    validate : If True, performs a dry-run without submitting the order.

    Returns
    -------
    dict: Kraken AddOrder response.
    """
    params = {
        "pair"     : pair,
        "type"     : side.lower(),
        "ordertype": "limit",
        "price"    : str(price),
        "volume"   : str(volume),
    }
    # validate flag instructs Kraken to check but not submit the order.
    if validate:
        params["validate"] = "true"

    response = client.query_private("AddOrder", params)
    return response["result"]
[14]
# Dry-run a limit BUY 1% below the current ask.
# Commented out to run locally with dummy data
# current_ask = get_ticker_price(client, "XBTUSD")["ask"]
current_ask = ticker["ask"]  # reuse dummy ticker from earlier
limit_price  = round(current_ask * 0.99, 1)

# limit_order = place_limit_order(
#     client, pair="XBTUSD", side="buy", volume=0.001, price=limit_price, validate=True
# )

limit_order = {  # Dummy data
    "descr": {"order": f"buy 0.00100000 XBTUSD @ limit {limit_price}"},
    "txid": "(validate=True, no txid)",
}

print("Limit Order (Dry Run):")
print(f"  Description: {limit_order.get('descr', {}).get('order', 'N/A')}")
print(f"  Limit Price: {limit_price:,.1f} USD")
print(f"  Tx IDs     : {limit_order.get('txid', '(validate=True, no txid)')}")
Limit Order (Dry Run):
  Description: buy 0.00100000 XBTUSD @ limit 59404.9
  Limit Price: 59,404.9 USD
  Tx IDs     : (validate=True, no txid)

7. Order Management Functions

7.1. get_order_status Function

Retrieves the current status and fill details for an existing order using its transaction ID.

[15]
def get_order_status(client: krakenex.API, txid: str) -> dict:
    """Return the status and fill details of an existing order.

    Parameters
    ----------
    client : Authenticated Kraken API client.
    txid   : Transaction ID of the order (returned by AddOrder).

    Returns
    -------
    dict: Contains 'status', 'vol_exec', and 'price'.
    """
    # QueryOrders returns a dict keyed by txid.
    response = client.query_private("QueryOrders", {"txid": txid})
    data = response["result"][txid]
    return {
        "status"  : data["status"],
        "vol_exec": float(data["vol_exec"]),
        "price"   : float(data["price"]),
    }
[16]
# Example: check order status (dummy data, since dry-run orders have no real txid).
# status = get_order_status(client, "OXXXXX-XXXXX-XXXXX")

status = {"status": "open", "vol_exec": 0.0, "price": 0.0}  # Dummy data

print(f"Status: {status['status']} | Executed: {status['vol_exec']} BTC @ {status['price']:,.2f} USD")
print("Note: Set validate=False in place_limit_order to get a real txid and query its status.")
Status: open | Executed: 0.0 BTC @ 0.00 USD
Note: Set validate=False in place_limit_order to get a real txid and query its status.

7.2. cancel_order Function

Cancels an open order on Kraken using its transaction ID. Fully-filled orders cannot be cancelled.

[17]
def cancel_order(client: krakenex.API, txid: str) -> dict:
    """Cancel an open order on Kraken.

    Parameters
    ----------
    client : Authenticated Kraken API client.
    txid   : Transaction ID of the order to cancel.

    Returns
    -------
    dict: Contains 'count' (number of orders cancelled).
    """
    # CancelOrder returns the number of orders successfully cancelled.
    response = client.query_private("CancelOrder", {"txid": txid})
    return response["result"]
[18]
# Example: cancel an order (dummy data, since dry-run orders have no real txid).
# result = cancel_order(client, "OXXXXX-XXXXX-XXXXX")

result = {"count": 1}  # Dummy data

print(f"Cancelled {result['count']} order(s).")
print("Note: Set validate=False in place_limit_order to get a real txid and cancel it.")
Cancelled 1 order(s).
Note: Set validate=False in place_limit_order to get a real txid and cancel it.

8. Execution Decision Framework

Choosing the right order type on Kraken depends on your execution goal:

When to Use a Market Order

  • You need to enter or exit immediately (e.g., stop-loss, rapid signal decay).
  • Order size is small relative to book depth (< 0.5% of visible liquidity).
  • Speed matters more than the exact fill price.

When to Use a Limit Order

  • You want price certainty and can wait for the market to come to you.
  • Order size is large (> 1% of book depth) — avoids consuming multiple levels.
  • Market is volatile (spread > 5 bps) — anchor your execution price.
  • You want to qualify for maker rebates by adding liquidity to the book.

Kraken-Specific Notes

  • Default fee tier (lowest volume): Taker 0.26% / Maker 0.16%.
  • Always use validate=True first to test your order parameters before going live.
  • Kraken pair notation differs from other exchanges: BTC = XBT, USD = USD, so BTC/USD = XBTUSD.
  • There is no testnet — dry-run via validate=True in AddOrder.

Conclusion

This notebook has provided a comprehensive guide to programmatically interacting with the Kraken cryptocurrency exchange. We've covered the essential steps from setting up your API client and authenticating with your credentials, to fetching real-time market data like ticker prices and order book depth. Understanding the fee structure was also addressed, differentiating between maker and taker fees which can significantly impact trading profitability.

The core of this demonstration focused on order execution and management. We explored how to place both market orders, for immediate execution, and limit orders, for price-controlled entry or exit. Crucially, the concept of a 'dry-run' using validate=True was highlighted as a safe way to test order parameters without risking real funds, a vital feature given Kraken's lack of a public testnet. Finally, we demonstrated how to retrieve order status and cancel open orders, providing a complete lifecycle management of trades.

The 'Execution Decision Framework' section offered practical guidance on when to apply market versus limit orders, considering factors like urgency, market depth, volatility, and fee incentives. This framework, combined with the practical code examples, equips you with the knowledge to make informed trading decisions and implement them efficiently on the Kraken platform.

Remember to always exercise caution when interacting with live trading APIs. Start with small volumes, thoroughly test your logic, and understand the implications of validate=False before deploying any automated trading strategies.

Kraken Execution · BitPredict