Compare commits
24 Commits
43f5ae3c53
...
develop
| Author | SHA1 | Date | |
|---|---|---|---|
| d46fe926d8 | |||
| 255dc03e57 | |||
| f2df48bced | |||
| 6bd6096013 | |||
| f4bfb5d584 | |||
| 9a5346cfe4 | |||
| 69b659d2cc | |||
| 8f72269f3e | |||
| 0a4cb4b673 | |||
| 9bb285f71e | |||
| 573f58fd5d | |||
| 2f0a4ea2e6 | |||
| 905f21eb55 | |||
| 59748d7cc8 | |||
| 42abda02f3 | |||
| 4e7040f6a3 | |||
| cecc72b595 | |||
| 60b6682b1c | |||
| f46fd31f34 | |||
| 141ea4e622 | |||
| 496477a05b | |||
| 1c4c75367d | |||
| e2034cfa65 | |||
| 1ef6cc4857 |
@@ -4,3 +4,9 @@ KRAKEN_API_SEC =
|
|||||||
R_VALUE_TARGET =
|
R_VALUE_TARGET =
|
||||||
INVESTMENT_COUNT =
|
INVESTMENT_COUNT =
|
||||||
INVESTMENT_VOLUME =
|
INVESTMENT_VOLUME =
|
||||||
|
LOG_LEVEL =
|
||||||
|
MATRIX_HOMESERVER_ADDRESS =
|
||||||
|
MATRIX_USER_ID =
|
||||||
|
MATRIX_USER_PASSWORD =
|
||||||
|
MATRIX_ROOM_ID =
|
||||||
|
MATRIX_DEVICE_ID =
|
||||||
|
|||||||
32
.gitea/workflows/ci.yaml
Normal file
32
.gitea/workflows/ci.yaml
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
name: ci
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- develop
|
||||||
|
merge:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
with:
|
||||||
|
driver: kubernetes
|
||||||
|
driver-opts: |
|
||||||
|
namespace=gitea
|
||||||
|
|
||||||
|
- name: Login to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
username: ${{ vars.DOCKERHUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Build and push
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
push: true
|
||||||
|
tags: ericomeehan/kraken-bot:latest
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Kraken bot
|
||||||
|
|
||||||
|
A Python bot for day-trading cryptocurrencies on the Kraken exchange.
|
||||||
|
|||||||
49
app.py
49
app.py
@@ -1,46 +1,19 @@
|
|||||||
import backtrader
|
import asyncio
|
||||||
import krakenex
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
|
|
||||||
from dotenv import load_dotenv
|
from dotenv import load_dotenv
|
||||||
from pykrakenapi import KrakenAPI
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
from kraken_bot.MovingAverageCrossOver import MA_CrossOver
|
from kraken_bot.KrakenBot import KrakenBot
|
||||||
|
from kraken_bot.LinearRegression import LinearRegression
|
||||||
|
from kraken_bot.MyStrategy import MyStrategy
|
||||||
|
|
||||||
load_dotenv()
|
load_dotenv()
|
||||||
token = os.getenv('KRAKEN_API_TOKEN')
|
|
||||||
secret = os.getenv('KRAKEN_API_SEC')
|
|
||||||
tier = os.getenv('KRAKEN_API_TIER', 'Pro')
|
|
||||||
asset_pair = os.getenv('KRAKEN_TRADABLE_ASSET_PAIR', 'XBTUSD')
|
|
||||||
interval = int(os.getenv('KRAKEN_DATA_INTERVAL', 60))
|
|
||||||
percents = float(os.getenv('CEREBRO_SIZER_PERCENTS', 50))
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
bot = KrakenBot(
|
||||||
kraken = KrakenAPI(krakenex.API(token, secret), tier)
|
tier = 'Pro',
|
||||||
cerebro = backtrader.Cerebro()
|
model = LinearRegression(),
|
||||||
|
trading_strategy = MyStrategy()
|
||||||
market = kraken.get_tradable_asset_pairs(pair=asset_pair).iloc[0]
|
)
|
||||||
sleep(kraken.factor)
|
|
||||||
cerebro.broker.setcommission(commission=market.fees[0][1]/100)
|
|
||||||
|
|
||||||
ohlc, last = kraken.get_ohlc_data(asset_pair, interval=interval, since=None, ascending=True)
|
|
||||||
sleep(kraken.factor)
|
|
||||||
cerebro.adddata(backtrader.feeds.PandasData(dataname=ohlc))
|
|
||||||
|
|
||||||
account_balance = kraken.get_trade_balance(asset='ZUSD').loc['eb'].ZUSD
|
|
||||||
sleep(kraken.factor)
|
|
||||||
cerebro.broker.setcash(account_balance)
|
|
||||||
cerebro.addsizer(backtrader.sizers.PercentSizer, percents=percents)
|
|
||||||
|
|
||||||
cerebro.addstrategy(MA_CrossOver)
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
start_balance = cerebro.broker.getvalue()
|
asyncio.run(bot.update(full=True))
|
||||||
print('Starting Portfolio Value: %.6f' % start_balance)
|
asyncio.run(bot.execute())
|
||||||
x = cerebro.run()
|
|
||||||
end_balance = cerebro.broker.getvalue()
|
|
||||||
print('Final Portfolio Value: %.6f' % end_balance)
|
|
||||||
print('Percent change: %.6f' % (((end_balance - start_balance)/start_balance) * 100))
|
|
||||||
cerebro.plot(style='candlestick')
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
import krakenex
|
import krakenex
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
import scipy
|
import scipy
|
||||||
import sys
|
import sys
|
||||||
@@ -7,16 +6,14 @@ import time
|
|||||||
|
|
||||||
from pykrakenapi import KrakenAPI
|
from pykrakenapi import KrakenAPI
|
||||||
|
|
||||||
|
from kraken_bot.MatrixLogger import MatrixLogger
|
||||||
|
|
||||||
class KrakenBot(KrakenAPI):
|
class KrakenBot(KrakenAPI):
|
||||||
def __init__(self, token, secret, tier, model, trading_strategy):
|
def __init__(self, tier, model, trading_strategy):
|
||||||
self.model = model
|
self.model = model
|
||||||
self.trading_strategy = trading_strategy
|
self.trading_strategy = trading_strategy
|
||||||
self.log = logging.getLogger(__name__)
|
self.log = MatrixLogger(str(self.__class__.__name__))
|
||||||
self.log.setLevel(
|
super().__init__(krakenex.API(os.getenv('KRAKEN_API_TOKEN'), os.getenv('KRAKEN_API_SEC')), tier=tier)
|
||||||
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.update(full=True)
|
self.update(full=True)
|
||||||
|
|
||||||
def _delay_factor(self):
|
def _delay_factor(self):
|
||||||
@@ -75,5 +72,30 @@ class KrakenBot(KrakenAPI):
|
|||||||
order.ordertype, order.type, order.pair, order.userref, order.volume, order.price, order.price2, order.trigger,
|
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.leverage, order.oflags, order.timeinforce, order.starttm, order.expiretm, order.close_ordertype,
|
||||||
order.close_price, order.close_price2, order.deadline, order.validate, order.otp)
|
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()
|
self.update()
|
||||||
|
|
||||||
|
def update(self, full=False):
|
||||||
|
self.log.debug(f"update: full={full}")
|
||||||
|
self._update_tradable_asset_pairs()
|
||||||
|
self._filter_tradable_asset_pairs()
|
||||||
|
self._update_account_balance()
|
||||||
|
self._filter_account_balance()
|
||||||
|
self._update_trade_balance()
|
||||||
|
if full:
|
||||||
|
self._update_ohlc_data()
|
||||||
|
self.model.update(
|
||||||
|
self.tradable_asset_pairs,
|
||||||
|
self.ohlc_data
|
||||||
|
)
|
||||||
|
self.trading_strategy.update(
|
||||||
|
self.tradable_asset_pairs,
|
||||||
|
self.ohlc_data,
|
||||||
|
self.model.analysis,
|
||||||
|
self.account_balance
|
||||||
|
)
|
||||||
|
|
||||||
|
def execute(self):
|
||||||
|
self.log.debug(f"execute")
|
||||||
|
for order in self.trading_strategy.orders:
|
||||||
|
self._add_standard_order(order)
|
||||||
|
|||||||
@@ -1,17 +1,13 @@
|
|||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
import pandas
|
import pandas
|
||||||
|
|
||||||
from kraken_bot.Model import Model
|
|
||||||
from scipy.stats import linregress
|
from scipy.stats import linregress
|
||||||
|
|
||||||
|
from kraken_bot.Model import Model
|
||||||
|
|
||||||
class LinearRegression(Model):
|
class LinearRegression(Model):
|
||||||
def __init__(self, r_value_target):
|
def __init__(self):
|
||||||
self.r_value_target = r_value_target
|
self.r_value_target = float(os.getenv('R_VALUE_TARGET'))
|
||||||
self.log = logging.getLogger(__name__)
|
|
||||||
self.log.setLevel(
|
|
||||||
getattr(logging, os.getenv('LOG_LEVEL', 'INFO'), logging.INFO)
|
|
||||||
)
|
|
||||||
self.analysis = pandas.DataFrame({})
|
self.analysis = pandas.DataFrame({})
|
||||||
super().__init__(
|
super().__init__(
|
||||||
interval = 1,
|
interval = 1,
|
||||||
@@ -54,5 +50,4 @@ class LinearRegression(Model):
|
|||||||
) for asset_pair in self._filter_asset_pairs(tradable_asset_pairs).index
|
) for asset_pair in self._filter_asset_pairs(tradable_asset_pairs).index
|
||||||
}).transpose()
|
}).transpose()
|
||||||
))
|
))
|
||||||
self.log.info(f"Completed linear regression analysis of OHLC market data")
|
self.log.debug(self.analysis)
|
||||||
self.log.info(self.analysis)
|
|
||||||
|
|||||||
62
kraken_bot/MatrixLogger.py
Normal file
62
kraken_bot/MatrixLogger.py
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
import asyncio
|
||||||
|
import logging
|
||||||
|
import os
|
||||||
|
|
||||||
|
from nio import AsyncClient, MatrixRoom, RoomMessageText
|
||||||
|
|
||||||
|
class MatrixHandler(logging.Handler):
|
||||||
|
def __init__(self):
|
||||||
|
super().__init__()
|
||||||
|
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.device_id = os.getenv('MATRIX_DEVICE_ID')
|
||||||
|
|
||||||
|
def message_callback(self, room: MatrixRoom, event: RoomMessageText) -> None:
|
||||||
|
return
|
||||||
|
|
||||||
|
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': 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)
|
||||||
|
self.log.setLevel(os.getenv('LOG_LEVEL', 'INFO'))
|
||||||
|
|
||||||
|
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'))
|
||||||
|
|
||||||
|
matrix_handler = MatrixHandler()
|
||||||
|
matrix_handler.setLevel(os.getenv('LOG_LEVEL', 'INFO'))
|
||||||
|
matrix_handler.setFormatter(logging.Formatter('%(levelname)s - %(name)s - %(message)s'))
|
||||||
|
|
||||||
|
self.log.addHandler(console_handler)
|
||||||
|
self.log.addHandler(matrix_handler)
|
||||||
|
|
||||||
|
def debug(self, message):
|
||||||
|
self.log.debug(message)
|
||||||
|
|
||||||
|
def info(self, message):
|
||||||
|
self.log.info(message)
|
||||||
|
|
||||||
|
def warning(self, message):
|
||||||
|
self.log.warning(message)
|
||||||
|
|
||||||
|
def error(self, message):
|
||||||
|
self.log.error(message)
|
||||||
|
|
||||||
|
def critical(self, message):
|
||||||
|
self.log.critical(message)
|
||||||
|
|
||||||
@@ -1,9 +1,12 @@
|
|||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
|
|
||||||
|
from kraken_bot.MatrixLogger import MatrixLogger
|
||||||
|
|
||||||
class Model(ABC):
|
class Model(ABC):
|
||||||
def __init__(self, interval, ascending):
|
def __init__(self, interval, ascending):
|
||||||
self.interval = interval
|
self.interval = interval
|
||||||
self.ascending = ascending
|
self.ascending = ascending
|
||||||
|
self.log = MatrixLogger(self.__class__.__name__)
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def since():
|
def since():
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ from kraken_bot.TradingStrategy import TradingStrategy
|
|||||||
from kraken_bot.KrakenOrder import KrakenOrder
|
from kraken_bot.KrakenOrder import KrakenOrder
|
||||||
|
|
||||||
class MyStrategy(TradingStrategy):
|
class MyStrategy(TradingStrategy):
|
||||||
def __init__(self, investment_count, investment_volume):
|
def __init__(self):
|
||||||
super().__init__(investment_count, investment_volume)
|
super().__init__()
|
||||||
|
|
||||||
def update(self, tradable_asset_pairs, ohlc_data, market_analysis, account_balance):
|
def update(self, tradable_asset_pairs, ohlc_data, market_analysis, account_balance):
|
||||||
self.orders = []
|
self.orders = []
|
||||||
|
|||||||
@@ -1,10 +1,14 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from kraken_bot.KrakenOrder import KrakenOrder
|
|
||||||
|
from kraken_bot.MatrixLogger import MatrixLogger
|
||||||
|
|
||||||
class TradingStrategy(ABC):
|
class TradingStrategy(ABC):
|
||||||
def __init__(self, investment_count, investment_volume):
|
def __init__(self):
|
||||||
self.investment_count = investment_count
|
self.investment_count = int(os.getenv('INVESTMENT_COUNT'))
|
||||||
self.investment_volume = investment_volume
|
self.investment_volume = float(os.getenv('INVESTMENT_VOLUME'))
|
||||||
|
self.log = MatrixLogger(self.__class__.__name__)
|
||||||
self.orders = []
|
self.orders = []
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
krakenex
|
krakenex
|
||||||
|
matrix-nio
|
||||||
pykrakenapi
|
pykrakenapi
|
||||||
python-dotenv
|
python-dotenv
|
||||||
|
scipy
|
||||||
backtrader
|
backtrader
|
||||||
matplotlib
|
matplotlib
|
||||||
|
|||||||
Reference in New Issue
Block a user