Skip to content

Commit

Permalink
Fix logging (whittlem#280)
Browse files Browse the repository at this point in the history
* Add Logger class.

* Add logger_parser.

* Configure parsers according to new Logger

* Configure rest of the project according to new Logger implementation

* Add logger configuration details to README.md

* Fix polling.

* Fix Logger initialization.
  • Loading branch information
emreeroglu authored May 30, 2021
1 parent 82a6bcf commit 216245a
Show file tree
Hide file tree
Showing 19 changed files with 488 additions and 378 deletions.
21 changes: 21 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -385,6 +385,27 @@ For telegram, add a piece to the config.json as follows:

You can use @botfather and @myidbot in telegram to create a bot with token and get a client id.

For configuring logger, add a piece to the config.json as follows:
*This is also default configuration of the logger, if no config is given and log is not disabled this configuration will apply.*

"logger" : {
"filelog": 1,
"logfile": "pycryptobot.log",
"fileloglevel": "DEBUG",
"consolelog": 1,
"consoleloglevel": "INFO"
}

"filelog" and "consolelog" can only get 1 (enable) or 0 (disable).
"--disablelog" argument or "disablelog" config will disable to writing logfile as backwards compatibility.
If you want to disable logging entirely, you can set "filelog" and "consolelog" to 0.

"logfile" is overriden by '--logfile' console argument.
If '--logfile' used when running bot "logfile": "pycryptobot.log" line in config file will be ignored.

"fileloglevel" and "consoleloglevel" can get one of 'CRITICAL', 'ERROR', 'WARNING', 'INFO', 'DEBUG', 'NOTSET'
For further detail in log levels: https://docs.python.org/3/library/logging.html#logging-levels

## Multi-Market Trading

The bot can trade multiple markets at once. This is also documented in my Medium articles. The bot will execute buys using the full "quote currency" balance it has access too and it will sell the full "base currency" balance it has access too. In order to ring-fence your non-bot funds you should create another "Portfolio" in Coinbase Pro and assign API keys to it. That way you limit exposure. You can so something similar with Binance using sub-accounts but I believe you need to be a certain level to do this.
Expand Down
33 changes: 5 additions & 28 deletions models/ConfigBuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from json import dumps
from re import compile as re_compile
from sys import exit as sys_exit
from models.helper.LogHelper import Logger

class ConfigBuilder():
def __init__(self) -> None:
Expand All @@ -12,10 +13,10 @@ def __init__(self) -> None:
self._t = 0

def init(self) -> None:
print ("*** config.json Configuration Builder ***\n")
Logger.info("*** config.json Configuration Builder ***")

if isfile('config.json'):
print ("config.json already exists.\n")
Logger.info("config.json already exists.")
sys_exit()

config = {}
Expand All @@ -26,8 +27,6 @@ def init(self) -> None:
config['coinbasepro'] = {}
config['coinbasepro']['api_url'] = 'https://api.pro.coinbase.com'

print ("\n")

while 'api_key' not in config['coinbasepro']:
api_key = input("What is your Coinbase Pro API Key? ")
p = re_compile(r"^[a-f0-9]{32,32}$")
Expand All @@ -46,8 +45,6 @@ def init(self) -> None:
if p.match(api_passphrase):
config['coinbasepro']['api_passphrase'] = api_passphrase

print ("\n")

config['coinbasepro']['config'] = {}

while 'base_currency' not in config['coinbasepro']['config']:
Expand All @@ -62,33 +59,27 @@ def init(self) -> None:
if p.match(quote_currency):
config['coinbasepro']['config']['quote_currency'] = quote_currency

print ("\n")

choice = input("Do you want to smart switch between 1 hour and 15 minute intervals (1=yes:default, 2=no)? ")
if choice == '2':
while 'granularity' not in config['coinbasepro']['config']:
choice = input("What granularity do you want to trade? (60, 300, 900, 3600, 21600, 86400)? ")
if int(choice) in [60, 300, 900, 3600, 21600, 86400]:
config['coinbasepro']['config']['granularity'] = int(choice)

print ("\n")

choice = input("Do you want to start live trading? (1=live, 2=test:default)? ")
if choice == '1':
config['coinbasepro']['config']['live'] = 1
else:
config['coinbasepro']['config']['live'] = 0

print ("\n")

choice = input("Do you have API keys for the Binance exchange (1=yes, 2=no:default)? ")
if choice == '1':
self._b = 1
config['binance'] = {}
config['binance']['api_url'] = 'https://api.binance.com'

print ("\n")

while 'api_key' not in config['binance']:
api_key = input("What is your Binance API Key? ")
p = re_compile(r"^[A-z0-9]{64,64}$")
Expand All @@ -101,8 +92,6 @@ def init(self) -> None:
if p.match(api_secret):
config['binance']['api_secret'] = api_secret

print ("\n")

config['binance']['config'] = {}

while 'base_currency' not in config['binance']['config']:
Expand All @@ -117,32 +106,24 @@ def init(self) -> None:
if p.match(quote_currency):
config['binance']['config']['quote_currency'] = quote_currency

print ("\n")

choice = input("Do you want to smart switch between 1 hour and 15 minute intervals (1=yes:default, 2=no)? ")
if choice == '2':
while 'granularity' not in config['binance']['config']:
choice = input("What granularity do you want to trade? (1m, 5m, 15m, 1h, 6h, 1d)? ")
if choice in ['1m', '5m', '15m', '1h', '6h', '1d']:
config['binance']['config']['granularity'] = choice

print ("\n")

choice = input("Do you want to start live trading? (1=live, 2=test:default)? ")
if choice == '1':
config['binance']['config']['live'] = 1
else:
config['binance']['config']['live'] = 0

print ("\n")

choice = input("Do you have a Telegram Token and Client ID (1=yes, 2=no:default)? ")
if choice == '1':
self._t = 1
config['telegram'] = {}

print ("\n")

while 'token' not in config['telegram']:
token = input("What is your Telegram token? ")
p = re_compile(r"^\d{1,10}:[A-z0-9-_]{35,35}$")
Expand All @@ -155,8 +136,6 @@ def init(self) -> None:
if p.match(client_id):
config['telegram']['client_id'] = client_id

print ("\n")

choice = input("Do you want to ever sell at a loss even to minimise losses (1:yes, 2=no:default)? ")
if choice == '1':
if self._c == 1:
Expand Down Expand Up @@ -220,15 +199,13 @@ def init(self) -> None:
if self._b == 1:
config['binance']['config']['autorestart'] = 1

print ("\n")

try:
config_json = dumps(config, indent=4)
fh = open('./config.json', 'w')
fh.write(config_json)
print ("config.json saved!\n")
Logger.info("config.json saved!")
fh.close()
except Exception as err:
print (err)
Logger.critical(err)

return None
102 changes: 56 additions & 46 deletions models/PyCryptoBot.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import pandas as pd
import argparse
import json
import logging
import math
import random
import re
Expand All @@ -14,16 +13,13 @@
from models.exchange.binance import AuthAPI as BAuthAPI, PublicAPI as BPublicAPI
from models.exchange.coinbase_pro import AuthAPI as CBAuthAPI, PublicAPI as CBPublicAPI
from models.chat import Telegram
from models.config import binanceConfigParser, binanceParseMarket, coinbaseProConfigParser, coinbaseProParseMarket, dummyConfigParser, dummyParseMarket
from models.config import binanceConfigParser, binanceParseMarket, coinbaseProConfigParser, coinbaseProParseMarket, dummyConfigParser, dummyParseMarket, loggerConfigParser
from models.ConfigBuilder import ConfigBuilder
from models.helper.LogHelper import Logger

# disable insecure ssl warning
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# reduce informational logging
logging.getLogger("requests").setLevel(logging.WARNING)
logging.getLogger("urllib3").setLevel(logging.WARNING)


def parse_arguments():
# instantiate the arguments parser
Expand Down Expand Up @@ -166,7 +162,11 @@ def __init__(self, exchange='coinbasepro', filename='config.json'):
self.disablelog = False
self.disabletracker = False

self.filelog = True
self.logfile = args['logfile'] if args['logfile'] else "pycryptobot.log"
self.fileloglevel = "DEBUG"
self.consolelog = True
self.consoleloglevel = "INFO"

if args['init']:
# config builder
Expand Down Expand Up @@ -195,6 +195,16 @@ def __init__(self, exchange='coinbasepro', filename='config.json'):
self._chat_client = Telegram(telegram['token'], telegram['client_id'])
self.telegram = True

if 'logger' in config:
loggerConfigParser(self, config['logger'])

if self.disablelog:
self.filelog = 0
self.fileloglevel = 'NOTSET'
self.logfile == "pycryptobot.log"

Logger.configure(filelog=self.filelog, logfile=self.logfile, fileloglevel=self.fileloglevel, consolelog=self.consolelog, consoleloglevel=self.consoleloglevel)

except json.decoder.JSONDecodeError as err:
sys.tracebacklimit = 0
raise ValueError('Invalid config.json: ' + str(err))
Expand Down Expand Up @@ -606,7 +616,7 @@ def getTakerFee(self):
return api.getTakerFee()
elif self.exchange == 'binance':
api = BAuthAPI(self.getAPIKey(), self.getAPISecret(), self.getAPIURL())
return api.getTakerFee(self.getMarket())
return api.getTakerFee()
else:
return 0.005

Expand Down Expand Up @@ -669,100 +679,100 @@ def setNoSellAtLoss(self, flag):

def startApp(self, account, last_action='', banner=True):
if banner:
print('--------------------------------------------------------------------------------')
print('| Python Crypto Bot |')
print('--------------------------------------------------------------------------------')
Logger.info('--------------------------------------------------------------------------------')
Logger.info('| Python Crypto Bot |')
Logger.info('--------------------------------------------------------------------------------')
txt = ' Release : ' + self.getVersionFromREADME()
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

print('--------------------------------------------------------------------------------')
Logger.info('-----------------------------------------------------------------------------')

if self.isVerbose():
txt = ' Market : ' + self.getMarket()
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')
txt = ' Granularity : ' + str(self.getGranularity()) + ' seconds'
print('|', txt, (' ' * (75 - len(txt))), '|')
print('--------------------------------------------------------------------------------')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')
Logger.info('-----------------------------------------------------------------------------')

if self.isLive():
txt = ' Bot Mode : LIVE - live trades using your funds!'
else:
txt = ' Bot Mode : TEST - test trades using dummy funds :)'

print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

txt = ' Bot Started : ' + str(datetime.now())
print('|', txt, (' ' * (75 - len(txt))), '|')
print('================================================================================')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')
Logger.info('================================================================================')

if self.sellUpperPcnt() != None:
txt = ' Sell Upper : ' + str(self.sellUpperPcnt()) + '%'
print('|', txt, (' ' * (75 - len(txt))), '|')
txt = ' Sell Upper : ' + str(self.sellUpperPcnt()) + '%'
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

if self.sellLowerPcnt() != None:
txt = ' Sell Lower : ' + str(self.sellLowerPcnt()) + '%'
print('|', txt, (' ' * (75 - len(txt))), '|')
txt = ' Sell Lower : ' + str(self.sellLowerPcnt()) + '%'
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

if self.trailingStopLoss() != None:
txt = ' Trailing Stop Loss : ' + str(self.trailingStopLoss()) + '%'
print('|', txt, (' ' * (75 - len(txt))), '|')
txt = ' Trailing Stop Loss : ' + str(self.trailingStopLoss()) + '%'
Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ')

txt = ' Sell At Loss : ' + str(self.allowSellAtLoss()) + ' --sellatloss ' + str(self.allowSellAtLoss())
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

txt = ' Sell At Resistance : ' + str(self.sellAtResistance()) + ' --sellatresistance'
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

txt = ' Trade Bull Only : ' + str(not self.disableBullOnly()) + ' --disablebullonly'
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

txt = ' Buy Near High : ' + str(not self.disableBuyNearHigh()) + ' --disablebuynearhigh'
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

txt = ' Use Buy MACD : ' + str(not self.disableBuyMACD()) + ' --disablebuymacd'
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

txt = ' Use Buy OBV : ' + str(not self.disableBuyOBV()) + ' --disablebuyobv'
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

txt = ' Use Buy Elder-Ray : ' + str(not self.disableBuyElderRay()) + ' --disablebuyelderray'
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

txt = ' Sell Fibonacci Low : ' + str(
not self.disableFailsafeFibonacciLow()) + ' --disablefailsafefibonaccilow'
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

if self.sellLowerPcnt() != None:
txt = ' Sell Lower Pcnt : ' + str(
not self.disableFailsafeLowerPcnt()) + ' --disablefailsafelowerpcnt'
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

if self.sellUpperPcnt() != None:
txt = ' Sell Upper Pcnt : ' + str(
not self.disableFailsafeLowerPcnt()) + ' --disableprofitbankupperpcnt'
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

txt = ' Candlestick Reversal : ' + str(
not self.disableProfitbankReversal()) + ' --disableprofitbankreversal'
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

txt = ' Telegram : ' + str(not self.disabletelegram) + ' --disabletelegram'
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

txt = ' Log : ' + str(not self.disableLog()) + ' --disablelog'
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

txt = ' Tracker : ' + str(not self.disableTracker()) + ' --disabletracker'
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

txt = ' Auto restart Bot : ' + str(self.autoRestart()) + ' --autorestart'
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

if self.getBuyMaxSize():
txt = ' Max Buy Size : ' + str(self.getBuyMaxSize()) + ' --buymaxsize <size>'
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info('| ' + txt + (' ' * (75 - len(txt))) + ' | ')

print('================================================================================')
Logger.info('================================================================================')

# if live
if self.isLive():
Expand Down Expand Up @@ -824,15 +834,15 @@ def startApp(self, account, last_action='', banner=True):
startDate = str(startDate.isoformat())
endDate = str(endDate.isoformat())
txt = ' Sampling start : ' + str(startDate)
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ')
txt = ' Sampling end : ' + str(endDate)
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ')
if self.simstartdate != None:
txt = ' WARNING: Using less than 300 intervals'
print('|', txt, (' ' * (75 - len(txt))), '|')
Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ')
txt = ' Interval size : ' + str(len(tradingData))
print('|', txt, (' ' * (75 - len(txt))), '|')
print('================================================================================')
Logger.info(' | ' + txt + (' ' * (75 - len(txt))) + ' | ')
Logger.info('================================================================================')

else:
tradingData = self.getHistoricalData(self.getMarket(), self.getGranularity())
Expand Down
Loading

0 comments on commit 216245a

Please sign in to comment.