diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index b177de962..6406fe769 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -107,7 +107,17 @@ def validate_pairs(pairs: List[str]) -> None: ) if pair not in markets: raise OperationalException( - 'Pair {} is not available at {}'.format(pair, _API.id.lower())) + 'Pair {} is not available at {}'.format(pair, get_name())) + + +def exchange_has(endpoint: str) -> bool: + """ + Checks if exchange implements a specific API endpoint. + Wrapper around ccxt 'has' attribute + :param endpoint: Name of endpoint (e.g. 'fetchOHLCV', 'fetchTickers') + :return: bool + """ + return endpoint in _API.has and _API.has[endpoint] def buy(pair: str, rate: float, amount: float) -> Dict: @@ -216,6 +226,23 @@ def get_balances() -> dict: raise OperationalException(e) +@retrier +def get_tickers() -> Dict: + try: + return _API.fetch_tickers() + except ccxt.NetworkError as e: + raise NetworkException( + 'Could not load tickers due to networking error. Message: {}'.format(e) + ) + except ccxt.BaseError as e: + raise OperationalException(e) + except ccxt.NotSupported as e: + raise OperationalException( + 'Exchange {} does not support fetching tickers in batch.' + 'Message: {}'.format(_API.name, e) + ) + + # TODO: remove refresh argument, keeping it to keep track of where it was intended to be used @retrier def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict: @@ -231,11 +258,6 @@ def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict: @retrier def get_ticker_history(pair: str, tick_interval: str) -> List[Dict]: - if 'fetchOHLCV' not in _API.has or not _API.has['fetchOHLCV']: - raise OperationalException( - 'Exchange {} does not support fetching historical candlestick data.'.format(_API.name) - ) - try: return _API.fetch_ohlcv(pair, timeframe=tick_interval) except ccxt.NetworkError as e: @@ -244,6 +266,11 @@ def get_ticker_history(pair: str, tick_interval: str) -> List[Dict]: ) except ccxt.BaseError as e: raise OperationalException('Could not fetch ticker data. Msg: {}'.format(e)) + except ccxt.NotSupported as e: + raise OperationalException( + 'Exchange {} does not support fetching historical candlestick data.' + 'Message: {}'.format(_API.name, e) + ) def cancel_order(order_id: str, pair: str) -> None: @@ -315,18 +342,11 @@ def get_id() -> str: return _API.id -def get_fee_maker() -> float: - return _API.fees['trading']['maker'] - - -def get_fee_taker() -> float: - return _API.fees['trading']['taker'] - - -def get_fee() -> float: +def get_fee(symbol='ETH/BTC', type='', side='', amount=1, + price=1, taker_or_maker='maker') -> float: # validate that markets are loaded before trying to get fee if _API.markets is None or len(_API.markets) == 0: _API.load_markets() - return _API.calculate_fee(symbol='ETH/BTC', type='', side='', amount=1, price=1)['rate'] - + return _API.calculate_fee(symbol=symbol, type=type, side=side, amount=amount, + price=price, takerOrMaker=taker_or_maker)['rate'] diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index b3f0b8144..02d22a6dc 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -107,10 +107,7 @@ class FreqtradeBot(object): Constants.PROCESS_THROTTLE_SECS ) - nb_assets = self.config.get( - 'dynamic_whitelist', - Constants.DYNAMIC_WHITELIST - ) + nb_assets = self.config.get('dynamic_whitelist', None) self._throttle(func=self._process, min_secs=min_secs, @@ -185,49 +182,60 @@ class FreqtradeBot(object): return state_changed @cached(TTLCache(maxsize=1, ttl=1800)) - def _gen_pair_whitelist(self, base_currency: str, key: str = 'BaseVolume') -> List[str]: + def _gen_pair_whitelist(self, base_currency: str, key: str = 'quoteVolume') -> List[str]: """ Updates the whitelist with with a dynamically generated list :param base_currency: base currency as str - :param key: sort key (defaults to 'BaseVolume') + :param key: sort key (defaults to 'quoteVolume') :return: List of pairs """ - summaries = sorted( - (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['symbol'] for s in summaries] + if not exchange.exchange_has('fetchTickers'): + raise OperationalException( + 'Exchange does not support dynamic whitelist.' + 'Please edit your config and restart the bot' + ) + + tickers = exchange.get_tickers() + # check length so that we make sure that '/' is actually in the string + tickers = [v for k, v in tickers.items() + if len(k.split('/')) == 2 and k.split('/')[1] == base_currency] + + sorted_tickers = sorted(tickers, reverse=True, key=lambda t: t[key]) + pairs = [s['symbol'] for s in sorted_tickers] + return pairs def _refresh_whitelist(self, whitelist: List[str]) -> List[str]: """ - Check wallet health and remove pair from whitelist if necessary + Check available markets and remove pair from whitelist if necessary :param whitelist: the sorted list (based on BaseVolume) of pairs the user might want to trade :return: the list of pairs the user wants to trade without the one unavailable or black_listed """ sanitized_whitelist = whitelist - health = exchange.get_wallet_health() + markets = exchange.get_markets() + + markets = [m for m in markets if m['quote'] == self.config['stake_currency']] known_pairs = set() - for symbol, status in health.items(): - pair = f"{status['base']}/{self.config['stake_currency']}" + for market in markets: + pair = market['symbol'] # 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['active']: + if not market['active']: sanitized_whitelist.remove(pair) self.logger.info( - 'Ignoring %s from whitelist (reason: %s).', - pair, status.get('Notice') or 'wallet is not active' + 'Ignoring %s from whitelist. Market is not active.', + pair ) # We need to remove pairs that are unknown final_list = [x for x in sanitized_whitelist if x in known_pairs] + return final_list def get_target_bid(self, ticker: Dict[str, float]) -> float: @@ -285,7 +293,7 @@ class FreqtradeBot(object): buy_limit = self.get_target_bid(exchange.get_ticker(pair)) amount = stake_amount / buy_limit - order_id = exchange.buy(pair, buy_limit, amount) + order_id = exchange.buy(pair, buy_limit, amount)['id'] stake_amount_fiat = self.fiat_converter.convert_amount( stake_amount, @@ -297,7 +305,7 @@ class FreqtradeBot(object): self.rpc.send_msg( '*{}:* Buying [{}]({}) with limit `{:.8f} ({:.6f} {}, {:.3f} {})` ' .format( - exchange.get_name().upper(), + exchange.get_name(), pair.replace('_', '/'), exchange.get_pair_detail_url(pair), buy_limit, @@ -312,10 +320,10 @@ class FreqtradeBot(object): pair=pair, stake_amount=stake_amount, amount=amount, - fee=exchange.get_fee_maker(), + fee=exchange.get_fee(taker_or_maker='maker'), open_rate=buy_limit, open_date=datetime.utcnow(), - exchange=exchange.get_name().upper(), + exchange=exchange.get_id(), open_order_id=order_id ) Trade.session.add(trade) @@ -347,7 +355,7 @@ class FreqtradeBot(object): if trade.open_order_id: # Update trade with order values self.logger.info('Found open order for %s', trade) - trade.update(exchange.get_order(trade.open_order_id)) + trade.update(exchange.get_order(trade.open_order_id, trade.pair)) if trade.is_open and trade.open_order_id is None: # Check if we can sell our current pair @@ -386,22 +394,22 @@ class FreqtradeBot(object): for trade in Trade.query.filter(Trade.open_order_id.isnot(None)).all(): try: - order = exchange.get_order(trade.open_order_id) + order = exchange.get_order(trade.open_order_id, trade.pair) except requests.exceptions.RequestException: self.logger.info( 'Cannot query order for %s due to %s', trade, traceback.format_exc()) continue - ordertime = arrow.get(order['opened']) + ordertime = arrow.get(order['datetime']).datetime # Check if trade is still actually open if int(order['remaining']) == 0: continue - if order['type'] == "LIMIT_BUY" and ordertime < timeoutthreashold: + if order['side'] == 'buy' and ordertime < timeoutthreashold: self.handle_timedout_limit_buy(trade, order) - elif order['type'] == "LIMIT_SELL" and ordertime < timeoutthreashold: + elif order['side'] == 'sell' and ordertime < timeoutthreashold: self.handle_timedout_limit_sell(trade, order) # FIX: 20180110, why is cancel.order unconditionally here, whereas @@ -463,7 +471,7 @@ class FreqtradeBot(object): :return: None """ # Execute sell and update trade record - order_id = exchange.sell(str(trade.pair), limit, trade.amount) + order_id = exchange.sell(str(trade.pair), limit, trade.amount)['id'] trade.open_order_id = order_id fmt_exp_profit = round(trade.calc_profit_percent(rate=limit) * 100, 2) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 6a06c6616..268d199e0 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -6,13 +6,11 @@ This module contains the backtesting logic from argparse import Namespace from typing import Dict, Tuple, Any, List, Optional -import ccxt import arrow 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 @@ -57,6 +55,9 @@ class Backtesting(object): # Reset keys for backtesting self.config['exchange']['key'] = '' self.config['exchange']['secret'] = '' + self.config['exchange']['password'] = '' + self.config['exchange']['uid'] = '' + self.config['dry_run'] = True exchange.init(self.config) @staticmethod @@ -199,8 +200,8 @@ class Backtesting(object): # record a tuple of pair, current_profit_percent, # entry-date, duration records.append((pair, trade_entry[1], - row.date.strftime('%s'), - row2.date.strftime('%s'), + row.date.timestamp(), + row2.date.timestamp(), row.date, trade_entry[3])) # For now export inside backtest(), maybe change so that backtest() # returns a tuple like: (dataframe, records, logs, etc) diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index 56fe336d2..55f53329a 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -111,20 +111,20 @@ class Trade(_DECL_BASE): :return: None """ # Ignore open and cancelled orders - if not order['closed'] or order['rate'] is None: + if order['status'] == 'open' or order['price'] is None: return logger.info('Updating trade (id=%d) ...', self.id) getcontext().prec = 8 # Bittrex do not go above 8 decimal - if order['type'] == 'LIMIT_BUY': + if order['type'] == 'limit' and order['side'] == 'buy': # Update open rate and actual amount - self.open_rate = Decimal(order['rate']) + self.open_rate = Decimal(order['price']) self.amount = Decimal(order['amount']) logger.info('LIMIT_BUY has been fulfilled for %s.', self) self.open_order_id = None - elif order['type'] == 'LIMIT_SELL': - self.close(order['rate']) + elif order['type'] == 'limit' and order['side'] == 'sell': + self.close(order['price']) else: raise ValueError('Unknown order type: {}'.format(order['type'])) cleanup() diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index b00baa747..3a85a6e9e 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -50,7 +50,7 @@ class RPC(object): for trade in trades: order = None if trade.open_order_id: - order = exchange.get_order(trade.open_order_id) + order = exchange.get_order(trade.open_order_id, trade.pair) # calculate profit and send message to user current_rate = exchange.get_ticker(trade.pair, False)['bid'] current_profit = trade.calc_profit_percent(current_rate) @@ -78,8 +78,8 @@ class RPC(object): amount=round(trade.amount, 8), close_profit=fmt_close_profit, current_profit=round(current_profit * 100, 2), - open_order='({} rem={:.8f})'.format( - order['type'], order['remaining'] + open_order='({} {} rem={:.8f})'.format( + order['type'], order['side'], order['remaining'] ) if order else None, ) result.append(message) @@ -311,17 +311,21 @@ class RPC(object): def _exec_forcesell(trade: Trade) -> None: # Check if there is there is an open order if trade.open_order_id: - order = exchange.get_order(trade.open_order_id) + order = exchange.get_order(trade.open_order_id, trade.pair) # Cancel open LIMIT_BUY orders and close trade - if order and not order['closed'] and order['type'] == 'LIMIT_BUY': - exchange.cancel_order(trade.open_order_id) - trade.close(order.get('rate') or trade.open_rate) + if order and order['status'] == 'open' \ + and order['type'] == 'limit' \ + and order['side'] == 'buy': + exchange.cancel_order(trade.open_order_id, trade.pair) + trade.close(order.get('price') or trade.open_rate) # TODO: sell amount which has been bought already return # Ignore trades with an attached LIMIT_SELL order - if order and not order['closed'] and order['type'] == 'LIMIT_SELL': + if order and order['status'] == 'open' \ + and order['type'] == 'limit' \ + and order['side'] == 'sell': return # Get current rate and execute sell diff --git a/freqtrade/tests/conftest.py b/freqtrade/tests/conftest.py index 898babf44..4a8e294b9 100644 --- a/freqtrade/tests/conftest.py +++ b/freqtrade/tests/conftest.py @@ -99,6 +99,11 @@ def update(): return _update +@pytest.fixture +def fee(): + return MagicMock(return_value=0.0025) + + @pytest.fixture def ticker(): return MagicMock(return_value={ @@ -127,32 +132,80 @@ def ticker_sell_down(): @pytest.fixture -def health(): - return MagicMock(return_value={ - "ETH/BTC": { +def markets(): + return MagicMock(return_value=[ + { + 'id': 'ethbtc', + 'symbol': 'ETH/BTC', 'base': 'ETH', + 'quote': 'BTC', 'active': True, - 'LastChecked': '2017-11-13T20:15:00.00', - 'Notice': None + 'precision': { + 'price': 8, + 'amount': 8, + 'cost': 8, + }, + 'lot': 0.00000001, + 'limits': { + 'amount': { + 'min': 0.01, + 'max': 1000, + }, + 'price': 500000, + 'cost': 500000, + }, + 'info': '', }, - "TRST/BTC": { - 'base': 'TRST', + { + 'id': 'tknbtc', + 'symbol': 'TKN/BTC', + 'base': 'TKN', + 'quote': 'BTC', 'active': True, - 'LastChecked': '2017-11-13T20:15:00.00', - 'Notice': None + 'precision': { + 'price': 8, + 'amount': 8, + 'cost': 8, + }, + 'lot': 0.00000001, + 'limits': { + 'amount': { + 'min': 0.01, + 'max': 1000, + }, + 'price': 500000, + 'cost': 500000, + }, + 'info': '', }, - "SWT/BTC": { - 'base': 'SWT', + { + 'id': 'blkbtc', + 'symbol': 'BLK/BTC', + 'base': 'BLK', + 'quote': 'BTC', 'active': True, - 'LastChecked': '2017-11-13T20:15:00.00', - 'Notice': None - }, - "BCC/BTC": { - 'base': 'BCC', - 'active': False, - 'LastChecked': '2017-11-13T20:15:00.00', - 'Notice': None - }}) + 'precision': { + 'price': 8, + 'amount': 8, + 'cost': 8, + }, + 'lot': 0.00000001, + 'limits': { + 'amount': { + 'min': 0.01, + 'max': 1000, + }, + 'price': 500000, + 'cost': 500000, + }, + 'info': '', + } + ]) + + +@pytest.fixture +def markets_empty(): + return MagicMock(return_value=[]) @pytest.fixture @@ -231,7 +284,7 @@ def limit_sell_order(): @pytest.fixture -def ticker_history_api(): +def ticker_history(): return [ [ 1511686200000, # unix timestamp ms @@ -261,66 +314,179 @@ def ticker_history_api(): @pytest.fixture -def ticker_history(): - return [ - { - "O": 8.794e-05, - "H": 8.948e-05, - "L": 8.794e-05, - "C": 8.88e-05, - "V": 991.09056638, - "T": "2017-11-26T08:50:00", - "BV": 0.0877869 +def tickers(): + return MagicMock(return_value={ + 'ETH/BTC': { + 'symbol': 'ETH/BTC', + 'timestamp': 1522014806207, + 'datetime': '2018-03-25T21:53:26.207Z', + 'high': 0.061697, + 'low': 0.060531, + 'bid': 0.061588, + 'bidVolume': 3.321, + 'ask': 0.061655, + 'askVolume': 0.212, + 'vwap': 0.06105296, + 'open': 0.060809, + 'close': 0.060761, + 'first': None, + 'last': 0.061588, + 'change': 1.281, + 'percentage': None, + 'average': None, + 'baseVolume': 111649.001, + 'quoteVolume': 6816.50176926, + 'info': {} }, - { - "O": 8.88e-05, - "H": 8.942e-05, - "L": 8.88e-05, - "C": 8.893e-05, - "V": 658.77935965, - "T": "2017-11-26T08:55:00", - "BV": 0.05874751 + 'TKN/BTC': { + 'symbol': 'TKN/BTC', + 'timestamp': 1522014806169, + 'datetime': '2018-03-25T21:53:26.169Z', + 'high': 0.01885, + 'low': 0.018497, + 'bid': 0.018799, + 'bidVolume': 8.38, + 'ask': 0.018802, + 'askVolume': 15.0, + 'vwap': 0.01869197, + 'open': 0.018585, + 'close': 0.018573, + 'baseVolume': 81058.66, + 'quoteVolume': 2247.48374509, }, - { - "O": 8.891e-05, - "H": 8.893e-05, - "L": 8.875e-05, - "C": 8.877e-05, - "V": 7920.73570705, - "T": "2017-11-26T09:00:00", - "BV": 0.7039405 + 'BLK/BTC': { + 'symbol': 'BLK/BTC', + 'timestamp': 1522014806072, + 'datetime': '2018-03-25T21:53:26.720Z', + 'high': 0.007745, + 'low': 0.007512, + 'bid': 0.007729, + 'bidVolume': 0.01, + 'ask': 0.007743, + 'askVolume': 21.37, + 'vwap': 0.00761466, + 'open': 0.007653, + 'close': 0.007652, + 'first': None, + 'last': 0.007743, + 'change': 1.176, + 'percentage': None, + 'average': None, + 'baseVolume': 295152.26, + 'quoteVolume': 1515.14631229, + 'info': {} + }, + 'LTC/BTC': { + 'symbol': 'LTC/BTC', + 'timestamp': 1523787258992, + 'datetime': '2018-04-15T10:14:19.992Z', + 'high': 0.015978, + 'low': 0.0157, + 'bid': 0.015954, + 'bidVolume': 12.83, + 'ask': 0.015957, + 'askVolume': 0.49, + 'vwap': 0.01581636, + 'open': 0.015823, + 'close': 0.01582, + 'first': None, + 'last': 0.015951, + 'change': 0.809, + 'percentage': None, + 'average': None, + 'baseVolume': 88620.68, + 'quoteVolume': 1401.65697943, + 'info': {} + }, + 'ETH/USDT': { + 'symbol': 'ETH/USDT', + 'timestamp': 1522014804118, + 'datetime': '2018-03-25T21:53:24.118Z', + 'high': 530.88, + 'low': 512.0, + 'bid': 529.73, + 'bidVolume': 0.2, + 'ask': 530.21, + 'askVolume': 0.2464, + 'vwap': 521.02438405, + 'open': 527.27, + 'close': 528.42, + 'first': None, + 'last': 530.21, + 'change': 0.558, + 'percentage': None, + 'average': None, + 'baseVolume': 72300.0659, + 'quoteVolume': 37670097.3022171, + 'info': {} + }, + 'TKN/USDT': { + 'symbol': 'TKN/USDT', + 'timestamp': 1522014806198, + 'datetime': '2018-03-25T21:53:26.198Z', + 'high': 8718.0, + 'low': 8365.77, + 'bid': 8603.64, + 'bidVolume': 0.15846, + 'ask': 8603.67, + 'askVolume': 0.069147, + 'vwap': 8536.35621697, + 'open': 8680.0, + 'close': 8680.0, + 'first': None, + 'last': 8603.67, + 'change': -0.879, + 'percentage': None, + 'average': None, + 'baseVolume': 30414.604298, + 'quoteVolume': 259629896.48584127, + 'info': {} + }, + 'BLK/USDT': { + 'symbol': 'BLK/USDT', + 'timestamp': 1522014806145, + 'datetime': '2018-03-25T21:53:26.145Z', + 'high': 66.95, + 'low': 63.38, + 'bid': 66.473, + 'bidVolume': 4.968, + 'ask': 66.54, + 'askVolume': 2.704, + 'vwap': 65.0526901, + 'open': 66.43, + 'close': 66.383, + 'first': None, + 'last': 66.5, + 'change': 0.105, + 'percentage': None, + 'average': None, + 'baseVolume': 294106.204, + 'quoteVolume': 19132399.743954, + 'info': {} + }, + 'LTC/USDT': { + 'symbol': 'LTC/USDT', + 'timestamp': 1523787257812, + 'datetime': '2018-04-15T10:14:18.812Z', + 'high': 129.94, + 'low': 124.0, + 'bid': 129.28, + 'bidVolume': 0.03201, + 'ask': 129.52, + 'askVolume': 0.14529, + 'vwap': 126.92838682, + 'open': 127.0, + 'close': 127.1, + 'first': None, + 'last': 129.28, + 'change': 1.795, + 'percentage': None, + 'average': None, + 'baseVolume': 59698.79897, + 'quoteVolume': 29132399.743954, + 'info': {} } - ] - - -@pytest.fixture -def ticker_history_without_bv(): - return [ - { - "O": 8.794e-05, - "H": 8.948e-05, - "L": 8.794e-05, - "C": 8.88e-05, - "V": 991.09056638, - "T": "2017-11-26T08:50:00" - }, - { - "O": 8.88e-05, - "H": 8.942e-05, - "L": 8.88e-05, - "C": 8.893e-05, - "V": 658.77935965, - "T": "2017-11-26T08:55:00" - }, - { - "O": 8.891e-05, - "H": 8.893e-05, - "L": 8.875e-05, - "C": 8.877e-05, - "V": 7920.73570705, - "T": "2017-11-26T09:00:00" - } - ] + }) @pytest.fixture @@ -328,7 +494,6 @@ def result(): with open('freqtrade/tests/testdata/UNITTEST_BTC-1m.json') as data_file: return Analyze.parse_ticker_dataframe(json.load(data_file)) - # FIX: # Create an fixture/function # that inserts a trade of some type and open-status diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index b45573d6a..d7a4b35a9 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -68,7 +68,8 @@ def test_validate_pairs_not_compatible(default_conf, mocker): api_mock.load_markets = MagicMock(return_value={ 'ETH/BTC': '', 'TKN/BTC': '', 'TRST/BTC': '', 'SWT/BTC': '', 'BCC/BTC': '' }) - default_conf['stake_currency'] = 'ETH' + conf = deepcopy(default_conf) + conf['stake_currency'] = 'ETH' mocker.patch('freqtrade.exchange._API', api_mock) mocker.patch.dict('freqtrade.exchange._CONF', conf) with pytest.raises(OperationalException, match=r'not compatible'): @@ -78,18 +79,20 @@ def test_validate_pairs_not_compatible(default_conf, mocker): def test_validate_pairs_exception(default_conf, mocker, caplog): caplog.set_level(logging.INFO) api_mock = MagicMock() - api_mock.load_markets = MagicMock(side_effect=ccxt.BaseError()) - api_mock.name = 'binance' + api_mock.name = 'Binance' mocker.patch('freqtrade.exchange._API', api_mock) mocker.patch.dict('freqtrade.exchange._CONF', default_conf) - with pytest.raises(OperationalException, match=r'Pair ETH/BTC is not available at binance'): + api_mock.load_markets = MagicMock(return_value={}) + with pytest.raises(OperationalException, match=r'Pair ETH/BTC is not available at Binance'): validate_pairs(default_conf['exchange']['pair_whitelist']) + api_mock.load_markets = MagicMock(side_effect=ccxt.BaseError()) validate_pairs(default_conf['exchange']['pair_whitelist']) assert log_has('Unable to validate pairs (assuming they are correct). Reason: ', caplog.record_tuples) + def test_validate_pairs_stake_exception(default_conf, mocker, caplog): caplog.set_level(logging.INFO) conf = deepcopy(default_conf) @@ -105,6 +108,7 @@ def test_validate_pairs_stake_exception(default_conf, mocker, caplog): ): validate_pairs(default_conf['exchange']['pair_whitelist']) + def test_buy_dry_run(default_conf, mocker): default_conf['dry_run'] = True mocker.patch.dict('freqtrade.exchange._CONF', default_conf) @@ -113,6 +117,7 @@ def test_buy_dry_run(default_conf, mocker): assert 'id' in order assert 'dry_run_buy_' in order['id'] + def test_buy_prod(default_conf, mocker): api_mock = MagicMock() order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index 8aa840846..ba57ed274 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -6,6 +6,7 @@ import random from copy import deepcopy from typing import List from unittest.mock import MagicMock +import pytest import numpy as np import pandas as pd @@ -15,10 +16,20 @@ from freqtrade import optimize from freqtrade.analyze import Analyze from freqtrade.arguments import Arguments from freqtrade.optimize.backtesting import Backtesting, start, setup_configuration -from freqtrade.tests.conftest import default_conf, log_has +from freqtrade.tests.conftest import log_has # Avoid to reinit the same object again and again -_BACKTESTING = Backtesting(default_conf()) +_BACKTESTING = None +_BACKTESTING_INITIALIZED = False + + +@pytest.fixture(scope='function') +def init_backtesting(default_conf, mocker): + global _BACKTESTING_INITIALIZED, _BACKTESTING + if not _BACKTESTING_INITIALIZED: + mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True)) + _BACKTESTING = Backtesting(default_conf) + _BACKTESTING_INITIALIZED = True def get_args(args) -> List[str]: @@ -34,44 +45,54 @@ def trim_dictlist(dict_list, num): def load_data_test(what): timerange = ((None, 'line'), None, -100) - data = optimize.load_data(None, ticker_interval='1m', pairs=['UNITTEST/BTC'], timerange=timerange) + data = optimize.load_data(None, ticker_interval='1m', + pairs=['UNITTEST/BTC'], timerange=timerange) pair = data['UNITTEST/BTC'] datalen = len(pair) # Depending on the what parameter we now adjust the # loaded data looks: - # pair :: [{'O': 0.123, 'H': 0.123, 'L': 0.123, - # 'C': 0.123, 'V': 123.123, - # 'T': '2017-11-04T23:02:00', 'BV': 0.123}] + # pair :: [[ 1509836520000, unix timestamp in ms + # 0.00162008, open + # 0.00162008, high + # 0.00162008, low + # 0.00162008, close + # 108.14853839 base volume + # ]] base = 0.001 if what == 'raise': - return {'UNITTEST/BTC': - [{'T': pair[x]['T'], # Keep old dates - 'V': pair[x]['V'], # Keep old volume - 'BV': pair[x]['BV'], # keep too - 'O': x * base, # But replace O,H,L,C - 'H': x * base + 0.0001, - 'L': x * base - 0.0001, - 'C': x * base} for x in range(0, datalen)]} + return {'UNITTEST/BTC': [ + [ + pair[x][0], # Keep old dates + x * base, # But replace O,H,L,C + x * base + 0.0001, + x * base - 0.0001, + x * base, + pair[x][5], # Keep old volume + ] for x in range(0, datalen) + ]} if what == 'lower': - return {'UNITTEST/BTC': - [{'T': pair[x]['T'], # Keep old dates - 'V': pair[x]['V'], # Keep old volume - 'BV': pair[x]['BV'], # keep too - 'O': 1 - x * base, # But replace O,H,L,C - 'H': 1 - x * base + 0.0001, - 'L': 1 - x * base - 0.0001, - 'C': 1 - x * base} for x in range(0, datalen)]} + return {'UNITTEST/BTC': [ + [ + pair[x][0], # Keep old dates + 1 - x * base, # But replace O,H,L,C + 1 - x * base + 0.0001, + 1 - x * base - 0.0001, + 1 - x * base, + pair[x][5] # Keep old volume + ] for x in range(0, datalen) + ]} if what == 'sine': hz = 0.1 # frequency - return {'UNITTEST/BTC': - [{'T': pair[x]['T'], # Keep old dates - 'V': pair[x]['V'], # Keep old volume - 'BV': pair[x]['BV'], # keep too - # But replace O,H,L,C - 'O': math.sin(x * hz) / 1000 + base, - 'H': math.sin(x * hz) / 1000 + base + 0.0001, - 'L': math.sin(x * hz) / 1000 + base - 0.0001, - 'C': math.sin(x * hz) / 1000 + base} for x in range(0, datalen)]} + return {'UNITTEST/BTC': [ + [ + pair[x][0], # Keep old dates + math.sin(x * hz) / 1000 + base, # But replace O,H,L,C + math.sin(x * hz) / 1000 + base + 0.0001, + math.sin(x * hz) / 1000 + base - 0.0001, + math.sin(x * hz) / 1000 + base, + pair[x][5] # Keep old volume + ] for x in range(0, datalen) + ]} return data @@ -247,11 +268,11 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non assert 'live' in config assert log_has('Parameter -l/--live detected ...', caplog.record_tuples) - assert 'realistic_simulation'in config + assert 'realistic_simulation' in config assert log_has('Parameter --realistic-simulation detected ...', caplog.record_tuples) assert log_has('Using max_open_trades: 1 ...', caplog.record_tuples) - assert 'refresh_pairs'in config + assert 'refresh_pairs' in config assert log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples) assert 'timerange' in config assert log_has( @@ -266,7 +287,7 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non ) -def test_start(mocker, default_conf, caplog) -> None: +def test_start(mocker, init_backtesting, default_conf, caplog) -> None: """ Test start() function """ @@ -306,10 +327,11 @@ def test_backtesting__init__(mocker, default_conf) -> None: assert init_mock.call_count == 1 -def test_backtesting_init(default_conf) -> None: +def test_backtesting_init(mocker, default_conf) -> None: """ Test Backtesting._init() method """ + mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True)) backtesting = Backtesting(default_conf) assert backtesting.config == default_conf assert isinstance(backtesting.analyze, Analyze) @@ -319,7 +341,7 @@ def test_backtesting_init(default_conf) -> None: assert callable(backtesting.populate_sell_trend) -def test_tickerdata_to_dataframe(default_conf) -> None: +def test_tickerdata_to_dataframe(init_backtesting, default_conf) -> None: """ Test Backtesting.tickerdata_to_dataframe() method """ @@ -338,7 +360,7 @@ def test_tickerdata_to_dataframe(default_conf) -> None: assert data['UNITTEST/BTC'].equals(data2['UNITTEST/BTC']) -def test_get_timeframe() -> None: +def test_get_timeframe(init_backtesting) -> None: """ Test Backtesting.get_timeframe() method """ @@ -356,7 +378,7 @@ def test_get_timeframe() -> None: assert max_date.isoformat() == '2017-11-14T22:59:00+00:00' -def test_generate_text_table(): +def test_generate_text_table(init_backtesting): """ Test Backtesting.generate_text_table() method """ @@ -391,12 +413,14 @@ def test_backtesting_start(default_conf, mocker, caplog) -> None: """ Test Backtesting.start() method """ + def get_timeframe(input1, input2): return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59) mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock()) mocker.patch('freqtrade.optimize.load_data', mocked_load_data) mocker.patch('freqtrade.exchange.get_ticker_history') + mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True)) mocker.patch.multiple( 'freqtrade.optimize.backtesting.Backtesting', backtest=MagicMock(), @@ -426,7 +450,7 @@ def test_backtesting_start(default_conf, mocker, caplog) -> None: assert log_has(line, caplog.record_tuples) -def test_backtest(default_conf) -> None: +def test_backtest(init_backtesting, default_conf) -> None: """ Test Backtesting.backtest() method """ @@ -445,7 +469,7 @@ def test_backtest(default_conf) -> None: assert not results.empty -def test_backtest_1min_ticker_interval(default_conf) -> None: +def test_backtest_1min_ticker_interval(init_backtesting, default_conf) -> None: """ Test Backtesting.backtest() method with 1 min ticker """ @@ -465,7 +489,7 @@ def test_backtest_1min_ticker_interval(default_conf) -> None: assert not results.empty -def test_processed() -> None: +def test_processed(init_backtesting) -> None: """ Test Backtesting.backtest() method with offline data """ @@ -481,14 +505,15 @@ def test_processed() -> None: assert col in cols -def test_backtest_pricecontours(default_conf) -> None: +def test_backtest_pricecontours(init_backtesting, default_conf, fee, mocker) -> None: + mocker.patch('freqtrade.optimize.backtesting.exchange.get_fee', fee) tests = [['raise', 17], ['lower', 0], ['sine', 17]] for [contour, numres] in tests: simple_backtest(default_conf, contour, numres) # Test backtest using offline data (testdata directory) -def test_backtest_ticks(default_conf): +def test_backtest_ticks(init_backtesting, default_conf): ticks = [1, 5] fun = _BACKTESTING.populate_buy_trend for tick in ticks: @@ -497,7 +522,7 @@ def test_backtest_ticks(default_conf): assert not results.empty -def test_backtest_clash_buy_sell(default_conf): +def test_backtest_clash_buy_sell(init_backtesting, default_conf): # Override the default buy trend function in our default_strategy def fun(dataframe=None): buy_value = 1 @@ -509,7 +534,7 @@ def test_backtest_clash_buy_sell(default_conf): assert results.empty -def test_backtest_only_sell(default_conf): +def test_backtest_only_sell(init_backtesting, default_conf): # Override the default buy trend function in our default_strategy def fun(dataframe=None): buy_value = 0 @@ -521,15 +546,17 @@ def test_backtest_only_sell(default_conf): assert results.empty -def test_backtest_alternate_buy_sell(default_conf): +def test_backtest_alternate_buy_sell(init_backtesting, default_conf, fee, mocker): + mocker.patch('freqtrade.optimize.backtesting.exchange.get_fee', fee) backtest_conf = _make_backtest_conf(conf=default_conf, pair='UNITTEST/BTC') results = _run_backtest_1(_trend_alternate, backtest_conf) assert len(results) == 3 -def test_backtest_record(default_conf, mocker): +def test_backtest_record(init_backtesting, default_conf, fee, mocker): names = [] records = [] + mocker.patch('freqtrade.optimize.backtesting.exchange.get_fee', fee) mocker.patch( 'freqtrade.optimize.backtesting.file_dump_json', new=lambda n, r: (names.append(n), records.append(r)) @@ -562,14 +589,16 @@ def test_backtest_record(default_conf, mocker): assert dur > 0 -def test_backtest_start_live(default_conf, mocker, caplog): - default_conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC'] +def test_backtest_start_live(init_backtesting, default_conf, mocker, caplog): + conf = deepcopy(default_conf) + conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC'] mocker.patch('freqtrade.exchange.get_ticker_history', new=lambda n, i: _load_pair_as_ticks(n, i)) + mocker.patch('freqtrade.exchange.validate_pairs', MagicMock()) mocker.patch('freqtrade.optimize.backtesting.Backtesting.backtest', MagicMock()) mocker.patch('freqtrade.optimize.backtesting.Backtesting._generate_text_table', MagicMock()) mocker.patch('freqtrade.configuration.open', mocker.mock_open( - read_data=json.dumps(default_conf) + read_data=json.dumps(conf) )) args = MagicMock() diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py index dba40b2f5..9f6b2e5df 100644 --- a/freqtrade/tests/optimize/test_hyperopt.py +++ b/freqtrade/tests/optimize/test_hyperopt.py @@ -1,20 +1,34 @@ # pragma pylint: disable=missing-docstring,W0212,C0103 -import json import os +import signal from copy import deepcopy from unittest.mock import MagicMock +import pytest import pandas as pd from freqtrade.optimize.__init__ import load_tickerdata_file from freqtrade.optimize.hyperopt import Hyperopt, start from freqtrade.strategy.strategy import Strategy -from freqtrade.tests.conftest import default_conf, log_has +from freqtrade.tests.conftest import log_has from freqtrade.tests.optimize.test_backtesting import get_args # Avoid to reinit the same object again and again -_HYPEROPT = Hyperopt(default_conf()) +_HYPEROPT_INITIALIZED = False +_HYPEROPT = None + + +@pytest.fixture(scope='function') +def init_hyperopt(default_conf, mocker): + global _HYPEROPT_INITIALIZED, _HYPEROPT + if not _HYPEROPT_INITIALIZED: + mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True)) + mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', + MagicMock(return_value=default_conf)) + mocker.patch('freqtrade.exchange.validate_pairs', MagicMock()) + _HYPEROPT = Hyperopt(default_conf) + _HYPEROPT_INITIALIZED = True # Functions for recurrent object patching @@ -52,9 +66,10 @@ def test_start(mocker, default_conf, caplog) -> None: start_mock = MagicMock() mocker.patch('freqtrade.logger.Logger.set_format', MagicMock()) mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock) - mocker.patch('freqtrade.configuration.open', mocker.mock_open( - read_data=json.dumps(default_conf) - )) + mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', + MagicMock(return_value=default_conf)) + mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock()) + args = [ '--config', 'config.json', '--strategy', 'default_strategy', @@ -75,7 +90,7 @@ def test_start(mocker, default_conf, caplog) -> None: assert start_mock.call_count == 1 -def test_loss_calculation_prefer_correct_trade_count() -> None: +def test_loss_calculation_prefer_correct_trade_count(init_hyperopt) -> None: """ Test Hyperopt.calculate_loss() """ @@ -89,7 +104,7 @@ def test_loss_calculation_prefer_correct_trade_count() -> None: assert under > correct -def test_loss_calculation_prefer_shorter_trades() -> None: +def test_loss_calculation_prefer_shorter_trades(init_hyperopt) -> None: """ Test Hyperopt.calculate_loss() """ @@ -100,7 +115,7 @@ def test_loss_calculation_prefer_shorter_trades() -> None: assert shorter < longer -def test_loss_calculation_has_limited_profit() -> None: +def test_loss_calculation_has_limited_profit(init_hyperopt) -> None: hyperopt = _HYPEROPT correct = hyperopt.calculate_loss(hyperopt.expected_max_profit, hyperopt.target_trades, 20) @@ -110,7 +125,7 @@ def test_loss_calculation_has_limited_profit() -> None: assert under > correct -def test_log_results_if_loss_improves(caplog) -> None: +def test_log_results_if_loss_improves(init_hyperopt, caplog) -> None: hyperopt = _HYPEROPT hyperopt.current_best_loss = 2 hyperopt.log_results( @@ -124,7 +139,7 @@ def test_log_results_if_loss_improves(caplog) -> None: assert log_has(' 1/2: foo. Loss 1.00000', caplog.record_tuples) -def test_no_log_if_loss_does_not_improve(caplog) -> None: +def test_no_log_if_loss_does_not_improve(init_hyperopt, caplog) -> None: hyperopt = _HYPEROPT hyperopt.current_best_loss = 2 hyperopt.log_results( @@ -135,7 +150,7 @@ def test_no_log_if_loss_does_not_improve(caplog) -> None: assert caplog.record_tuples == [] -def test_fmin_best_results(mocker, default_conf, caplog) -> None: +def test_fmin_best_results(mocker, init_hyperopt, default_conf, caplog) -> None: fmin_result = { "macd_below_zero": 0, "adx": 1, @@ -170,6 +185,7 @@ def test_fmin_best_results(mocker, default_conf, caplog) -> None: mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value=fmin_result) mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf) mocker.patch('freqtrade.logger.Logger.set_format', MagicMock()) + mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock()) Strategy({'strategy': 'default_strategy'}) hyperopt = Hyperopt(conf) @@ -204,7 +220,7 @@ def test_fmin_best_results(mocker, default_conf, caplog) -> None: assert line in caplog.text -def test_fmin_throw_value_error(mocker, default_conf, caplog) -> None: +def test_fmin_throw_value_error(mocker, init_hyperopt, default_conf, caplog) -> None: mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock()) mocker.patch('freqtrade.optimize.hyperopt.fmin', side_effect=ValueError()) @@ -215,6 +231,8 @@ def test_fmin_throw_value_error(mocker, default_conf, caplog) -> None: conf.update({'spaces': 'all'}) mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf) mocker.patch('freqtrade.logger.Logger.set_format', MagicMock()) + mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock()) + Strategy({'strategy': 'default_strategy'}) hyperopt = Hyperopt(conf) hyperopt.trials = create_trials(mocker) @@ -232,7 +250,7 @@ def test_fmin_throw_value_error(mocker, default_conf, caplog) -> None: assert line in caplog.text -def test_resuming_previous_hyperopt_results_succeeds(mocker, default_conf) -> None: +def test_resuming_previous_hyperopt_results_succeeds(mocker, init_hyperopt, default_conf) -> None: trials = create_trials(mocker) conf = deepcopy(default_conf) @@ -257,6 +275,7 @@ def test_resuming_previous_hyperopt_results_succeeds(mocker, default_conf) -> No mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={}) mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf) mocker.patch('freqtrade.logger.Logger.set_format', MagicMock()) + mocker.patch('freqtrade.exchange.validate_pairs', MagicMock()) Strategy({'strategy': 'default_strategy'}) hyperopt = Hyperopt(conf) @@ -275,7 +294,7 @@ def test_resuming_previous_hyperopt_results_succeeds(mocker, default_conf) -> No assert total_tries == (current_tries + len(trials.results)) -def test_save_trials_saves_trials(mocker, caplog) -> None: +def test_save_trials_saves_trials(mocker, init_hyperopt, caplog) -> None: create_trials(mocker) mock_dump = mocker.patch('freqtrade.optimize.hyperopt.pickle.dump', return_value=None) @@ -284,22 +303,24 @@ def test_save_trials_saves_trials(mocker, caplog) -> None: hyperopt.save_trials() + trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle') assert log_has( - 'Saving Trials to \'freqtrade/tests/optimize/ut_trials.pickle\'', + 'Saving Trials to \'{}\''.format(trials_file), caplog.record_tuples ) mock_dump.assert_called_once() -def test_read_trials_returns_trials_file(mocker, caplog) -> None: +def test_read_trials_returns_trials_file(mocker, init_hyperopt, caplog) -> None: trials = create_trials(mocker) mock_load = mocker.patch('freqtrade.optimize.hyperopt.pickle.load', return_value=trials) mock_open = mocker.patch('freqtrade.optimize.hyperopt.open', return_value=mock_load) hyperopt = _HYPEROPT hyperopt_trial = hyperopt.read_trials() + trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle') assert log_has( - 'Reading Trials from \'freqtrade/tests/optimize/ut_trials.pickle\'', + 'Reading Trials from \'{}\''.format(trials_file), caplog.record_tuples ) assert hyperopt_trial == trials @@ -307,7 +328,7 @@ def test_read_trials_returns_trials_file(mocker, caplog) -> None: mock_load.assert_called_once() -def test_roi_table_generation() -> None: +def test_roi_table_generation(init_hyperopt) -> None: params = { 'roi_t1': 5, 'roi_t2': 10, @@ -321,10 +342,11 @@ def test_roi_table_generation() -> None: assert hyperopt.generate_roi_table(params) == {0: 6, 15: 3, 25: 1, 30: 0} -def test_start_calls_fmin(mocker, default_conf) -> None: +def test_start_calls_fmin(mocker, init_hyperopt, default_conf) -> None: trials = create_trials(mocker) mocker.patch('freqtrade.optimize.hyperopt.sorted', return_value=trials.results) mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock()) + mocker.patch('freqtrade.exchange.validate_pairs', MagicMock()) mock_fmin = mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={}) conf = deepcopy(default_conf) @@ -342,7 +364,7 @@ def test_start_calls_fmin(mocker, default_conf) -> None: mock_fmin.assert_called_once() -def test_start_uses_mongotrials(mocker, default_conf) -> None: +def test_start_uses_mongotrials(mocker, init_hyperopt, default_conf) -> None: mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock()) mock_fmin = mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={}) mock_mongotrials = mocker.patch( @@ -357,6 +379,7 @@ def test_start_uses_mongotrials(mocker, default_conf) -> None: conf.update({'timerange': None}) conf.update({'spaces': 'all'}) mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf) + mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock()) hyperopt = Hyperopt(conf) hyperopt.tickerdata_to_dataframe = MagicMock() @@ -385,7 +408,7 @@ def test_format_results(): assert x.find(' 66.67%') -def test_signal_handler(mocker): +def test_signal_handler(mocker, init_hyperopt): """ Test Hyperopt.signal_handler() """ @@ -395,11 +418,11 @@ def test_signal_handler(mocker): mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.log_trials_result', m) hyperopt = _HYPEROPT - hyperopt.signal_handler(9, None) + hyperopt.signal_handler(signal.SIGTERM, None) assert m.call_count == 3 -def test_has_space(): +def test_has_space(init_hyperopt): """ Test Hyperopt.has_space() method """ @@ -412,7 +435,7 @@ def test_has_space(): assert _HYPEROPT.has_space('buy') -def test_populate_indicators() -> None: +def test_populate_indicators(init_hyperopt) -> None: """ Test Hyperopt.populate_indicators() """ @@ -427,7 +450,7 @@ def test_populate_indicators() -> None: assert 'cci' in dataframe -def test_buy_strategy_generator() -> None: +def test_buy_strategy_generator(init_hyperopt) -> None: """ Test Hyperopt.buy_strategy_generator() """ @@ -484,7 +507,7 @@ def test_buy_strategy_generator() -> None: assert 1 in result['buy'] -def test_generate_optimizer(mocker, default_conf) -> None: +def test_generate_optimizer(mocker, init_hyperopt, default_conf) -> None: """ Test Hyperopt.generate_optimizer() function """ @@ -503,6 +526,7 @@ def test_generate_optimizer(mocker, default_conf) -> None: 'freqtrade.optimize.hyperopt.Hyperopt.backtest', MagicMock(return_value=backtest_result) ) + mocker.patch('freqtrade.exchange.validate_pairs', MagicMock()) optimizer_param = { 'adx': {'enabled': False}, diff --git a/freqtrade/tests/optimize/test_optimize.py b/freqtrade/tests/optimize/test_optimize.py index befb7fa23..411714ec8 100644 --- a/freqtrade/tests/optimize/test_optimize.py +++ b/freqtrade/tests/optimize/test_optimize.py @@ -52,11 +52,11 @@ def test_load_data_30min_ticker(ticker_history, mocker, caplog) -> None: """ mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history) - file = 'freqtrade/tests/testdata/UNITTEST_BTC-30m.json' + file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-30m.json') _backup_file(file, copy_file=True) optimize.load_data(None, pairs=['UNITTEST/BTC'], ticker_interval='30m') assert os.path.isfile(file) is True - assert not log_has('Download the pair: "ETH/BTC", Interval: 30 min', caplog.record_tuples) + assert not log_has('Download the pair: "UNITTEST/BTC", Interval: 30m', caplog.record_tuples) _clean_test_file(file) @@ -66,11 +66,11 @@ def test_load_data_5min_ticker(ticker_history, mocker, caplog) -> None: """ mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history) - file = 'freqtrade/tests/testdata/ETH_BTC-5m.json' + file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-5m.json') _backup_file(file, copy_file=True) - optimize.load_data(None, pairs=['ETH/BTC'], ticker_interval='5m') + optimize.load_data(None, pairs=['UNITTEST/BTC'], ticker_interval='5m') assert os.path.isfile(file) is True - assert not log_has('Download the pair: "ETH/BTC", Interval: 5 min', caplog.record_tuples) + assert not log_has('Download the pair: "UNITTEST/BTC", Interval: 5m', caplog.record_tuples) _clean_test_file(file) @@ -80,11 +80,11 @@ def test_load_data_1min_ticker(ticker_history, mocker, caplog) -> None: """ mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history) - file = 'freqtrade/tests/testdata/ETH_BTC-1m.json' + file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-1m.json') _backup_file(file, copy_file=True) - optimize.load_data(None, ticker_interval='1m', pairs=['ETH/BTC']) + optimize.load_data(None, ticker_interval='1m', pairs=['UNITTEST/BTC']) assert os.path.isfile(file) is True - assert not log_has('Download the pair: "ETH/BTC", Interval: 1 min', caplog.record_tuples) + assert not log_has('Download the pair: "UNITTEST/BTC", Interval: 1m', caplog.record_tuples) _clean_test_file(file) @@ -94,11 +94,12 @@ def test_load_data_with_new_pair_1min(ticker_history, mocker, caplog) -> None: """ mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history) - file = 'freqtrade/tests/testdata/MEME_BTC-1m.json' + file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json') + _backup_file(file) optimize.load_data(None, ticker_interval='1m', pairs=['MEME/BTC']) assert os.path.isfile(file) is True - assert log_has('Download the pair: "MEME/BTC", Interval: 1 min', caplog.record_tuples) + assert log_has('Download the pair: "MEME/BTC", Interval: 1m', caplog.record_tuples) _clean_test_file(file) @@ -109,10 +110,10 @@ def test_testdata_path() -> None: def test_download_pairs(ticker_history, mocker) -> None: mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history) - file1_1 = 'freqtrade/tests/testdata/MEME_BTC-1m.json' - file1_5 = 'freqtrade/tests/testdata/MEME_BTC-5m.json' - file2_1 = 'freqtrade/tests/testdata/CFI_BTC-1m.json' - file2_5 = 'freqtrade/tests/testdata/CFI_BTC-5m.json' + file1_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json') + file1_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-5m.json') + file2_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'CFI_BTC-1m.json') + file2_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'CFI_BTC-5m.json') _backup_file(file1_1) _backup_file(file1_5) @@ -149,8 +150,8 @@ def test_download_pairs_exception(ticker_history, mocker, caplog) -> None: mocker.patch('freqtrade.optimize.__init__.download_backtesting_testdata', side_effect=BaseException('File Error')) - file1_1 = 'freqtrade/tests/testdata/MEME_BTC-1m.json' - file1_5 = 'freqtrade/tests/testdata/MEME_BTC-5m.json' + file1_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json') + file1_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-5m.json') _backup_file(file1_1) _backup_file(file1_5) @@ -158,21 +159,21 @@ def test_download_pairs_exception(ticker_history, mocker, caplog) -> None: # clean files freshly downloaded _clean_test_file(file1_1) _clean_test_file(file1_5) - assert log_has('Failed to download the pair: "MEME/BTC", Interval: 1 min', caplog.record_tuples) + assert log_has('Failed to download the pair: "MEME/BTC", Interval: 1m', caplog.record_tuples) def test_download_backtesting_testdata(ticker_history, mocker) -> None: mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history) # Download a 1 min ticker file - file1 = 'freqtrade/tests/testdata/XEL_BTC-1m.json' + file1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'XEL_BTC-1m.json') _backup_file(file1) download_backtesting_testdata(None, pair="XEL/BTC", interval='1m') assert os.path.isfile(file1) is True _clean_test_file(file1) # Download a 5 min ticker file - file2 = 'freqtrade/tests/testdata/STORJ_BTC-5m.json' + file2 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'STORJ_BTC-5m.json') _backup_file(file2) download_backtesting_testdata(None, pair="STORJ/BTC", interval='5m') @@ -181,7 +182,10 @@ def test_download_backtesting_testdata(ticker_history, mocker) -> None: def test_download_backtesting_testdata2(mocker) -> None: - tick = [{'T': 'bar'}, {'T': 'foo'}] + tick = [ + [1509836520000, 0.00162008, 0.00162008, 0.00162008, 0.00162008, 108.14853839], + [1509836580000, 0.00161, 0.00161, 0.00161, 0.00161, 82.390199] + ] mocker.patch('freqtrade.misc.file_dump_json', return_value=None) mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=tick) assert download_backtesting_testdata(None, pair="UNITTEST/BTC", interval='1m') @@ -211,7 +215,8 @@ def test_init(default_conf, mocker) -> None: def test_trim_tickerlist() -> None: - with open('freqtrade/tests/testdata/ETH_BTC-1m.json') as data_file: + file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-1m.json') + with open(file) as data_file: ticker_list = json.load(data_file) ticker_list_len = len(ticker_list) @@ -260,7 +265,8 @@ def test_file_dump_json() -> None: Test file_dump_json() :return: None """ - file = 'freqtrade/tests/testdata/test_{id}.json'.format(id=str(uuid.uuid4())) + file = os.path.join(os.path.dirname(__file__), '..', 'testdata', + 'test_{id}.json'.format(id=str(uuid.uuid4()))) data = {'bar': 'foo'} # check the file we will create does not exist diff --git a/freqtrade/tests/rpc/test_rpc.py b/freqtrade/tests/rpc/test_rpc.py index bbaa2385f..b6722569e 100644 --- a/freqtrade/tests/rpc/test_rpc.py +++ b/freqtrade/tests/rpc/test_rpc.py @@ -25,7 +25,7 @@ def prec_satoshi(a, b) -> float: # Unit tests -def test_rpc_trade_status(default_conf, ticker, mocker) -> None: +def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: """ Test rpc_trade_status() method """ @@ -35,7 +35,8 @@ def test_rpc_trade_status(default_conf, ticker, mocker) -> None: mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker + get_ticker=ticker, + get_fee=fee ) freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -67,13 +68,13 @@ def test_rpc_trade_status(default_conf, ticker, mocker) -> None: '*Current Rate:* `0.00001098`\n' '*Close Profit:* `None`\n' '*Current Profit:* `-0.59%`\n' - '*Open Order:* `(LIMIT_BUY rem=0.00000000)`' + '*Open Order:* `(limit buy rem=0.00000000)`' ] assert result == result_message assert trade.find('[ETH/BTC]') >= 0 -def test_rpc_status_table(default_conf, ticker, mocker) -> None: +def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: """ Test rpc_status_table() method """ @@ -83,7 +84,8 @@ def test_rpc_status_table(default_conf, ticker, mocker) -> None: mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker + get_ticker=ticker, + get_fee=fee ) freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -106,8 +108,8 @@ def test_rpc_status_table(default_conf, ticker, mocker) -> None: assert '-0.59%' in result['Profit'].all() -def test_rpc_daily_profit(default_conf, update, ticker, limit_buy_order, limit_sell_order, mocker)\ - -> None: +def test_rpc_daily_profit(default_conf, update, ticker, fee, + limit_buy_order, limit_sell_order, mocker) -> None: """ Test rpc_daily_profit() method """ @@ -117,7 +119,8 @@ def test_rpc_daily_profit(default_conf, update, ticker, limit_buy_order, limit_s mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker + get_ticker=ticker, + get_fee=fee ) freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -158,8 +161,8 @@ def test_rpc_daily_profit(default_conf, update, ticker, limit_buy_order, limit_s assert days.find('must be an integer greater than 0') >= 0 -def test_rpc_trade_statistics( - default_conf, ticker, ticker_sell_up, limit_buy_order, limit_sell_order, mocker) -> None: +def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee, + limit_buy_order, limit_sell_order, mocker) -> None: """ Test rpc_trade_statistics() method """ @@ -173,7 +176,8 @@ def test_rpc_trade_statistics( mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker + get_ticker=ticker, + get_fee=fee ) freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -220,8 +224,8 @@ def test_rpc_trade_statistics( # Test that rpc_trade_statistics can handle trades that lacks # trade.open_rate (it is set to None) -def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, ticker_sell_up, limit_buy_order, - limit_sell_order): +def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, + ticker_sell_up, limit_buy_order, limit_sell_order): """ Test rpc_trade_statistics() method """ @@ -235,7 +239,8 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, ticker_sell_u mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker + get_ticker=ticker, + get_fee=fee ) freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -253,7 +258,8 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, ticker_sell_u mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker_sell_up + get_ticker=ticker_sell_up, + get_fee=fee ) trade.update(limit_sell_order) trade.close_date = datetime.utcnow() @@ -401,8 +407,9 @@ def test_rpc_forcesell(default_conf, ticker, mocker) -> None: cancel_order=cancel_order_mock, get_order=MagicMock( return_value={ - 'closed': True, - 'type': 'LIMIT_BUY', + 'status': 'closed', + 'type': 'limit', + 'side': 'buy' } ) ) @@ -448,8 +455,9 @@ def test_rpc_forcesell(default_conf, ticker, mocker) -> None: mocker.patch( 'freqtrade.freqtradebot.exchange.get_order', return_value={ - 'closed': None, - 'type': 'LIMIT_BUY' + 'status': 'open', + 'type': 'limit', + 'side': 'buy' } ) # check that the trade is called, which is done @@ -464,8 +472,9 @@ def test_rpc_forcesell(default_conf, ticker, mocker) -> None: mocker.patch( 'freqtrade.freqtradebot.exchange.get_order', return_value={ - 'closed': None, - 'type': 'LIMIT_SELL' + 'status': 'open', + 'type': 'limit', + 'side': 'sell' } ) (error, res) = rpc.rpc_forcesell('2') @@ -475,7 +484,7 @@ def test_rpc_forcesell(default_conf, ticker, mocker) -> None: assert cancel_order_mock.call_count == 1 -def test_performance_handle(default_conf, ticker, limit_buy_order, +def test_performance_handle(default_conf, ticker, limit_buy_order, fee, limit_sell_order, mocker) -> None: """ Test rpc_performance() method @@ -487,7 +496,8 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_balances=MagicMock(return_value=ticker), - get_ticker=ticker + get_ticker=ticker, + get_fee=fee ) freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index eccfb890b..1c90db53d 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -333,7 +333,7 @@ def test_status_table_handle(default_conf, update, ticker, mocker) -> None: 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - buy=MagicMock(return_value='mocked_order_id') + buy=MagicMock(return_value={'id': 'mocked_order_id'}) ) msg_mock = MagicMock() mocker.patch.multiple( @@ -374,7 +374,7 @@ def test_status_table_handle(default_conf, update, ticker, mocker) -> None: assert msg_mock.call_count == 1 -def test_daily_handle(default_conf, update, ticker, limit_buy_order, +def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee, limit_sell_order, mocker) -> None: """ Test _daily() method @@ -389,7 +389,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_pair_detail_url=MagicMock() + get_fee=fee ) msg_mock = MagicMock() mocker.patch.multiple( @@ -486,7 +486,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: assert str('Daily Profit over the last 7 days') in msg_mock.call_args_list[0][0][0] -def test_profit_handle(default_conf, update, ticker, ticker_sell_up, +def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee, limit_buy_order, limit_sell_order, mocker) -> None: """ Test _profit() method @@ -497,7 +497,8 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker + get_ticker=ticker, + get_fee=fee ) msg_mock = MagicMock() mocker.patch.multiple( @@ -749,7 +750,7 @@ def test_stop_handle_already_stopped(default_conf, update, mocker) -> None: assert 'already stopped' in msg_mock.call_args_list[0][0][0] -def test_forcesell_handle(default_conf, update, ticker, ticker_sell_up, mocker) -> None: +def test_forcesell_handle(default_conf, update, ticker, fee, ticker_sell_up, mocker) -> None: """ Test _forcesell() method """ @@ -761,7 +762,8 @@ def test_forcesell_handle(default_conf, update, ticker, ticker_sell_up, mocker) mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker + get_ticker=ticker, + get_fee=fee ) freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -788,7 +790,7 @@ def test_forcesell_handle(default_conf, update, ticker, ticker_sell_up, mocker) assert '0.919 USD' in rpc_mock.call_args_list[-1][0][0] -def test_forcesell_down_handle(default_conf, update, ticker, ticker_sell_down, mocker) -> None: +def test_forcesell_down_handle(default_conf, update, ticker, fee, ticker_sell_down, mocker) -> None: """ Test _forcesell() method """ @@ -800,7 +802,8 @@ def test_forcesell_down_handle(default_conf, update, ticker, ticker_sell_down, m mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker + get_ticker=ticker, + get_fee=fee ) freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -831,7 +834,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, ticker_sell_down, m assert '-0.824 USD' in rpc_mock.call_args_list[-1][0][0] -def test_forcesell_all_handle(default_conf, update, ticker, mocker) -> None: +def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None: """ Test _forcesell() method """ @@ -844,7 +847,8 @@ def test_forcesell_all_handle(default_conf, update, ticker, mocker) -> None: mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker + get_ticker=ticker, + get_fee=fee ) freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -907,8 +911,8 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None: assert 'Invalid argument.' in msg_mock.call_args_list[0][0][0] -def test_performance_handle(default_conf, update, ticker, limit_buy_order, - limit_sell_order, mocker) -> None: +def test_performance_handle(default_conf, update, ticker, fee, + limit_buy_order, limit_sell_order, mocker) -> None: """ Test _performance() method """ @@ -923,7 +927,8 @@ def test_performance_handle(default_conf, update, ticker, limit_buy_order, mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker + get_ticker=ticker, + get_fee=fee ) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -987,7 +992,7 @@ def test_count_handle(default_conf, update, ticker, mocker) -> None: 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - buy=MagicMock(return_value='mocked_order_id') + buy=MagicMock(return_value={'id': 'mocked_order_id'}) ) freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) telegram = Telegram(freqtradebot) diff --git a/freqtrade/tests/strategy/test_default_strategy.py b/freqtrade/tests/strategy/test_default_strategy.py index 93cb76366..cb6cbd4ca 100644 --- a/freqtrade/tests/strategy/test_default_strategy.py +++ b/freqtrade/tests/strategy/test_default_strategy.py @@ -1,18 +1,8 @@ -import json - -import pytest from pandas import DataFrame -from freqtrade.analyze import Analyze from freqtrade.strategy.default_strategy import DefaultStrategy, class_name -@pytest.fixture -def result(): - with open('freqtrade/tests/testdata/ETH_BTC-1m.json') as data_file: - return Analyze.parse_ticker_dataframe(json.load(data_file)) - - def test_default_strategy_class_name(): assert class_name == DefaultStrategy.__name__ diff --git a/freqtrade/tests/test_acl_pair.py b/freqtrade/tests/test_acl_pair.py index 174f1fde9..07375260e 100644 --- a/freqtrade/tests/test_acl_pair.py +++ b/freqtrade/tests/test_acl_pair.py @@ -1,6 +1,7 @@ # pragma pylint: disable=missing-docstring,C0103,protected-access import freqtrade.tests.conftest as tt # test tools +from unittest.mock import MagicMock # whitelist, blacklist, filtering, all of that will # eventually become some rules to run on a generic ACL engine @@ -26,81 +27,12 @@ def whitelist_conf(): return config -def get_market_summaries(): - return { - 'TKN/BTC': { - 'symbol': 'TKN/BTC', - 'info': { - 'High': 0.00000919, - 'Low': 0.00000820, - 'Volume': 74339.61396015, - 'Last': 0.00000820, - 'BaseVolume': 1664, - 'TimeStamp': '2014-07-09T07:19:30.15', - 'Bid': 0.00000820, - 'Ask': 0.00000831, - 'OpenBuyOrders': 15, - 'OpenSellOrders': 15, - 'PrevDay': 0.00000821, - 'Created': '2014-03-20T06:00:00', - 'DisplayMarketName': '' - } - }, - 'ETH/BTC': { - 'symbol': 'ETH/BTC', - 'info': { - 'High': 0.00000072, - 'Low': 0.00000001, - 'Volume': 166340678.42280999, - 'Last': 0.00000005, - 'BaseVolume': 42, - 'TimeStamp': '2014-07-09T07:21:40.51', - 'Bid': 0.00000004, - 'Ask': 0.00000005, - 'OpenBuyOrders': 18, - 'OpenSellOrders': 18, - 'PrevDay': 0.00000002, - 'Created': '2014-05-30T07:57:49.637', - 'DisplayMarketName': '' - } - }, - 'BLK/BTC': { - 'symbol': 'BLK/BTC', - 'info': { - 'High': 0.00000072, - 'Low': 0.00000001, - 'Volume': 166340678.42280999, - 'Last': 0.00000005, - 'BaseVolume': 3, - 'TimeStamp': '2014-07-09T07:21:40.51', - 'Bid': 0.00000004, - 'Ask': 0.00000005, - 'OpenBuyOrders': 18, - 'OpenSellOrders': 18, - 'PrevDay': 0.00000002, - 'Created': '2014-05-30T07:57:49.637', - 'DisplayMarketName': '' - }} - } - - -def get_health(): - return { - 'ETH/BTC': {'base': 'ETH', 'active': True}, - 'TKN/BTC': {'base': 'TKN', 'active': True}, - 'BLK/BTC': {'base': 'BLK', 'active': True}} - - -def get_health_empty(): - return {} - - -def test_refresh_market_pair_not_in_whitelist(mocker): +def test_refresh_market_pair_not_in_whitelist(mocker, markets): conf = whitelist_conf() freqtradebot = tt.get_patched_freqtradebot(mocker, conf) - mocker.patch('freqtrade.freqtradebot.exchange.get_wallet_health', get_health) + mocker.patch('freqtrade.freqtradebot.exchange.get_markets', markets) refreshedwhitelist = freqtradebot._refresh_whitelist( conf['exchange']['pair_whitelist'] + ['XXX/BTC'] ) @@ -110,11 +42,11 @@ def test_refresh_market_pair_not_in_whitelist(mocker): assert whitelist == refreshedwhitelist -def test_refresh_whitelist(mocker): +def test_refresh_whitelist(mocker, markets): conf = whitelist_conf() freqtradebot = tt.get_patched_freqtradebot(mocker, conf) - mocker.patch('freqtrade.freqtradebot.exchange.get_wallet_health', get_health) + mocker.patch('freqtrade.freqtradebot.exchange.get_markets', markets) refreshedwhitelist = freqtradebot._refresh_whitelist(conf['exchange']['pair_whitelist']) # List ordered by BaseVolume @@ -123,17 +55,18 @@ def test_refresh_whitelist(mocker): assert whitelist == refreshedwhitelist -def test_refresh_whitelist_dynamic(mocker): +def test_refresh_whitelist_dynamic(mocker, markets, tickers): conf = whitelist_conf() freqtradebot = tt.get_patched_freqtradebot(mocker, conf) mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', - get_wallet_health=get_health, - get_market_summaries=get_market_summaries + get_markets=markets, + get_tickers=tickers, + exchange_has=MagicMock(return_value=True) ) # argument: use the whitelist dynamically by exchange-volume - whitelist = ['TKN/BTC', 'ETH/BTC'] + whitelist = ['ETH/BTC', 'TKN/BTC'] refreshedwhitelist = freqtradebot._refresh_whitelist( freqtradebot._gen_pair_whitelist(conf['stake_currency']) @@ -142,10 +75,10 @@ def test_refresh_whitelist_dynamic(mocker): assert whitelist == refreshedwhitelist -def test_refresh_whitelist_dynamic_empty(mocker): +def test_refresh_whitelist_dynamic_empty(mocker, markets_empty): conf = whitelist_conf() freqtradebot = tt.get_patched_freqtradebot(mocker, conf) - mocker.patch('freqtrade.freqtradebot.exchange.get_wallet_health', get_health_empty) + mocker.patch('freqtrade.freqtradebot.exchange.get_markets', markets_empty) # argument: use the whitelist dynamically by exchange-volume whitelist = [] diff --git a/freqtrade/tests/test_analyze.py b/freqtrade/tests/test_analyze.py index e1a288bab..8d521150d 100644 --- a/freqtrade/tests/test_analyze.py +++ b/freqtrade/tests/test_analyze.py @@ -50,7 +50,7 @@ def test_dataframe_correct_length(result): def test_dataframe_correct_columns(result): assert result.columns.tolist() == \ - ['close', 'high', 'low', 'open', 'date', 'volume'] + ['date', 'open', 'high', 'low', 'close', 'volume'] def test_populates_buy_trend(result): @@ -169,17 +169,13 @@ def test_get_signal_handles_exceptions(mocker): assert _ANALYZE.get_signal('ETH/BTC', '5m') == (False, False) -def test_parse_ticker_dataframe(ticker_history, ticker_history_without_bv): - columns = ['close', 'high', 'low', 'open', 'date', 'volume'] +def test_parse_ticker_dataframe(ticker_history): + columns = ['date', 'open', 'high', 'low', 'close', 'volume'] # Test file with BV data dataframe = Analyze.parse_ticker_dataframe(ticker_history) assert dataframe.columns.tolist() == columns - # Test file without BV data - dataframe = Analyze.parse_ticker_dataframe(ticker_history_without_bv) - assert dataframe.columns.tolist() == columns - def test_tickerdata_to_dataframe(default_conf) -> None: """ diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index 3792fd88d..8f727838e 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -201,29 +201,28 @@ def test_throttle_with_assets(mocker, default_conf) -> None: assert result == -1 -def test_gen_pair_whitelist(mocker, default_conf, get_market_summaries_data) -> None: +def test_gen_pair_whitelist(mocker, default_conf, tickers) -> None: """ Test _gen_pair_whitelist() method """ freqtrade = get_patched_freqtradebot(mocker, default_conf) - mocker.patch( - 'freqtrade.freqtradebot.exchange.get_market_summaries', - return_value=get_market_summaries_data - ) + mocker.patch('freqtrade.freqtradebot.exchange.get_tickers', tickers) + mocker.patch('freqtrade.freqtradebot.exchange.exchange_has', MagicMock(return_value=True)) + mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True)) - # Test to retrieved BTC sorted on BaseVolume + # Test to retrieved BTC sorted on quoteVolume (default) whitelist = freqtrade._gen_pair_whitelist(base_currency='BTC') - assert whitelist == ['ZCL/BTC', 'ZEC/BTC', 'XZC/BTC', 'XWC/BTC'] + assert whitelist == ['ETH/BTC', 'TKN/BTC', 'BLK/BTC', 'LTC/BTC'] - # Test to retrieved BTC sorted on OpenBuyOrders - whitelist = freqtrade._gen_pair_whitelist(base_currency='BTC', key='OpenBuyOrders') - assert whitelist == ['XWC/BTC', 'ZCL/BTC', 'ZEC/BTC', 'XZC/BTC'] + # Test to retrieve BTC sorted on bidVolume + whitelist = freqtrade._gen_pair_whitelist(base_currency='BTC', key='bidVolume') + assert whitelist == ['LTC/BTC', 'TKN/BTC', 'ETH/BTC', 'BLK/BTC'] - # Test with USDT sorted on BaseVolume + # Test with USDT sorted on quoteVolume (default) whitelist = freqtrade._gen_pair_whitelist(base_currency='USDT') - assert whitelist == ['XRP/USDT', 'XVG/USDT', 'XMR/USDT', 'ZEC/USDT'] + assert whitelist == ['TKN/USDT', 'ETH/USDT', 'LTC/USDT', 'BLK/USDT'] - # Test with ETH (our fixture does not have ETH, but Bittrex returns them) + # Test with ETH (our fixture does not have ETH, so result should be empty) whitelist = freqtrade._gen_pair_whitelist(base_currency='ETH') assert whitelist == [] @@ -247,7 +246,7 @@ def test_create_trade(default_conf, ticker, limit_buy_order, mocker) -> None: 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - buy=MagicMock(return_value='mocked_limit_buy') + buy=MagicMock(return_value={'id': limit_buy_order['id']}) ) # Save state of current whitelist @@ -271,14 +270,14 @@ def test_create_trade(default_conf, ticker, limit_buy_order, mocker) -> None: assert whitelist == default_conf['exchange']['pair_whitelist'] -def test_create_trade_minimal_amount(default_conf, ticker, mocker) -> None: +def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order, mocker) -> None: """ Test create_trade() method """ patch_get_signal(mocker) patch_RPCManager(mocker) patch_coinmarketcap(mocker) - buy_mock = MagicMock(return_value='mocked_limit_buy') + buy_mock = MagicMock(return_value={'id': limit_buy_order['id']}) mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), @@ -295,7 +294,7 @@ def test_create_trade_minimal_amount(default_conf, ticker, mocker) -> None: assert rate * amount >= conf['stake_amount'] -def test_create_trade_no_stake_amount(default_conf, ticker, mocker) -> None: +def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, mocker) -> None: """ Test create_trade() method """ @@ -306,7 +305,7 @@ def test_create_trade_no_stake_amount(default_conf, ticker, mocker) -> None: 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - buy=MagicMock(return_value='mocked_limit_buy'), + buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5) ) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -315,7 +314,7 @@ def test_create_trade_no_stake_amount(default_conf, ticker, mocker) -> None: freqtrade.create_trade() -def test_create_trade_no_pairs(default_conf, ticker, mocker) -> None: +def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, mocker) -> None: """ Test create_trade() method """ @@ -326,7 +325,7 @@ def test_create_trade_no_pairs(default_conf, ticker, mocker) -> None: 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - buy=MagicMock(return_value='mocked_limit_buy') + buy=MagicMock(return_value={'id': limit_buy_order['id']}) ) conf = deepcopy(default_conf) @@ -340,7 +339,8 @@ def test_create_trade_no_pairs(default_conf, ticker, mocker) -> None: freqtrade.create_trade() -def test_create_trade_no_pairs_after_blacklist(default_conf, ticker, mocker) -> None: +def test_create_trade_no_pairs_after_blacklist(default_conf, ticker, + limit_buy_order, mocker) -> None: """ Test create_trade() method """ @@ -351,7 +351,7 @@ def test_create_trade_no_pairs_after_blacklist(default_conf, ticker, mocker) -> 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - buy=MagicMock(return_value='mocked_limit_buy') + buy=MagicMock(return_value={'id': limit_buy_order['id']}) ) conf = deepcopy(default_conf) @@ -392,7 +392,7 @@ def test_create_trade_no_signal(default_conf, mocker) -> None: def test_process_trade_creation(default_conf, ticker, limit_buy_order, - health, mocker, caplog) -> None: + markets, mocker, caplog) -> None: """ Test the trade creation in _process() method """ @@ -403,8 +403,8 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order, 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_wallet_health=health, - buy=MagicMock(return_value='mocked_limit_buy'), + get_markets=markets, + buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_order=MagicMock(return_value=limit_buy_order) ) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -432,7 +432,7 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order, ) -def test_process_exchange_failures(default_conf, ticker, health, mocker) -> None: +def test_process_exchange_failures(default_conf, ticker, markets, mocker) -> None: """ Test _process() method when a RequestException happens """ @@ -443,7 +443,7 @@ def test_process_exchange_failures(default_conf, ticker, health, mocker) -> None 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_wallet_health=health, + get_markets=markets, buy=MagicMock(side_effect=requests.exceptions.RequestException) ) sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None) @@ -454,7 +454,7 @@ def test_process_exchange_failures(default_conf, ticker, health, mocker) -> None assert sleep_mock.has_calls() -def test_process_operational_exception(default_conf, ticker, health, mocker) -> None: +def test_process_operational_exception(default_conf, ticker, markets, mocker) -> None: """ Test _process() method when an OperationalException happens """ @@ -465,7 +465,7 @@ def test_process_operational_exception(default_conf, ticker, health, mocker) -> 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_wallet_health=health, + get_markets=markets, buy=MagicMock(side_effect=OperationalException) ) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -477,7 +477,7 @@ def test_process_operational_exception(default_conf, ticker, health, mocker) -> assert 'OperationalException' in msg_mock.call_args_list[-1][0][0] -def test_process_trade_handling(default_conf, ticker, limit_buy_order, health, mocker) -> None: +def test_process_trade_handling(default_conf, ticker, limit_buy_order, markets, mocker) -> None: """ Test _process() """ @@ -488,8 +488,8 @@ def test_process_trade_handling(default_conf, ticker, limit_buy_order, health, m 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - get_wallet_health=health, - buy=MagicMock(return_value='mocked_limit_buy'), + get_markets=markets, + buy=MagicMock(return_value={'id': limit_buy_order['id']}), get_order=MagicMock(return_value=limit_buy_order) ) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -577,7 +577,7 @@ def test_process_maybe_execute_sell(mocker, default_conf) -> None: assert freqtrade.process_maybe_execute_sell(trade) -def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, mocker) -> None: +def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, fee, mocker) -> None: """ Test check_handle() method """ @@ -591,8 +591,9 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, mocker) - 'ask': 0.00001173, 'last': 0.00001172 }), - buy=MagicMock(return_value='mocked_limit_buy'), - sell=MagicMock(return_value='mocked_limit_sell') + buy=MagicMock(return_value={'id': limit_buy_order['id']}), + sell=MagicMock(return_value={'id': limit_sell_order['id']}), + get_fee=fee ) patch_coinmarketcap(mocker, value={'price_usd': 15000.0}) @@ -603,12 +604,13 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, mocker) - trade = Trade.query.first() assert trade + time.sleep(0.01) # Race condition fix trade.update(limit_buy_order) assert trade.is_open is True patch_get_signal(mocker, value=(False, True)) assert freqtrade.handle_trade(trade) is True - assert trade.open_order_id == 'mocked_limit_sell' + assert trade.open_order_id == limit_sell_order['id'] # Simulate fulfilled LIMIT_SELL order for trade trade.update(limit_sell_order) @@ -619,7 +621,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, mocker) - assert trade.close_date is not None -def test_handle_overlpapping_signals(default_conf, ticker, mocker) -> None: +def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, mocker) -> None: """ Test check_handle() method """ @@ -634,7 +636,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, mocker) -> None: 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - buy=MagicMock(return_value='mocked_limit_buy') + buy=MagicMock(return_value={'id': limit_buy_order['id']}) ) freqtrade = FreqtradeBot(conf, create_engine('sqlite://')) @@ -676,7 +678,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, mocker) -> None: assert freqtrade.handle_trade(trades[0]) is True -def test_handle_trade_roi(default_conf, ticker, mocker, caplog) -> None: +def test_handle_trade_roi(default_conf, ticker, limit_buy_order, mocker, caplog) -> None: """ Test check_handle() method """ @@ -691,7 +693,7 @@ def test_handle_trade_roi(default_conf, ticker, mocker, caplog) -> None: 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - buy=MagicMock(return_value='mocked_limit_buy') + buy=MagicMock(return_value={'id': limit_buy_order['id']}) ) mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=True) @@ -711,7 +713,7 @@ def test_handle_trade_roi(default_conf, ticker, mocker, caplog) -> None: assert log_has('Required profit reached. Selling..', caplog.record_tuples) -def test_handle_trade_experimental(default_conf, ticker, mocker, caplog) -> None: +def test_handle_trade_experimental(default_conf, ticker, limit_buy_order, mocker, caplog) -> None: """ Test check_handle() method """ @@ -726,7 +728,7 @@ def test_handle_trade_experimental(default_conf, ticker, mocker, caplog) -> None 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - buy=MagicMock(return_value='mocked_limit_buy') + buy=MagicMock(return_value={'id': limit_buy_order['id']}) ) mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False) @@ -755,7 +757,7 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, mo 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), get_ticker=ticker, - buy=MagicMock(return_value='mocked_limit_buy') + buy=MagicMock(return_value={'id': limit_buy_order['id']}) ) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -773,7 +775,7 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, mo freqtrade.handle_trade(trade) -def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, mocker) -> None: +def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, fee, mocker) -> None: """ Test check_handle_timedout() method """ @@ -785,14 +787,15 @@ def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, mo validate_pairs=MagicMock(), get_ticker=ticker, get_order=MagicMock(return_value=limit_buy_order_old), - cancel_order=cancel_order_mock + cancel_order=cancel_order_mock, + get_fee=fee ) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) trade_buy = Trade( pair='ETH/BTC', open_rate=0.00001099, - exchange='BITTREX', + exchange='bittrex', open_order_id='123456789', amount=90.99181073, fee=0.0, @@ -831,7 +834,7 @@ def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old, trade_sell = Trade( pair='ETH/BTC', open_rate=0.00001099, - exchange='BITTREX', + exchange='bittrex', open_order_id='123456789', amount=90.99181073, fee=0.0, @@ -870,7 +873,7 @@ def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old trade_buy = Trade( pair='ETH/BTC', open_rate=0.00001099, - exchange='BITTREX', + exchange='bittrex', open_order_id='123456789', amount=90.99181073, fee=0.0, @@ -917,7 +920,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) - trade_buy = Trade( pair='ETH/BTC', open_rate=0.00001099, - exchange='BITTREX', + exchange='bittrex', open_order_id='123456789', amount=90.99181073, fee=0.0, @@ -989,7 +992,7 @@ def test_handle_timedout_limit_sell(mocker, default_conf) -> None: assert cancel_order_mock.call_count == 1 -def test_execute_sell_up(default_conf, ticker, ticker_sell_up, mocker) -> None: +def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> None: """ Test execute_sell() method with a ticker going UP """ @@ -999,7 +1002,8 @@ def test_execute_sell_up(default_conf, ticker, ticker_sell_up, mocker) -> None: mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker + get_ticker=ticker, + get_fee=fee ) mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -1029,7 +1033,7 @@ def test_execute_sell_up(default_conf, ticker, ticker_sell_up, mocker) -> None: assert '0.919 USD' in rpc_mock.call_args_list[-1][0][0] -def test_execute_sell_down(default_conf, ticker, ticker_sell_down, mocker) -> None: +def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker) -> None: """ Test execute_sell() method with a ticker going DOWN """ @@ -1040,7 +1044,8 @@ def test_execute_sell_down(default_conf, ticker, ticker_sell_down, mocker) -> No mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker + get_ticker=ticker, + get_fee=fee ) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -1068,7 +1073,8 @@ def test_execute_sell_down(default_conf, ticker, ticker_sell_down, mocker) -> No assert '-0.824 USD' in rpc_mock.call_args_list[-1][0][0] -def test_execute_sell_without_conf_sell_up(default_conf, ticker, ticker_sell_up, mocker) -> None: +def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee, + ticker_sell_up, mocker) -> None: """ Test execute_sell() method with a ticker going DOWN and with a bot config empty """ @@ -1078,7 +1084,8 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, ticker_sell_up, mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker + get_ticker=ticker, + get_fee=fee ) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -1107,7 +1114,7 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, ticker_sell_up, assert 'USD' not in rpc_mock.call_args_list[-1][0][0] -def test_execute_sell_without_conf_sell_down(default_conf, ticker, +def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker) -> None: """ Test execute_sell() method with a ticker going DOWN and with a bot config empty @@ -1118,7 +1125,8 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker, mocker.patch.multiple( 'freqtrade.freqtradebot.exchange', validate_pairs=MagicMock(), - get_ticker=ticker + get_ticker=ticker, + get_fee=fee ) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) @@ -1161,7 +1169,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, mocker) - 'ask': 0.00002173, 'last': 0.00002172 }), - buy=MagicMock(return_value='mocked_limit_buy') + buy=MagicMock(return_value={'id': limit_buy_order['id']}) ) conf = deepcopy(default_conf) conf['experimental'] = { @@ -1193,7 +1201,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, mocker) 'ask': 0.00002173, 'last': 0.00002172 }), - buy=MagicMock(return_value='mocked_limit_buy') + buy=MagicMock(return_value={'id': limit_buy_order['id']}) ) conf = deepcopy(default_conf) conf['experimental'] = { @@ -1225,7 +1233,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, mocker) -> 'ask': 0.00000173, 'last': 0.00000172 }), - buy=MagicMock(return_value='mocked_limit_buy') + buy=MagicMock(return_value={'id': limit_buy_order['id']}) ) conf = deepcopy(default_conf) conf['experimental'] = { @@ -1257,7 +1265,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, mocker) -> 'ask': 0.00000173, 'last': 0.00000172 }), - buy=MagicMock(return_value='mocked_limit_buy') + buy=MagicMock(return_value={'id': limit_buy_order['id']}) ) conf = deepcopy(default_conf) diff --git a/freqtrade/tests/test_persistence.py b/freqtrade/tests/test_persistence.py index 59ea2cd48..97f2cdcfe 100644 --- a/freqtrade/tests/test_persistence.py +++ b/freqtrade/tests/test_persistence.py @@ -89,7 +89,7 @@ def test_init_prod_db(default_conf, mocker): os.rename(prod_db_swp, prod_db) -def test_update_with_bittrex(limit_buy_order, limit_sell_order): +def test_update_with_bittrex(limit_buy_order, limit_sell_order, fee): """ On this test we will buy and sell a crypto currency. @@ -120,7 +120,7 @@ def test_update_with_bittrex(limit_buy_order, limit_sell_order): trade = Trade( pair='ETH/BTC', stake_amount=0.001, - fee=0.0025, + fee=fee.return_value, exchange='bittrex', ) assert trade.open_order_id is None @@ -143,11 +143,11 @@ def test_update_with_bittrex(limit_buy_order, limit_sell_order): assert trade.close_date is not None -def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order): +def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order, fee): trade = Trade( pair='ETH/BTC', stake_amount=0.001, - fee=0.0025, + fee=fee.return_value, exchange='bittrex', ) @@ -165,11 +165,11 @@ def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order): assert trade.calc_profit_percent() == 0.06201057 -def test_calc_close_trade_price_exception(limit_buy_order): +def test_calc_close_trade_price_exception(limit_buy_order, fee): trade = Trade( pair='ETH/BTC', stake_amount=0.001, - fee=0.0025, + fee=fee.return_value, exchange='bittrex', ) @@ -191,7 +191,7 @@ def test_update_open_order(limit_buy_order): assert trade.close_profit is None assert trade.close_date is None - limit_buy_order['closed'] = False + limit_buy_order['status'] = 'open' trade.update(limit_buy_order) assert trade.open_order_id is None @@ -212,11 +212,11 @@ def test_update_invalid_order(limit_buy_order): trade.update(limit_buy_order) -def test_calc_open_trade_price(limit_buy_order): +def test_calc_open_trade_price(limit_buy_order, fee): trade = Trade( pair='ETH/BTC', stake_amount=0.001, - fee=0.0025, + fee=fee.return_value, exchange='bittrex', ) trade.open_order_id = 'open_trade' @@ -229,11 +229,11 @@ def test_calc_open_trade_price(limit_buy_order): assert trade.calc_open_trade_price(fee=0.003) == 0.001003000 -def test_calc_close_trade_price(limit_buy_order, limit_sell_order): +def test_calc_close_trade_price(limit_buy_order, limit_sell_order, fee): trade = Trade( pair='ETH/BTC', stake_amount=0.001, - fee=0.0025, + fee=fee.return_value, exchange='bittrex', ) trade.open_order_id = 'close_trade' @@ -250,11 +250,11 @@ def test_calc_close_trade_price(limit_buy_order, limit_sell_order): assert trade.calc_close_trade_price(fee=0.005) == 0.0010619972 -def test_calc_profit(limit_buy_order, limit_sell_order): +def test_calc_profit(limit_buy_order, limit_sell_order, fee): trade = Trade( pair='ETH/BTC', stake_amount=0.001, - fee=0.0025, + fee=fee.return_value, exchange='bittrex', ) trade.open_order_id = 'profit_percent' @@ -280,11 +280,11 @@ def test_calc_profit(limit_buy_order, limit_sell_order): assert trade.calc_profit(fee=0.003) == 0.00006163 -def test_calc_profit_percent(limit_buy_order, limit_sell_order): +def test_calc_profit_percent(limit_buy_order, limit_sell_order, fee): trade = Trade( pair='ETH/BTC', stake_amount=0.001, - fee=0.0025, + fee=fee.return_value, exchange='bittrex', ) trade.open_order_id = 'profit_percent' @@ -304,7 +304,7 @@ def test_calc_profit_percent(limit_buy_order, limit_sell_order): assert trade.calc_profit_percent(fee=0.003) == 0.0614782 -def test_clean_dry_run_db(default_conf): +def test_clean_dry_run_db(default_conf, fee): init(default_conf, create_engine('sqlite://')) # Simulate dry_run entries @@ -312,7 +312,7 @@ def test_clean_dry_run_db(default_conf): pair='ETH/BTC', stake_amount=0.001, amount=123.0, - fee=0.0025, + fee=fee.return_value, open_rate=0.123, exchange='bittrex', open_order_id='dry_run_buy_12345' @@ -323,7 +323,7 @@ def test_clean_dry_run_db(default_conf): pair='ETC/BTC', stake_amount=0.001, amount=123.0, - fee=0.0025, + fee=fee.return_value, open_rate=0.123, exchange='bittrex', open_order_id='dry_run_sell_12345' @@ -335,7 +335,7 @@ def test_clean_dry_run_db(default_conf): pair='ETC/BTC', stake_amount=0.001, amount=123.0, - fee=0.0025, + fee=fee.return_value, open_rate=0.123, exchange='bittrex', open_order_id='prod_buy_12345' diff --git a/user_data/hyperopt_conf.py b/user_data/hyperopt_conf.py index 1e9ab88a4..c3a6e2a29 100644 --- a/user_data/hyperopt_conf.py +++ b/user_data/hyperopt_conf.py @@ -25,6 +25,7 @@ def hyperopt_optimize_conf() -> dict: "ask_last_balance": 0.0 }, "exchange": { + "name": "bittrex", "pair_whitelist": [ "ETH/BTC", "LTC/BTC",