Merge branch 'feat/objectify-ccxt' into ccxt-objectify-pr1
This commit is contained in:
commit
916165a7c5
@ -13,19 +13,19 @@
|
||||
"key": "your_exchange_key",
|
||||
"secret": "your_exchange_secret",
|
||||
"pair_whitelist": [
|
||||
"BTC_ETH",
|
||||
"BTC_LTC",
|
||||
"BTC_ETC",
|
||||
"BTC_DASH",
|
||||
"BTC_ZEC",
|
||||
"BTC_XLM",
|
||||
"BTC_NXT",
|
||||
"BTC_POWR",
|
||||
"BTC_ADA",
|
||||
"BTC_XMR"
|
||||
"ETH/BTC",
|
||||
"LTC/BTC",
|
||||
"ETC/BTC",
|
||||
"DASH/BTC",
|
||||
"ZEC/BTC",
|
||||
"XLM/BTC",
|
||||
"NXT/BTC",
|
||||
"POWR/BTC",
|
||||
"ADA/BTC",
|
||||
"XMR/BTC"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BTC_DOGE"
|
||||
"DOGE/BTC"
|
||||
]
|
||||
},
|
||||
"experimental": {
|
||||
|
@ -21,19 +21,19 @@
|
||||
"key": "your_exchange_key",
|
||||
"secret": "your_exchange_secret",
|
||||
"pair_whitelist": [
|
||||
"BTC_ETH",
|
||||
"BTC_LTC",
|
||||
"BTC_ETC",
|
||||
"BTC_DASH",
|
||||
"BTC_ZEC",
|
||||
"BTC_XLM",
|
||||
"BTC_NXT",
|
||||
"BTC_POWR",
|
||||
"BTC_ADA",
|
||||
"BTC_XMR"
|
||||
"ETH/BTC",
|
||||
"LTC/BTC",
|
||||
"ETC/BTC",
|
||||
"DASH/BTC",
|
||||
"ZEC/BTC",
|
||||
"XLM/BTC",
|
||||
"NXT/BTC",
|
||||
"POWR/BTC",
|
||||
"ADA/BTC",
|
||||
"XMR/BTC"
|
||||
],
|
||||
"pair_blacklist": [
|
||||
"BTC_DOGE"
|
||||
"DOGE/BTC"
|
||||
]
|
||||
},
|
||||
"experimental": {
|
||||
|
@ -44,12 +44,14 @@ class Analyze(object):
|
||||
:param ticker: See exchange.get_ticker_history
|
||||
:return: DataFrame
|
||||
"""
|
||||
columns = {'C': 'close', 'V': 'volume', 'O': 'open', 'H': 'high', 'L': 'low', 'T': 'date'}
|
||||
frame = DataFrame(ticker) \
|
||||
.rename(columns=columns)
|
||||
if 'BV' in frame:
|
||||
frame.drop('BV', 1, inplace=True)
|
||||
frame['date'] = to_datetime(frame['date'], utc=True, infer_datetime_format=True)
|
||||
cols = ['date', 'open', 'high', 'low', 'close', 'volume']
|
||||
frame = DataFrame(ticker, columns=cols)
|
||||
|
||||
frame['date'] = to_datetime(frame['date'],
|
||||
unit='ms',
|
||||
utc=True,
|
||||
infer_datetime_format=True)
|
||||
|
||||
frame.sort_values('date', inplace=True)
|
||||
return frame
|
||||
|
||||
|
@ -2,8 +2,11 @@
|
||||
""" 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
|
||||
from datetime import datetime
|
||||
|
||||
import ccxt
|
||||
import arrow
|
||||
@ -11,10 +14,12 @@ from cachetools import cached, TTLCache
|
||||
|
||||
from freqtrade import OperationalException, DependencyException, NetworkException
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Current selected exchange
|
||||
_API: ccxt.Exchange = None
|
||||
|
||||
_CONF: dict = {}
|
||||
API_RETRY_COUNT = 4
|
||||
|
||||
@ -23,7 +28,6 @@ _DRY_RUN_OPEN_ORDERS: Dict[str, Any] = {}
|
||||
|
||||
_TICKER_CACHE: dict = {}
|
||||
|
||||
|
||||
def retrier(f):
|
||||
def wrapper(*args, **kwargs):
|
||||
count = kwargs.pop('count', API_RETRY_COUNT)
|
||||
@ -85,6 +89,10 @@ def validate_pairs(pairs: List[str]) -> None:
|
||||
:param pairs: list of pairs
|
||||
:return: None
|
||||
"""
|
||||
|
||||
if not _API.markets:
|
||||
_API.load_markets()
|
||||
|
||||
try:
|
||||
markets = _API.load_markets()
|
||||
except ccxt.BaseError as e:
|
||||
@ -314,7 +322,15 @@ def get_markets() -> List[dict]:
|
||||
|
||||
|
||||
def get_name() -> str:
|
||||
return _API.name
|
||||
return _API.__class__.__name__.capitalize()
|
||||
|
||||
|
||||
def get_fee_maker() -> float:
|
||||
return _API.fees['trading']['maker']
|
||||
|
||||
|
||||
def get_fee_taker() -> float:
|
||||
return _API.fees['trading']['taker']
|
||||
|
||||
|
||||
def get_fee() -> float:
|
||||
|
@ -193,13 +193,12 @@ class FreqtradeBot(object):
|
||||
:return: List of pairs
|
||||
"""
|
||||
summaries = sorted(
|
||||
(s for s in exchange.get_market_summaries() if
|
||||
s['MarketName'].startswith(base_currency)),
|
||||
key=lambda s: s.get(key) or 0.0,
|
||||
(v for s, v in exchange.get_market_summaries().items() if v['symbol'].endswith(base_currency)),
|
||||
key=lambda v: v.get('info').get(key) or 0.0,
|
||||
reverse=True
|
||||
)
|
||||
|
||||
return [s['MarketName'].replace('-', '_') for s in summaries]
|
||||
return [s['symbol'] for s in summaries]
|
||||
|
||||
def _refresh_whitelist(self, whitelist: List[str]) -> List[str]:
|
||||
"""
|
||||
@ -212,15 +211,15 @@ class FreqtradeBot(object):
|
||||
sanitized_whitelist = whitelist
|
||||
health = exchange.get_wallet_health()
|
||||
known_pairs = set()
|
||||
for status in health:
|
||||
pair = '{}_{}'.format(self.config['stake_currency'], status['Currency'])
|
||||
for symbol, status in health.items():
|
||||
pair = f"{status['base']}/{self.config['stake_currency']}"
|
||||
# pair is not int the generated dynamic market, or in the blacklist ... ignore it
|
||||
if pair not in whitelist or pair in self.config['exchange'].get('pair_blacklist', []):
|
||||
continue
|
||||
# else the pair is valid
|
||||
known_pairs.add(pair)
|
||||
# Market is not active
|
||||
if not status['IsActive']:
|
||||
if not status['active']:
|
||||
sanitized_whitelist.remove(pair)
|
||||
self.logger.info(
|
||||
'Ignoring %s from whitelist (reason: %s).',
|
||||
@ -313,7 +312,7 @@ class FreqtradeBot(object):
|
||||
pair=pair,
|
||||
stake_amount=stake_amount,
|
||||
amount=amount,
|
||||
fee=exchange.get_fee(),
|
||||
fee=exchange.get_fee_maker(),
|
||||
open_rate=buy_limit,
|
||||
open_date=datetime.utcnow(),
|
||||
exchange=exchange.get_name().upper(),
|
||||
|
@ -72,3 +72,11 @@ def file_dump_json(filename, data) -> None:
|
||||
"""
|
||||
with open(filename, 'w') as fp:
|
||||
json.dump(data, fp, default=str)
|
||||
|
||||
|
||||
def format_ms_time(date: str) -> str:
|
||||
"""
|
||||
convert MS date to readable format.
|
||||
: epoch-string in ms
|
||||
"""
|
||||
return datetime.fromtimestamp(date/1000.0).strftime('%Y-%m-%dT%H:%M:%S')
|
||||
|
@ -35,7 +35,7 @@ def load_tickerdata_file(
|
||||
"""
|
||||
path = make_testdata_path(datadir)
|
||||
file = os.path.join(path, '{pair}-{ticker_interval}.json'.format(
|
||||
pair=pair,
|
||||
pair=pair.replace('/', '_'),
|
||||
ticker_interval=ticker_interval,
|
||||
))
|
||||
gzipfile = file + '.gz'
|
||||
@ -126,7 +126,7 @@ def download_backtesting_testdata(datadir: str, pair: str, interval: int = 5) ->
|
||||
interval
|
||||
)
|
||||
|
||||
filepair = pair.replace("-", "_")
|
||||
filepair = pair.replace("/", "_")
|
||||
filename = os.path.join(path, '{pair}-{interval}.json'.format(
|
||||
pair=filepair,
|
||||
interval=interval,
|
||||
@ -135,8 +135,8 @@ def download_backtesting_testdata(datadir: str, pair: str, interval: int = 5) ->
|
||||
if os.path.isfile(filename):
|
||||
with open(filename, "rt") as file:
|
||||
data = json.load(file)
|
||||
logger.debug("Current Start: %s", data[1]['T'])
|
||||
logger.debug("Current End: %s", data[-1:][0]['T'])
|
||||
logger.debug("Current Start: %s", misc.format_ms_time(data[1][0]))
|
||||
logger.debug("Current End: %s", misc.format_ms_time(data[-1:][0][0]))
|
||||
else:
|
||||
data = []
|
||||
logger.debug("Current Start: None")
|
||||
@ -146,9 +146,9 @@ def download_backtesting_testdata(datadir: str, pair: str, interval: int = 5) ->
|
||||
for row in new_data:
|
||||
if row not in data:
|
||||
data.append(row)
|
||||
logger.debug("New Start: %s", data[1]['T'])
|
||||
logger.debug("New End: %s", data[-1:][0]['T'])
|
||||
data = sorted(data, key=lambda data: data['T'])
|
||||
logger.debug("New Start: %s", misc.format_ms_time(data[0][0]))
|
||||
logger.debug("New End: %s", misc.format_ms_time(data[-1:][0][0]))
|
||||
data = sorted(data, key=lambda data: data[0])
|
||||
|
||||
misc.file_dump_json(filename, data)
|
||||
|
||||
|
@ -12,6 +12,7 @@ from pandas import DataFrame, Series
|
||||
from tabulate import tabulate
|
||||
|
||||
import freqtrade.optimize as optimize
|
||||
import freqtrade.exchange as exchange
|
||||
from freqtrade import exchange
|
||||
from freqtrade.analyze import Analyze
|
||||
from freqtrade.arguments import Arguments
|
||||
@ -52,7 +53,11 @@ class Backtesting(object):
|
||||
self.tickerdata_to_dataframe = self.analyze.tickerdata_to_dataframe
|
||||
self.populate_buy_trend = self.analyze.populate_buy_trend
|
||||
self.populate_sell_trend = self.analyze.populate_sell_trend
|
||||
exchange._API = ccxt.bittrex({'key': '', 'secret': ''})
|
||||
|
||||
# Reset keys for backtesting
|
||||
self.config['exchange']['key'] = ''
|
||||
self.config['exchange']['secret'] = ''
|
||||
exchange.init(self.config)
|
||||
|
||||
@staticmethod
|
||||
def get_timeframe(data: Dict[str, DataFrame]) -> Tuple[arrow.Arrow, arrow.Arrow]:
|
||||
|
@ -128,32 +128,31 @@ def ticker_sell_down():
|
||||
|
||||
@pytest.fixture
|
||||
def health():
|
||||
return MagicMock(return_value=[{
|
||||
'Currency': 'BTC',
|
||||
'IsActive': True,
|
||||
return MagicMock(return_value={
|
||||
"ETH/BTC": {
|
||||
'base': 'ETH',
|
||||
'active': True,
|
||||
'LastChecked': '2017-11-13T20:15:00.00',
|
||||
'Notice': None
|
||||
}, {
|
||||
'Currency': 'ETH',
|
||||
'IsActive': True,
|
||||
},
|
||||
"TRST/BTC": {
|
||||
'base': 'TRST',
|
||||
'active': True,
|
||||
'LastChecked': '2017-11-13T20:15:00.00',
|
||||
'Notice': None
|
||||
}, {
|
||||
'Currency': 'TRST',
|
||||
'IsActive': True,
|
||||
},
|
||||
"SWT/BTC": {
|
||||
'base': 'SWT',
|
||||
'active': True,
|
||||
'LastChecked': '2017-11-13T20:15:00.00',
|
||||
'Notice': None
|
||||
}, {
|
||||
'Currency': 'SWT',
|
||||
'IsActive': True,
|
||||
},
|
||||
"BCC/BTC": {
|
||||
'base': 'BCC',
|
||||
'active': False,
|
||||
'LastChecked': '2017-11-13T20:15:00.00',
|
||||
'Notice': None
|
||||
}, {
|
||||
'Currency': 'BCC',
|
||||
'IsActive': False,
|
||||
'LastChecked': '2017-11-13T20:15:00.00',
|
||||
'Notice': None
|
||||
}])
|
||||
}})
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@ -336,4 +335,3 @@ def result():
|
||||
# that inserts a trade of some type and open-status
|
||||
# return the open-order-id
|
||||
# See tests in rpc/main that could use this
|
||||
|
||||
|
@ -334,7 +334,8 @@ def test_get_ticker_history(default_conf, mocker):
|
||||
type(api_mock).has = has
|
||||
api_mock.fetch_ohlcv = MagicMock(return_value=tick)
|
||||
mocker.patch('freqtrade.exchange._API', api_mock)
|
||||
|
||||
mocker.patch('freqtrade.exchange._API.has', {'fetchOHLCV': True})
|
||||
mocker.patch('freqtrade.exchange._API.fetch_ohlcv', return_value=tick)
|
||||
# retrieve original ticker
|
||||
ticks = get_ticker_history('ETH/BTC', default_conf['ticker_interval'])
|
||||
assert ticks[0]['O'] == 1
|
||||
|
@ -59,7 +59,7 @@ def test_rpc_trade_status(default_conf, ticker, mocker) -> None:
|
||||
result_message = [
|
||||
'*Trade ID:* `1`\n'
|
||||
'*Current Pair:* '
|
||||
'[BTC_ETH](https://www.bittrex.com/Market/Index?MarketName=BTC-ETH)\n'
|
||||
'[ETH/BTC](https://bittrex.com/Market/Index?MarketName=BTC-ETH)\n'
|
||||
'*Open Since:* `just now`\n'
|
||||
'*Amount:* `90.99181074`\n'
|
||||
'*Open Rate:* `0.00001099`\n'
|
||||
@ -70,7 +70,7 @@ def test_rpc_trade_status(default_conf, ticker, mocker) -> None:
|
||||
'*Open Order:* `(LIMIT_BUY rem=0.00000000)`'
|
||||
]
|
||||
assert result == result_message
|
||||
assert trade.find('[BTC_ETH]') >= 0
|
||||
assert trade.find('[ETH/BTC]') >= 0
|
||||
|
||||
|
||||
def test_rpc_status_table(default_conf, ticker, mocker) -> None:
|
||||
@ -102,7 +102,7 @@ def test_rpc_status_table(default_conf, ticker, mocker) -> None:
|
||||
freqtradebot.create_trade()
|
||||
(error, result) = rpc.rpc_status_table()
|
||||
assert 'just now' in result['Since'].all()
|
||||
assert 'BTC_ETH' in result['Pair'].all()
|
||||
assert 'ETH/BTC' in result['Pair'].all()
|
||||
assert '-0.59%' in result['Profit'].all()
|
||||
|
||||
|
||||
@ -214,7 +214,7 @@ def test_rpc_trade_statistics(
|
||||
assert stats['first_trade_date'] == 'just now'
|
||||
assert stats['latest_trade_date'] == 'just now'
|
||||
assert stats['avg_duration'] == '0:00:00'
|
||||
assert stats['best_pair'] == 'BTC_ETH'
|
||||
assert stats['best_pair'] == 'ETH/BTC'
|
||||
assert prec_satoshi(stats['best_rate'], 6.2)
|
||||
|
||||
|
||||
@ -274,7 +274,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, ticker_sell_u
|
||||
assert stats['first_trade_date'] == 'just now'
|
||||
assert stats['latest_trade_date'] == 'just now'
|
||||
assert stats['avg_duration'] == '0:00:00'
|
||||
assert stats['best_pair'] == 'BTC_ETH'
|
||||
assert stats['best_pair'] == 'ETH/BTC'
|
||||
assert prec_satoshi(stats['best_rate'], 6.2)
|
||||
|
||||
|
||||
@ -509,7 +509,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order,
|
||||
(error, res) = rpc.rpc_performance()
|
||||
assert not error
|
||||
assert len(res) == 1
|
||||
assert res[0]['pair'] == 'BTC_ETH'
|
||||
assert res[0]['pair'] == 'ETH/BTC'
|
||||
assert res[0]['count'] == 1
|
||||
assert prec_satoshi(res[0]['profit'], 6.2)
|
||||
|
||||
|
@ -248,7 +248,8 @@ def test_status(default_conf, update, mocker, ticker) -> None:
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker
|
||||
get_ticker=ticker,
|
||||
get_pair_detail_url=MagicMock()
|
||||
)
|
||||
msg_mock = MagicMock()
|
||||
status_table = MagicMock()
|
||||
@ -319,7 +320,7 @@ def test_status_handle(default_conf, update, ticker, mocker) -> None:
|
||||
telegram._status(bot=MagicMock(), update=update)
|
||||
|
||||
assert msg_mock.call_count == 1
|
||||
assert '[BTC_ETH]' in msg_mock.call_args_list[0][0][0]
|
||||
assert '[ETH/BTC]' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_status_table_handle(default_conf, update, ticker, mocker) -> None:
|
||||
@ -369,7 +370,7 @@ def test_status_table_handle(default_conf, update, ticker, mocker) -> None:
|
||||
fields = re.sub('[ ]+', ' ', line[2].strip()).split(' ')
|
||||
|
||||
assert int(fields[0]) == 1
|
||||
assert fields[1] == 'BTC_ETH'
|
||||
assert fields[1] == 'ETH/BTC'
|
||||
assert msg_mock.call_count == 1
|
||||
|
||||
|
||||
@ -387,7 +388,8 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order,
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
get_ticker=ticker
|
||||
get_ticker=ticker,
|
||||
get_pair_detail_url=MagicMock()
|
||||
)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
@ -541,7 +543,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up,
|
||||
assert '∙ `0.00006217 BTC (6.20%)`' in msg_mock.call_args_list[-1][0][0]
|
||||
assert '∙ `0.933 USD`' in msg_mock.call_args_list[-1][0][0]
|
||||
|
||||
assert '*Best Performing:* `BTC_ETH: 6.20%`' in msg_mock.call_args_list[-1][0][0]
|
||||
assert '*Best Performing:* `ETH/BTC: 6.20%`' in msg_mock.call_args_list[-1][0][0]
|
||||
|
||||
|
||||
def test_telegram_balance_handle(default_conf, update, mocker) -> None:
|
||||
@ -779,7 +781,7 @@ def test_forcesell_handle(default_conf, update, ticker, ticker_sell_up, mocker)
|
||||
|
||||
assert rpc_mock.call_count == 2
|
||||
assert 'Selling' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '[BTC_ETH]' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '[ETH/BTC]' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert 'Amount' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '0.00001172' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert 'profit: 6.11%, 0.00006126' in rpc_mock.call_args_list[-1][0][0]
|
||||
@ -822,7 +824,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, ticker_sell_down, m
|
||||
|
||||
assert rpc_mock.call_count == 2
|
||||
assert 'Selling' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '[BTC_ETH]' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '[ETH/BTC]' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert 'Amount' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '0.00001044' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert 'loss: -5.48%, -0.00005492' in rpc_mock.call_args_list[-1][0][0]
|
||||
@ -838,6 +840,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, mocker) -> None:
|
||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram._init', MagicMock())
|
||||
mocker.patch('freqtrade.exchange.get_pair_detail_url', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.freqtradebot.exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
@ -942,7 +945,7 @@ def test_performance_handle(default_conf, update, ticker, limit_buy_order,
|
||||
telegram._performance(bot=MagicMock(), update=update)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'Performance' in msg_mock.call_args_list[0][0][0]
|
||||
assert '<code>BTC_ETH\t6.20% (1)</code>' in msg_mock.call_args_list[0][0][0]
|
||||
assert '<code>ETH/BTC\t6.20% (1)</code>' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_performance_handle_invalid(default_conf, update, mocker) -> None:
|
||||
|
@ -12,23 +12,25 @@ def whitelist_conf():
|
||||
|
||||
config['stake_currency'] = 'BTC'
|
||||
config['exchange']['pair_whitelist'] = [
|
||||
'BTC_ETH',
|
||||
'BTC_TKN',
|
||||
'BTC_TRST',
|
||||
'BTC_SWT',
|
||||
'BTC_BCC'
|
||||
'ETH/BTC',
|
||||
'TKN/BTC',
|
||||
'TRST/BTC',
|
||||
'SWT/BTC',
|
||||
'BCC/BTC'
|
||||
]
|
||||
|
||||
config['exchange']['pair_blacklist'] = [
|
||||
'BTC_BLK'
|
||||
'BLK/BTC'
|
||||
]
|
||||
|
||||
return config
|
||||
|
||||
|
||||
def get_market_summaries():
|
||||
return [{
|
||||
'MarketName': 'BTC-TKN',
|
||||
return {
|
||||
'TKN/BTC': {
|
||||
'symbol': 'TKN/BTC',
|
||||
'info': {
|
||||
'High': 0.00000919,
|
||||
'Low': 0.00000820,
|
||||
'Volume': 74339.61396015,
|
||||
@ -42,8 +44,11 @@ def get_market_summaries():
|
||||
'PrevDay': 0.00000821,
|
||||
'Created': '2014-03-20T06:00:00',
|
||||
'DisplayMarketName': ''
|
||||
}, {
|
||||
'MarketName': 'BTC-ETH',
|
||||
}
|
||||
},
|
||||
'ETH/BTC': {
|
||||
'symbol': 'ETH/BTC',
|
||||
'info': {
|
||||
'High': 0.00000072,
|
||||
'Low': 0.00000001,
|
||||
'Volume': 166340678.42280999,
|
||||
@ -57,8 +62,11 @@ def get_market_summaries():
|
||||
'PrevDay': 0.00000002,
|
||||
'Created': '2014-05-30T07:57:49.637',
|
||||
'DisplayMarketName': ''
|
||||
}, {
|
||||
'MarketName': 'BTC-BLK',
|
||||
}
|
||||
},
|
||||
'BLK/BTC': {
|
||||
'symbol': 'BLK/BTC',
|
||||
'info': {
|
||||
'High': 0.00000072,
|
||||
'Low': 0.00000001,
|
||||
'Volume': 166340678.42280999,
|
||||
@ -72,17 +80,19 @@ def get_market_summaries():
|
||||
'PrevDay': 0.00000002,
|
||||
'Created': '2014-05-30T07:57:49.637',
|
||||
'DisplayMarketName': ''
|
||||
}]
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
def get_health():
|
||||
return [{'Currency': 'ETH', 'IsActive': True},
|
||||
{'Currency': 'TKN', 'IsActive': True},
|
||||
{'Currency': 'BLK', 'IsActive': True}]
|
||||
return {
|
||||
'ETH/BTC': {'base': 'ETH', 'active': True},
|
||||
'TKN/BTC': {'base': 'TKN', 'active': True},
|
||||
'BLK/BTC': {'base': 'BLK', 'active': True}}
|
||||
|
||||
|
||||
def get_health_empty():
|
||||
return []
|
||||
return {}
|
||||
|
||||
|
||||
def test_refresh_market_pair_not_in_whitelist(mocker):
|
||||
@ -92,10 +102,10 @@ def test_refresh_market_pair_not_in_whitelist(mocker):
|
||||
|
||||
mocker.patch('freqtrade.freqtradebot.exchange.get_wallet_health', get_health)
|
||||
refreshedwhitelist = freqtradebot._refresh_whitelist(
|
||||
conf['exchange']['pair_whitelist'] + ['BTC_XXX']
|
||||
conf['exchange']['pair_whitelist'] + ['XXX/BTC']
|
||||
)
|
||||
# List ordered by BaseVolume
|
||||
whitelist = ['BTC_ETH', 'BTC_TKN']
|
||||
whitelist = ['ETH/BTC', 'TKN/BTC']
|
||||
# Ensure all except those in whitelist are removed
|
||||
assert whitelist == refreshedwhitelist
|
||||
|
||||
@ -108,7 +118,7 @@ def test_refresh_whitelist(mocker):
|
||||
refreshedwhitelist = freqtradebot._refresh_whitelist(conf['exchange']['pair_whitelist'])
|
||||
|
||||
# List ordered by BaseVolume
|
||||
whitelist = ['BTC_ETH', 'BTC_TKN']
|
||||
whitelist = ['ETH/BTC', 'TKN/BTC']
|
||||
# Ensure all except those in whitelist are removed
|
||||
assert whitelist == refreshedwhitelist
|
||||
|
||||
@ -123,7 +133,7 @@ def test_refresh_whitelist_dynamic(mocker):
|
||||
)
|
||||
|
||||
# argument: use the whitelist dynamically by exchange-volume
|
||||
whitelist = ['BTC_TKN', 'BTC_ETH']
|
||||
whitelist = ['TKN/BTC', 'ETH/BTC']
|
||||
|
||||
refreshedwhitelist = freqtradebot._refresh_whitelist(
|
||||
freqtradebot._gen_pair_whitelist(conf['stake_currency'])
|
||||
|
@ -213,15 +213,15 @@ def test_gen_pair_whitelist(mocker, default_conf, get_market_summaries_data) ->
|
||||
|
||||
# Test to retrieved BTC sorted on BaseVolume
|
||||
whitelist = freqtrade._gen_pair_whitelist(base_currency='BTC')
|
||||
assert whitelist == ['BTC_ZCL', 'BTC_ZEC', 'BTC_XZC', 'BTC_XWC']
|
||||
assert whitelist == ['ZCL/BTC', 'ZEC/BTC', 'XZC/BTC', 'XWC/BTC']
|
||||
|
||||
# Test to retrieved BTC sorted on OpenBuyOrders
|
||||
whitelist = freqtrade._gen_pair_whitelist(base_currency='BTC', key='OpenBuyOrders')
|
||||
assert whitelist == ['BTC_XWC', 'BTC_ZCL', 'BTC_ZEC', 'BTC_XZC']
|
||||
assert whitelist == ['XWC/BTC', 'ZCL/BTC', 'ZEC/BTC', 'XZC/BTC']
|
||||
|
||||
# Test with USDT sorted on BaseVolume
|
||||
whitelist = freqtrade._gen_pair_whitelist(base_currency='USDT')
|
||||
assert whitelist == ['USDT_XRP', 'USDT_XVG', 'USDT_XMR', 'USDT_ZEC']
|
||||
assert whitelist == ['XRP/USDT', 'XVG/USDT', 'XMR/USDT', 'ZEC/USDT']
|
||||
|
||||
# Test with ETH (our fixture does not have ETH, but Bittrex returns them)
|
||||
whitelist = freqtrade._gen_pair_whitelist(base_currency='ETH')
|
||||
@ -330,8 +330,8 @@ def test_create_trade_no_pairs(default_conf, ticker, mocker) -> None:
|
||||
)
|
||||
|
||||
conf = deepcopy(default_conf)
|
||||
conf['exchange']['pair_whitelist'] = ["BTC_ETH"]
|
||||
conf['exchange']['pair_blacklist'] = ["BTC_ETH"]
|
||||
conf['exchange']['pair_whitelist'] = ["ETH/BTC"]
|
||||
conf['exchange']['pair_blacklist'] = ["ETH/BTC"]
|
||||
freqtrade = FreqtradeBot(conf, create_engine('sqlite://'))
|
||||
|
||||
freqtrade.create_trade()
|
||||
@ -355,8 +355,8 @@ def test_create_trade_no_pairs_after_blacklist(default_conf, ticker, mocker) ->
|
||||
)
|
||||
|
||||
conf = deepcopy(default_conf)
|
||||
conf['exchange']['pair_whitelist'] = ["BTC_ETH"]
|
||||
conf['exchange']['pair_blacklist'] = ["BTC_ETH"]
|
||||
conf['exchange']['pair_whitelist'] = ["ETH/BTC"]
|
||||
conf['exchange']['pair_blacklist'] = ["ETH/BTC"]
|
||||
freqtrade = FreqtradeBot(conf, create_engine('sqlite://'))
|
||||
|
||||
freqtrade.create_trade()
|
||||
@ -790,7 +790,7 @@ def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, mo
|
||||
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
|
||||
|
||||
trade_buy = Trade(
|
||||
pair='BTC_ETH',
|
||||
pair='ETH/BTC',
|
||||
open_rate=0.00001099,
|
||||
exchange='BITTREX',
|
||||
open_order_id='123456789',
|
||||
@ -829,7 +829,7 @@ def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old,
|
||||
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
|
||||
|
||||
trade_sell = Trade(
|
||||
pair='BTC_ETH',
|
||||
pair='ETH/BTC',
|
||||
open_rate=0.00001099,
|
||||
exchange='BITTREX',
|
||||
open_order_id='123456789',
|
||||
@ -868,7 +868,7 @@ def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old
|
||||
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
|
||||
|
||||
trade_buy = Trade(
|
||||
pair='BTC_ETH',
|
||||
pair='ETH/BTC',
|
||||
open_rate=0.00001099,
|
||||
exchange='BITTREX',
|
||||
open_order_id='123456789',
|
||||
@ -915,7 +915,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -
|
||||
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
|
||||
|
||||
trade_buy = Trade(
|
||||
pair='BTC_ETH',
|
||||
pair='ETH/BTC',
|
||||
open_rate=0.00001099,
|
||||
exchange='BITTREX',
|
||||
open_order_id='123456789',
|
||||
@ -928,7 +928,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -
|
||||
|
||||
Trade.session.add(trade_buy)
|
||||
regexp = re.compile(
|
||||
'Cannot query order for Trade(id=1, pair=BTC_ETH, amount=90.99181073, '
|
||||
'Cannot query order for Trade(id=1, pair=ETH/BTC, amount=90.99181073, '
|
||||
'open_rate=0.00001099, open_since=10 hours ago) due to Traceback (most '
|
||||
'recent call last):\n.*'
|
||||
)
|
||||
@ -1021,7 +1021,7 @@ def test_execute_sell_up(default_conf, ticker, ticker_sell_up, mocker) -> None:
|
||||
|
||||
assert rpc_mock.call_count == 2
|
||||
assert 'Selling' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '[BTC_ETH]' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '[ETH/BTC]' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert 'Amount' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert 'Profit' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '0.00001172' in rpc_mock.call_args_list[-1][0][0]
|
||||
@ -1061,7 +1061,7 @@ def test_execute_sell_down(default_conf, ticker, ticker_sell_down, mocker) -> No
|
||||
|
||||
assert rpc_mock.call_count == 2
|
||||
assert 'Selling' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '[BTC_ETH]' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '[ETH/BTC]' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert 'Amount' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '0.00001044' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert 'loss: -5.48%, -0.00005492' in rpc_mock.call_args_list[-1][0][0]
|
||||
@ -1100,7 +1100,7 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, ticker_sell_up,
|
||||
|
||||
assert rpc_mock.call_count == 2
|
||||
assert 'Selling' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '[BTC_ETH]' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '[ETH/BTC]' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert 'Amount' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '0.00001172' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '(profit: 6.11%, 0.00006126)' in rpc_mock.call_args_list[-1][0][0]
|
||||
@ -1140,7 +1140,7 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker,
|
||||
|
||||
assert rpc_mock.call_count == 2
|
||||
assert 'Selling' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '[BTC_ETH]' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '[ETH/BTC]' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert '0.00001044' in rpc_mock.call_args_list[-1][0][0]
|
||||
assert 'loss: -5.48%, -0.00005492' in rpc_mock.call_args_list[-1][0][0]
|
||||
|
||||
|
@ -145,7 +145,7 @@ def test_update_with_bittrex(limit_buy_order, limit_sell_order):
|
||||
|
||||
def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order):
|
||||
trade = Trade(
|
||||
pair='BTC_ETH',
|
||||
pair='ETH/BTC',
|
||||
stake_amount=0.001,
|
||||
fee=0.0025,
|
||||
exchange='bittrex',
|
||||
@ -167,7 +167,7 @@ def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order):
|
||||
|
||||
def test_calc_close_trade_price_exception(limit_buy_order):
|
||||
trade = Trade(
|
||||
pair='BTC_ETH',
|
||||
pair='ETH/BTC',
|
||||
stake_amount=0.001,
|
||||
fee=0.0025,
|
||||
exchange='bittrex',
|
||||
@ -180,7 +180,7 @@ def test_calc_close_trade_price_exception(limit_buy_order):
|
||||
|
||||
def test_update_open_order(limit_buy_order):
|
||||
trade = Trade(
|
||||
pair='BTC_ETH',
|
||||
pair='ETH/BTC',
|
||||
stake_amount=1.00,
|
||||
fee=0.1,
|
||||
exchange='bittrex',
|
||||
@ -202,7 +202,7 @@ def test_update_open_order(limit_buy_order):
|
||||
|
||||
def test_update_invalid_order(limit_buy_order):
|
||||
trade = Trade(
|
||||
pair='BTC_ETH',
|
||||
pair='ETH/BTC',
|
||||
stake_amount=1.00,
|
||||
fee=0.1,
|
||||
exchange='bittrex',
|
||||
@ -214,7 +214,7 @@ def test_update_invalid_order(limit_buy_order):
|
||||
|
||||
def test_calc_open_trade_price(limit_buy_order):
|
||||
trade = Trade(
|
||||
pair='BTC_ETH',
|
||||
pair='ETH/BTC',
|
||||
stake_amount=0.001,
|
||||
fee=0.0025,
|
||||
exchange='bittrex',
|
||||
@ -231,7 +231,7 @@ def test_calc_open_trade_price(limit_buy_order):
|
||||
|
||||
def test_calc_close_trade_price(limit_buy_order, limit_sell_order):
|
||||
trade = Trade(
|
||||
pair='BTC_ETH',
|
||||
pair='ETH/BTC',
|
||||
stake_amount=0.001,
|
||||
fee=0.0025,
|
||||
exchange='bittrex',
|
||||
@ -252,7 +252,7 @@ def test_calc_close_trade_price(limit_buy_order, limit_sell_order):
|
||||
|
||||
def test_calc_profit(limit_buy_order, limit_sell_order):
|
||||
trade = Trade(
|
||||
pair='BTC_ETH',
|
||||
pair='ETH/BTC',
|
||||
stake_amount=0.001,
|
||||
fee=0.0025,
|
||||
exchange='bittrex',
|
||||
@ -282,7 +282,7 @@ def test_calc_profit(limit_buy_order, limit_sell_order):
|
||||
|
||||
def test_calc_profit_percent(limit_buy_order, limit_sell_order):
|
||||
trade = Trade(
|
||||
pair='BTC_ETH',
|
||||
pair='ETH/BTC',
|
||||
stake_amount=0.001,
|
||||
fee=0.0025,
|
||||
exchange='bittrex',
|
||||
@ -309,35 +309,35 @@ def test_clean_dry_run_db(default_conf):
|
||||
|
||||
# Simulate dry_run entries
|
||||
trade = Trade(
|
||||
pair='BTC_ETH',
|
||||
pair='ETH/BTC',
|
||||
stake_amount=0.001,
|
||||
amount=123.0,
|
||||
fee=0.0025,
|
||||
open_rate=0.123,
|
||||
exchange='BITTREX',
|
||||
exchange='bittrex',
|
||||
open_order_id='dry_run_buy_12345'
|
||||
)
|
||||
Trade.session.add(trade)
|
||||
|
||||
trade = Trade(
|
||||
pair='BTC_ETC',
|
||||
pair='ETC/BTC',
|
||||
stake_amount=0.001,
|
||||
amount=123.0,
|
||||
fee=0.0025,
|
||||
open_rate=0.123,
|
||||
exchange='BITTREX',
|
||||
exchange='bittrex',
|
||||
open_order_id='dry_run_sell_12345'
|
||||
)
|
||||
Trade.session.add(trade)
|
||||
|
||||
# Simulate prod entry
|
||||
trade = Trade(
|
||||
pair='BTC_ETC',
|
||||
pair='ETC/BTC',
|
||||
stake_amount=0.001,
|
||||
amount=123.0,
|
||||
fee=0.0025,
|
||||
open_rate=0.123,
|
||||
exchange='BITTREX',
|
||||
exchange='bittrex',
|
||||
open_order_id='prod_buy_12345'
|
||||
)
|
||||
Trade.session.add(trade)
|
||||
|
@ -1,4 +1,4 @@
|
||||
python-bittrex==0.3.0
|
||||
ccxt==1.11.149
|
||||
SQLAlchemy==1.2.5
|
||||
python-telegram-bot==10.0.1
|
||||
arrow==0.12.1
|
||||
|
@ -55,7 +55,7 @@ def plot_analyzed_dataframe(args: Namespace) -> None:
|
||||
if args.live:
|
||||
logger.info('Downloading pair.')
|
||||
# Init Bittrex to use public API
|
||||
exchange._API = exchange.Bittrex({'key': '', 'secret': ''})
|
||||
exchange.init({'key': '', 'secret': ''})
|
||||
tickers[pair] = exchange.get_ticker_history(pair, tick_interval)
|
||||
else:
|
||||
tickers = optimize.load_data(
|
||||
|
Loading…
Reference in New Issue
Block a user