Merge pull request #603 from enenn/ccxt-objectify-pr3_1

[3/3] Add support for multiple exchanges with ccxt (objectified version)
This commit is contained in:
Samuel Husso 2018-04-18 15:23:33 +03:00 committed by GitHub
commit 42c0d7c7c3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 664 additions and 459 deletions

View File

@ -107,7 +107,17 @@ def validate_pairs(pairs: List[str]) -> None:
) )
if pair not in markets: if pair not in markets:
raise OperationalException( 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: def buy(pair: str, rate: float, amount: float) -> Dict:
@ -216,6 +226,23 @@ def get_balances() -> dict:
raise OperationalException(e) 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 # TODO: remove refresh argument, keeping it to keep track of where it was intended to be used
@retrier @retrier
def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict: 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 @retrier
def get_ticker_history(pair: str, tick_interval: str) -> List[Dict]: 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: try:
return _API.fetch_ohlcv(pair, timeframe=tick_interval) return _API.fetch_ohlcv(pair, timeframe=tick_interval)
except ccxt.NetworkError as e: 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: except ccxt.BaseError as e:
raise OperationalException('Could not fetch ticker data. Msg: {}'.format(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: def cancel_order(order_id: str, pair: str) -> None:
@ -315,18 +342,11 @@ def get_id() -> str:
return _API.id return _API.id
def get_fee_maker() -> float: def get_fee(symbol='ETH/BTC', type='', side='', amount=1,
return _API.fees['trading']['maker'] price=1, taker_or_maker='maker') -> float:
def get_fee_taker() -> float:
return _API.fees['trading']['taker']
def get_fee() -> float:
# validate that markets are loaded before trying to get fee # validate that markets are loaded before trying to get fee
if _API.markets is None or len(_API.markets) == 0: if _API.markets is None or len(_API.markets) == 0:
_API.load_markets() _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']

View File

@ -107,10 +107,7 @@ class FreqtradeBot(object):
Constants.PROCESS_THROTTLE_SECS Constants.PROCESS_THROTTLE_SECS
) )
nb_assets = self.config.get( nb_assets = self.config.get('dynamic_whitelist', None)
'dynamic_whitelist',
Constants.DYNAMIC_WHITELIST
)
self._throttle(func=self._process, self._throttle(func=self._process,
min_secs=min_secs, min_secs=min_secs,
@ -185,49 +182,60 @@ class FreqtradeBot(object):
return state_changed return state_changed
@cached(TTLCache(maxsize=1, ttl=1800)) @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 Updates the whitelist with with a dynamically generated list
:param base_currency: base currency as str :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 :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]: 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 :param whitelist: the sorted list (based on BaseVolume) of pairs the user might want to
trade trade
:return: the list of pairs the user wants to trade without the one unavailable or :return: the list of pairs the user wants to trade without the one unavailable or
black_listed black_listed
""" """
sanitized_whitelist = whitelist 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() known_pairs = set()
for symbol, status in health.items(): for market in markets:
pair = f"{status['base']}/{self.config['stake_currency']}" pair = market['symbol']
# pair is not int the generated dynamic market, or in the blacklist ... ignore it # 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', []): if pair not in whitelist or pair in self.config['exchange'].get('pair_blacklist', []):
continue continue
# else the pair is valid # else the pair is valid
known_pairs.add(pair) known_pairs.add(pair)
# Market is not active # Market is not active
if not status['active']: if not market['active']:
sanitized_whitelist.remove(pair) sanitized_whitelist.remove(pair)
self.logger.info( self.logger.info(
'Ignoring %s from whitelist (reason: %s).', 'Ignoring %s from whitelist. Market is not active.',
pair, status.get('Notice') or 'wallet is not active' pair
) )
# We need to remove pairs that are unknown # We need to remove pairs that are unknown
final_list = [x for x in sanitized_whitelist if x in known_pairs] final_list = [x for x in sanitized_whitelist if x in known_pairs]
return final_list return final_list
def get_target_bid(self, ticker: Dict[str, float]) -> float: 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)) buy_limit = self.get_target_bid(exchange.get_ticker(pair))
amount = stake_amount / buy_limit 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_fiat = self.fiat_converter.convert_amount(
stake_amount, stake_amount,
@ -297,7 +305,7 @@ class FreqtradeBot(object):
self.rpc.send_msg( self.rpc.send_msg(
'*{}:* Buying [{}]({}) with limit `{:.8f} ({:.6f} {}, {:.3f} {})` ' '*{}:* Buying [{}]({}) with limit `{:.8f} ({:.6f} {}, {:.3f} {})` '
.format( .format(
exchange.get_name().upper(), exchange.get_name(),
pair.replace('_', '/'), pair.replace('_', '/'),
exchange.get_pair_detail_url(pair), exchange.get_pair_detail_url(pair),
buy_limit, buy_limit,
@ -312,10 +320,10 @@ class FreqtradeBot(object):
pair=pair, pair=pair,
stake_amount=stake_amount, stake_amount=stake_amount,
amount=amount, amount=amount,
fee=exchange.get_fee_maker(), fee=exchange.get_fee(taker_or_maker='maker'),
open_rate=buy_limit, open_rate=buy_limit,
open_date=datetime.utcnow(), open_date=datetime.utcnow(),
exchange=exchange.get_name().upper(), exchange=exchange.get_id(),
open_order_id=order_id open_order_id=order_id
) )
Trade.session.add(trade) Trade.session.add(trade)
@ -347,7 +355,7 @@ class FreqtradeBot(object):
if trade.open_order_id: if trade.open_order_id:
# Update trade with order values # Update trade with order values
self.logger.info('Found open order for %s', trade) 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: if trade.is_open and trade.open_order_id is None:
# Check if we can sell our current pair # 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(): for trade in Trade.query.filter(Trade.open_order_id.isnot(None)).all():
try: try:
order = exchange.get_order(trade.open_order_id) order = exchange.get_order(trade.open_order_id, trade.pair)
except requests.exceptions.RequestException: except requests.exceptions.RequestException:
self.logger.info( self.logger.info(
'Cannot query order for %s due to %s', 'Cannot query order for %s due to %s',
trade, trade,
traceback.format_exc()) traceback.format_exc())
continue continue
ordertime = arrow.get(order['opened']) ordertime = arrow.get(order['datetime']).datetime
# Check if trade is still actually open # Check if trade is still actually open
if int(order['remaining']) == 0: if int(order['remaining']) == 0:
continue continue
if order['type'] == "LIMIT_BUY" and ordertime < timeoutthreashold: if order['side'] == 'buy' and ordertime < timeoutthreashold:
self.handle_timedout_limit_buy(trade, order) 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) self.handle_timedout_limit_sell(trade, order)
# FIX: 20180110, why is cancel.order unconditionally here, whereas # FIX: 20180110, why is cancel.order unconditionally here, whereas
@ -463,7 +471,7 @@ class FreqtradeBot(object):
:return: None :return: None
""" """
# Execute sell and update trade record # 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 trade.open_order_id = order_id
fmt_exp_profit = round(trade.calc_profit_percent(rate=limit) * 100, 2) fmt_exp_profit = round(trade.calc_profit_percent(rate=limit) * 100, 2)

View File

@ -6,13 +6,11 @@ This module contains the backtesting logic
from argparse import Namespace from argparse import Namespace
from typing import Dict, Tuple, Any, List, Optional from typing import Dict, Tuple, Any, List, Optional
import ccxt
import arrow import arrow
from pandas import DataFrame, Series from pandas import DataFrame, Series
from tabulate import tabulate from tabulate import tabulate
import freqtrade.optimize as optimize import freqtrade.optimize as optimize
import freqtrade.exchange as exchange
from freqtrade import exchange from freqtrade import exchange
from freqtrade.analyze import Analyze from freqtrade.analyze import Analyze
from freqtrade.arguments import Arguments from freqtrade.arguments import Arguments
@ -57,6 +55,9 @@ class Backtesting(object):
# Reset keys for backtesting # Reset keys for backtesting
self.config['exchange']['key'] = '' self.config['exchange']['key'] = ''
self.config['exchange']['secret'] = '' self.config['exchange']['secret'] = ''
self.config['exchange']['password'] = ''
self.config['exchange']['uid'] = ''
self.config['dry_run'] = True
exchange.init(self.config) exchange.init(self.config)
@staticmethod @staticmethod
@ -199,8 +200,8 @@ class Backtesting(object):
# record a tuple of pair, current_profit_percent, # record a tuple of pair, current_profit_percent,
# entry-date, duration # entry-date, duration
records.append((pair, trade_entry[1], records.append((pair, trade_entry[1],
row.date.strftime('%s'), row.date.timestamp(),
row2.date.strftime('%s'), row2.date.timestamp(),
row.date, trade_entry[3])) row.date, trade_entry[3]))
# For now export inside backtest(), maybe change so that backtest() # For now export inside backtest(), maybe change so that backtest()
# returns a tuple like: (dataframe, records, logs, etc) # returns a tuple like: (dataframe, records, logs, etc)

View File

@ -111,20 +111,20 @@ class Trade(_DECL_BASE):
:return: None :return: None
""" """
# Ignore open and cancelled orders # Ignore open and cancelled orders
if not order['closed'] or order['rate'] is None: if order['status'] == 'open' or order['price'] is None:
return return
logger.info('Updating trade (id=%d) ...', self.id) logger.info('Updating trade (id=%d) ...', self.id)
getcontext().prec = 8 # Bittrex do not go above 8 decimal 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 # Update open rate and actual amount
self.open_rate = Decimal(order['rate']) self.open_rate = Decimal(order['price'])
self.amount = Decimal(order['amount']) self.amount = Decimal(order['amount'])
logger.info('LIMIT_BUY has been fulfilled for %s.', self) logger.info('LIMIT_BUY has been fulfilled for %s.', self)
self.open_order_id = None self.open_order_id = None
elif order['type'] == 'LIMIT_SELL': elif order['type'] == 'limit' and order['side'] == 'sell':
self.close(order['rate']) self.close(order['price'])
else: else:
raise ValueError('Unknown order type: {}'.format(order['type'])) raise ValueError('Unknown order type: {}'.format(order['type']))
cleanup() cleanup()

View File

@ -50,7 +50,7 @@ class RPC(object):
for trade in trades: for trade in trades:
order = None order = None
if trade.open_order_id: 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 # calculate profit and send message to user
current_rate = exchange.get_ticker(trade.pair, False)['bid'] current_rate = exchange.get_ticker(trade.pair, False)['bid']
current_profit = trade.calc_profit_percent(current_rate) current_profit = trade.calc_profit_percent(current_rate)
@ -78,8 +78,8 @@ class RPC(object):
amount=round(trade.amount, 8), amount=round(trade.amount, 8),
close_profit=fmt_close_profit, close_profit=fmt_close_profit,
current_profit=round(current_profit * 100, 2), current_profit=round(current_profit * 100, 2),
open_order='({} rem={:.8f})'.format( open_order='({} {} rem={:.8f})'.format(
order['type'], order['remaining'] order['type'], order['side'], order['remaining']
) if order else None, ) if order else None,
) )
result.append(message) result.append(message)
@ -311,17 +311,21 @@ class RPC(object):
def _exec_forcesell(trade: Trade) -> None: def _exec_forcesell(trade: Trade) -> None:
# Check if there is there is an open order # Check if there is there is an open order
if trade.open_order_id: 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 # Cancel open LIMIT_BUY orders and close trade
if order and not order['closed'] and order['type'] == 'LIMIT_BUY': if order and order['status'] == 'open' \
exchange.cancel_order(trade.open_order_id) and order['type'] == 'limit' \
trade.close(order.get('rate') or trade.open_rate) 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 # TODO: sell amount which has been bought already
return return
# Ignore trades with an attached LIMIT_SELL order # 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 return
# Get current rate and execute sell # Get current rate and execute sell

