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:
raise OperationalException(
'Pair {} is not available at {}'.format(pair, _API.id.lower()))
'Pair {} is not available at {}'.format(pair, get_name()))
def exchange_has(endpoint: str) -> bool:
"""
Checks if exchange implements a specific API endpoint.
Wrapper around ccxt 'has' attribute
:param endpoint: Name of endpoint (e.g. 'fetchOHLCV', 'fetchTickers')
:return: bool
"""
return endpoint in _API.has and _API.has[endpoint]
def buy(pair: str, rate: float, amount: float) -> Dict:
@ -216,6 +226,23 @@ def get_balances() -> dict:
raise OperationalException(e)
@retrier
def get_tickers() -> Dict:
try:
return _API.fetch_tickers()
except ccxt.NetworkError as e:
raise NetworkException(
'Could not load tickers due to networking error. Message: {}'.format(e)
)
except ccxt.BaseError as e:
raise OperationalException(e)
except ccxt.NotSupported as e:
raise OperationalException(
'Exchange {} does not support fetching tickers in batch.'
'Message: {}'.format(_API.name, e)
)
# TODO: remove refresh argument, keeping it to keep track of where it was intended to be used
@retrier
def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict:
@ -231,11 +258,6 @@ def get_ticker(pair: str, refresh: Optional[bool] = True) -> dict:
@retrier
def get_ticker_history(pair: str, tick_interval: str) -> List[Dict]:
if 'fetchOHLCV' not in _API.has or not _API.has['fetchOHLCV']:
raise OperationalException(
'Exchange {} does not support fetching historical candlestick data.'.format(_API.name)
)
try:
return _API.fetch_ohlcv(pair, timeframe=tick_interval)
except ccxt.NetworkError as e:
@ -244,6 +266,11 @@ def get_ticker_history(pair: str, tick_interval: str) -> List[Dict]:
)
except ccxt.BaseError as e:
raise OperationalException('Could not fetch ticker data. Msg: {}'.format(e))
except ccxt.NotSupported as e:
raise OperationalException(
'Exchange {} does not support fetching historical candlestick data.'
'Message: {}'.format(_API.name, e)
)
def cancel_order(order_id: str, pair: str) -> None:
@ -315,18 +342,11 @@ def get_id() -> str:
return _API.id
def get_fee_maker() -> float:
return _API.fees['trading']['maker']
def get_fee_taker() -> float:
return _API.fees['trading']['taker']
def get_fee() -> float:
def get_fee(symbol='ETH/BTC', type='', side='', amount=1,
price=1, taker_or_maker='maker') -> float:
# validate that markets are loaded before trying to get fee
if _API.markets is None or len(_API.markets) == 0:
_API.load_markets()
return _API.calculate_fee(symbol='ETH/BTC', type='', side='', amount=1, price=1)['rate']
return _API.calculate_fee(symbol=symbol, type=type, side=side, amount=amount,
price=price, takerOrMaker=taker_or_maker)['rate']

View File

