commit bfe6a0553b265135f4828c31f1cefd1630da8033 Author: Eric Meehan Date: Thu Apr 2 17:14:33 2026 -0400 Initial commit diff --git a/kraken.py b/kraken.py new file mode 100644 index 0000000..08f9de2 --- /dev/null +++ b/kraken.py @@ -0,0 +1,174 @@ +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()