View File

@ -99,6 +99,11 @@ def update():
return _update return _update
@pytest.fixture
def fee():
return MagicMock(return_value=0.0025)
@pytest.fixture @pytest.fixture
def ticker(): def ticker():
return MagicMock(return_value={ return MagicMock(return_value={
@ -127,32 +132,80 @@ def ticker_sell_down():
@pytest.fixture @pytest.fixture
def health(): def markets():
return MagicMock(return_value={ return MagicMock(return_value=[
"ETH/BTC": { {
'id': 'ethbtc',
'symbol': 'ETH/BTC',
'base': 'ETH', 'base': 'ETH',
'quote': 'BTC',
'active': True, 'active': True,
'LastChecked': '2017-11-13T20:15:00.00', 'precision': {
'Notice': None '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, 'active': True,
'LastChecked': '2017-11-13T20:15:00.00', 'precision': {
'Notice': None '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, 'active': True,
'LastChecked': '2017-11-13T20:15:00.00', 'precision': {
'Notice': None 'price': 8,
}, 'amount': 8,
"BCC/BTC": { 'cost': 8,
'base': 'BCC', },
'active': False, 'lot': 0.00000001,
'LastChecked': '2017-11-13T20:15:00.00', 'limits': {
'Notice': None 'amount': {
}}) 'min': 0.01,
'max': 1000,
},
'price': 500000,
'cost': 500000,
},
'info': '',
}
])
@pytest.fixture
def markets_empty():
return MagicMock(return_value=[])
@pytest.fixture @pytest.fixture
@ -231,7 +284,7 @@ def limit_sell_order():
@pytest.fixture @pytest.fixture
def ticker_history_api(): def ticker_history():
return [ return [
[ [
1511686200000, # unix timestamp ms 1511686200000, # unix timestamp ms
@ -261,66 +314,179 @@ def ticker_history_api():
@pytest.fixture @pytest.fixture
def ticker_history(): def tickers():
return [ return MagicMock(return_value={
{ 'ETH/BTC': {
"O": 8.794e-05, 'symbol': 'ETH/BTC',
"H": 8.948e-05, 'timestamp': 1522014806207,
"L": 8.794e-05, 'datetime': '2018-03-25T21:53:26.207Z',
"C": 8.88e-05, 'high': 0.061697,
"V": 991.09056638, 'low': 0.060531,
"T": "2017-11-26T08:50:00", 'bid': 0.061588,
"BV": 0.0877869 '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': {}
}, },
{ 'TKN/BTC': {
"O": 8.88e-05, 'symbol': 'TKN/BTC',
"H": 8.942e-05, 'timestamp': 1522014806169,
"L": 8.88e-05, 'datetime': '2018-03-25T21:53:26.169Z',
"C": 8.893e-05, 'high': 0.01885,
"V": 658.77935965, 'low': 0.018497,
"T": "2017-11-26T08:55:00", 'bid': 0.018799,
"BV": 0.05874751 'bidVolume': 8.38,
'ask': 0.018802,
'askVolume': 15.0,
'vwap': 0.01869197,
'open': 0.018585,
'close': 0.018573,
'baseVolume': 81058.66,
'quoteVolume': 2247.48374509,
}, },
{ 'BLK/BTC': {
"O": 8.891e-05, 'symbol': 'BLK/BTC',
"H": 8.893e-05, 'timestamp': 1522014806072,
"L": 8.875e-05, 'datetime': '2018-03-25T21:53:26.720Z',
"C": 8.877e-05, 'high': 0.007745,
"V": 7920.73570705, 'low': 0.007512,
"T": "2017-11-26T09:00:00", 'bid': 0.007729,
"BV": 0.7039405 '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 @pytest.fixture
@ -328,7 +494,6 @@ def result():
with open('freqtrade/tests/testdata/UNITTEST_BTC-1m.json') as data_file: with open('freqtrade/tests/testdata/UNITTEST_BTC-1m.json') as data_file:
return Analyze.parse_ticker_dataframe(json.load(data_file)) return Analyze.parse_ticker_dataframe(json.load(data_file))
# FIX: # FIX:
# Create an fixture/function # Create an fixture/function
# that inserts a trade of some type and open-status # that inserts a trade of some type and open-status

View File

@ -68,7 +68,8 @@ def test_validate_pairs_not_compatible(default_conf, mocker):
api_mock.load_markets = MagicMock(return_value={ api_mock.load_markets = MagicMock(return_value={
'ETH/BTC': '', 'TKN/BTC': '', 'TRST/BTC': '', 'SWT/BTC': '', 'BCC/BTC': '' '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('freqtrade.exchange._API', api_mock)
mocker.patch.dict('freqtrade.exchange._CONF', conf) mocker.patch.dict('freqtrade.exchange._CONF', conf)
with pytest.raises(OperationalException, match=r'not compatible'): 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): def test_validate_pairs_exception(default_conf, mocker, caplog):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
api_mock = MagicMock() 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('freqtrade.exchange._API', api_mock)
mocker.patch.dict('freqtrade.exchange._CONF', default_conf) 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']) validate_pairs(default_conf['exchange']['pair_whitelist'])
api_mock.load_markets = MagicMock(side_effect=ccxt.BaseError())
validate_pairs(default_conf['exchange']['pair_whitelist']) validate_pairs(default_conf['exchange']['pair_whitelist'])
assert log_has('Unable to validate pairs (assuming they are correct). Reason: ', assert log_has('Unable to validate pairs (assuming they are correct). Reason: ',
caplog.record_tuples) caplog.record_tuples)
def test_validate_pairs_stake_exception(default_conf, mocker, caplog): def test_validate_pairs_stake_exception(default_conf, mocker, caplog):
caplog.set_level(logging.INFO) caplog.set_level(logging.INFO)
conf = deepcopy(default_conf) 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']) validate_pairs(default_conf['exchange']['pair_whitelist'])
def test_buy_dry_run(default_conf, mocker): def test_buy_dry_run(default_conf, mocker):
default_conf['dry_run'] = True default_conf['dry_run'] = True
mocker.patch.dict('freqtrade.exchange._CONF', default_conf) 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 'id' in order
assert 'dry_run_buy_' in order['id'] assert 'dry_run_buy_' in order['id']
def test_buy_prod(default_conf, mocker): def test_buy_prod(default_conf, mocker):
api_mock = MagicMock() api_mock = MagicMock()
order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6)) order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6))

View File

@ -6,6 +6,7 @@ import random
from copy import deepcopy from copy import deepcopy
from typing import List from typing import List
from unittest.mock import MagicMock from unittest.mock import MagicMock
import pytest
import numpy as np import numpy as np
import pandas as pd import pandas as pd
@ -15,10 +16,20 @@ from freqtrade import optimize
from freqtrade.analyze import Analyze from freqtrade.analyze import Analyze
from freqtrade.arguments import Arguments from freqtrade.arguments import Arguments
from freqtrade.optimize.backtesting import Backtesting, start, setup_configuration 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 # 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]: def get_args(args) -> List[str]:
@ -34,44 +45,54 @@ def trim_dictlist(dict_list, num):
def load_data_test(what): def load_data_test(what):
timerange = ((None, 'line'), None, -100) 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'] pair = data['UNITTEST/BTC']
datalen = len(pair) datalen = len(pair)
# Depending on the what parameter we now adjust the # Depending on the what parameter we now adjust the
# loaded data looks: # loaded data looks:
# pair :: [{'O': 0.123, 'H': 0.123, 'L': 0.123, # pair :: [[ 1509836520000, unix timestamp in ms
# 'C': 0.123, 'V': 123.123, # 0.00162008, open
# 'T': '2017-11-04T23:02:00', 'BV': 0.123}] # 0.00162008, high
# 0.00162008, low
# 0.00162008, close
# 108.14853839 base volume
# ]]
base = 0.001 base = 0.001
if what == 'raise': if what == 'raise':
return {'UNITTEST/BTC': return {'UNITTEST/BTC': [
[{'T': pair[x]['T'], # Keep old dates [
'V': pair[x]['V'], # Keep old volume pair[x][0], # Keep old dates
'BV': pair[x]['BV'], # keep too x * base, # But replace O,H,L,C
'O': x * base, # But replace O,H,L,C x * base + 0.0001,
'H': x * base + 0.0001, x * base - 0.0001,
'L': x * base - 0.0001, x * base,
'C': x * base} for x in range(0, datalen)]} pair[x][5], # Keep old volume
] for x in range(0, datalen)
]}
if what == 'lower': if what == 'lower':
return {'UNITTEST/BTC': return {'UNITTEST/BTC': [
[{'T': pair[x]['T'], # Keep old dates [
'V': pair[x]['V'], # Keep old volume pair[x][0], # Keep old dates
'BV': pair[x]['BV'], # keep too 1 - x * base, # But replace O,H,L,C
'O': 1 - x * base, # But replace O,H,L,C 1 - x * base + 0.0001,
'H': 1 - x * base + 0.0001, 1 - x * base - 0.0001,
'L': 1 - x * base - 0.0001, 1 - x * base,
'C': 1 - x * base} for x in range(0, datalen)]} pair[x][5] # Keep old volume
] for x in range(0, datalen)
]}
if what == 'sine': if what == 'sine':
hz = 0.1 # frequency hz = 0.1 # frequency
return {'UNITTEST/BTC': return {'UNITTEST/BTC': [
[{'T': pair[x]['T'], # Keep old dates [
'V': pair[x]['V'], # Keep old volume pair[x][0], # Keep old dates
'BV': pair[x]['BV'], # keep too math.sin(x * hz) / 1000 + base, # But replace O,H,L,C
# But replace O,H,L,C math.sin(x * hz) / 1000 + base + 0.0001,
'O': math.sin(x * hz) / 1000 + base, math.sin(x * hz) / 1000 + base - 0.0001,
'H': math.sin(x * hz) / 1000 + base + 0.0001, math.sin(x * hz) / 1000 + base,
'L': math.sin(x * hz) / 1000 + base - 0.0001, pair[x][5] # Keep old volume
'C': math.sin(x * hz) / 1000 + base} for x in range(0, datalen)]} ] for x in range(0, datalen)
]}
return data return data
@ -247,11 +268,11 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non
assert 'live' in config assert 'live' in config
assert log_has('Parameter -l/--live detected ...', caplog.record_tuples) 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('Parameter --realistic-simulation detected ...', caplog.record_tuples)
assert log_has('Using max_open_trades: 1 ...', 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 log_has('Parameter -r/--refresh-pairs-cached detected ...', caplog.record_tuples)
assert 'timerange' in config assert 'timerange' in config
assert log_has( 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 Test start() function
""" """
@ -306,10 +327,11 @@ def test_backtesting__init__(mocker, default_conf) -> None:
assert init_mock.call_count == 1 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 Test Backtesting._init() method
""" """
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
backtesting = Backtesting(default_conf) backtesting = Backtesting(default_conf)
assert backtesting.config == default_conf assert backtesting.config == default_conf
assert isinstance(backtesting.analyze, Analyze) assert isinstance(backtesting.analyze, Analyze)
@ -319,7 +341,7 @@ def test_backtesting_init(default_conf) -> None:
assert callable(backtesting.populate_sell_trend) 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 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']) 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 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' 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 Test Backtesting.generate_text_table() method
""" """
@ -391,12 +413,14 @@ def test_backtesting_start(default_conf, mocker, caplog) -> None:
""" """
Test Backtesting.start() method Test Backtesting.start() method
""" """
def get_timeframe(input1, input2): def get_timeframe(input1, input2):
return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59) return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59)
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock()) mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
mocker.patch('freqtrade.optimize.load_data', mocked_load_data) mocker.patch('freqtrade.optimize.load_data', mocked_load_data)
mocker.patch('freqtrade.exchange.get_ticker_history') mocker.patch('freqtrade.exchange.get_ticker_history')
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.optimize.backtesting.Backtesting', 'freqtrade.optimize.backtesting.Backtesting',
backtest=MagicMock(), backtest=MagicMock(),
@ -426,7 +450,7 @@ def test_backtesting_start(default_conf, mocker, caplog) -> None:
assert log_has(line, caplog.record_tuples) 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 Test Backtesting.backtest() method
""" """
@ -445,7 +469,7 @@ def test_backtest(default_conf) -> None:
assert not results.empty 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 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 assert not results.empty
def test_processed() -> None: def test_processed(init_backtesting) -> None:
""" """
Test Backtesting.backtest() method with offline data Test Backtesting.backtest() method with offline data
""" """
@ -481,14 +505,15 @@ def test_processed() -> None:
assert col in cols 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]] tests = [['raise', 17], ['lower', 0], ['sine', 17]]
for [contour, numres] in tests: for [contour, numres] in tests:
simple_backtest(default_conf, contour, numres) simple_backtest(default_conf, contour, numres)
# Test backtest using offline data (testdata directory) # Test backtest using offline data (testdata directory)
def test_backtest_ticks(default_conf): def test_backtest_ticks(init_backtesting, default_conf):
ticks = [1, 5] ticks = [1, 5]
fun = _BACKTESTING.populate_buy_trend fun = _BACKTESTING.populate_buy_trend
for tick in ticks: for tick in ticks:
@ -497,7 +522,7 @@ def test_backtest_ticks(default_conf):
assert not results.empty 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 # Override the default buy trend function in our default_strategy
def fun(dataframe=None): def fun(dataframe=None):
buy_value = 1 buy_value = 1
@ -509,7 +534,7 @@ def test_backtest_clash_buy_sell(default_conf):
assert results.empty 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 # Override the default buy trend function in our default_strategy
def fun(dataframe=None): def fun(dataframe=None):
buy_value = 0 buy_value = 0
@ -521,15 +546,17 @@ def test_backtest_only_sell(default_conf):
assert results.empty 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') backtest_conf = _make_backtest_conf(conf=default_conf, pair='UNITTEST/BTC')
results = _run_backtest_1(_trend_alternate, backtest_conf) results = _run_backtest_1(_trend_alternate, backtest_conf)
assert len(results) == 3 assert len(results) == 3
def test_backtest_record(default_conf, mocker): def test_backtest_record(init_backtesting, default_conf, fee, mocker):
names = [] names = []
records = [] records = []
mocker.patch('freqtrade.optimize.backtesting.exchange.get_fee', fee)
mocker.patch( mocker.patch(
'freqtrade.optimize.backtesting.file_dump_json', 'freqtrade.optimize.backtesting.file_dump_json',
new=lambda n, r: (names.append(n), records.append(r)) new=lambda n, r: (names.append(n), records.append(r))
@ -562,14 +589,16 @@ def test_backtest_record(default_conf, mocker):
assert dur > 0 assert dur > 0
def test_backtest_start_live(default_conf, mocker, caplog): def test_backtest_start_live(init_backtesting, default_conf, mocker, caplog):
default_conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC'] conf = deepcopy(default_conf)
conf['exchange']['pair_whitelist'] = ['UNITTEST/BTC']
mocker.patch('freqtrade.exchange.get_ticker_history', mocker.patch('freqtrade.exchange.get_ticker_history',
new=lambda n, i: _load_pair_as_ticks(n, i)) 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.backtest', MagicMock())
mocker.patch('freqtrade.optimize.backtesting.Backtesting._generate_text_table', MagicMock()) mocker.patch('freqtrade.optimize.backtesting.Backtesting._generate_text_table', MagicMock())
mocker.patch('freqtrade.configuration.open', mocker.mock_open( mocker.patch('freqtrade.configuration.open', mocker.mock_open(
read_data=json.dumps(default_conf) read_data=json.dumps(conf)
)) ))
args = MagicMock() args = MagicMock()

View File

@ -1,20 +1,34 @@
# pragma pylint: disable=missing-docstring,W0212,C0103 # pragma pylint: disable=missing-docstring,W0212,C0103
import json
import os import os
import signal
from copy import deepcopy from copy import deepcopy
from unittest.mock import MagicMock from unittest.mock import MagicMock
import pytest
import pandas as pd import pandas as pd
from freqtrade.optimize.__init__ import load_tickerdata_file from freqtrade.optimize.__init__ import load_tickerdata_file
from freqtrade.optimize.hyperopt import Hyperopt, start from freqtrade.optimize.hyperopt import Hyperopt, start
from freqtrade.strategy.strategy import Strategy 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 from freqtrade.tests.optimize.test_backtesting import get_args
# Avoid to reinit the same object again and again # 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 # Functions for recurrent object patching
@ -52,9 +66,10 @@ def test_start(mocker, default_conf, caplog) -> None:
start_mock = MagicMock() start_mock = MagicMock()
mocker.patch('freqtrade.logger.Logger.set_format', MagicMock()) mocker.patch('freqtrade.logger.Logger.set_format', MagicMock())
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock) mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock)
mocker.patch('freqtrade.configuration.open', mocker.mock_open( mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf',
read_data=json.dumps(default_conf) MagicMock(return_value=default_conf))
)) mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock())
args = [ args = [
'--config', 'config.json', '--config', 'config.json',
'--strategy', 'default_strategy', '--strategy', 'default_strategy',
@ -75,7 +90,7 @@ def test_start(mocker, default_conf, caplog) -> None:
assert start_mock.call_count == 1 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() Test Hyperopt.calculate_loss()
""" """
@ -89,7 +104,7 @@ def test_loss_calculation_prefer_correct_trade_count() -> None:
assert under > correct 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() Test Hyperopt.calculate_loss()
""" """
@ -100,7 +115,7 @@ def test_loss_calculation_prefer_shorter_trades() -> None:
assert shorter < longer assert shorter < longer
def test_loss_calculation_has_limited_profit() -> None: def test_loss_calculation_has_limited_profit(init_hyperopt) -> None:
hyperopt = _HYPEROPT hyperopt = _HYPEROPT
correct = hyperopt.calculate_loss(hyperopt.expected_max_profit, hyperopt.target_trades, 20) 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 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 = _HYPEROPT
hyperopt.current_best_loss = 2 hyperopt.current_best_loss = 2
hyperopt.log_results( 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) 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 = _HYPEROPT
hyperopt.current_best_loss = 2 hyperopt.current_best_loss = 2
hyperopt.log_results( hyperopt.log_results(
@ -135,7 +150,7 @@ def test_no_log_if_loss_does_not_improve(caplog) -> None:
assert caplog.record_tuples == [] 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 = { fmin_result = {
"macd_below_zero": 0, "macd_below_zero": 0,
"adx": 1, "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.fmin', return_value=fmin_result)
mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf) mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf)
mocker.patch('freqtrade.logger.Logger.set_format', MagicMock()) mocker.patch('freqtrade.logger.Logger.set_format', MagicMock())
mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock())
Strategy({'strategy': 'default_strategy'}) Strategy({'strategy': 'default_strategy'})
hyperopt = Hyperopt(conf) hyperopt = Hyperopt(conf)
@ -204,7 +220,7 @@ def test_fmin_best_results(mocker, default_conf, caplog) -> None:
assert line in caplog.text 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.load_data', MagicMock())
mocker.patch('freqtrade.optimize.hyperopt.fmin', side_effect=ValueError()) 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'}) conf.update({'spaces': 'all'})
mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf) mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf)
mocker.patch('freqtrade.logger.Logger.set_format', MagicMock()) mocker.patch('freqtrade.logger.Logger.set_format', MagicMock())
mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock())
Strategy({'strategy': 'default_strategy'}) Strategy({'strategy': 'default_strategy'})
hyperopt = Hyperopt(conf) hyperopt = Hyperopt(conf)
hyperopt.trials = create_trials(mocker) 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 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) trials = create_trials(mocker)
conf = deepcopy(default_conf) 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.fmin', return_value={})
mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf) mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf)
mocker.patch('freqtrade.logger.Logger.set_format', MagicMock()) mocker.patch('freqtrade.logger.Logger.set_format', MagicMock())
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock())
Strategy({'strategy': 'default_strategy'}) Strategy({'strategy': 'default_strategy'})
hyperopt = Hyperopt(conf) 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)) 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) create_trials(mocker)
mock_dump = mocker.patch('freqtrade.optimize.hyperopt.pickle.dump', return_value=None) 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() hyperopt.save_trials()
trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle')
assert log_has( assert log_has(
'Saving Trials to \'freqtrade/tests/optimize/ut_trials.pickle\'', 'Saving Trials to \'{}\''.format(trials_file),
caplog.record_tuples caplog.record_tuples
) )
mock_dump.assert_called_once() 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) trials = create_trials(mocker)
mock_load = mocker.patch('freqtrade.optimize.hyperopt.pickle.load', return_value=trials) mock_load = mocker.patch('freqtrade.optimize.hyperopt.pickle.load', return_value=trials)
mock_open = mocker.patch('freqtrade.optimize.hyperopt.open', return_value=mock_load) mock_open = mocker.patch('freqtrade.optimize.hyperopt.open', return_value=mock_load)
hyperopt = _HYPEROPT hyperopt = _HYPEROPT
hyperopt_trial = hyperopt.read_trials() hyperopt_trial = hyperopt.read_trials()
trials_file = os.path.join('freqtrade', 'tests', 'optimize', 'ut_trials.pickle')
assert log_has( assert log_has(
'Reading Trials from \'freqtrade/tests/optimize/ut_trials.pickle\'', 'Reading Trials from \'{}\''.format(trials_file),
caplog.record_tuples caplog.record_tuples
) )
assert hyperopt_trial == trials assert hyperopt_trial == trials
@ -307,7 +328,7 @@ def test_read_trials_returns_trials_file(mocker, caplog) -> None:
mock_load.assert_called_once() mock_load.assert_called_once()
def test_roi_table_generation() -> None: def test_roi_table_generation(init_hyperopt) -> None:
params = { params = {
'roi_t1': 5, 'roi_t1': 5,
'roi_t2': 10, '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} 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) trials = create_trials(mocker)
mocker.patch('freqtrade.optimize.hyperopt.sorted', return_value=trials.results) mocker.patch('freqtrade.optimize.hyperopt.sorted', return_value=trials.results)
mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock()) 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={}) mock_fmin = mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={})
conf = deepcopy(default_conf) conf = deepcopy(default_conf)
@ -342,7 +364,7 @@ def test_start_calls_fmin(mocker, default_conf) -> None:
mock_fmin.assert_called_once() 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()) mocker.patch('freqtrade.optimize.hyperopt.load_data', MagicMock())
mock_fmin = mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={}) mock_fmin = mocker.patch('freqtrade.optimize.hyperopt.fmin', return_value={})
mock_mongotrials = mocker.patch( mock_mongotrials = mocker.patch(
@ -357,6 +379,7 @@ def test_start_uses_mongotrials(mocker, default_conf) -> None:
conf.update({'timerange': None}) conf.update({'timerange': None})
conf.update({'spaces': 'all'}) conf.update({'spaces': 'all'})
mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf) mocker.patch('freqtrade.optimize.hyperopt.hyperopt_optimize_conf', return_value=conf)
mocker.patch('freqtrade.freqtradebot.exchange.validate_pairs', MagicMock())
hyperopt = Hyperopt(conf) hyperopt = Hyperopt(conf)
hyperopt.tickerdata_to_dataframe = MagicMock() hyperopt.tickerdata_to_dataframe = MagicMock()
@ -385,7 +408,7 @@ def test_format_results():
assert x.find(' 66.67%') assert x.find(' 66.67%')
def test_signal_handler(mocker): def test_signal_handler(mocker, init_hyperopt):
""" """
Test Hyperopt.signal_handler() Test Hyperopt.signal_handler()
""" """
@ -395,11 +418,11 @@ def test_signal_handler(mocker):
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.log_trials_result', m) mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.log_trials_result', m)
hyperopt = _HYPEROPT hyperopt = _HYPEROPT
hyperopt.signal_handler(9, None) hyperopt.signal_handler(signal.SIGTERM, None)
assert m.call_count == 3 assert m.call_count == 3
def test_has_space(): def test_has_space(init_hyperopt):
""" """
Test Hyperopt.has_space() method Test Hyperopt.has_space() method
""" """
@ -412,7 +435,7 @@ def test_has_space():
assert _HYPEROPT.has_space('buy') assert _HYPEROPT.has_space('buy')
def test_populate_indicators() -> None: def test_populate_indicators(init_hyperopt) -> None:
""" """
Test Hyperopt.populate_indicators() Test Hyperopt.populate_indicators()
""" """
@ -427,7 +450,7 @@ def test_populate_indicators() -> None:
assert 'cci' in dataframe assert 'cci' in dataframe
def test_buy_strategy_generator() -> None: def test_buy_strategy_generator(init_hyperopt) -> None:
""" """
Test Hyperopt.buy_strategy_generator() Test Hyperopt.buy_strategy_generator()
""" """
@ -484,7 +507,7 @@ def test_buy_strategy_generator() -> None:
assert 1 in result['buy'] 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 Test Hyperopt.generate_optimizer() function
""" """
@ -503,6 +526,7 @@ def test_generate_optimizer(mocker, default_conf) -> None:
'freqtrade.optimize.hyperopt.Hyperopt.backtest', 'freqtrade.optimize.hyperopt.Hyperopt.backtest',
MagicMock(return_value=backtest_result) MagicMock(return_value=backtest_result)
) )
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock())
optimizer_param = { optimizer_param = {
'adx': {'enabled': False}, 'adx': {'enabled': False},

View File

@ -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) 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) _backup_file(file, copy_file=True)
optimize.load_data(None, pairs=['UNITTEST/BTC'], ticker_interval='30m') optimize.load_data(None, pairs=['UNITTEST/BTC'], ticker_interval='30m')
assert os.path.isfile(file) is True 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) _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) 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) _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 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) _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) 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) _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 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) _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) 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) _backup_file(file)
optimize.load_data(None, ticker_interval='1m', pairs=['MEME/BTC']) optimize.load_data(None, ticker_interval='1m', pairs=['MEME/BTC'])
assert os.path.isfile(file) is True 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) _clean_test_file(file)
@ -109,10 +110,10 @@ def test_testdata_path() -> None:
def test_download_pairs(ticker_history, mocker) -> None: def test_download_pairs(ticker_history, mocker) -> None:
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history) mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history)
file1_1 = 'freqtrade/tests/testdata/MEME_BTC-1m.json' file1_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
file1_5 = 'freqtrade/tests/testdata/MEME_BTC-5m.json' file1_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-5m.json')
file2_1 = 'freqtrade/tests/testdata/CFI_BTC-1m.json' file2_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'CFI_BTC-1m.json')
file2_5 = 'freqtrade/tests/testdata/CFI_BTC-5m.json' file2_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'CFI_BTC-5m.json')
_backup_file(file1_1) _backup_file(file1_1)
_backup_file(file1_5) _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', mocker.patch('freqtrade.optimize.__init__.download_backtesting_testdata',
side_effect=BaseException('File Error')) side_effect=BaseException('File Error'))
file1_1 = 'freqtrade/tests/testdata/MEME_BTC-1m.json' file1_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
file1_5 = 'freqtrade/tests/testdata/MEME_BTC-5m.json' file1_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-5m.json')
_backup_file(file1_1) _backup_file(file1_1)
_backup_file(file1_5) _backup_file(file1_5)
@ -158,21 +159,21 @@ def test_download_pairs_exception(ticker_history, mocker, caplog) -> None:
# clean files freshly downloaded # clean files freshly downloaded
_clean_test_file(file1_1) _clean_test_file(file1_1)
_clean_test_file(file1_5) _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: def test_download_backtesting_testdata(ticker_history, mocker) -> None:
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history) mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history)
# Download a 1 min ticker file # 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) _backup_file(file1)
download_backtesting_testdata(None, pair="XEL/BTC", interval='1m') download_backtesting_testdata(None, pair="XEL/BTC", interval='1m')
assert os.path.isfile(file1) is True assert os.path.isfile(file1) is True
_clean_test_file(file1) _clean_test_file(file1)
# Download a 5 min ticker file # 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) _backup_file(file2)
download_backtesting_testdata(None, pair="STORJ/BTC", interval='5m') 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: 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.misc.file_dump_json', return_value=None)
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=tick) mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=tick)
assert download_backtesting_testdata(None, pair="UNITTEST/BTC", interval='1m') 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: 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 = json.load(data_file)
ticker_list_len = len(ticker_list) ticker_list_len = len(ticker_list)
@ -260,7 +265,8 @@ def test_file_dump_json() -> None:
Test file_dump_json() Test file_dump_json()
:return: None :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'} data = {'bar': 'foo'}
# check the file we will create does not exist # check the file we will create does not exist

