From b16485e29ef65ddc0e77bb39701a4e9bb7c91fbf Mon Sep 17 00:00:00 2001 From: Eric Meehan Date: Mon, 27 Jan 2025 22:07:52 -0500 Subject: [PATCH 1/4] Initial attempt to add matrix-nio to a logging module --- README.md | 3 +++ kraken_bot/MatrixLogger.py | 55 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 58 insertions(+) create mode 100644 kraken_bot/MatrixLogger.py diff --git a/README.md b/README.md index e69de29..925dbbb 100644 --- a/README.md +++ b/README.md @@ -0,0 +1,3 @@ +# Kraken bot + +A Python bot for day-trading cryptocurrencies on the Kraken exchange. diff --git a/kraken_bot/MatrixLogger.py b/kraken_bot/MatrixLogger.py new file mode 100644 index 0000000..45830fc --- /dev/null +++ b/kraken_bot/MatrixLogger.py @@ -0,0 +1,55 @@ +import asyncio +import logging + +from nio import AsyncClient, MatrixRoom, RoomMessageText + +class MatrixHandler(logging.Handler): + def __init__(self, url, user_id, user_password, room_id): + super().__init__() + self.client = AsyncClient(url, user_id) + self.room_id = room_id + self.client.add_event_callback(self.message_callback, RoomMessageText) + await self.client.login(user_password) + + def message_callback(self, room: MatrixRoom, event: RoomMessageText) -> None: + return + + def emit(self, record): + self.client.room_send( + self.room_id, + message_type='m.room.message', + content={'msgtype': 'm.text', 'body': record} + ) + +class MatrixLogger: + def __init__(self, name): + self.logger = logging.getLogger(name) + self.logger.setLevel(logging.DEBUG) + + console_handler = logging.StreamHandler() + console_handler.setLevel(logging.DEBUG) + console_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')) + + + matrix_handler = MatrixHandler(room_id) + matrix_handler.setLevel(logging.INFO) + matrix_handler.setFormatter(logging.Formatter('%(levelname)s - %(message)s')) + + self.logger.addHandler(console_handler) + self.logger.addHandler(matrix_handler) + + def debug(self, message): + self.logger.debug(message) + + def info(self, message): + self.logger.info(message) + + def warning(self, message): + self.logger.warning(message) + + def error(self, message): + self.logger.error(message) + + def critical(self, message): + self.logger.critical(message) + -- 2.45.2 From 40d80a6e835cb46577e8853a6b4d1b5cc25ff22e Mon Sep 17 00:00:00 2001 From: Eric Meehan Date: Tue, 4 Feb 2025 23:01:42 -0500 Subject: [PATCH 2/4] Matrix logger implementation --- .env_example | 5 ++++ app.py | 23 ++++-------------- kraken_bot/KrakenBot.py | 15 +++++------- kraken_bot/LinearRegression.py | 15 ++++-------- kraken_bot/MatrixLogger.py | 43 ++++++++++++++++++---------------- kraken_bot/Model.py | 3 +++ kraken_bot/MyStrategy.py | 4 ++-- kraken_bot/TradingStrategy.py | 12 ++++++---- 8 files changed, 56 insertions(+), 64 deletions(-) diff --git a/.env_example b/.env_example index b55dda3..69f6742 100644 --- a/.env_example +++ b/.env_example @@ -4,3 +4,8 @@ KRAKEN_API_SEC = R_VALUE_TARGET = INVESTMENT_COUNT = INVESTMENT_VOLUME = +LOG_LEVEL = +MATRIX_HOMESERVER_ADDRESS = +MATRIX_USER_ID = +MATRIX_USER_PASSWORD = +MATRIX_ROOM_ID = diff --git a/app.py b/app.py index 7366996..c5a35ba 100644 --- a/app.py +++ b/app.py @@ -1,30 +1,15 @@ -import logging -import os +from dotenv import load_dotenv from kraken_bot.KrakenBot import KrakenBot from kraken_bot.LinearRegression import LinearRegression -from kraken_bot.HighLowCutoff import HighLowCutoff -from dotenv import load_dotenv +from kraken_bot.MyStrategy import MyStrategy load_dotenv() -logger = logging.getLogger(__name__) - -model = LinearRegression( - r_value_target = float(os.getenv('R_VALUE_TARGET')) - ) -strategy = HighLowCutoff( - investment_count = int(os.getenv('INVESTMENT_COUNT')), - investment_volume = float(os.getenv('INVESTMENT_VOLUME')), - target_gain = float(os.getenv('TARGET_GAIN')), - max_loss = float(os.getenv('MAX_LOSS')) - ) bot = KrakenBot( - token = os.getenv('KRAKEN_API_TOKEN'), - secret = os.getenv('KRAKEN_API_SEC'), tier = 'Pro', - model = model, - trading_strategy = strategy + model = LinearRegression(), + trading_strategy = MyStrategy() ) if __name__ == '__main__': diff --git a/kraken_bot/KrakenBot.py b/kraken_bot/KrakenBot.py index de6ad0b..441976f 100644 --- a/kraken_bot/KrakenBot.py +++ b/kraken_bot/KrakenBot.py @@ -1,5 +1,4 @@ import krakenex -import logging import os import scipy import sys @@ -7,16 +6,14 @@ import time from pykrakenapi import KrakenAPI +from kraken_bot.MatrixLogger import MatrixLogger + class KrakenBot(KrakenAPI): - def __init__(self, token, secret, tier, model, trading_strategy): + def __init__(self, tier, model, trading_strategy): self.model = model self.trading_strategy = trading_strategy - self.log = logging.getLogger(__name__) - self.log.setLevel( - getattr(logging, os.getenv('LOG_LEVEL', 'INFO'), logging.INFO) - ) - self.log.addHandler(logging.StreamHandler(sys.stdout)) - super().__init__(krakenex.API(token, secret), tier=tier) + self.log = MatrixLogger(self.__class__.__name__) + super().__init__(krakenex.API(os.getenv('KRAKEN_API_TOKEN'), os.getenv('KRAKEN_API_SEC')), tier=tier) self.update(full=True) def _delay_factor(self): @@ -75,7 +72,7 @@ class KrakenBot(KrakenAPI): 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.log.info(f"Added order to %{order.type} %{order.volume} on {order.pair}") self.update() def update(self, full=False): diff --git a/kraken_bot/LinearRegression.py b/kraken_bot/LinearRegression.py index a135493..2953d18 100644 --- a/kraken_bot/LinearRegression.py +++ b/kraken_bot/LinearRegression.py @@ -1,17 +1,13 @@ -import logging import os import pandas -from kraken_bot.Model import Model from scipy.stats import linregress +from kraken_bot.Model import Model + 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) - ) + def __init__(self): + self.r_value_target = float(os.getenv('R_VALUE_TARGET')) self.analysis = pandas.DataFrame({}) super().__init__( interval = 1, @@ -54,5 +50,4 @@ class LinearRegression(Model): ) 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) + self.log.debug(self.analysis) diff --git a/kraken_bot/MatrixLogger.py b/kraken_bot/MatrixLogger.py index 45830fc..62ee7cf 100644 --- a/kraken_bot/MatrixLogger.py +++ b/kraken_bot/MatrixLogger.py @@ -1,15 +1,19 @@ import asyncio import logging +import os -from nio import AsyncClient, MatrixRoom, RoomMessageText +from nio import HttpClient, MatrixRoom, RoomMessageText class MatrixHandler(logging.Handler): - def __init__(self, url, user_id, user_password, room_id): + def __init__(self): super().__init__() - self.client = AsyncClient(url, user_id) - self.room_id = room_id + self.homeserver_address = os.getenv('MATRIX_HOMESERVER_ADDRESS'), + self.user_id = os.getenv('MATRIX_USER_ID'), + self.user_password = os.getenv('MATRIX_USER_PASSWORD'), + self.room_id = os.getenv('MATRIX_ROOM_ID') + self.client = HttpClient(self.address, self.user_id) self.client.add_event_callback(self.message_callback, RoomMessageText) - await self.client.login(user_password) + self.client.login(self.user_password) def message_callback(self, room: MatrixRoom, event: RoomMessageText) -> None: return @@ -23,33 +27,32 @@ class MatrixHandler(logging.Handler): class MatrixLogger: def __init__(self, name): - self.logger = logging.getLogger(name) - self.logger.setLevel(logging.DEBUG) + self.log = logging.getLogger(name) + self.log.setLevel(os.getenv('LOG_LEVEL', 'INFO')) console_handler = logging.StreamHandler() - console_handler.setLevel(logging.DEBUG) - console_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(message)s')) + console_handler.setLevel(os.getenv('LOG_LEVEL', 'INFO')) + console_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %{name}s - %(message)s')) - - matrix_handler = MatrixHandler(room_id) - matrix_handler.setLevel(logging.INFO) - matrix_handler.setFormatter(logging.Formatter('%(levelname)s - %(message)s')) + matrix_handler = MatrixHandler() + matrix_handler.setLevel(os.getenv('LOG_LEVEL', 'INFO')) + matrix_handler.setFormatter(logging.Formatter('%(levelname)s - %{name}s - %(message)s')) - self.logger.addHandler(console_handler) - self.logger.addHandler(matrix_handler) + self.log.addHandler(console_handler) + self.log.addHandler(matrix_handler) def debug(self, message): - self.logger.debug(message) + self.log.debug(message) def info(self, message): - self.logger.info(message) + self.log.info(message) def warning(self, message): - self.logger.warning(message) + self.log.warning(message) def error(self, message): - self.logger.error(message) + self.log.error(message) def critical(self, message): - self.logger.critical(message) + self.log.critical(message) diff --git a/kraken_bot/Model.py b/kraken_bot/Model.py index d1016ea..880cfb2 100644 --- a/kraken_bot/Model.py +++ b/kraken_bot/Model.py @@ -1,9 +1,12 @@ from abc import ABC, abstractmethod +from kraken_bot.MatrixLogger import MatrixLogger + class Model(ABC): def __init__(self, interval, ascending): self.interval = interval self.ascending = ascending + self.log = MatrixLogger(self.__class__.__name__) @abstractmethod def since(): diff --git a/kraken_bot/MyStrategy.py b/kraken_bot/MyStrategy.py index 059f712..f5684d9 100644 --- a/kraken_bot/MyStrategy.py +++ b/kraken_bot/MyStrategy.py @@ -2,8 +2,8 @@ from kraken_bot.TradingStrategy import TradingStrategy from kraken_bot.KrakenOrder import KrakenOrder class MyStrategy(TradingStrategy): - def __init__(self, investment_count, investment_volume): - super().__init__(investment_count, investment_volume) + def __init__(self): + super().__init__() def update(self, tradable_asset_pairs, ohlc_data, market_analysis, account_balance): self.orders = [] diff --git a/kraken_bot/TradingStrategy.py b/kraken_bot/TradingStrategy.py index 1b48089..321e240 100644 --- a/kraken_bot/TradingStrategy.py +++ b/kraken_bot/TradingStrategy.py @@ -1,10 +1,14 @@ +import os + from abc import ABC, abstractmethod -from kraken_bot.KrakenOrder import KrakenOrder + +from kraken_bot.MatrixLogger import MatrixLogger class TradingStrategy(ABC): - def __init__(self, investment_count, investment_volume): - self.investment_count = investment_count - self.investment_volume = investment_volume + def __init__(self): + self.investment_count = int(os.getenv('INVESTMENT_COUNT')), + self.investment_volume = float(os.getenv('INVESTMENT_VOLUME')), + self.log = MatrixLogger(self.__class__.__name__) self.orders = [] @abstractmethod -- 2.45.2 From ce9b6118432f8b7821a97763bba9439723db5335 Mon Sep 17 00:00:00 2001 From: Eric Meehan Date: Sat, 8 Feb 2025 11:28:25 -0500 Subject: [PATCH 3/4] Tweaks --- kraken_bot/MatrixLogger.py | 1 - requirements.txt | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/kraken_bot/MatrixLogger.py b/kraken_bot/MatrixLogger.py index 62ee7cf..83fbb03 100644 --- a/kraken_bot/MatrixLogger.py +++ b/kraken_bot/MatrixLogger.py @@ -1,4 +1,3 @@ -import asyncio import logging import os diff --git a/requirements.txt b/requirements.txt index 9fc2a67..ec05604 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ krakenex +nio pykrakenapi python-dotenv backtrader -- 2.45.2 From 0ffd06e1627c17cc970e44cce538610ab5d69014 Mon Sep 17 00:00:00 2001 From: Eric Meehan Date: Sun, 9 Feb 2025 10:43:45 -0500 Subject: [PATCH 4/4] Matrix logging is functional --- app.py | 6 ++++-- kraken_bot/KrakenBot.py | 2 +- kraken_bot/MatrixLogger.py | 29 +++++++++++++++++------------ kraken_bot/TradingStrategy.py | 4 ++-- requirements.txt | 3 ++- 5 files changed, 26 insertions(+), 18 deletions(-) diff --git a/app.py b/app.py index c5a35ba..62184a3 100644 --- a/app.py +++ b/app.py @@ -1,3 +1,5 @@ +import asyncio + from dotenv import load_dotenv from kraken_bot.KrakenBot import KrakenBot @@ -13,5 +15,5 @@ bot = KrakenBot( ) if __name__ == '__main__': - bot.update(full=True) - bot.execute() + asyncio.run(bot.update(full=True)) + asyncio.run(bot.execute()) diff --git a/kraken_bot/KrakenBot.py b/kraken_bot/KrakenBot.py index 441976f..e45ef0e 100644 --- a/kraken_bot/KrakenBot.py +++ b/kraken_bot/KrakenBot.py @@ -12,7 +12,7 @@ class KrakenBot(KrakenAPI): def __init__(self, tier, model, trading_strategy): self.model = model self.trading_strategy = trading_strategy - self.log = MatrixLogger(self.__class__.__name__) + self.log = MatrixLogger(str(self.__class__.__name__)) super().__init__(krakenex.API(os.getenv('KRAKEN_API_TOKEN'), os.getenv('KRAKEN_API_SEC')), tier=tier) self.update(full=True) diff --git a/kraken_bot/MatrixLogger.py b/kraken_bot/MatrixLogger.py index 83fbb03..cdc0a0d 100644 --- a/kraken_bot/MatrixLogger.py +++ b/kraken_bot/MatrixLogger.py @@ -1,29 +1,34 @@ +import asyncio import logging import os -from nio import HttpClient, MatrixRoom, RoomMessageText +from nio import AsyncClient, MatrixRoom, RoomMessageText class MatrixHandler(logging.Handler): def __init__(self): super().__init__() - self.homeserver_address = os.getenv('MATRIX_HOMESERVER_ADDRESS'), - self.user_id = os.getenv('MATRIX_USER_ID'), - self.user_password = os.getenv('MATRIX_USER_PASSWORD'), + self.address = os.getenv('MATRIX_HOMESERVER_ADDRESS') + self.user_id = os.getenv('MATRIX_USER_ID') + self.user_password = os.getenv('MATRIX_USER_PASSWORD') self.room_id = os.getenv('MATRIX_ROOM_ID') - self.client = HttpClient(self.address, self.user_id) - self.client.add_event_callback(self.message_callback, RoomMessageText) - self.client.login(self.user_password) + self.device_id = os.getenv('MATRIX_DEVICE_ID') def message_callback(self, room: MatrixRoom, event: RoomMessageText) -> None: return - def emit(self, record): - self.client.room_send( + async def send_to_matrix(self, record): + client = AsyncClient(self.address, self.user_id) + await client.login(self.user_password, self.device_id) + client.add_event_callback(self.message_callback, RoomMessageText) + await client.room_send( self.room_id, message_type='m.room.message', - content={'msgtype': 'm.text', 'body': record} + content={'msgtype': 'm.text', 'body': self.formatter.format(record)} ) + def emit(self, record): + asyncio.run(self.send_to_matrix(record)) + class MatrixLogger: def __init__(self, name): self.log = logging.getLogger(name) @@ -31,11 +36,11 @@ class MatrixLogger: console_handler = logging.StreamHandler() console_handler.setLevel(os.getenv('LOG_LEVEL', 'INFO')) - console_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %{name}s - %(message)s')) + console_handler.setFormatter(logging.Formatter('%(asctime)s - %(levelname)s - %(name)s - %(message)s')) matrix_handler = MatrixHandler() matrix_handler.setLevel(os.getenv('LOG_LEVEL', 'INFO')) - matrix_handler.setFormatter(logging.Formatter('%(levelname)s - %{name}s - %(message)s')) + matrix_handler.setFormatter(logging.Formatter('%(levelname)s - %(name)s - %(message)s')) self.log.addHandler(console_handler) self.log.addHandler(matrix_handler) diff --git a/kraken_bot/TradingStrategy.py b/kraken_bot/TradingStrategy.py index 321e240..3adfc06 100644 --- a/kraken_bot/TradingStrategy.py +++ b/kraken_bot/TradingStrategy.py @@ -6,8 +6,8 @@ from kraken_bot.MatrixLogger import MatrixLogger class TradingStrategy(ABC): def __init__(self): - self.investment_count = int(os.getenv('INVESTMENT_COUNT')), - self.investment_volume = float(os.getenv('INVESTMENT_VOLUME')), + self.investment_count = int(os.getenv('INVESTMENT_COUNT')) + self.investment_volume = float(os.getenv('INVESTMENT_VOLUME')) self.log = MatrixLogger(self.__class__.__name__) self.orders = [] diff --git a/requirements.txt b/requirements.txt index ec05604..8fba69d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,6 +1,7 @@ krakenex -nio +matrix-nio pykrakenapi python-dotenv +scipy backtrader matplotlib -- 2.45.2