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.
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
| Step | Description |
|---|---|
| Authentication | Connect to Kraken via API key and private key |
| Market Data | Fetch ticker prices and order book depth |
| Fee Lookup | Retrieve trading fee rates for a trading pair |
| Market Order | Place an immediate buy/sell order |
| Limit Order | Place a price-controlled buy/sell order |
| Order Status | Query and display order fill details |
| Cancel Order | Cancel an open limit order |
Note: Kraken does not have a public testnet. Use
validate=Truein the order calls to perform a dry-run without actually submitting the order. Removevalidate=True(or set it toFalse) 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.
# Install the Kraken client library if not already present.
!pip install krakenex --quietPreparing 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.
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.
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 clientThe 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.
# ── 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).
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.
}# 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.
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]],
}# 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.
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,
}# 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.
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"]# 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.
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"]# 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.
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"]),
}# 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.
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"]# 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=Truefirst 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=Truein 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.