View File

@ -25,7 +25,7 @@ def prec_satoshi(a, b) -> float:
# Unit tests # 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 Test rpc_trade_status() method
""" """
@ -35,7 +35,8 @@ def test_rpc_trade_status(default_conf, ticker, mocker) -> None:
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker get_ticker=ticker,
get_fee=fee
) )
freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) 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' '*Current Rate:* `0.00001098`\n'
'*Close Profit:* `None`\n' '*Close Profit:* `None`\n'
'*Current Profit:* `-0.59%`\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 result == result_message
assert trade.find('[ETH/BTC]') >= 0 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 Test rpc_status_table() method
""" """
@ -83,7 +84,8 @@ def test_rpc_status_table(default_conf, ticker, mocker) -> None:
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker get_ticker=ticker,
get_fee=fee
) )
freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) 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() assert '-0.59%' in result['Profit'].all()
def test_rpc_daily_profit(default_conf, update, ticker, limit_buy_order, limit_sell_order, mocker)\ def test_rpc_daily_profit(default_conf, update, ticker, fee,
-> None: limit_buy_order, limit_sell_order, mocker) -> None:
""" """
Test rpc_daily_profit() method 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( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker get_ticker=ticker,
get_fee=fee
) )
freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) 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 assert days.find('must be an integer greater than 0') >= 0
def test_rpc_trade_statistics( def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
default_conf, ticker, ticker_sell_up, limit_buy_order, limit_sell_order, mocker) -> None: limit_buy_order, limit_sell_order, mocker) -> None:
""" """
Test rpc_trade_statistics() method Test rpc_trade_statistics() method
""" """
@ -173,7 +176,8 @@ def test_rpc_trade_statistics(
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker get_ticker=ticker,
get_fee=fee
) )
freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) 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 # Test that rpc_trade_statistics can handle trades that lacks
# trade.open_rate (it is set to None) # trade.open_rate (it is set to None)
def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, ticker_sell_up, limit_buy_order, def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee,
limit_sell_order): ticker_sell_up, limit_buy_order, limit_sell_order):
""" """
Test rpc_trade_statistics() method 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( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker get_ticker=ticker,
get_fee=fee
) )
freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) 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( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker_sell_up get_ticker=ticker_sell_up,
get_fee=fee
) )
trade.update(limit_sell_order) trade.update(limit_sell_order)
trade.close_date = datetime.utcnow() trade.close_date = datetime.utcnow()
@ -401,8 +407,9 @@ def test_rpc_forcesell(default_conf, ticker, mocker) -> None:
cancel_order=cancel_order_mock, cancel_order=cancel_order_mock,
get_order=MagicMock( get_order=MagicMock(
return_value={ return_value={
'closed': True, 'status': 'closed',
'type': 'LIMIT_BUY', 'type': 'limit',
'side': 'buy'
} }
) )
) )
@ -448,8 +455,9 @@ def test_rpc_forcesell(default_conf, ticker, mocker) -> None:
mocker.patch( mocker.patch(
'freqtrade.freqtradebot.exchange.get_order', 'freqtrade.freqtradebot.exchange.get_order',
return_value={ return_value={
'closed': None, 'status': 'open',
'type': 'LIMIT_BUY' 'type': 'limit',
'side': 'buy'
} }
) )
# check that the trade is called, which is done # check that the trade is called, which is done
@ -464,8 +472,9 @@ def test_rpc_forcesell(default_conf, ticker, mocker) -> None:
mocker.patch( mocker.patch(
'freqtrade.freqtradebot.exchange.get_order', 'freqtrade.freqtradebot.exchange.get_order',
return_value={ return_value={
'closed': None, 'status': 'open',
'type': 'LIMIT_SELL' 'type': 'limit',
'side': 'sell'
} }
) )
(error, res) = rpc.rpc_forcesell('2') (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 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: limit_sell_order, mocker) -> None:
""" """
Test rpc_performance() method Test rpc_performance() method
@ -487,7 +496,8 @@ def test_performance_handle(default_conf, ticker, limit_buy_order,
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_balances=MagicMock(return_value=ticker), get_balances=MagicMock(return_value=ticker),
get_ticker=ticker get_ticker=ticker,
get_fee=fee
) )
freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://'))

