exhcange now uses ccxt in dry_run, update config
This commit is contained in:
parent
14d16d573c
commit
40a0689183
@ -91,7 +91,7 @@ class Constants(object):
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'string',
|
||||
'pattern': '^[0-9A-Z]+_[0-9A-Z]+$'
|
||||
'pattern': '^[0-9A-Z]+/[0-9A-Z]+$'
|
||||
},
|
||||
'uniqueItems': True
|
||||
},
|
||||
@ -99,7 +99,7 @@ class Constants(object):
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'string',
|
||||
'pattern': '^[0-9A-Z]+_[0-9A-Z]+$'
|
||||
'pattern': '^[0-9A-Z]+/[0-9A-Z]+$'
|
||||
},
|
||||
'uniqueItems': True
|
||||
}
|
||||
|
@ -2,32 +2,56 @@
|
||||
""" Cryptocurrency Exchanges support """
|
||||
import enum
|
||||
import logging
|
||||
import ccxt
|
||||
from random import randint
|
||||
from typing import List, Dict, Any, Optional
|
||||
from cachetools import cached, TTLCache
|
||||
|
||||
import arrow
|
||||
import requests
|
||||
from cachetools import cached, TTLCache
|
||||
|
||||
from freqtrade import OperationalException
|
||||
from freqtrade.exchange.bittrex import Bittrex
|
||||
from freqtrade.exchange.interface import Exchange
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Current selected exchange
|
||||
_API: Exchange = None
|
||||
_API = None
|
||||
_CONF: dict = {}
|
||||
API_RETRY_COUNT = 4
|
||||
|
||||
# Holds all open sell orders for dry_run
|
||||
_DRY_RUN_OPEN_ORDERS: Dict[str, Any] = {}
|
||||
|
||||
def retrier(f):
|
||||
def wrapper(*args, **kwargs):
|
||||
count = kwargs.pop('count', API_RETRY_COUNT)
|
||||
try:
|
||||
return f(*args, **kwargs)
|
||||
# TODO dont be a gotta-catch-them-all pokemon collector
|
||||
except Exception as ex:
|
||||
logger.warn('%s returned exception: "%s"', f, ex)
|
||||
if count > 0:
|
||||
count -= 1
|
||||
kwargs.update({'count': count})
|
||||
logger.warn('retrying %s still for %s times', f, count)
|
||||
return wrapper(*args, **kwargs)
|
||||
else:
|
||||
raise OperationalException('Giving up retrying: %s', f)
|
||||
return wrapper
|
||||
|
||||
class Exchanges(enum.Enum):
|
||||
"""
|
||||
Maps supported exchange names to correspondent classes.
|
||||
"""
|
||||
BITTREX = Bittrex
|
||||
|
||||
def _get_market_url(exchange):
|
||||
"get market url for exchange"
|
||||
# TODO: PR to ccxt
|
||||
base = exchange.urls.get('www')
|
||||
market = ""
|
||||
if 'bittrex' in get_name():
|
||||
market = base + '/Market/Index?MarketName={}'
|
||||
if 'binance' in get_name():
|
||||
market = base + '/trade.html?symbol={}'
|
||||
|
||||
return market
|
||||
|
||||
|
||||
def init(config: dict) -> None:
|
||||
@ -49,12 +73,21 @@ def init(config: dict) -> None:
|
||||
|
||||
# Find matching class for the given exchange name
|
||||
name = exchange_config['name']
|
||||
|
||||
# TODO add check for a list of supported exchanges
|
||||
|
||||
try:
|
||||
exchange_class = Exchanges[name.upper()].value
|
||||
# exchange_class = Exchanges[name.upper()].value
|
||||
_API = getattr(ccxt, name.lower())({
|
||||
'apiKey': exchange_config.get('key'),
|
||||
'secret': exchange_config.get('secret'),
|
||||
})
|
||||
logger.info('Using Exchange %s', name.capitalize())
|
||||
except KeyError:
|
||||
raise OperationalException('Exchange {} is not supported'.format(name))
|
||||
|
||||
_API = exchange_class(exchange_config)
|
||||
# we need load api markets
|
||||
_API.load_markets()
|
||||
|
||||
# Check if all pairs are available
|
||||
validate_pairs(config['exchange']['pair_whitelist'])
|
||||
@ -67,15 +100,22 @@ def validate_pairs(pairs: List[str]) -> None:
|
||||
:param pairs: list of pairs
|
||||
:return: None
|
||||
"""
|
||||
|
||||
if not _API.markets:
|
||||
_API.load_markets()
|
||||
|
||||
try:
|
||||
markets = _API.get_markets()
|
||||
markets = _API.markets
|
||||
except requests.exceptions.RequestException as e:
|
||||
logger.warning('Unable to validate pairs (assuming they are correct). Reason: %s', e)
|
||||
return
|
||||
|
||||
stake_cur = _CONF['stake_currency']
|
||||
for pair in pairs:
|
||||
if not pair.startswith(stake_cur):
|
||||
# Note: ccxt has BaseCurrency/QuoteCurrency format for pairs
|
||||
pair = pair.replace('_', '/')
|
||||
# TODO: add a support for having coins in BTC/USDT format
|
||||
if not pair.endswith(stake_cur):
|
||||
raise OperationalException(
|
||||
'Pair {} not compatible with stake_currency: {}'.format(pair, stake_cur)
|
||||
)
|
||||
@ -124,23 +164,31 @@ def get_balance(currency: str) -> float:
|
||||
if _CONF['dry_run']:
|
||||
return 999.9
|
||||
|
||||
return _API.get_balance(currency)
|
||||
return _API.fetch_balance()[currency]
|
||||
|
||||
|
||||
def get_balances():
|
||||
if _CONF['dry_run']:
|
||||
return []
|
||||
|
||||
return _API.get_balances()
|
||||
|
||||
return _API.fetch_balance()
|
||||
|
||||
# @cached(TTLCache(maxsize=100, ttl=30))
|
||||
@retrier
|
||||
def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict:
|
||||
return _API.get_ticker(pair, refresh)
|
||||
return _API.fetch_ticker(pair)
|
||||
|
||||
|
||||
@cached(TTLCache(maxsize=100, ttl=30))
|
||||
# @cached(TTLCache(maxsize=100, ttl=30))
|
||||
@retrier
|
||||
def get_ticker_history(pair: str, tick_interval) -> List[Dict]:
|
||||
return _API.get_ticker_history(pair, tick_interval)
|
||||
# TODO: tickers need to be in format 1m,5m
|
||||
# fetch_ohlcv returns an [[datetime,o,h,l,c,v]]
|
||||
if not _API.markets:
|
||||
_API.load_markets()
|
||||
ohlcv = _API.fetch_ohlcv(pair, str(tick_interval)+'m')
|
||||
|
||||
return ohlcv
|
||||
|
||||
|
||||
def cancel_order(order_id: str) -> None:
|
||||
@ -162,7 +210,9 @@ def get_order(order_id: str) -> Dict:
|
||||
|
||||
|
||||
def get_pair_detail_url(pair: str) -> str:
|
||||
return _API.get_pair_detail_url(pair)
|
||||
return _get_market_url(_API).format(
|
||||
_API.markets[pair]['id']
|
||||
)
|
||||
|
||||
|
||||
def get_markets() -> List[str]:
|
||||
@ -170,16 +220,27 @@ def get_markets() -> List[str]:
|
||||
|
||||
|
||||
def get_market_summaries() -> List[Dict]:
|
||||
return _API.get_market_summaries()
|
||||
return _API.fetch_tickers()
|
||||
|
||||
|
||||
def get_name() -> str:
|
||||
return _API.name
|
||||
return _API.__class__.__name__
|
||||
|
||||
|
||||
def get_fee_maker() -> float:
|
||||
return _API.fees['trading']['maker']
|
||||
|
||||
|
||||
def get_fee_taker() -> float:
|
||||
return _API.fees['trading']['taker']
|
||||
|
||||
|
||||
def get_fee() -> float:
|
||||
return _API.fee
|
||||
return _API.fees['trading']
|
||||
|
||||
|
||||
def get_wallet_health() -> List[Dict]:
|
||||
return _API.get_wallet_health()
|
||||
if not _API.markets:
|
||||
_API.load_markets()
|
||||
|
||||
return _API.markets
|
||||
|
Loading…
Reference in New Issue
Block a user