Getting Kraken tickers

This commit is contained in:
2026-01-01 19:46:07 +01:00
parent c51ac0eaac
commit 79e784fb73
6 changed files with 372 additions and 0 deletions

292
API/Krakenfutures_api.py Normal file
View File

@@ -0,0 +1,292 @@
import time
import base64
import hashlib
import hmac
import json
import urllib.request as urllib2
import urllib.parse as urllib
import ssl
class KrakenFutureClient(object):
def __init__(self, api_key="", api_secret="", timeout=10, checkCertificate=True, useNonce=False):
self.apiPath = 'https://futures.kraken.com'
self._api_key = api_key
self._api_secret = api_secret
self.timeout = timeout
self.nonce = 0
self.checkCertificate = checkCertificate
self.useNonce = useNonce
''' public endpoints '''
# returns all instruments with specifications
def get_instruments(self):
endpoint = "/derivatives/api/v3/instruments"
return self.make_request("GET", endpoint)['instruments']
# returns market data for all instruments
def get_tickers(self):
endpoint = "/derivatives/api/v3/tickers"
return self.make_request("GET", endpoint)
# returns the entire order book of a futures
def get_orderbook(self, symbol):
endpoint = "/derivatives/api/v3/orderbook"
postUrl = "symbol=%s" % symbol
return self.make_request("GET", endpoint, postUrl=postUrl)
# returns historical data for futures and indices
def get_history(self, symbol, lastTime=""):
endpoint = "/derivatives/api/v3/history"
if lastTime != "":
postUrl = "symbol=%s&lastTime=%s" % (symbol, lastTime)
else:
postUrl = "symbol=%s" % symbol
return self.make_request("GET", endpoint, postUrl=postUrl)
''' private endpoints '''
# returns key account information
# Deprecated because it returns info about the Futures margin account
# Use get_accounts instead
def get_account(self):
endpoint = "/derivatives/api/v3/account"
return self.make_request("GET", endpoint)
# returns key account information
def get_accounts(self):
endpoint = "/derivatives/api/v3/accounts"
return self.make_request("GET", endpoint)['accounts']
# places an order
def send_order(self, orderType, symbol, side, size, limitPrice, stopPrice=None, clientOrderId=None):
endpoint = "/derivatives/api/v3/sendorder"
postBody = "orderType=%s&symbol=%s&side=%s&size=%s&limitPrice=%s" % (
orderType, symbol, side, size, limitPrice)
if orderType == "stp" and stopPrice is not None:
postBody += "&stopPrice=%s" % stopPrice
if clientOrderId is not None:
postBody += "&cliOrdId=%s" % clientOrderId
return self.make_request("POST", endpoint, postBody=postBody)
# places an order
def send_order_1(self, order):
endpoint = "/derivatives/api/v3/sendorder"
postBody = urllib.urlencode(order)
return self.make_request("POST", endpoint, postBody=postBody)
# edit an order
def edit_order(self, edit):
endpoint = "/derivatives/api/v3/editorder"
postBody = urllib.urlencode(edit)
return self.make_request("POST", endpoint, postBody=postBody)
# cancels an order
def cancel_order(self, order_id=None, cli_ord_id=None):
endpoint = "/derivatives/api/v3/cancelorder"
if order_id is None:
postBody = "cliOrdId=%s" % cli_ord_id
else:
postBody = "order_id=%s" % order_id
return self.make_request("POST", endpoint, postBody=postBody)
# cancel all orders
def cancel_all_orders(self, symbol=None):
endpoint = "/derivatives/api/v3/cancelallorders"
if symbol is not None:
postbody = "symbol=%s" % symbol
else:
postbody = ""
return self.make_request("POST", endpoint, postBody=postbody)
# cancel all orders after
def cancel_all_orders_after(self, timeoutInSeconds=60):
endpoint = "/derivatives/api/v3/cancelallordersafter"
postbody = "timeout=%s" % timeoutInSeconds
return self.make_request("POST", endpoint, postBody=postbody)
# places or cancels orders in batch
def send_batchorder(self, jsonElement):
endpoint = "/derivatives/api/v3/batchorder"
postBody = "json=%s" % jsonElement
return self.make_request("POST", endpoint, postBody=postBody)
# returns all open orders
def get_openorders(self):
endpoint = "/derivatives/api/v3/openorders"
return self.make_request("GET", endpoint)
# returns filled orders
def get_fills(self, lastFillTime=""):
endpoint = "/derivatives/api/v3/fills"
if lastFillTime != "":
postUrl = "lastFillTime=%s" % lastFillTime
else:
postUrl = ""
return self.make_request("GET", endpoint, postUrl=postUrl)
# returns all open positions
def get_openpositions(self):
endpoint = "/derivatives/api/v3/openpositions"
return self.make_request("GET", endpoint)
# sends an xbt withdrawal request
def send_withdrawal(self, targetAddress, currency, amount):
endpoint = "/derivatives/api/v3/withdrawal"
postBody = "targetAddress=%s&currency=%s&amount=%s" % (
targetAddress, currency, amount)
return self.make_request("POST", endpoint, postBody=postBody)
# returns xbt transfers
def get_transfers(self, lastTransferTime=""):
endpoint = "/derivatives/api/v3/transfers"
if lastTransferTime != "":
postUrl = "lastTransferTime=%s" % lastTransferTime
else:
postUrl = ""
return self.make_request("GET", endpoint, postUrl=postUrl)
# returns all notifications
def get_notifications(self):
endpoint = "/derivatives/api/v3/notifications"
return self.make_request("GET", endpoint)
# makes an internal transfer
def transfer(self, fromAccount, toAccount, unit, amount):
endpoint = "/derivatives/api/v3/transfer"
postBody = "fromAccount=%s&toAccount=%s&unit=%s&amount=%s" % (
fromAccount, toAccount, unit, amount)
return self.make_request("POST", endpoint, postBody=postBody)
# accountlog csv
def get_accountlog(self):
endpoint = "/api/history/v2/accountlogcsv"
return self.make_request("GET", endpoint)
def _get_partial_historical_elements(self, elementType, since=None, contToken=None):
endpoint = "/api/history/v2/%s" % elementType
if contToken is not None:
return self.make_request_raw("GET", endpoint, postUrl="continuationToken=%s" % contToken)
else:
if since is not None:
return self.make_request_raw("GET", endpoint, postUrl="since=%s" % since)
return self.make_request_raw("GET", endpoint)
def _get_historical_elements(self, elementType, since=None):
elements = []
more = True
contToken = None
while more:
res = self._get_partial_historical_elements(elementType, since, contToken)
body = json.loads(res.read().decode('utf-8'))
elements = elements + body['elements']
if res.headers['is-truncated'] is None or res.headers['is-truncated'] == "false":
more = False
contToken = None
else:
contToken = res.headers['next-continuation-token']
elements.sort(key=lambda el: el['timestamp'], reverse=True)
return elements
# historical orders after a certain point in reverse chronological order
def get_historical_orders(self, since=None):
return self._get_historical_elements('orders', since)
# recent orders in reverse chronological order
def get_recent_orders(self):
return self.get_historical_orders(None)
# historical executions after a certain point in reverse chronological order
def get_historical_executions(self, since=None):
return self._get_historical_elements('executions', since)
# recent executions in reverse chronological order
def get_recent_executions(self):
return self.get_historical_executions(None)
# signs a message
def sign_message(self, endpoint, postData, nonce=""):
if endpoint.startswith('/derivatives'):
endpoint = endpoint[len('/derivatives'):]
# step 1: concatenate postData, nonce + endpoint
message = postData + nonce + endpoint
# step 2: hash the result of step 1 with SHA256
sha256_hash = hashlib.sha256()
sha256_hash.update(message.encode('utf8'))
hash_digest = sha256_hash.digest()
# step 3: base64 decode apiPrivateKey
secretDecoded = base64.b64decode(self._api_secret)
# step 4: use result of step 3 to has the result of step 2 with HMAC-SHA512
hmac_digest = hmac.new(secretDecoded, hash_digest,
hashlib.sha512).digest()
# step 5: base64 encode the result of step 4 and return
return base64.b64encode(hmac_digest)
# creates a unique nonce
def get_nonce(self):
# https://en.wikipedia.org/wiki/Modulo_operation
self.nonce = (self.nonce + 1) & 8191
return str(int(time.time() * 1000)) + str(self.nonce).zfill(4)
# sends an HTTP request
def make_request_raw(self, requestType, endpoint, postUrl="", postBody=""):
# create authentication headers
postData = postUrl + postBody
if self.useNonce:
nonce = self.get_nonce()
signature = self.sign_message(endpoint, postData, nonce=nonce)
authentHeaders = {"APIKey": self._api_key,
"Nonce": nonce, "Authent": signature}
else:
signature = self.sign_message(endpoint, postData)
authentHeaders = {
"APIKey": self._api_key, "Authent": signature}
authentHeaders["User-Agent"] = "cf-api-python/1.0"
# create request
if postUrl != "":
url = self.apiPath + endpoint + "?" + postUrl
else:
url = self.apiPath + endpoint
request = urllib2.Request(url, str.encode(postBody), authentHeaders)
request.get_method = lambda: requestType
# read response
if self.checkCertificate:
response = urllib2.urlopen(request, timeout=self.timeout)
else:
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
response = urllib2.urlopen(
request, context=ctx, timeout=self.timeout)
# return
return response
# sends an HTTP request and read response body
def make_request(self, requestType, endpoint, postUrl="", postBody=""):
output = self.make_request_raw(requestType, endpoint, postUrl, postBody)
return json.loads(output.read().decode('utf-8'))

