Files
kraken-library/kraken.py
2026-04-02 17:14:33 -04:00

175 lines
6.2 KiB
Python

import http.client
import urllib.request
import urllib.parse
import hashlib
import hmac
import base64
import json
import os
import time
API_KEY = "q1QjOkt6S0oOmmknkuh2MSyBqKE1suEGfNb6DlRm9mUpAhZsz7GFpnx7"
API_SECRET = "ZhIrO4Sh5e6khajbXmfGEYvnPE91EerdYXdDiPcJlBAUYNq5G4qr+tF6dwIH8vN/wTlBXCJyAJVZ3RkH4HaVfA=="
ASSET_MAP = {
"XETH": "ETH",
"XXBT": "BTC"
}
ANSI_RESET = "\033[0m"
ANSI_BOLD = "\033[1m"
ANSI_GREEN = "\033[32m"
ANSI_RED = "\033[31m"
ANSI_CYAN = "\033[36m"
INTERVAL = 10
def main():
previous_trade_balance = None
previous_portfolio = None
while True:
trade_balance, portfolio = get_account_overview()
os.system("clear")
print(f"{ANSI_RESET}{ANSI_BOLD}Kraken Pro Portfolio{ANSI_RESET}")
print("Currency\tBalance\t\tRate\t\tValue")
for investment in portfolio:
if previous_portfolio and previous_portfolio[investment] and previous_portfolio[investment]['value'] <= portfolio[investment]['value']:
print(f"{ANSI_GREEN}{investment}\t\t{portfolio[investment]['balance']:010.8f}\t${portfolio[investment]['exchange_rate']:010.4f}\t${portfolio[investment]['value']:010.4f}{ANSI_RESET}")
elif previous_portfolio and previous_portfolio[investment] and previous_portfolio[investment]['value'] > portfolio[investment]['value']:
print(f"{ANSI_RED}{investment}\t\t{portfolio[investment]['balance']:010.8f}\t${portfolio[investment]['exchange_rate']:010.4f}\t${portfolio[investment]['value']:010.4f}{ANSI_RESET}")
else:
print(f"{ANSI_RESET}{investment}\t\t{portfolio[investment]['balance']:010.8f}\t${portfolio[investment]['exchange_rate']:010.4f}\t${portfolio[investment]['value']:010.4f}{ANSI_RESET}")
print(f"{ANSI_RESET}------------------------------------------------------------")
if previous_trade_balance and previous_trade_balance <= trade_balance:
print(f"{ANSI_GREEN}Total\t\t\t\t\t\t${trade_balance:010.4f}{ANSI_RESET}")
elif previous_trade_balance and previous_trade_balance > trade_balance:
print(f"{ANSI_RED}Total\t\t\t\t\t\t${trade_balance:010.4f}{ANSI_RESET}")
else:
print(f"{ANSI_RESET}Total\t\t\t\t\t\t${trade_balance:010.4f}{ANSI_RESET}")
time.sleep(INTERVAL)
previous_trade_balance = trade_balance
previous_portfolio = portfolio
def get_account_overview():
account_balance = get_account_balance()
if account_balance['error'] != []:
for error in account_balance['error']:
raise Exception(error)
else:
account_balance = account_balance['result']
investments = [each for each in account_balance if float(account_balance[each]) > 0]
portfolio = {}
for currency in investments:
if currency in ASSET_MAP:
pair = f"{ASSET_MAP[currency]}/USD"
else:
pair = f"{currency}/USD"
ohlc = get_ohlc_data(pair)
if ohlc['error'] != []:
for error in ohlc['error']:
print(f"{error}: {pair}")
continue
else:
ohlc = ohlc['result']
for pair in ohlc:
portfolio[currency] = {
'balance': float(account_balance[currency]),
'exchange_rate': float(ohlc[pair][-2][4]),
'value': float(account_balance[currency]) * float(ohlc[pair][-2][4])
}
if portfolio[currency]['value'] < 0.01:
portfolio.pop(currency)
break
trade_balance = get_trade_balance()
if trade_balance['error'] != []:
for error in trade_balance['error']:
raise Exception(error)
else:
trade_balance = float(trade_balance['result']['eb'])
return trade_balance, portfolio
def get_ohlc_data(pair):
response = request(
method="GET",
path="/0/public/OHLC",
query={
"pair": pair,
"interval": 5,
},
environment="https://api.kraken.com",
)
return json.loads(response.read().decode())
def get_account_balance():
response = request(
method="POST",
path="/0/private/Balance",
public_key=API_KEY,
private_key=API_SECRET,
environment="https://api.kraken.com",
)
return json.loads(response.read().decode())
def get_trade_balance():
response = request(
method="POST",
path="/0/private/TradeBalance",
public_key=API_KEY,
private_key=API_SECRET,
environment="https://api.kraken.com",
)
return json.loads(response.read().decode())
def request(method: str = "GET", path: str = "", query: dict | None = None, body: dict | None = None, public_key: str = "", private_key: str = "", environment: str = "") -> http.client.HTTPResponse:
url = environment + path
query_str = ""
if query is not None and len(query) > 0:
query_str = urllib.parse.urlencode(query)
url += "?" + query_str
nonce = ""
if len(public_key) > 0:
if body is None:
body = {}
nonce = body.get("nonce")
if nonce is None:
nonce = get_nonce()
body["nonce"] = nonce
headers = {}
body_str = ""
if body is not None and len(body) > 0:
body_str = json.dumps(body)
headers["Content-Type"] = "application/json"
if len(public_key) > 0:
headers["API-Key"] = public_key
headers["API-Sign"] = get_signature(private_key, query_str+body_str, nonce, path)
req = urllib.request.Request(
method=method,
url=url,
data=body_str.encode(),
headers=headers,
)
return urllib.request.urlopen(req)
def get_nonce() -> str:
return str(int(time.time() * 1000))
def get_signature(private_key: str, data: str, nonce: str, path: str) -> str:
return sign(
private_key=private_key,
message=path.encode() + hashlib.sha256(
(nonce + data)
.encode()
).digest()
)
def sign(private_key: str, message: bytes) -> str:
return base64.b64encode(
hmac.new(
key=base64.b64decode(private_key),
msg=message,
digestmod=hashlib.sha512,
).digest()
).decode()
if __name__ == "__main__":
main()