@ -107,10 +107,7 @@ class FreqtradeBot(object):
Constants.PROCESS_THROTTLE_SECS
)
nb_assets = self.config.get(
'dynamic_whitelist',
Constants.DYNAMIC_WHITELIST
)
nb_assets = self.config.get('dynamic_whitelist', None)
self._throttle(func=self._process,
min_secs=min_secs,
@ -185,49 +182,60 @@ class FreqtradeBot(object):
return state_changed
@cached(TTLCache(maxsize=1, ttl=1800))
def _gen_pair_whitelist(self, base_currency: str, key: str = 'BaseVolume') -> List[str]:
def _gen_pair_whitelist(self, base_currency: str, key: str = 'quoteVolume') -> List[str]:
"""
Updates the whitelist with with a dynamically generated list
:param base_currency: base currency as str
:param key: sort key (defaults to 'BaseVolume')
:param key: sort key (defaults to 'quoteVolume')
:return: List of pairs
"""
summaries = sorted(
(v for s, v in exchange.get_market_summaries().items() if v['symbol'].endswith(base_currency)),
key=lambda v: v.get('info').get(key) or 0.0,
reverse=True
)
return [s['symbol'] for s in summaries]
if not exchange.exchange_has('fetchTickers'):
raise OperationalException(
'Exchange does not support dynamic whitelist.'
'Please edit your config and restart the bot'
)
tickers = exchange.get_tickers()
# check length so that we make sure that '/' is actually in the string
tickers = [v for k, v in tickers.items()
if len(k.split('/')) == 2 and k.split('/')[1] == base_currency]
sorted_tickers = sorted(tickers, reverse=True, key=lambda t: t[key])
pairs = [s['symbol'] for s in sorted_tickers]
return pairs
def _refresh_whitelist(self, whitelist: List[str]) -> List[str]:
"""
Check wallet health and remove pair from whitelist if necessary
Check available markets and remove pair from whitelist if necessary
:param whitelist: the sorted list (based on BaseVolume) of pairs the user might want to
trade
:return: the list of pairs the user wants to trade without the one unavailable or
black_listed
"""
sanitized_whitelist = whitelist
health = exchange.get_wallet_health()
markets = exchange.get_markets()
markets = [m for m in markets if m['quote'] == self.config['stake_currency']]
known_pairs = set()
for symbol, status in health.items():
pair = f"{status['base']}/{self.config['stake_currency']}"
for market in markets:
pair = market['symbol']
# pair is not int the generated dynamic market, or in the blacklist ... ignore it
if pair not in whitelist or pair in self.config['exchange'].get('pair_blacklist', []):
continue
# else the pair is valid
known_pairs.add(pair)
# Market is not active
if not status['active']:
if not market['active']:
sanitized_whitelist.remove(pair)
self.logger.info(
'Ignoring %s from whitelist (reason: %s).',
pair, status.get('Notice') or 'wallet is not active'
'Ignoring %s from whitelist. Market is not active.',
pair
)
# We need to remove pairs that are unknown
final_list = [x for x in sanitized_whitelist if x in known_pairs]
return final_list
def get_target_bid(self, ticker: Dict[str, float]) -> float:
@ -285,7 +293,7 @@ class FreqtradeBot(object):
buy_limit = self.get_target_bid(exchange.get_ticker(pair))
amount = stake_amount / buy_limit
order_id = exchange.buy(pair, buy_limit, amount)
order_id = exchange.buy(pair, buy_limit, amount)['id']
stake_amount_fiat = self.fiat_converter.convert_amount(
stake_amount,
@ -297,7 +305,7 @@ class FreqtradeBot(object):
self.rpc.send_msg(
'*{}:* Buying [{}]({}) with limit `{:.8f} ({:.6f} {}, {:.3f} {})` '
.format(
exchange.get_name().upper(),
exchange.get_name(),
pair.replace('_', '/'),
exchange.get_pair_detail_url(pair),
buy_limit,
@ -312,10 +320,10 @@ class FreqtradeBot(object):
pair=pair,
stake_amount=stake_amount,
amount=amount,
fee=exchange.get_fee_maker(),
fee=exchange.get_fee(taker_or_maker='maker'),
open_rate=buy_limit,
open_date=datetime.utcnow(),
exchange=exchange.get_name().upper(),
exchange=exchange.get_id(),
open_order_id=order_id
)
Trade.session.add(trade)
@ -347,7 +355,7 @@ class FreqtradeBot(object):
if trade.open_order_id:
# Update trade with order values
self.logger.info('Found open order for %s', trade)
trade.update(exchange.get_order(trade.open_order_id))
trade.update(exchange.get_order(trade.open_order_id, trade.pair))
if trade.is_open and trade.open_order_id is None:
# Check if we can sell our current pair
@ -386,22 +394,22 @@ class FreqtradeBot(object):
for trade in Trade.query.filter(Trade.open_order_id.isnot(None)).all():
try:
order = exchange.get_order(trade.open_order_id)
order = exchange.get_order(trade.open_order_id, trade.pair)
except requests.exceptions.RequestException:
self.logger.info(
'Cannot query order for %s due to %s',
trade,
traceback.format_exc())
continue
ordertime = arrow.get(order['opened'])
ordertime = arrow.get(order['datetime']).datetime
# Check if trade is still actually open
if int(order['remaining']) == 0:
continue
if order['type'] == "LIMIT_BUY" and ordertime < timeoutthreashold:
if order['side'] == 'buy' and ordertime < timeoutthreashold:
self.handle_timedout_limit_buy(trade, order)
elif order['type'] == "LIMIT_SELL" and ordertime < timeoutthreashold:
elif order['side'] == 'sell' and ordertime < timeoutthreashold:
self.handle_timedout_limit_sell(trade, order)
# FIX: 20180110, why is cancel.order unconditionally here, whereas
@ -463,7 +471,7 @@ class FreqtradeBot(object):
:return: None
"""
# Execute sell and update trade record
order_id = exchange.sell(str(trade.pair), limit, trade.amount)
order_id = exchange.sell(str(trade.pair), limit, trade.amount)['id']
trade.open_order_id = order_id
fmt_exp_profit = round(trade.calc_profit_percent(rate=limit) * 100, 2)

View File

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

View File

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

View File

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

View File

@ -99,6 +99,11 @@ def update():
return _update
@pytest.fixture
def fee():
return MagicMock(return_value=0.0025)
@pytest.fixture
def ticker():
return MagicMock(return_value={
@ -127,32 +132,80 @@ def ticker_sell_down():
@pytest.fixture
def health():
return MagicMock(return_value={
"ETH/BTC": {
def markets():
return MagicMock(return_value=[
{
'id': 'ethbtc',
'symbol': 'ETH/BTC',
'base': 'ETH',
'quote': 'BTC',
'active': True,
'LastChecked': '2017-11-13T20:15:00.00',
'Notice': None
'precision': {
'price': 8,
'amount': 8,
'cost': 8,
},
'lot': 0.00000001,
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
},
'price': 500000,
'cost': 500000,
},
'info': '',
},
"TRST/BTC": {
'base': 'TRST',
{
'id': 'tknbtc',
'symbol': 'TKN/BTC',
'base': 'TKN',
'quote': 'BTC',
'active': True,
'LastChecked': '2017-11-13T20:15:00.00',
'Notice': None
'precision': {
'price': 8,
'amount': 8,
'cost': 8,
},
'lot': 0.00000001,
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
},
'price': 500000,
'cost': 500000,
},
'info': '',
},
"SWT/BTC": {
'base': 'SWT',
{
'id': 'blkbtc',
'symbol': 'BLK/BTC',
'base': 'BLK',
'quote': 'BTC',
'active': True,
'LastChecked': '2017-11-13T20:15:00.00',
'Notice': None
},
"BCC/BTC": {
'base': 'BCC',
'active': False,
'LastChecked': '2017-11-13T20:15:00.00',
'Notice': None
}})
'precision': {
'price': 8,
'amount': 8,
'cost': 8,
},
'lot': 0.00000001,
'limits': {
'amount': {
'min': 0.01,
'max': 1000,
},
'price': 500000,
'cost': 500000,
},
'info': '',
}
])
@pytest.fixture
def markets_empty():
return MagicMock(return_value=[])
@pytest.fixture
@ -231,7 +284,7 @@ def limit_sell_order():
@pytest.fixture
def ticker_history_api():
def ticker_history():
return [
[
1511686200000, # unix timestamp ms
@ -261,66 +314,179 @@ def ticker_history_api():
@pytest.fixture
def ticker_history():
return [
{
"O": 8.794e-05,
"H": 8.948e-05,
"L": 8.794e-05,
"C": 8.88e-05,
"V": 991.09056638,
"T": "2017-11-26T08:50:00",
"BV": 0.0877869
def tickers():
return MagicMock(return_value={
'ETH/BTC': {
'symbol': 'ETH/BTC',
'timestamp': 1522014806207,
'datetime': '2018-03-25T21:53:26.207Z',
'high': 0.061697,
'low': 0.060531,
'bid': 0.061588,
'bidVolume': 3.321,
'ask': 0.061655,
'askVolume': 0.212,
'vwap': 0.06105296,
'open': 0.060809,
'close': 0.060761,
'first': None,
'last': 0.061588,
'change': 1.281,
'percentage': None,
'average': None,
'baseVolume': 111649.001,
'quoteVolume': 6816.50176926,
'info': {}
},
{
"O": 8.88e-05,
"H": 8.942e-05,
"L": 8.88e-05,
"C": 8.893e-05,
"V": 658.77935965,
"T": "2017-11-26T08:55:00",
"BV": 0.05874751
'TKN/BTC': {
'symbol': 'TKN/BTC',
'timestamp': 1522014806169,
'datetime': '2018-03-25T21:53:26.169Z',
'high': 0.01885,
'low': 0.018497,
'bid': 0.018799,
'bidVolume': 8.38,
'ask': 0.018802,
'askVolume': 15.0,
'vwap': 0.01869197,
'open': 0.018585,
'close': 0.018573,
'baseVolume': 81058.66,
'quoteVolume': 2247.48374509,
},
{
"O": 8.891e-05,
"H": 8.893e-05,
"L": 8.875e-05,
"C": 8.877e-05,
"V": 7920.73570705,
"T": "2017-11-26T09:00:00",
"BV": 0.7039405
'BLK/BTC': {
'symbol': 'BLK/BTC',
'timestamp': 1522014806072,
'datetime': '2018-03-25T21:53:26.720Z',
'high': 0.007745,
'low': 0.007512,
'bid': 0.007729,
'bidVolume': 0.01,
'ask': 0.007743,
'askVolume': 21.37,
'vwap': 0.00761466,
'open': 0.007653,
'close': 0.007652,
'first': None,
'last': 0.007743,
'change': 1.176,
'percentage': None,
'average': None,
'baseVolume': 295152.26,
'quoteVolume': 1515.14631229,
'info': {}
},
'LTC/BTC': {
'symbol': 'LTC/BTC',
'timestamp': 1523787258992,
'datetime': '2018-04-15T10:14:19.992Z',
'high': 0.015978,
'low': 0.0157,
'bid': 0.015954,
'bidVolume': 12.83,
'ask': 0.015957,
'askVolume': 0.49,
'vwap': 0.01581636,
'open': 0.015823,
'close': 0.01582,
'first': None,
'last': 0.015951,
'change': 0.809,
'percentage': None,
'average': None,
'baseVolume': 88620.68,
'quoteVolume': 1401.65697943,
'info': {}
},
'ETH/USDT': {
'symbol': 'ETH/USDT',
'timestamp': 1522014804118,
'datetime': '2018-03-25T21:53:24.118Z',
'high': 530.88,
'low': 512.0,
'bid': 529.73,
'bidVolume': 0.2,
'ask': 530.21,
'askVolume': 0.2464,
'vwap': 521.02438405,
'open': 527.27,
'close': 528.42,
'first': None,
'last': 530.21,
'change': 0.558,
'percentage': None,
'average': None,
'baseVolume': 72300.0659,
'quoteVolume': 37670097.3022171,
'info': {}
},
'TKN/USDT': {
'symbol': 'TKN/USDT',
'timestamp': 1522014806198,
'datetime': '2018-03-25T21:53:26.198Z',
'high': 8718.0,
'low': 8365.77,
'bid': 8603.64,
'bidVolume': 0.15846,
'ask': 8603.67,
'askVolume': 0.069147,
'vwap': 8536.35621697,
'open': 8680.0,
'close': 8680.0,
'first': None,
'last': 8603.67,
'change': -0.879,
'percentage': None,
'average': None,
'baseVolume': 30414.604298,
'quoteVolume': 259629896.48584127,
'info': {}
},
'BLK/USDT': {
'symbol': 'BLK/USDT',
'timestamp': 1522014806145,
'datetime': '2018-03-25T21:53:26.145Z',
'high': 66.95,
'low': 63.38,
'bid': 66.473,
'bidVolume': 4.968,
'ask': 66.54,
'askVolume': 2.704,
'vwap': 65.0526901,
'open': 66.43,
'close': 66.383,
'first': None,
'last': 66.5,
'change': 0.105,
'percentage': None,
'average': None,
'baseVolume': 294106.204,
'quoteVolume': 19132399.743954,
'info': {}
},
'LTC/USDT': {
'symbol': 'LTC/USDT',
'timestamp': 1523787257812,
'datetime': '2018-04-15T10:14:18.812Z',
'high': 129.94,
'low': 124.0,
'bid': 129.28,
'bidVolume': 0.03201,
'ask': 129.52,
'askVolume': 0.14529,
'vwap': 126.92838682,
'open': 127.0,
'close': 127.1,
'first': None,
'last': 129.28,
'change': 1.795,
'percentage': None,
'average': None,
'baseVolume': 59698.79897,
'quoteVolume': 29132399.743954,
'info': {}
}
]
@pytest.fixture
def ticker_history_without_bv():
return [
{
"O": 8.794e-05,
"H": 8.948e-05,
"L": 8.794e-05,
"C": 8.88e-05,
"V": 991.09056638,
"T": "2017-11-26T08:50:00"
},
{
"O": 8.88e-05,
"H": 8.942e-05,
"L": 8.88e-05,
"C": 8.893e-05,
"V": 658.77935965,
"T": "2017-11-26T08:55:00"
},
{
"O": 8.891e-05,
"H": 8.893e-05,
"L": 8.875e-05,
"C": 8.877e-05,
"V": 7920.73570705,
"T": "2017-11-26T09:00:00"
}
]
})
@pytest.fixture
@ -328,7 +494,6 @@ def result():
with open('freqtrade/tests/testdata/UNITTEST_BTC-1m.json') as data_file:
return Analyze.parse_ticker_dataframe(json.load(data_file))
# FIX:
# Create an fixture/function
# that inserts a trade of some type and open-status

View File

@ -68,7 +68,8 @@ def test_validate_pairs_not_compatible(default_conf, mocker):
api_mock.load_markets = MagicMock(return_value={
'ETH/BTC': '', 'TKN/BTC': '', 'TRST/BTC': '', 'SWT/BTC': '', 'BCC/BTC': ''
})
default_conf['stake_currency'] = 'ETH'
conf = deepcopy(default_conf)
conf['stake_currency'] = 'ETH'
mocker.patch('freqtrade.exchange._API', api_mock)
mocker.patch.dict('freqtrade.exchange._CONF', conf)
with pytest.raises(OperationalException, match=r'not compatible'):
@ -78,18 +79,20 @@ def test_validate_pairs_not_compatible(default_conf, mocker):
def test_validate_pairs_exception(default_conf, mocker, caplog):
caplog.set_level(logging.INFO)
api_mock = MagicMock()
api_mock.load_markets = MagicMock(side_effect=ccxt.BaseError())
api_mock.name = 'binance'
api_mock.name = 'Binance'
mocker.patch('freqtrade.exchange._API', api_mock)
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
with pytest.raises(OperationalException, match=r'Pair ETH/BTC is not available at binance'):
api_mock.load_markets = MagicMock(return_value={})
with pytest.raises(OperationalException, match=r'Pair ETH/BTC is not available at Binance'):
validate_pairs(default_conf['exchange']['pair_whitelist'])
api_mock.load_markets = MagicMock(side_effect=ccxt.BaseError())
validate_pairs(default_conf['exchange']['pair_whitelist'])
assert log_has('Unable to validate pairs (assuming they are correct). Reason: ',
caplog.record_tuples)
def test_validate_pairs_stake_exception(default_conf, mocker, caplog):
caplog.set_level(logging.INFO)
conf = deepcopy(default_conf)
@ -105,6 +108,7 @@ def test_validate_pairs_stake_exception(default_conf, mocker, caplog):
):
validate_pairs(default_conf['exchange']['pair_whitelist'])
def test_buy_dry_run(default_conf, mocker):
default_conf['dry_run'] = True
mocker.patch.dict('freqtrade.exchange._CONF', default_conf)
@ -113,6 +117,7 @@ def test_buy_dry_run(default_conf, mocker):
assert 'id' in order
assert 'dry_run_buy_' in order['id']
def test_buy_prod(default_conf, mocker):
api_mock = MagicMock()
order_id = 'test_prod_buy_{}'.format(randint(0, 10 ** 6))

View File

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

View File

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

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)
file = 'freqtrade/tests/testdata/UNITTEST_BTC-30m.json'
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-30m.json')
_backup_file(file, copy_file=True)
optimize.load_data(None, pairs=['UNITTEST/BTC'], ticker_interval='30m')
assert os.path.isfile(file) is True
assert not log_has('Download the pair: "ETH/BTC", Interval: 30 min', caplog.record_tuples)
assert not log_has('Download the pair: "UNITTEST/BTC", Interval: 30m', caplog.record_tuples)
_clean_test_file(file)
@ -66,11 +66,11 @@ def test_load_data_5min_ticker(ticker_history, mocker, caplog) -> None:
"""
mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)
file = 'freqtrade/tests/testdata/ETH_BTC-5m.json'
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-5m.json')
_backup_file(file, copy_file=True)
optimize.load_data(None, pairs=['ETH/BTC'], ticker_interval='5m')
optimize.load_data(None, pairs=['UNITTEST/BTC'], ticker_interval='5m')
assert os.path.isfile(file) is True
assert not log_has('Download the pair: "ETH/BTC", Interval: 5 min', caplog.record_tuples)
assert not log_has('Download the pair: "UNITTEST/BTC", Interval: 5m', caplog.record_tuples)
_clean_test_file(file)
@ -80,11 +80,11 @@ def test_load_data_1min_ticker(ticker_history, mocker, caplog) -> None:
"""
mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)
file = 'freqtrade/tests/testdata/ETH_BTC-1m.json'
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-1m.json')
_backup_file(file, copy_file=True)
optimize.load_data(None, ticker_interval='1m', pairs=['ETH/BTC'])
optimize.load_data(None, ticker_interval='1m', pairs=['UNITTEST/BTC'])
assert os.path.isfile(file) is True
assert not log_has('Download the pair: "ETH/BTC", Interval: 1 min', caplog.record_tuples)
assert not log_has('Download the pair: "UNITTEST/BTC", Interval: 1m', caplog.record_tuples)
_clean_test_file(file)
@ -94,11 +94,12 @@ def test_load_data_with_new_pair_1min(ticker_history, mocker, caplog) -> None:
"""
mocker.patch('freqtrade.optimize.get_ticker_history', return_value=ticker_history)
file = 'freqtrade/tests/testdata/MEME_BTC-1m.json'
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
_backup_file(file)
optimize.load_data(None, ticker_interval='1m', pairs=['MEME/BTC'])
assert os.path.isfile(file) is True
assert log_has('Download the pair: "MEME/BTC", Interval: 1 min', caplog.record_tuples)
assert log_has('Download the pair: "MEME/BTC", Interval: 1m', caplog.record_tuples)
_clean_test_file(file)
@ -109,10 +110,10 @@ def test_testdata_path() -> None:
def test_download_pairs(ticker_history, mocker) -> None:
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history)
file1_1 = 'freqtrade/tests/testdata/MEME_BTC-1m.json'
file1_5 = 'freqtrade/tests/testdata/MEME_BTC-5m.json'
file2_1 = 'freqtrade/tests/testdata/CFI_BTC-1m.json'
file2_5 = 'freqtrade/tests/testdata/CFI_BTC-5m.json'
file1_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
file1_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-5m.json')
file2_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'CFI_BTC-1m.json')
file2_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'CFI_BTC-5m.json')
_backup_file(file1_1)
_backup_file(file1_5)
@ -149,8 +150,8 @@ def test_download_pairs_exception(ticker_history, mocker, caplog) -> None:
mocker.patch('freqtrade.optimize.__init__.download_backtesting_testdata',
side_effect=BaseException('File Error'))
file1_1 = 'freqtrade/tests/testdata/MEME_BTC-1m.json'
file1_5 = 'freqtrade/tests/testdata/MEME_BTC-5m.json'
file1_1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-1m.json')
file1_5 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'MEME_BTC-5m.json')
_backup_file(file1_1)
_backup_file(file1_5)
@ -158,21 +159,21 @@ def test_download_pairs_exception(ticker_history, mocker, caplog) -> None:
# clean files freshly downloaded
_clean_test_file(file1_1)
_clean_test_file(file1_5)
assert log_has('Failed to download the pair: "MEME/BTC", Interval: 1 min', caplog.record_tuples)
assert log_has('Failed to download the pair: "MEME/BTC", Interval: 1m', caplog.record_tuples)
def test_download_backtesting_testdata(ticker_history, mocker) -> None:
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=ticker_history)
# Download a 1 min ticker file
file1 = 'freqtrade/tests/testdata/XEL_BTC-1m.json'
file1 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'XEL_BTC-1m.json')
_backup_file(file1)
download_backtesting_testdata(None, pair="XEL/BTC", interval='1m')
assert os.path.isfile(file1) is True
_clean_test_file(file1)
# Download a 5 min ticker file
file2 = 'freqtrade/tests/testdata/STORJ_BTC-5m.json'
file2 = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'STORJ_BTC-5m.json')
_backup_file(file2)
download_backtesting_testdata(None, pair="STORJ/BTC", interval='5m')
@ -181,7 +182,10 @@ def test_download_backtesting_testdata(ticker_history, mocker) -> None:
def test_download_backtesting_testdata2(mocker) -> None:
tick = [{'T': 'bar'}, {'T': 'foo'}]
tick = [
[1509836520000, 0.00162008, 0.00162008, 0.00162008, 0.00162008, 108.14853839],
[1509836580000, 0.00161, 0.00161, 0.00161, 0.00161, 82.390199]
]
mocker.patch('freqtrade.misc.file_dump_json', return_value=None)
mocker.patch('freqtrade.optimize.__init__.get_ticker_history', return_value=tick)
assert download_backtesting_testdata(None, pair="UNITTEST/BTC", interval='1m')
@ -211,7 +215,8 @@ def test_init(default_conf, mocker) -> None:
def test_trim_tickerlist() -> None:
with open('freqtrade/tests/testdata/ETH_BTC-1m.json') as data_file:
file = os.path.join(os.path.dirname(__file__), '..', 'testdata', 'UNITTEST_BTC-1m.json')
with open(file) as data_file:
ticker_list = json.load(data_file)
ticker_list_len = len(ticker_list)
@ -260,7 +265,8 @@ def test_file_dump_json() -> None:
Test file_dump_json()
:return: None
"""
file = 'freqtrade/tests/testdata/test_{id}.json'.format(id=str(uuid.uuid4()))
file = os.path.join(os.path.dirname(__file__), '..', 'testdata',
'test_{id}.json'.format(id=str(uuid.uuid4())))
data = {'bar': 'foo'}
# check the file we will create does not exist

View File

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

View File

@ -333,7 +333,7 @@ def test_status_table_handle(default_conf, update, ticker, mocker) -> None:
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
buy=MagicMock(return_value='mocked_order_id')
buy=MagicMock(return_value={'id': 'mocked_order_id'})
)
msg_mock = MagicMock()
mocker.patch.multiple(
@ -374,7 +374,7 @@ def test_status_table_handle(default_conf, update, ticker, mocker) -> None:
assert msg_mock.call_count == 1
def test_daily_handle(default_conf, update, ticker, limit_buy_order,
def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
limit_sell_order, mocker) -> None:
"""
Test _daily() method
@ -389,7 +389,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order,
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
get_pair_detail_url=MagicMock()
get_fee=fee
)
msg_mock = MagicMock()
mocker.patch.multiple(
@ -486,7 +486,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None:
assert str('Daily Profit over the last 7 days') in msg_mock.call_args_list[0][0][0]
def test_profit_handle(default_conf, update, ticker, ticker_sell_up,
def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
limit_buy_order, limit_sell_order, mocker) -> None:
"""
Test _profit() method
@ -497,7 +497,8 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up,
mocker.patch.multiple(
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker
get_ticker=ticker,
get_fee=fee
)
msg_mock = MagicMock()
mocker.patch.multiple(
@ -749,7 +750,7 @@ def test_stop_handle_already_stopped(default_conf, update, mocker) -> None:
assert 'already stopped' in msg_mock.call_args_list[0][0][0]
def test_forcesell_handle(default_conf, update, ticker, ticker_sell_up, mocker) -> None:
def test_forcesell_handle(default_conf, update, ticker, fee, ticker_sell_up, mocker) -> None:
"""
Test _forcesell() method
"""
@ -761,7 +762,8 @@ def test_forcesell_handle(default_conf, update, ticker, ticker_sell_up, mocker)
mocker.patch.multiple(
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker
get_ticker=ticker,
get_fee=fee
)
freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://'))
@ -788,7 +790,7 @@ def test_forcesell_handle(default_conf, update, ticker, ticker_sell_up, mocker)
assert '0.919 USD' in rpc_mock.call_args_list[-1][0][0]
def test_forcesell_down_handle(default_conf, update, ticker, ticker_sell_down, mocker) -> None:
def test_forcesell_down_handle(default_conf, update, ticker, fee, ticker_sell_down, mocker) -> None:
"""
Test _forcesell() method
"""
@ -800,7 +802,8 @@ def test_forcesell_down_handle(default_conf, update, ticker, ticker_sell_down, m
mocker.patch.multiple(
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker
get_ticker=ticker,
get_fee=fee
)
freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://'))
@ -831,7 +834,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, ticker_sell_down, m
assert '-0.824 USD' in rpc_mock.call_args_list[-1][0][0]
def test_forcesell_all_handle(default_conf, update, ticker, mocker) -> None:
def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None:
"""
Test _forcesell() method
"""
@ -844,7 +847,8 @@ def test_forcesell_all_handle(default_conf, update, ticker, mocker) -> None:
mocker.patch.multiple(
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker
get_ticker=ticker,
get_fee=fee
)
freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://'))
@ -907,8 +911,8 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:
assert 'Invalid argument.' in msg_mock.call_args_list[0][0][0]
def test_performance_handle(default_conf, update, ticker, limit_buy_order,
limit_sell_order, mocker) -> None:
def test_performance_handle(default_conf, update, ticker, fee,
limit_buy_order, limit_sell_order, mocker) -> None:
"""
Test _performance() method
"""
@ -923,7 +927,8 @@ def test_performance_handle(default_conf, update, ticker, limit_buy_order,
mocker.patch.multiple(
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker
get_ticker=ticker,
get_fee=fee
)
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://'))
@ -987,7 +992,7 @@ def test_count_handle(default_conf, update, ticker, mocker) -> None:
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
buy=MagicMock(return_value='mocked_order_id')
buy=MagicMock(return_value={'id': 'mocked_order_id'})
)
freqtradebot = FreqtradeBot(default_conf, create_engine('sqlite://'))
telegram = Telegram(freqtradebot)

View File

@ -1,18 +1,8 @@
import json
import pytest
from pandas import DataFrame
from freqtrade.analyze import Analyze
from freqtrade.strategy.default_strategy import DefaultStrategy, class_name
@pytest.fixture
def result():
with open('freqtrade/tests/testdata/ETH_BTC-1m.json') as data_file:
return Analyze.parse_ticker_dataframe(json.load(data_file))
def test_default_strategy_class_name():
assert class_name == DefaultStrategy.__name__

View File

@ -1,6 +1,7 @@
# pragma pylint: disable=missing-docstring,C0103,protected-access
import freqtrade.tests.conftest as tt # test tools
from unittest.mock import MagicMock
# whitelist, blacklist, filtering, all of that will
# eventually become some rules to run on a generic ACL engine
@ -26,81 +27,12 @@ def whitelist_conf():
return config
def get_market_summaries():
return {
'TKN/BTC': {
'symbol': 'TKN/BTC',
'info': {
'High': 0.00000919,
'Low': 0.00000820,
'Volume': 74339.61396015,
'Last': 0.00000820,
'BaseVolume': 1664,
'TimeStamp': '2014-07-09T07:19:30.15',
'Bid': 0.00000820,
'Ask': 0.00000831,
'OpenBuyOrders': 15,
'OpenSellOrders': 15,
'PrevDay': 0.00000821,
'Created': '2014-03-20T06:00:00',
'DisplayMarketName': ''
}
},
'ETH/BTC': {
'symbol': 'ETH/BTC',
'info': {
'High': 0.00000072,
'Low': 0.00000001,
'Volume': 166340678.42280999,
'Last': 0.00000005,
'BaseVolume': 42,
'TimeStamp': '2014-07-09T07:21:40.51',
'Bid': 0.00000004,
'Ask': 0.00000005,
'OpenBuyOrders': 18,
'OpenSellOrders': 18,
'PrevDay': 0.00000002,
'Created': '2014-05-30T07:57:49.637',
'DisplayMarketName': ''
}
},
'BLK/BTC': {
'symbol': 'BLK/BTC',
'info': {
'High': 0.00000072,
'Low': 0.00000001,
'Volume': 166340678.42280999,
'Last': 0.00000005,
'BaseVolume': 3,
'TimeStamp': '2014-07-09T07:21:40.51',
'Bid': 0.00000004,
'Ask': 0.00000005,
'OpenBuyOrders': 18,
'OpenSellOrders': 18,
'PrevDay': 0.00000002,
'Created': '2014-05-30T07:57:49.637',
'DisplayMarketName': ''
}}
}
def get_health():
return {
'ETH/BTC': {'base': 'ETH', 'active': True},
'TKN/BTC': {'base': 'TKN', 'active': True},
'BLK/BTC': {'base': 'BLK', 'active': True}}
def get_health_empty():
return {}
def test_refresh_market_pair_not_in_whitelist(mocker):
def test_refresh_market_pair_not_in_whitelist(mocker, markets):
conf = whitelist_conf()
freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
mocker.patch('freqtrade.freqtradebot.exchange.get_wallet_health', get_health)
mocker.patch('freqtrade.freqtradebot.exchange.get_markets', markets)
refreshedwhitelist = freqtradebot._refresh_whitelist(
conf['exchange']['pair_whitelist'] + ['XXX/BTC']
)
@ -110,11 +42,11 @@ def test_refresh_market_pair_not_in_whitelist(mocker):
assert whitelist == refreshedwhitelist
def test_refresh_whitelist(mocker):
def test_refresh_whitelist(mocker, markets):
conf = whitelist_conf()
freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
mocker.patch('freqtrade.freqtradebot.exchange.get_wallet_health', get_health)
mocker.patch('freqtrade.freqtradebot.exchange.get_markets', markets)
refreshedwhitelist = freqtradebot._refresh_whitelist(conf['exchange']['pair_whitelist'])
# List ordered by BaseVolume
@ -123,17 +55,18 @@ def test_refresh_whitelist(mocker):
assert whitelist == refreshedwhitelist
def test_refresh_whitelist_dynamic(mocker):
def test_refresh_whitelist_dynamic(mocker, markets, tickers):
conf = whitelist_conf()
freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
mocker.patch.multiple(
'freqtrade.freqtradebot.exchange',
get_wallet_health=get_health,
get_market_summaries=get_market_summaries
get_markets=markets,
get_tickers=tickers,
exchange_has=MagicMock(return_value=True)
)
# argument: use the whitelist dynamically by exchange-volume
whitelist = ['TKN/BTC', 'ETH/BTC']
whitelist = ['ETH/BTC', 'TKN/BTC']
refreshedwhitelist = freqtradebot._refresh_whitelist(
freqtradebot._gen_pair_whitelist(conf['stake_currency'])
@ -142,10 +75,10 @@ def test_refresh_whitelist_dynamic(mocker):
assert whitelist == refreshedwhitelist
def test_refresh_whitelist_dynamic_empty(mocker):
def test_refresh_whitelist_dynamic_empty(mocker, markets_empty):
conf = whitelist_conf()
freqtradebot = tt.get_patched_freqtradebot(mocker, conf)
mocker.patch('freqtrade.freqtradebot.exchange.get_wallet_health', get_health_empty)
mocker.patch('freqtrade.freqtradebot.exchange.get_markets', markets_empty)
# argument: use the whitelist dynamically by exchange-volume
whitelist = []

View File

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

View File

@ -201,29 +201,28 @@ def test_throttle_with_assets(mocker, default_conf) -> None:
assert result == -1
def test_gen_pair_whitelist(mocker, default_conf, get_market_summaries_data) -> None:
def test_gen_pair_whitelist(mocker, default_conf, tickers) -> None:
"""
Test _gen_pair_whitelist() method
"""
freqtrade = get_patched_freqtradebot(mocker, default_conf)
mocker.patch(
'freqtrade.freqtradebot.exchange.get_market_summaries',
return_value=get_market_summaries_data
)
mocker.patch('freqtrade.freqtradebot.exchange.get_tickers', tickers)
mocker.patch('freqtrade.freqtradebot.exchange.exchange_has', MagicMock(return_value=True))
mocker.patch('freqtrade.exchange.validate_pairs', MagicMock(return_value=True))
# Test to retrieved BTC sorted on BaseVolume
# Test to retrieved BTC sorted on quoteVolume (default)
whitelist = freqtrade._gen_pair_whitelist(base_currency='BTC')
assert whitelist == ['ZCL/BTC', 'ZEC/BTC', 'XZC/BTC', 'XWC/BTC']
assert whitelist == ['ETH/BTC', 'TKN/BTC', 'BLK/BTC', 'LTC/BTC']
# Test to retrieved BTC sorted on OpenBuyOrders
whitelist = freqtrade._gen_pair_whitelist(base_currency='BTC', key='OpenBuyOrders')
assert whitelist == ['XWC/BTC', 'ZCL/BTC', 'ZEC/BTC', 'XZC/BTC']
# Test to retrieve BTC sorted on bidVolume
whitelist = freqtrade._gen_pair_whitelist(base_currency='BTC', key='bidVolume')
assert whitelist == ['LTC/BTC', 'TKN/BTC', 'ETH/BTC', 'BLK/BTC']
# Test with USDT sorted on BaseVolume
# Test with USDT sorted on quoteVolume (default)
whitelist = freqtrade._gen_pair_whitelist(base_currency='USDT')
assert whitelist == ['XRP/USDT', 'XVG/USDT', 'XMR/USDT', 'ZEC/USDT']
assert whitelist == ['TKN/USDT', 'ETH/USDT', 'LTC/USDT', 'BLK/USDT']
# Test with ETH (our fixture does not have ETH, but Bittrex returns them)
# Test with ETH (our fixture does not have ETH, so result should be empty)
whitelist = freqtrade._gen_pair_whitelist(base_currency='ETH')
assert whitelist == []
@ -247,7 +246,7 @@ def test_create_trade(default_conf, ticker, limit_buy_order, mocker) -> None:
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
buy=MagicMock(return_value='mocked_limit_buy')
buy=MagicMock(return_value={'id': limit_buy_order['id']})
)
# Save state of current whitelist
@ -271,14 +270,14 @@ def test_create_trade(default_conf, ticker, limit_buy_order, mocker) -> None:
assert whitelist == default_conf['exchange']['pair_whitelist']
def test_create_trade_minimal_amount(default_conf, ticker, mocker) -> None:
def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order, mocker) -> None:
"""
Test create_trade() method
"""
patch_get_signal(mocker)
patch_RPCManager(mocker)
patch_coinmarketcap(mocker)
buy_mock = MagicMock(return_value='mocked_limit_buy')
buy_mock = MagicMock(return_value={'id': limit_buy_order['id']})
mocker.patch.multiple(
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
@ -295,7 +294,7 @@ def test_create_trade_minimal_amount(default_conf, ticker, mocker) -> None:
assert rate * amount >= conf['stake_amount']
def test_create_trade_no_stake_amount(default_conf, ticker, mocker) -> None:
def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order, mocker) -> None:
"""
Test create_trade() method
"""
@ -306,7 +305,7 @@ def test_create_trade_no_stake_amount(default_conf, ticker, mocker) -> None:
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
buy=MagicMock(return_value='mocked_limit_buy'),
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_balance=MagicMock(return_value=default_conf['stake_amount'] * 0.5)
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
@ -315,7 +314,7 @@ def test_create_trade_no_stake_amount(default_conf, ticker, mocker) -> None:
freqtrade.create_trade()
def test_create_trade_no_pairs(default_conf, ticker, mocker) -> None:
def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, mocker) -> None:
"""
Test create_trade() method
"""
@ -326,7 +325,7 @@ def test_create_trade_no_pairs(default_conf, ticker, mocker) -> None:
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
buy=MagicMock(return_value='mocked_limit_buy')
buy=MagicMock(return_value={'id': limit_buy_order['id']})
)
conf = deepcopy(default_conf)
@ -340,7 +339,8 @@ def test_create_trade_no_pairs(default_conf, ticker, mocker) -> None:
freqtrade.create_trade()
def test_create_trade_no_pairs_after_blacklist(default_conf, ticker, mocker) -> None:
def test_create_trade_no_pairs_after_blacklist(default_conf, ticker,
limit_buy_order, mocker) -> None:
"""
Test create_trade() method
"""
@ -351,7 +351,7 @@ def test_create_trade_no_pairs_after_blacklist(default_conf, ticker, mocker) ->
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
buy=MagicMock(return_value='mocked_limit_buy')
buy=MagicMock(return_value={'id': limit_buy_order['id']})
)
conf = deepcopy(default_conf)
@ -392,7 +392,7 @@ def test_create_trade_no_signal(default_conf, mocker) -> None:
def test_process_trade_creation(default_conf, ticker, limit_buy_order,
health, mocker, caplog) -> None:
markets, mocker, caplog) -> None:
"""
Test the trade creation in _process() method
"""
@ -403,8 +403,8 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order,
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
get_wallet_health=health,
buy=MagicMock(return_value='mocked_limit_buy'),
get_markets=markets,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_order=MagicMock(return_value=limit_buy_order)
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
@ -432,7 +432,7 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order,
)
def test_process_exchange_failures(default_conf, ticker, health, mocker) -> None:
def test_process_exchange_failures(default_conf, ticker, markets, mocker) -> None:
"""
Test _process() method when a RequestException happens
"""
@ -443,7 +443,7 @@ def test_process_exchange_failures(default_conf, ticker, health, mocker) -> None
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
get_wallet_health=health,
get_markets=markets,
buy=MagicMock(side_effect=requests.exceptions.RequestException)
)
sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None)
@ -454,7 +454,7 @@ def test_process_exchange_failures(default_conf, ticker, health, mocker) -> None
assert sleep_mock.has_calls()
def test_process_operational_exception(default_conf, ticker, health, mocker) -> None:
def test_process_operational_exception(default_conf, ticker, markets, mocker) -> None:
"""
Test _process() method when an OperationalException happens
"""
@ -465,7 +465,7 @@ def test_process_operational_exception(default_conf, ticker, health, mocker) ->
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
get_wallet_health=health,
get_markets=markets,
buy=MagicMock(side_effect=OperationalException)
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
@ -477,7 +477,7 @@ def test_process_operational_exception(default_conf, ticker, health, mocker) ->
assert 'OperationalException' in msg_mock.call_args_list[-1][0][0]
def test_process_trade_handling(default_conf, ticker, limit_buy_order, health, mocker) -> None:
def test_process_trade_handling(default_conf, ticker, limit_buy_order, markets, mocker) -> None:
"""
Test _process()
"""
@ -488,8 +488,8 @@ def test_process_trade_handling(default_conf, ticker, limit_buy_order, health, m
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
get_wallet_health=health,
buy=MagicMock(return_value='mocked_limit_buy'),
get_markets=markets,
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
get_order=MagicMock(return_value=limit_buy_order)
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
@ -577,7 +577,7 @@ def test_process_maybe_execute_sell(mocker, default_conf) -> None:
assert freqtrade.process_maybe_execute_sell(trade)
def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, mocker) -> None:
def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, fee, mocker) -> None:
"""
Test check_handle() method
"""
@ -591,8 +591,9 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, mocker) -
'ask': 0.00001173,
'last': 0.00001172
}),
buy=MagicMock(return_value='mocked_limit_buy'),
sell=MagicMock(return_value='mocked_limit_sell')
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
get_fee=fee
)
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
@ -603,12 +604,13 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, mocker) -
trade = Trade.query.first()
assert trade
time.sleep(0.01) # Race condition fix
trade.update(limit_buy_order)
assert trade.is_open is True
patch_get_signal(mocker, value=(False, True))
assert freqtrade.handle_trade(trade) is True
assert trade.open_order_id == 'mocked_limit_sell'
assert trade.open_order_id == limit_sell_order['id']
# Simulate fulfilled LIMIT_SELL order for trade
trade.update(limit_sell_order)
@ -619,7 +621,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, mocker) -
assert trade.close_date is not None
def test_handle_overlpapping_signals(default_conf, ticker, mocker) -> None:
def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order, mocker) -> None:
"""
Test check_handle() method
"""
@ -634,7 +636,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, mocker) -> None:
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
buy=MagicMock(return_value='mocked_limit_buy')
buy=MagicMock(return_value={'id': limit_buy_order['id']})
)
freqtrade = FreqtradeBot(conf, create_engine('sqlite://'))
@ -676,7 +678,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, mocker) -> None:
assert freqtrade.handle_trade(trades[0]) is True
def test_handle_trade_roi(default_conf, ticker, mocker, caplog) -> None:
def test_handle_trade_roi(default_conf, ticker, limit_buy_order, mocker, caplog) -> None:
"""
Test check_handle() method
"""
@ -691,7 +693,7 @@ def test_handle_trade_roi(default_conf, ticker, mocker, caplog) -> None:
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
buy=MagicMock(return_value='mocked_limit_buy')
buy=MagicMock(return_value={'id': limit_buy_order['id']})
)
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=True)
@ -711,7 +713,7 @@ def test_handle_trade_roi(default_conf, ticker, mocker, caplog) -> None:
assert log_has('Required profit reached. Selling..', caplog.record_tuples)
def test_handle_trade_experimental(default_conf, ticker, mocker, caplog) -> None:
def test_handle_trade_experimental(default_conf, ticker, limit_buy_order, mocker, caplog) -> None:
"""
Test check_handle() method
"""
@ -726,7 +728,7 @@ def test_handle_trade_experimental(default_conf, ticker, mocker, caplog) -> None
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
buy=MagicMock(return_value='mocked_limit_buy')
buy=MagicMock(return_value={'id': limit_buy_order['id']})
)
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
@ -755,7 +757,7 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, mo
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker,
buy=MagicMock(return_value='mocked_limit_buy')
buy=MagicMock(return_value={'id': limit_buy_order['id']})
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
@ -773,7 +775,7 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order, mo
freqtrade.handle_trade(trade)
def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, mocker) -> None:
def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, fee, mocker) -> None:
"""
Test check_handle_timedout() method
"""
@ -785,14 +787,15 @@ def test_check_handle_timedout_buy(default_conf, ticker, limit_buy_order_old, mo
validate_pairs=MagicMock(),
get_ticker=ticker,
get_order=MagicMock(return_value=limit_buy_order_old),
cancel_order=cancel_order_mock
cancel_order=cancel_order_mock,
get_fee=fee
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
trade_buy = Trade(
pair='ETH/BTC',
open_rate=0.00001099,
exchange='BITTREX',
exchange='bittrex',
open_order_id='123456789',
amount=90.99181073,
fee=0.0,
@ -831,7 +834,7 @@ def test_check_handle_timedout_sell(default_conf, ticker, limit_sell_order_old,
trade_sell = Trade(
pair='ETH/BTC',
open_rate=0.00001099,
exchange='BITTREX',
exchange='bittrex',
open_order_id='123456789',
amount=90.99181073,
fee=0.0,
@ -870,7 +873,7 @@ def test_check_handle_timedout_partial(default_conf, ticker, limit_buy_order_old
trade_buy = Trade(
pair='ETH/BTC',
open_rate=0.00001099,
exchange='BITTREX',
exchange='bittrex',
open_order_id='123456789',
amount=90.99181073,
fee=0.0,
@ -917,7 +920,7 @@ def test_check_handle_timedout_exception(default_conf, ticker, mocker, caplog) -
trade_buy = Trade(
pair='ETH/BTC',
open_rate=0.00001099,
exchange='BITTREX',
exchange='bittrex',
open_order_id='123456789',
amount=90.99181073,
fee=0.0,
@ -989,7 +992,7 @@ def test_handle_timedout_limit_sell(mocker, default_conf) -> None:
assert cancel_order_mock.call_count == 1
def test_execute_sell_up(default_conf, ticker, ticker_sell_up, mocker) -> None:
def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, mocker) -> None:
"""
Test execute_sell() method with a ticker going UP
"""
@ -999,7 +1002,8 @@ def test_execute_sell_up(default_conf, ticker, ticker_sell_up, mocker) -> None:
mocker.patch.multiple(
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker
get_ticker=ticker,
get_fee=fee
)
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
@ -1029,7 +1033,7 @@ def test_execute_sell_up(default_conf, ticker, ticker_sell_up, mocker) -> None:
assert '0.919 USD' in rpc_mock.call_args_list[-1][0][0]
def test_execute_sell_down(default_conf, ticker, ticker_sell_down, mocker) -> None:
def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, mocker) -> None:
"""
Test execute_sell() method with a ticker going DOWN
"""
@ -1040,7 +1044,8 @@ def test_execute_sell_down(default_conf, ticker, ticker_sell_down, mocker) -> No
mocker.patch.multiple(
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker
get_ticker=ticker,
get_fee=fee
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
@ -1068,7 +1073,8 @@ def test_execute_sell_down(default_conf, ticker, ticker_sell_down, mocker) -> No
assert '-0.824 USD' in rpc_mock.call_args_list[-1][0][0]
def test_execute_sell_without_conf_sell_up(default_conf, ticker, ticker_sell_up, mocker) -> None:
def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee,
ticker_sell_up, mocker) -> None:
"""
Test execute_sell() method with a ticker going DOWN and with a bot config empty
"""
@ -1078,7 +1084,8 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, ticker_sell_up,
mocker.patch.multiple(
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker
get_ticker=ticker,
get_fee=fee
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
@ -1107,7 +1114,7 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, ticker_sell_up,
assert 'USD' not in rpc_mock.call_args_list[-1][0][0]
def test_execute_sell_without_conf_sell_down(default_conf, ticker,
def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee,
ticker_sell_down, mocker) -> None:
"""
Test execute_sell() method with a ticker going DOWN and with a bot config empty
@ -1118,7 +1125,8 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker,
mocker.patch.multiple(
'freqtrade.freqtradebot.exchange',
validate_pairs=MagicMock(),
get_ticker=ticker
get_ticker=ticker,
get_fee=fee
)
freqtrade = FreqtradeBot(default_conf, create_engine('sqlite://'))
@ -1161,7 +1169,7 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order, mocker) -
'ask': 0.00002173,
'last': 0.00002172
}),
buy=MagicMock(return_value='mocked_limit_buy')
buy=MagicMock(return_value={'id': limit_buy_order['id']})
)
conf = deepcopy(default_conf)
conf['experimental'] = {
@ -1193,7 +1201,7 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order, mocker)
'ask': 0.00002173,
'last': 0.00002172
}),
buy=MagicMock(return_value='mocked_limit_buy')
buy=MagicMock(return_value={'id': limit_buy_order['id']})
)
conf = deepcopy(default_conf)
conf['experimental'] = {
@ -1225,7 +1233,7 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, mocker) ->
'ask': 0.00000173,
'last': 0.00000172
}),
buy=MagicMock(return_value='mocked_limit_buy')
buy=MagicMock(return_value={'id': limit_buy_order['id']})
)
conf = deepcopy(default_conf)
conf['experimental'] = {
@ -1257,7 +1265,7 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, mocker) ->
'ask': 0.00000173,
'last': 0.00000172
}),
buy=MagicMock(return_value='mocked_limit_buy')
buy=MagicMock(return_value={'id': limit_buy_order['id']})
)
conf = deepcopy(default_conf)