0
API/__init__.py Normal file
View File

Binary file not shown.

Binary file not shown.

3
conf/config.ini Normal file
View File

@@ -0,0 +1,3 @@
[KRAKEN]
api_key = i2fnn1LqzTqfoChwdoak/JbrpNytaCb6OAAvkgvdMbB+f9yR+mY7b49H
api_secret = 8UgJ4fYIs2BIBAHuy0f2kRBWF+4qcrxcMtF9bAQAA/7u+dZzqbZskNxsM9pWDQGELx2wq67Bqx+QdphHvCYitg==

77
main.py Normal file
View File

@@ -0,0 +1,77 @@
import configparser
import os
import sys
# ------------------------------------------------------------------
# 1. Gestion des imports depuis le dossier ./API
# ------------------------------------------------------------------
sys.path.append(os.path.join(os.path.dirname(__file__), 'API'))
try:
from API.Krakenfutures_api import KrakenFutureClient
except ImportError:
# Alternative si le dossier API n'est pas vu comme un module
from Krakenfutures_api import KrakenFutureClient
# ------------------------------------------------------------------
# 2. Lecture de la configuration
# ------------------------------------------------------------------
config = configparser.ConfigParser()
config_path = os.path.join(os.path.dirname(__file__), 'conf', 'config.ini')
# Vérification que le fichier existe
if not os.path.exists(config_path):
raise FileNotFoundError(f"Le fichier de configuration est introuvable ici : {config_path}")
config.read(config_path)
# Récupération des clés
try:
api_key = config['KRAKEN']['api_key']
api_secret = config['KRAKEN']['api_secret']
except KeyError as e:
raise ValueError(f"Erreur de lecture du config.ini. La clé ou la section est manquante : {e}")
# ------------------------------------------------------------------
# 3. Initialisation du Client et Test
# ------------------------------------------------------------------
if __name__ == "__main__":
print("Initialisation du client Kraken Futures...")
# Création de l'instance du client
kraken_client = KrakenFutureClient(
api_key=api_key,
api_secret=api_secret
)
try:
print("Tentative de connexion à l'API...")
response = kraken_client.get_tickers()
# Vérification si la clé 'tickers' existe
if 'tickers' in response:
tickers_list = response['tickers']
print("Connexion réussie !")
if len(tickers_list) > 0:
# On prend le premier élément de la liste
first_ticker = tickers_list[0]
# On affiche le contenu de ce premier ticker
print("\n--- Exemple de premier ticker reçu ---")
print(first_ticker)
# Si vous voulez afficher spécifiquement le nom du symbole (souvent 'symbol' ou 'pair')
# Vous pouvez faire : print(f"Symbole : {first_ticker.get('symbol', 'Inconnu')}")
else:
print("La liste des tickers est vide.")
else:
print("Réponse reçue, mais format inattendu (pas de clé 'tickers') :", response)
except Exception as e:
print(f"Une erreur est survenue lors de l'appel API : {e}")
# Pour tester un appel PRIVÉ (nécessite que les clés soient valides)
# accounts = kraken_client.get_accounts()
# print(accounts)