From 3430b37280cd149dbf4cad717014a55b9f2204f1 Mon Sep 17 00:00:00 2001 From: eric o meehan Date: Sun, 22 Sep 2024 00:05:32 -0400 Subject: [PATCH] v0.0.13 --- kraken-bot/__init__.py => __init__.py | 0 app.py | 25 +++++ kraken-bot/KrakenBot.py | 105 ------------------ kraken-bot/app.py | 17 --- kraken_bot/KrakenBot.py | 146 ++++++++++++++++++++++++++ kraken_bot/KrakenOrder.py | 46 ++++++++ kraken_bot/LinearRegression.py | 58 ++++++++++ kraken_bot/Model.py | 14 +++ kraken_bot/__init__.py | 0 9 files changed, 289 insertions(+), 122 deletions(-) rename kraken-bot/__init__.py => __init__.py (100%) create mode 100644 app.py delete mode 100644 kraken-bot/KrakenBot.py delete mode 100644 kraken-bot/app.py create mode 100644 kraken_bot/KrakenBot.py create mode 100644 kraken_bot/KrakenOrder.py create mode 100644 kraken_bot/LinearRegression.py create mode 100644 kraken_bot/Model.py create mode 100644 kraken_bot/__init__.py diff --git a/kraken-bot/__init__.py b/__init__.py similarity index 100% rename from kraken-bot/__init__.py rename to __init__.py diff --git a/app.py b/app.py new file mode 100644 index 0000000..44b4cfb --- /dev/null +++ b/app.py @@ -0,0 +1,25 @@ +import logging +import os + +from kraken_bot.KrakenBot import KrakenBot +from kraken_bot.LinearRegression import LinearRegression +from dotenv import load_dotenv + +load_dotenv() + +logger = logging.getLogger(__name__) + +bot = KrakenBot( + token = os.getenv('KRAKEN_API_TOKEN'), + secret = os.getenv('KRAKEN_API_SEC'), + tier = 'Pro', + investment_count = int(os.getenv('INVESTMENT_COUNT')), + investment_volume = float(os.getenv('INVESTMENT_VOLUME')) + ) +model = LinearRegression( + r_value_target = float(os.getenv('R_VALUE_TARGET')) + ) + +if __name__ == '__main__': + bot.update() + bot.trade(model) diff --git a/kraken-bot/KrakenBot.py b/kraken-bot/KrakenBot.py deleted file mode 100644 index 4371cf7..0000000 --- a/kraken-bot/KrakenBot.py +++ /dev/null @@ -1,105 +0,0 @@ -import krakenex -import pandas -import time - -from pykrakenapi import KrakenAPI -from scipy.stats import linregress - -class KrakenBot(): - def __init__(self, kraken_api_token, kraken_api_sec, tier, r_value_target, investment_count, investment_volume): - self.r_value_target = r_value_target - self.investment_count = investment_count - self.investment_volume = investment_volume - k = krakenex.API(kraken_api_token, kraken_api_sec) - self.kraken = KrakenAPI(k, tier=tier) - - def filter_asset_pairs(self, tradable_asset_pairs): - return tradable_asset_pairs[ - (tradable_asset_pairs['quote'] == 'ZUSD') & - (tradable_asset_pairs['status'] == 'online') - ] - - def model_asset_pair(self, asset_pair): - ohlc = self.kraken.get_ohlc_data(asset_pair)[0] - time.sleep(self.kraken.factor) - model = pandas.DataFrame({ - 'open': linregress(ohlc['time'].astype(float), ohlc['open'].astype(float)), - 'high': linregress(ohlc['time'].astype(float), ohlc['high'].astype(float)), - 'low': linregress(ohlc['time'].astype(float), ohlc['low'].astype(float)), - 'close': linregress(ohlc['time'].astype(float), ohlc['close'].astype(float)) - }).transpose() - model.columns = ['slope', 'intercept', 'r_value', 'p_value', 'stderr'] - return model - - def grade_models(self, models): - model_averages = pandas.DataFrame({ - model: { - 'slope': models[model].slope.mean(), - 'intercept': models[model].intercept.mean(), - 'r_value': models[model].r_value.mean(), - 'p_value': models[model].p_value.mean(), - 'stderr': models[model].stderr.mean() - } for model in models - }).transpose() - filtered_models = model_averages[ - (model_averages['r_value'] >= self.r_value_target) & - (model_averages['slope'] > 0) - ] - return filtered_models.sort_values(by='slope', ascending=False) - - def sell_asset(self, market, volume): - self.kraken.add_standard_order( - ordertype = 'market', - type = 'sell', - pair = market, - volume = volume, - validate = False - ) - time.sleep(self.kraken.factor) - print(f"Sold {volume} on {market}") - - def buy_asset(self, market, volume): - self.kraken.add_standard_order( - ordertype = 'market', - type = 'buy', - pair = market, - volume = volume, - validate = False - ) - print(f"Purchased {volume} on {market}") - time.sleep(self.kraken.factor) - - def update(self): - asset_pairs = self.filter_asset_pairs(self.kraken.get_tradable_asset_pairs()) - time.sleep(self.kraken.factor) - graded_models = self.grade_models( - {asset_pair: self.model_asset_pair(asset_pair) for asset_pair in asset_pairs.index} - ) - print(graded_models) - - account_balance = self.kraken.get_account_balance() - for asset in account_balance[(account_balance.vol > 0) & (account_balance.index != 'ZUSD')].index: - market = asset_pairs[ - (asset_pairs.base == asset) & - (asset_pairs.quote == 'ZUSD') - ] - if market.index[0] not in graded_models[:self.investment_count * 2].index: - self.sell_asset(market.index[0], account_balance.loc[asset].vol) - - account_balance = self.kraken.get_account_balance() - while ((len(account_balance[account_balance.vol > 0]) - 1 < self.investment_count) & - (account_balance.loc['ZUSD'].vol > self.investment_volume)): - for market in graded_models.index: - if asset_pairs.loc[market].base not in account_balance[account_balance.vol > 0].index: - ohlc, last = self.kraken.get_ohlc_data(market) - volume = self.investment_volume/ohlc.iloc[-1].close - try: - self.buy_asset(market, volume) - account_balance = self.kraken.get_account_balance() - time.sleep(self.kraken.factor) - break - except: - continue - - print(self.kraken.get_account_balance()) - print(self.kraken.get_trade_balance(asset='ZUSD')) diff --git a/kraken-bot/app.py b/kraken-bot/app.py deleted file mode 100644 index d6fa8a4..0000000 --- a/kraken-bot/app.py +++ /dev/null @@ -1,17 +0,0 @@ -import os - -from KrakenBot import KrakenBot -from dotenv import load_dotenv - -load_dotenv() -bot = KrakenBot( - kraken_api_token = os.getenv('KRAKEN_API_TOKEN'), - kraken_api_sec = os.getenv('KRAKEN_API_SEC'), - tier = 'Pro', - r_value_target = float(os.getenv('R_VALUE_TARGET')), - investment_count = int(os.getenv('INVESTMENT_COUNT')), - investment_volume = float(os.getenv('INVESTMENT_VOLUME')) - ) - -if __name__ == '__main__': - bot.update() diff --git a/kraken_bot/KrakenBot.py b/kraken_bot/KrakenBot.py new file mode 100644 index 0000000..18770ef --- /dev/null +++ b/kraken_bot/KrakenBot.py @@ -0,0 +1,146 @@ +import krakenex +import logging +import os +import scipy +import time + +from pykrakenapi import KrakenAPI + +class KrakenBot(KrakenAPI): + class KrakenOrder(): + def __init__(self, ordertype, type, pair, userref=None, volume=None, price=None, price2=None, trigger=None, leverage=None, + oflags=None, timeinforce=None, starttm=0, expiretm=0, close_ordertype=None, close_price=None, close_price2=None, + deadline=None, validate=True, otp=None): + self.ordertype = ordertype + self.type = type + self.pair = pair + self.userref = userref + self.volume = volume + self.price = price + self.price2 = price2 + self.trigger = trigger + self.leverage = leverage + self.oflags = oflags + self.timeinforce = timeinforce + self.starttm = starttm + self.expiretm = expiretm + self.close_ordertype = close_ordertype + self.close_price = close_price + self.close_price2 = close_price2 + self.deadline = deadline + self.validate = validate + self.otp = otp + + def __str__(self): + return str({ + 'ordertype': self.ordertype, + 'type': self.type, + 'pair': self.pair, + 'userref': self.userref, + 'volume': self.volume, + 'price': self.price, + 'price2': self.price2, + 'trigger': self.trigger, + 'leverage': self.leverage, + 'oflags': self.oflags, + 'timeinforce': self.timeinforce, + 'starttm': self.starttm, + 'expiretm': self.expiretm, + 'close_ordertype': self.close_ordertype, + 'close_price': self.close_price, + 'close_price2': self.close_price2, + 'deadline': self.deadline, + 'validate': self.validate, + 'otp': self.otp + }) + + def __init__(self, token, secret, tier, investment_count, investment_volume): + self.investment_count = investment_count + self.investment_volume = investment_volume + self.log = logging.getLogger(__name__) + self.log.setLevel( + getattr(logging, os.getenv('LOG_LEVEL', 'INFO'), logging.INFO) + ) + super().__init__(krakenex.API(token, secret), tier=tier) + self.update() + + def _delay_factor(self): + self.log.debug(f"_delay_factor") + time.sleep(self.factor) + self.log.debug(f"Delayed {self.factor} seconds") + + def _update_tradable_asset_pairs(self, info=None, pair=None): + self.log.debug(f"update_tradable_asset_pairs: info={info}, pair={pair}") + self._delay_factor() + self.tradable_asset_pairs = self.get_tradable_asset_pairs(info, pair) + + def _update_account_balance(self, otp=None): + self.log.debug(f"update_account_balance: otp={otp}") + self._delay_factor() + self.account_balance = self.get_account_balance(otp) + self.log.debug(self.account_balance) + + def _update_trade_balance(self, aclass='currency', asset='ZUSD', otp=None): + self.log.debug(f"update_trade_balance: aclass={aclass}, asset={asset}, otp={otp}") + self._delay_factor() + self.trade_balance = self.get_trade_balance(aclass, asset, otp) + self.log.debug(self.trade_balance) + + def _get_ohlc_data(self, pair, interval=1, since=None, ascending=False): + self.log.debug(f"get_ohlc_data: pair={pair}, interval={interval}, since={since}, ascending={ascending}") + self._delay_factor() + return self.get_ohlc_data(pair, interval, since, ascending) + + def _add_standard_order(self, order): + self.log.debug(f"add_standard_order: order={order}") + self._delay_factor() + order = self.add_standard_order( + order.ordertype, order.type, order.pair, order.userref, order.volume, order.price, order.price2, order.trigger, + order.leverage, order.oflags, order.timeinforce, order.starttm, order.expiretm, order.close_ordertype, + order.close_price, order.close_price2, order.deadline, order.validate, order.otp) + self.log.info(order) + self.update() + + def update(self): + self.log.debug(f"update") + self._update_tradable_asset_pairs() + self._update_account_balance() + self._update_trade_balance() + + def trade(self, model): + self.log.debug(f"trade") + model.update( + self.tradable_asset_pairs, + { + asset_pair: self._get_ohlc_data( + asset_pair, model.interval, model.since(), model.ascending + ) for asset_pair in self.tradable_asset_pairs.index + } + ) + for asset in self.account_balance[(self.account_balance.index != 'ZUSD') & (self.account_balance.vol > 0)].index: + market = self.tradable_asset_pairs[ + (self.tradable_asset_pairs.base == asset) & + (self.tradable_asset_pairs.quote == 'ZUSD') + ].iloc[0] + if market.altname not in model.analysis.index: + self._add_standard_order(self.KrakenOrder( + ordertype = 'market', + type = 'sell', + pair = market.altname, + volume = self.account_balance.loc[asset].vol, + validate = False + )) + while ((len(self.account_balance[self.account_balance.vol > 0]) - 1 < self.investment_count) & + (self.account_balance.loc['ZUSD'].vol > self.investment_volume)): + for market in model.analysis.index: + if self.tradable_asset_pairs.loc[market].base not in self.account_balance[self.account_balance.vol > 0].index: + ohlc, last = self._get_ohlc_data(market) + volume = self.investment_volume / ohlc.iloc[-1].close + self._add_standard_order(self.KrakenOrder( + ordertype = 'market', + type = 'buy', + pair = market, + volume = volume, + validate = False + )) + break diff --git a/kraken_bot/KrakenOrder.py b/kraken_bot/KrakenOrder.py new file mode 100644 index 0000000..3817f17 --- /dev/null +++ b/kraken_bot/KrakenOrder.py @@ -0,0 +1,46 @@ +class KrakenOrder(): + def __init__(self, ordertype, type, pair, userref=None, volume=None, price=None, price2=None, trigger=None, leverage=None, + oflags=None, timeinforce=None, starttm=0, expiretm=0, closeordertype=None, close_price=None, close_price2=None, + deadline=None, validate=True, otp=None): + self.ordertype = ordertype + self.type = type + self.pair = pair + self.userref = userref + self.volume = volume + self.price = price + self.price2 = price2 + self.trigger = trigger + self.leverage = leverage + self.oflags = oflags + self.timeinforce = timeinforce + self.starttm = starttm + self.expiretm = expiretm + self.closeordertype = closeordertype + self.close_price = close_price + self.close_price2 = clowe_price2 + self.deadline = deadline + self.validate = validate + self.otp = otp + + def __str__(self): + return str({ + 'ordertype': self.ordertype + 'type': self.type + 'pair': self.pair + 'userref': self.userref + 'volume': self.volume + 'price': self.price + 'price2': self.price2 + 'trigger': self.trigger + 'leverage': self.leverage + 'oflags': self.oflags + 'timeinforce': self.timeinforce + 'starttm': self.starttm + 'expiretm': self.expiretm + 'closeordertype': self.closeordertype + 'close_price': self.close_price + 'close_price2': self.clowe_price2 + 'deadline': self.deadline + 'validate': self.validate + 'otp': self.otp + }) diff --git a/kraken_bot/LinearRegression.py b/kraken_bot/LinearRegression.py new file mode 100644 index 0000000..a135493 --- /dev/null +++ b/kraken_bot/LinearRegression.py @@ -0,0 +1,58 @@ +import logging +import os +import pandas + +from kraken_bot.Model import Model +from scipy.stats import linregress + +class LinearRegression(Model): + def __init__(self, r_value_target): + self.r_value_target = r_value_target + self.log = logging.getLogger(__name__) + self.log.setLevel( + getattr(logging, os.getenv('LOG_LEVEL', 'INFO'), logging.INFO) + ) + self.analysis = pandas.DataFrame({}) + super().__init__( + interval = 1, + ascending = False + ) + + def _linear_regression_for_ohlc_data(self, ohlc_data): + self.log.debug(f"_linear_regression_for_ohlc_data: ohlc_data=...") + return linregress(ohlc_data['time'].astype(float), ohlc_data['close'].astype(float)) + + def _filter_asset_pairs(self, tradable_asset_pairs): + self.log.debug(f"_filter_asset_pairs: tradable_asset_pairs=...") + return tradable_asset_pairs[ + (tradable_asset_pairs['quote'] == 'ZUSD') & + (tradable_asset_pairs['status'] == 'online') + ] + + def _filter_regressions(self, regressions): + self.log.debug(f"_filter_regressions: regressions=...") + regressions.columns = ['slope', 'intercept', 'r_value', 'p_value', 'stderr'] + return regressions[ + (regressions.r_value >= self.r_value_target) & + (regressions.slope > 0) + ] + + def _order_regressions(self, regressions): + self.log.debug(f"_order_regressions: regressions=...") + return regressions.sort_values(by='slope', ascending=False) + + def since(self): + return None + + def update(self, tradable_asset_pairs, ohlc_data): + self.log.debug(f"update: tradable_asset_pairs=..., ohlc_data=...") + self.analysis = self._order_regressions( + self._filter_regressions( + pandas.DataFrame({ + asset_pair: self._linear_regression_for_ohlc_data( + ohlc_data[asset_pair][0] + ) for asset_pair in self._filter_asset_pairs(tradable_asset_pairs).index + }).transpose() + )) + self.log.info(f"Completed linear regression analysis of OHLC market data") + self.log.info(self.analysis) diff --git a/kraken_bot/Model.py b/kraken_bot/Model.py new file mode 100644 index 0000000..d1016ea --- /dev/null +++ b/kraken_bot/Model.py @@ -0,0 +1,14 @@ +from abc import ABC, abstractmethod + +class Model(ABC): + def __init__(self, interval, ascending): + self.interval = interval + self.ascending = ascending + + @abstractmethod + def since(): + pass + + @abstractmethod + def update(self, tradable_asset_pairs, ohlc_data, account_balance, trade_balance): + pass diff --git a/kraken_bot/__init__.py b/kraken_bot/__init__.py new file mode 100644 index 0000000..e69de29