View File

@ -333,7 +333,7 @@ def test_status_table_handle(default_conf, update, ticker, mocker) -> None:
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, get_ticker=ticker,
buy=MagicMock(return_value='mocked_order_id') buy=MagicMock(return_value={'id': 'mocked_order_id'})
) )
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.multiple( mocker.patch.multiple(
@ -374,7 +374,7 @@ def test_status_table_handle(default_conf, update, ticker, mocker) -> None:
assert msg_mock.call_count == 1 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: limit_sell_order, mocker) -> None:
""" """
Test _daily() method Test _daily() method
@ -389,7 +389,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order,
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, get_ticker=ticker,
get_pair_detail_url=MagicMock() get_fee=fee
) )
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.multiple( 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] 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: limit_buy_order, limit_sell_order, mocker) -> None:
""" """
Test _profit() method Test _profit() method
@ -497,7 +497,8 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up,
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker get_ticker=ticker,
get_fee=fee
) )
msg_mock = MagicMock() msg_mock = MagicMock()
mocker.patch.multiple( 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] 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 Test _forcesell() method
""" """
@ -761,7 +762,8 @@ def test_forcesell_handle(default_conf, update, ticker, ticker_sell_up, mocker)
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker get_ticker=ticker,
get_fee=fee
) )
freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) 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] 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 Test _forcesell() method
""" """
@ -800,7 +802,8 @@ def test_forcesell_down_handle(default_conf, update, ticker, ticker_sell_down, m
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker get_ticker=ticker,
get_fee=fee
) )
freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) 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] 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 Test _forcesell() method
""" """
@ -844,7 +847,8 @@ def test_forcesell_all_handle(default_conf, update, ticker, mocker) -> None:
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker get_ticker=ticker,
get_fee=fee
) )
freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) 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] assert 'Invalid argument.' in msg_mock.call_args_list[0][0][0]
def test_performance_handle(default_conf, update, ticker, limit_buy_order, def test_performance_handle(default_conf, update, ticker, fee,
limit_sell_order, mocker) -> None: limit_buy_order, limit_sell_order, mocker) -> None:
""" """
Test _performance() method Test _performance() method
""" """
@ -923,7 +927,8 @@ def test_performance_handle(default_conf, update, ticker, limit_buy_order,
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker get_ticker=ticker,
get_fee=fee
) )
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock()) mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://')) freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://'))
@ -987,7 +992,7 @@ def test_count_handle(default_conf, update, ticker, mocker) -> None:
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, 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://')) freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://'))
telegram = Telegram(freqtradebot) telegram = Telegram(freqtradebot)

