Skip to content

Commit

Permalink
Code refactoring (whittlem#159)
Browse files Browse the repository at this point in the history
* Move Telegram into chat package

* Move configuration parsing into a package
- remove duplicate
- create parsing functions for Coinbase and Binance
- change the way app's arguments are interpreted (dict instead of object) in order
to make possible to merge them with the configuration

* Move Binance and CoinbasePro into the package exchange
  • Loading branch information
dthevenin authored May 12, 2021
1 parent 31974e5 commit c4f5482
Show file tree
Hide file tree
Showing 21 changed files with 479 additions and 1,271 deletions.
2 changes: 0 additions & 2 deletions create-graphs.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
from models.PyCryptoBot import PyCryptoBot
from models.Trading import TechnicalAnalysis
from models.Binance import AuthAPI as BAuthAPI, PublicAPI as BPublicAPI
from models.CoinbasePro import AuthAPI as CBAuthAPI, PublicAPI as CBPublicAPI
from views.TradingGraphs import TradingGraphs

#app = PyCryptoBot()
Expand Down
1,244 changes: 28 additions & 1,216 deletions models/PyCryptoBot.py

Large diffs are not rendered by default.

6 changes: 3 additions & 3 deletions models/Trading.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"""Technical analysis on a trading Pandas DataFrame"""

import json, math
import math
import numpy as np
import pandas as pd
import re, sys
import re
from statsmodels.tsa.statespace.sarimax import SARIMAX
from models.CoinbasePro import AuthAPI


class TechnicalAnalysis():
def __init__(self, data=pd.DataFrame()):
Expand Down
9 changes: 4 additions & 5 deletions models/TradingAccount.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
"""Live or test trading account"""

import sys
import numpy as np
import pandas as pd
import json, math, re, requests, sys
from datetime import datetime
import re, requests
from binance.client import Client
from models.Binance import AuthAPI as BAuthAPI, PublicAPI as BPublicAPI
from models.CoinbasePro import AuthAPI as CBAuthAPI, PublicAPI as CBPublicAPI
from models.exchange.binance import AuthAPI as BAuthAPI, PublicAPI as BPublicAPI
from models.exchange.coinbase_pro import AuthAPI as CBAuthAPI


class TradingAccount():
def __init__(self, app=None):
Expand Down
1 change: 1 addition & 0 deletions models/chat/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from .telegram import Telegram
22 changes: 14 additions & 8 deletions models/Telegram.py → models/chat/telegram.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import requests, re
import logging

class Telegram():
def __init__(self, token='', client_id=''):
self.api = 'https://api.telegram.org/bot'
self._token = token
self._client_id = str(client_id)
self.logger = logging.getLogger('pyCryptoBot')

p = re.compile(r"^\d{1,10}:[A-z0-9-_]{35,35}$")
if not p.match(token):
Expand All @@ -14,27 +16,31 @@ def __init__(self, token='', client_id=''):
if not p.match(client_id):
raise Exception('Telegram client_id is invalid')

def send(self, message=''):
self.logger.info('Telegram configure with for client "' + client_id + '" with token "' + token + '"')

def send(self, message='') -> str:
try:
payload = self.api + self._token + '/sendMessage?chat_id=' + self._client_id + '&parse_mode=Markdown&text=' + message
resp = requests.get(payload)

self.logger.debug('Telegram send:' + payload)

if resp.status_code != 200:
return None
return ''

resp.raise_for_status()
json = resp.json()

except requests.ConnectionError as err:
print (err)
return ('')
self.logger.error(err)
return ''

except requests.exceptions.HTTPError as err:
print (err)
return ('')
self.logger.error(err)
return ''

except requests.Timeout as err:
print (err)
return ('')
print(err)
return ''

return json
2 changes: 2 additions & 0 deletions models/config/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
from .binance_parser import parser as binanceConfigParser, parseMarket as binanceParseMarket
from .coinbase_pro_parser import parser as coinbaseProConfigParser, parseMarket as coinbaseProParseMarket
155 changes: 155 additions & 0 deletions models/config/binance_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
import re, logging

from .default_parser import isCurrencyValid, defaultConfigParse

def isMarketValid(market):
if market == None:
return False
p = re.compile(r"^[A-Z]{6,12}$")
return p.match(market)

def parseMarket(market):
if not isMarketValid(market):
raise ValueError('Binance market invalid: ' + market)

if market.endswith('BTC'):
base_currency = market.replace('BTC', '')
quote_currency = 'BTC'
elif market.endswith('BNB'):
base_currency = market.replace('BNB', '')
quote_currency = 'BNB'
elif market.endswith('ETH'):
base_currency = market.replace('ETH', '')
quote_currency = 'ETH'
elif market.endswith('USDT'):
base_currency = market.replace('USDT', '')
quote_currency = 'USDT'
elif market.endswith('TUSD'):
base_currency = market.replace('TUSD', '')
quote_currency = 'TUSD'
elif market.endswith('BUSD'):
base_currency = market.replace('BUSD', '')
quote_currency = 'BUSD'
elif market.endswith('DAX'):
base_currency = market.replace('DAX', '')
quote_currency = 'DAX'
elif market.endswith('NGN'):
base_currency = market.replace('NGN', '')
quote_currency = 'NGN'
elif market.endswith('RUB'):
base_currency = market.replace('RUB', '')
quote_currency = 'RUB'
elif market.endswith('TRY'):
base_currency = market.replace('TRY', '')
quote_currency = 'TRY'
elif market.endswith('EUR'):
base_currency = market.replace('EUR', '')
quote_currency = 'EUR'
elif market.endswith('GBP'):
base_currency = market.replace('GBP', '')
quote_currency = 'GBP'
elif market.endswith('ZAR'):
base_currency = market.replace('ZAR', '')
quote_currency = 'ZAR'
elif market.endswith('UAH'):
base_currency = market.replace('UAH', '')
quote_currency = 'UAH'
elif market.endswith('DAI'):
base_currency = market.replace('DAI', '')
quote_currency = 'DAI'
elif market.endswith('BIDR'):
base_currency = market.replace('BIDR', '')
quote_currency = 'BIDR'
elif market.endswith('AUD'):
base_currency = market.replace('AUD', '')
quote_currency = 'AUD'
elif market.endswith('US'):
base_currency = market.replace('US', '')
quote_currency = 'US'
elif market.endswith('NGN'):
base_currency = market.replace('NGN', '')
quote_currency = 'NGN'
elif market.endswith('BRL'):
base_currency = market.replace('BRL', '')
quote_currency = 'BRL'
elif market.endswith('BVND'):
base_currency = market.replace('BVND', '')
quote_currency = 'BVND'
elif market.endswith('VAI'):
base_currency = market.replace('VAI', '')
quote_currency = 'VAI'

if len(market) != len(base_currency) + len(quote_currency):
raise ValueError('Binance market error.')

return market, base_currency, quote_currency

def parser(app, binance_config, args = {}):
logging.info('CoinbasePro Configuration parse')

if not binance_config:
raise Exception('There is an error in your config dictionnary')

if not app:
raise Exception('No app is passed')

if 'api_key' in binance_config and 'api_secret' in binance_config and 'api_url' in binance_config:
# validates the api key is syntactically correct
p = re.compile(r"^[A-z0-9]{64,64}$")
if not p.match(binance_config['api_key']):
raise TypeError('Binance API key is invalid')

app.api_key = binance_config['api_key']

# validates the api secret is syntactically correct
p = re.compile(r"^[A-z0-9]{64,64}$")
if not p.match(binance_config['api_secret']):
raise TypeError('Binance API secret is invalid')

app.api_secret = binance_config['api_secret']

valid_urls = [
'https://api.binance.com/',
'https://testnet.binance.vision/api/'
]

# validate Binance API
if binance_config['api_url'] not in valid_urls:
raise ValueError('Binance API URL is invalid, Should be: ' + ', '.join(valid_urls))

app.api_url = binance_config['api_url']
app.base_currency = 'BTC'
app.quote_currency = 'GBP'
app.granularity = '1h'

if 'config' in binance_config:
config = {**binance_config['config'], **args}
else:
config = args

defaultConfigParse(app, config)

if 'base_currency' in config and config['base_currency'] != None:
if not isCurrencyValid(config['base_currency']):
raise TypeError('Base currency is invalid.')
app.base_currency = config['base_currency']

if 'quote_currency' in config and config['quote_currency'] != None:
if not isCurrencyValid(config['quote_currency']):
raise TypeError('Quote currency is invalid.')
app.quote_currency = config['quote_currency']

if 'market' in config and config['market'] != None:
app.market, app.base_currency, app.quote_currency = parseMarket(config['market'])

if app.base_currency != '' and app.quote_currency != '':
app.market = app.base_currency + app.quote_currency

if 'granularity' in config and config['granularity'] != None:
if isinstance(config['granularity'], str):
if config['granularity'] in ['1m', '5m', '15m', '1h', '6h', '1d']:
app.granularity = config['granularity']
app.smart_switch = 0

else:
raise Exception('There is an error in your config dictionnary')
88 changes: 88 additions & 0 deletions models/config/coinbase_pro_parser.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import re, logging

from .default_parser import isCurrencyValid, defaultConfigParse

def isMarketValid(market):
p = re.compile(r"^[1-9A-Z]{2,5}\-[1-9A-Z]{2,5}$")
return p.match(market)

def parseMarket(market):
if not isMarketValid(market):
raise ValueError('Coinbase Pro market invalid: ' + market)

base_currency, quote_currency = market.split('-', 2)
return market, base_currency, quote_currency

def parser(app, coinbase_config, args = {}):
logging.info('CoinbasePro Configuration parse')

if not coinbase_config:
raise Exception('There is an error in your config dictionnary')

if not app:
raise Exception('No app is passed')

if 'api_key' in coinbase_config and 'api_secret' in coinbase_config and 'api_passphrase' in coinbase_config and 'api_url' in coinbase_config:

# validates the api key is syntactically correct
p = re.compile(r"^[a-f0-9]{32,32}$")
if not p.match(coinbase_config['api_key']):
raise TypeError('Coinbase Pro API key is invalid')

app.api_key = coinbase_config['api_key']

# validates the api secret is syntactically correct
p = re.compile(r"^[A-z0-9+\/]+==$")
if not p.match(coinbase_config['api_secret']):
raise TypeError('Coinbase Pro API secret is invalid')

app.api_secret = coinbase_config['api_secret']

# validates the api passphrase is syntactically correct
p = re.compile(r"^[a-z0-9]{10,11}$")
if not p.match(coinbase_config['api_passphrase']):
raise TypeError('Coinbase Pro API passphrase is invalid')

app.api_passphrase = coinbase_config['api_passphrase']

valid_urls = [
'https://api.pro.coinbase.com/'
]

# validate Coinbase Pro API
if coinbase_config['api_url'] not in valid_urls:
raise ValueError('Coinbase Pro API URL is invalid, Should be: ' + ', '.join(valid_urls))

app.api_url = coinbase_config['api_url']

if 'config' in coinbase_config:
config = {**coinbase_config['config'], **args}
else:
config = args

defaultConfigParse(app, config)

if 'base_currency' in config and config['base_currency'] != None:
if not isCurrencyValid(config['base_currency']):
raise TypeError('Base currency is invalid.')
app.base_currency = config['base_currency']

if 'quote_currency' in config and config['quote_currency'] != None:
if not isCurrencyValid(config['quote_currency']):
raise TypeError('Quote currency is invalid.')
app.quote_currency = config['quote_currency']

if 'market' in config and config['market'] != None:
app.market, app.base_currency, app.quote_currency = parseMarket(config['market'])

if app.base_currency != '' and app.quote_currency != '':
app.market = app.base_currency + '-' + app.quote_currency

if 'granularity' in config and config['granularity'] != None:
if isinstance(config['granularity'], int):
if config['granularity'] in [60, 300, 900, 3600, 21600, 86400]:
app.granularity = config['granularity']
app.smart_switch = 0

else:
raise Exception('There is an error in your config dictionnary')
Loading

0 comments on commit c4f5482

Please sign in to comment.