View File

@ -89,7 +89,7 @@ def test_init_prod_db(default_conf, mocker):
os.rename(prod_db_swp, prod_db)
def test_update_with_bittrex(limit_buy_order, limit_sell_order):
def test_update_with_bittrex(limit_buy_order, limit_sell_order, fee):
"""
On this test we will buy and sell a crypto currency.
@ -120,7 +120,7 @@ def test_update_with_bittrex(limit_buy_order, limit_sell_order):
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
fee=0.0025,
fee=fee.return_value,
exchange='bittrex',
)
assert trade.open_order_id is None
@ -143,11 +143,11 @@ def test_update_with_bittrex(limit_buy_order, limit_sell_order):
assert trade.close_date is not None
def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order):
def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order, fee):
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
fee=0.0025,
fee=fee.return_value,
exchange='bittrex',
)
@ -165,11 +165,11 @@ def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order):
assert trade.calc_profit_percent() == 0.06201057
def test_calc_close_trade_price_exception(limit_buy_order):
def test_calc_close_trade_price_exception(limit_buy_order, fee):
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
fee=0.0025,
fee=fee.return_value,
exchange='bittrex',
)
@ -191,7 +191,7 @@ def test_update_open_order(limit_buy_order):
assert trade.close_profit is None
assert trade.close_date is None
limit_buy_order['closed'] = False
limit_buy_order['status'] = 'open'
trade.update(limit_buy_order)
assert trade.open_order_id is None
@ -212,11 +212,11 @@ def test_update_invalid_order(limit_buy_order):
trade.update(limit_buy_order)
def test_calc_open_trade_price(limit_buy_order):
def test_calc_open_trade_price(limit_buy_order, fee):
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
fee=0.0025,
fee=fee.return_value,
exchange='bittrex',
)
trade.open_order_id = 'open_trade'
@ -229,11 +229,11 @@ def test_calc_open_trade_price(limit_buy_order):
assert trade.calc_open_trade_price(fee=0.003) == 0.001003000
def test_calc_close_trade_price(limit_buy_order, limit_sell_order):
def test_calc_close_trade_price(limit_buy_order, limit_sell_order, fee):
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
fee=0.0025,
fee=fee.return_value,
exchange='bittrex',
)
trade.open_order_id = 'close_trade'
@ -250,11 +250,11 @@ def test_calc_close_trade_price(limit_buy_order, limit_sell_order):
assert trade.calc_close_trade_price(fee=0.005) == 0.0010619972
def test_calc_profit(limit_buy_order, limit_sell_order):
def test_calc_profit(limit_buy_order, limit_sell_order, fee):
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
fee=0.0025,
fee=fee.return_value,
exchange='bittrex',
)
trade.open_order_id = 'profit_percent'
@ -280,11 +280,11 @@ def test_calc_profit(limit_buy_order, limit_sell_order):
assert trade.calc_profit(fee=0.003) == 0.00006163
def test_calc_profit_percent(limit_buy_order, limit_sell_order):
def test_calc_profit_percent(limit_buy_order, limit_sell_order, fee):
trade = Trade(
pair='ETH/BTC',
stake_amount=0.001,
fee=0.0025,
fee=fee.return_value,
exchange='bittrex',
)
trade.open_order_id = 'profit_percent'
@ -304,7 +304,7 @@ def test_calc_profit_percent(limit_buy_order, limit_sell_order):
assert trade.calc_profit_percent(fee=0.003) == 0.0614782
def test_clean_dry_run_db(default_conf):
def test_clean_dry_run_db(default_conf, fee):
init(default_conf, create_engine('sqlite://'))
# Simulate dry_run entries
@ -312,7 +312,7 @@ def test_clean_dry_run_db(default_conf):
pair='ETH/BTC',
stake_amount=0.001,
amount=123.0,
fee=0.0025,
fee=fee.return_value,
open_rate=0.123,
exchange='bittrex',
open_order_id='dry_run_buy_12345'
@ -323,7 +323,7 @@ def test_clean_dry_run_db(default_conf):
pair='ETC/BTC',
stake_amount=0.001,
amount=123.0,
fee=0.0025,
fee=fee.return_value,
open_rate=0.123,
exchange='bittrex',
open_order_id='dry_run_sell_12345'
@ -335,7 +335,7 @@ def test_clean_dry_run_db(default_conf):
pair='ETC/BTC',
stake_amount=0.001,
amount=123.0,
fee=0.0025,
fee=fee.return_value,
open_rate=0.123,
exchange='bittrex',
open_order_id='prod_buy_12345'

View File

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