View File

@ -1,18 +1,8 @@
import json
import pytest
from pandas import DataFrame from pandas import DataFrame
from freqtrade.analyze import Analyze
from freqtrade.strategy.default_strategy import DefaultStrategy, class_name 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(): def test_default_strategy_class_name():
assert class_name == DefaultStrategy.__name__ assert class_name == DefaultStrategy.__name__

View File

@ -1,6 +1,7 @@
# pragma pylint: disable=missing-docstring,C0103,protected-access # pragma pylint: disable=missing-docstring,C0103,protected-access
import freqtrade.tests.conftest as tt # test tools import freqtrade.tests.conftest as tt # test tools
from unittest.mock import MagicMock
# whitelist, blacklist, filtering, all of that will # whitelist, blacklist, filtering, all of that will
# eventually become some rules to run on a generic ACL engine # eventually become some rules to run on a generic ACL engine
@ -26,81 +27,12 @@ def whitelist_conf():
return config return config
def get_market_summaries(): def test_refresh_market_pair_not_in_whitelist(mocker, markets):
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):
conf = whitelist_conf() conf = whitelist_conf()
freqtradebot = tt.get_patched_freqtradebot(mocker, 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( refreshedwhitelist = freqtradebot._refresh_whitelist(
conf['exchange']['pair_whitelist'] + ['XXX/BTC'] conf['exchange']['pair_whitelist'] + ['XXX/BTC']
) )
@ -110,11 +42,11 @@ def test_refresh_market_pair_not_in_whitelist(mocker):
assert whitelist == refreshedwhitelist assert whitelist == refreshedwhitelist
def test_refresh_whitelist(mocker): def test_refresh_whitelist(mocker, markets):
conf = whitelist_conf() conf = whitelist_conf()
freqtradebot = tt.get_patched_freqtradebot(mocker, 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']) refreshedwhitelist = freqtradebot._refresh_whitelist(conf['exchange']['pair_whitelist'])
# List ordered by BaseVolume # List ordered by BaseVolume
@ -123,17 +55,18 @@ def test_refresh_whitelist(mocker):
assert whitelist == refreshedwhitelist assert whitelist == refreshedwhitelist
def test_refresh_whitelist_dynamic(mocker): def test_refresh_whitelist_dynamic(mocker, markets, tickers):
conf = whitelist_conf() conf = whitelist_conf()
freqtradebot = tt.get_patched_freqtradebot(mocker, conf) freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
get_wallet_health=get_health, get_markets=markets,
get_market_summaries=get_market_summaries get_tickers=tickers,
exchange_has=MagicMock(return_value=True)
) )
# argument: use the whitelist dynamically by exchange-volume # argument: use the whitelist dynamically by exchange-volume
whitelist = ['TKN/BTC', 'ETH/BTC'] whitelist = ['ETH/BTC', 'TKN/BTC']
refreshedwhitelist = freqtradebot._refresh_whitelist( refreshedwhitelist = freqtradebot._refresh_whitelist(
freqtradebot._gen_pair_whitelist(conf['stake_currency']) freqtradebot._gen_pair_whitelist(conf['stake_currency'])
@ -142,10 +75,10 @@ def test_refresh_whitelist_dynamic(mocker):
assert whitelist == refreshedwhitelist assert whitelist == refreshedwhitelist
def test_refresh_whitelist_dynamic_empty(mocker): def test_refresh_whitelist_dynamic_empty(mocker, markets_empty):
conf = whitelist_conf() conf = whitelist_conf()
freqtradebot = tt.get_patched_freqtradebot(mocker, 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 # argument: use the whitelist dynamically by exchange-volume
whitelist = [] whitelist = []

View File

@ -50,7 +50,7 @@ def test_dataframe_correct_length(result):
def test_dataframe_correct_columns(result): def test_dataframe_correct_columns(result):
assert result.columns.tolist() == \ assert result.columns.tolist() == \
['close', 'high', 'low', 'open', 'date', 'volume'] ['date', 'open', 'high', 'low', 'close', 'volume']
def test_populates_buy_trend(result): 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) assert _ANALYZE.get_signal('ETH/BTC', '5m') == (False, False)
def test_parse_ticker_dataframe(ticker_history, ticker_history_without_bv): def test_parse_ticker_dataframe(ticker_history):
columns = ['close', 'high', 'low', 'open', 'date', 'volume'] columns = ['date', 'open', 'high', 'low', 'close', 'volume']
# Test file with BV data # Test file with BV data
dataframe = Analyze.parse_ticker_dataframe(ticker_history) dataframe = Analyze.parse_ticker_dataframe(ticker_history)
assert dataframe.columns.tolist() == columns 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: def test_tickerdata_to_dataframe(default_conf) -> None:
""" """

View File

@ -201,29 +201,28 @@ def test_throttle_with_assets(mocker, default_conf) -> None:
assert result == -1 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 Test _gen_pair_whitelist() method
""" """
freqtrade = get_patched_freqtradebot(mocker, default_conf) freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch( mocker.patch('freqtrade.freqtradebot.exchange.get_tickers', tickers)
'freqtrade.freqtradebot.exchange.get_market_summaries', mocker.patch('freqtrade.freqtradebot.exchange.exchange_has', MagicMock(return_value=True))
return_value=get_market_summaries_data 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') 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 # Test to retrieve BTC sorted on bidVolume
whitelist = freqtrade._gen_pair_whitelist(base_currency='BTC', key='OpenBuyOrders') whitelist = freqtrade._gen_pair_whitelist(base_currency='BTC', key='bidVolume')
assert whitelist == ['XWC/BTC', 'ZCL/BTC', 'ZEC/BTC', 'XZC/BTC'] 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') 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') whitelist = freqtrade._gen_pair_whitelist(base_currency='ETH')
assert whitelist == [] assert whitelist == []
@ -247,7 +246,7 @@ def test_create_trade(default_conf, ticker, limit_buy_order, mocker) -> None:
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, get_ticker=ticker,
buy=MagicMock(return_value='mocked_limit_buy') buy=MagicMock(return_value={'id': limit_buy_order['id']})
) )
# Save state of current whitelist # 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'] 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 Test create_trade() method
""" """
patch_get_signal(mocker) patch_get_signal(mocker)
patch_RPCManager(mocker) patch_RPCManager(mocker)
patch_coinmarketcap(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( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
@ -295,7 +294,7 @@ def test_create_trade_minimal_amount(default_conf, ticker, mocker) -> None:
assert rate * amount >= conf['stake_amount'] 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 Test create_trade() method
""" """
@ -306,7 +305,7 @@ def test_create_trade_no_stake_amount(default_conf, ticker, mocker) -> None:
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, 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) get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5)
) )
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) 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() 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 Test create_trade() method
""" """
@ -326,7 +325,7 @@ def test_create_trade_no_pairs(default_conf, ticker, mocker) -> None:
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, get_ticker=ticker,
buy=MagicMock(return_value='mocked_limit_buy') buy=MagicMock(return_value={'id': limit_buy_order['id']})
) )
conf = deepcopy(default_conf) conf = deepcopy(default_conf)
@ -340,7 +339,8 @@ def test_create_trade_no_pairs(default_conf, ticker, mocker) -> None:
freqtrade.create_trade() 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 Test create_trade() method
""" """
@ -351,7 +351,7 @@ def test_create_trade_no_pairs_after_blacklist(default_conf, ticker, mocker) ->
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, get_ticker=ticker,
buy=MagicMock(return_value='mocked_limit_buy') buy=MagicMock(return_value={'id': limit_buy_order['id']})
) )
conf = deepcopy(default_conf) 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, 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 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', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, get_ticker=ticker,
get_wallet_health=health, get_markets=markets,
buy=MagicMock(return_value='mocked_limit_buy'), buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_order=MagicMock(return_value=limit_buy_order) get_order=MagicMock(return_value=limit_buy_order)
) )
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) 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 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', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, get_ticker=ticker,
get_wallet_health=health, get_markets=markets,
buy=MagicMock(side_effect=requests.exceptions.RequestException) buy=MagicMock(side_effect=requests.exceptions.RequestException)
) )
sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None) 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() 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 Test _process() method when an OperationalException happens
""" """
@ -465,7 +465,7 @@ def test_process_operational_exception(default_conf, ticker, health, mocker) ->
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, get_ticker=ticker,
get_wallet_health=health, get_markets=markets,
buy=MagicMock(side_effect=OperationalException) buy=MagicMock(side_effect=OperationalException)
) )
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) 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] 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() Test _process()
""" """
@ -488,8 +488,8 @@ def test_process_trade_handling(default_conf, ticker, limit_buy_order, health, m
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, get_ticker=ticker,
get_wallet_health=health, get_markets=markets,
buy=MagicMock(return_value='mocked_limit_buy'), buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_order=MagicMock(return_value=limit_buy_order) get_order=MagicMock(return_value=limit_buy_order)
) )
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) 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) 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 Test check_handle() method
""" """
@ -591,8 +591,9 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, mocker) -
'ask': 0.00001173, 'ask': 0.00001173,
'last': 0.00001172 'last': 0.00001172
}), }),
buy=MagicMock(return_value='mocked_limit_buy'), buy=MagicMock(return_value={'id': limit_buy_order['id']}),
sell=MagicMock(return_value='mocked_limit_sell') sell=MagicMock(return_value={'id': limit_sell_order['id']}),
get_fee=fee
) )
patch_coinmarketcap(mocker, value={'price_usd': 15000.0}) 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() trade = Trade.query.first()
assert trade assert trade
time.sleep(0.01) # Race condition fix
trade.update(limit_buy_order) trade.update(limit_buy_order)
assert trade.is_open is True assert trade.is_open is True
patch_get_signal(mocker, value=(False, True)) patch_get_signal(mocker, value=(False, True))
assert freqtrade.handle_trade(trade) is 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 # Simulate fulfilled LIMIT_SELL order for trade
trade.update(limit_sell_order) 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 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 Test check_handle() method
""" """
@ -634,7 +636,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, mocker) -> None:
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, 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://')) 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 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 Test check_handle() method
""" """
@ -691,7 +693,7 @@ def test_handle_trade_roi(default_conf, ticker, mocker, caplog) -> None:
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, 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) 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) 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 Test check_handle() method
""" """
@ -726,7 +728,7 @@ def test_handle_trade_experimental(default_conf, ticker, mocker, caplog) -> None
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, 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) 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', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker, 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://')) 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) 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 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(), validate_pairs=MagicMock(),
get_ticker=ticker, get_ticker=ticker,
get_order=MagicMock(return_value=limit_buy_order_old), 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://')) freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
trade_buy = Trade( trade_buy = Trade(
pair='ETH/BTC', pair='ETH/BTC',
open_rate=0.00001099, open_rate=0.00001099,
exchange='BITTREX', exchange='bittrex',
open_order_id='123456789', open_order_id='123456789',
amount=90.99181073, amount=90.99181073,
fee=0.0, fee=0.0,
@ -831,7 +834,7 @@ def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old,
trade_sell = Trade( trade_sell = Trade(
pair='ETH/BTC', pair='ETH/BTC',
open_rate=0.00001099, open_rate=0.00001099,
exchange='BITTREX', exchange='bittrex',
open_order_id='123456789', open_order_id='123456789',
amount=90.99181073, amount=90.99181073,
fee=0.0, fee=0.0,
@ -870,7 +873,7 @@ def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old
trade_buy = Trade( trade_buy = Trade(
pair='ETH/BTC', pair='ETH/BTC',
open_rate=0.00001099, open_rate=0.00001099,
exchange='BITTREX', exchange='bittrex',
open_order_id='123456789', open_order_id='123456789',
amount=90.99181073, amount=90.99181073,
fee=0.0, fee=0.0,
@ -917,7 +920,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -
trade_buy = Trade( trade_buy = Trade(
pair='ETH/BTC', pair='ETH/BTC',
open_rate=0.00001099, open_rate=0.00001099,
exchange='BITTREX', exchange='bittrex',
open_order_id='123456789', open_order_id='123456789',
amount=90.99181073, amount=90.99181073,
fee=0.0, fee=0.0,
@ -989,7 +992,7 @@ def test_handle_timedout_limit_sell(mocker, default_conf) -> None:
assert cancel_order_mock.call_count == 1 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 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( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker get_ticker=ticker,
get_fee=fee
) )
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0) mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) 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] 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 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( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker get_ticker=ticker,
get_fee=fee
) )
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) 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] 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 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( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker get_ticker=ticker,
get_fee=fee
) )
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) 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] 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: ticker_sell_down, mocker) -> None:
""" """
Test execute_sell() method with a ticker going DOWN and with a bot config empty 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( mocker.patch.multiple(
'freqtrade.freqtradebot.exchange', 'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(), validate_pairs=MagicMock(),
get_ticker=ticker get_ticker=ticker,
get_fee=fee
) )
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://')) 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, 'ask': 0.00002173,
'last': 0.00002172 'last': 0.00002172
}), }),
buy=MagicMock(return_value='mocked_limit_buy') buy=MagicMock(return_value={'id': limit_buy_order['id']})
) )
conf = deepcopy(default_conf) conf = deepcopy(default_conf)
conf['experimental'] = { conf['experimental'] = {
@ -1193,7 +1201,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, mocker)
'ask': 0.00002173, 'ask': 0.00002173,
'last': 0.00002172 'last': 0.00002172
}), }),
buy=MagicMock(return_value='mocked_limit_buy') buy=MagicMock(return_value={'id': limit_buy_order['id']})
) )
conf = deepcopy(default_conf) conf = deepcopy(default_conf)
conf['experimental'] = { conf['experimental'] = {
@ -1225,7 +1233,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, mocker) ->
'ask': 0.00000173, 'ask': 0.00000173,
'last': 0.00000172 'last': 0.00000172
}), }),
buy=MagicMock(return_value='mocked_limit_buy') buy=MagicMock(return_value={'id': limit_buy_order['id']})
) )
conf = deepcopy(default_conf) conf = deepcopy(default_conf)
conf['experimental'] = { conf['experimental'] = {
@ -1257,7 +1265,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, mocker) ->
'ask': 0.00000173, 'ask': 0.00000173,
'last': 0.00000172 'last': 0.00000172
}), }),
buy=MagicMock(return_value='mocked_limit_buy') buy=MagicMock(return_value={'id': limit_buy_order['id']})
) )
conf = deepcopy(default_conf) conf = deepcopy(default_conf)

View File

@ -89,7 +89,7 @@ def test_init_prod_db(default_conf, mocker):
os.rename(prod_db_swp, prod_db) 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. 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( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
fee=0.0025, fee=fee.return_value,
exchange='bittrex', exchange='bittrex',
) )
assert trade.open_order_id is None 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 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( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
fee=0.0025, fee=fee.return_value,
exchange='bittrex', 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 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( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
fee=0.0025, fee=fee.return_value,
exchange='bittrex', exchange='bittrex',
) )
@ -191,7 +191,7 @@ def test_update_open_order(limit_buy_order):
assert trade.close_profit is None assert trade.close_profit is None
assert trade.close_date is None assert trade.close_date is None
limit_buy_order['closed'] = False limit_buy_order['status'] = 'open'
trade.update(limit_buy_order) trade.update(limit_buy_order)
assert trade.open_order_id is None assert trade.open_order_id is None
@ -212,11 +212,11 @@ def test_update_invalid_order(limit_buy_order):
trade.update(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( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
fee=0.0025, fee=fee.return_value,
exchange='bittrex', exchange='bittrex',
) )
trade.open_order_id = 'open_trade' 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 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( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
fee=0.0025, fee=fee.return_value,
exchange='bittrex', exchange='bittrex',
) )
trade.open_order_id = 'close_trade' 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 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( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
fee=0.0025, fee=fee.return_value,
exchange='bittrex', exchange='bittrex',
) )
trade.open_order_id = 'profit_percent' 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 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( trade = Trade(
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
fee=0.0025, fee=fee.return_value,
exchange='bittrex', exchange='bittrex',
) )
trade.open_order_id = 'profit_percent' 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 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://')) init(default_conf, create_engine('sqlite://'))
# Simulate dry_run entries # Simulate dry_run entries
@ -312,7 +312,7 @@ def test_clean_dry_run_db(default_conf):
pair='ETH/BTC', pair='ETH/BTC',
stake_amount=0.001, stake_amount=0.001,
amount=123.0, amount=123.0,
fee=0.0025, fee=fee.return_value,
open_rate=0.123, open_rate=0.123,
exchange='bittrex', exchange='bittrex',
open_order_id='dry_run_buy_12345' open_order_id='dry_run_buy_12345'
@ -323,7 +323,7 @@ def test_clean_dry_run_db(default_conf):
pair='ETC/BTC', pair='ETC/BTC',
stake_amount=0.001, stake_amount=0.001,
amount=123.0, amount=123.0,
fee=0.0025, fee=fee.return_value,
open_rate=0.123, open_rate=0.123,
exchange='bittrex', exchange='bittrex',
open_order_id='dry_run_sell_12345' open_order_id='dry_run_sell_12345'
@ -335,7 +335,7 @@ def test_clean_dry_run_db(default_conf):
pair='ETC/BTC', pair='ETC/BTC',
stake_amount=0.001, stake_amount=0.001,
amount=123.0, amount=123.0,
fee=0.0025, fee=fee.return_value,
open_rate=0.123, open_rate=0.123,
exchange='bittrex', exchange='bittrex',
open_order_id='prod_buy_12345' open_order_id='prod_buy_12345'

View File

@ -25,6 +25,7 @@ def hyperopt_optimize_conf() -> dict:
"ask_last_balance": 0.0 "ask_last_balance": 0.0
}, },
"exchange": { "exchange": {
"name": "bittrex",
"pair_whitelist": [ "pair_whitelist": [
"ETH/BTC", "ETH/BTC",
"LTC/BTC", "LTC/BTC",