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:
commit
42c0d7c7c3
@ -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']
|
||||||
|
@ -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)
|
||||||
|
@ -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)
|
||||||
|
@ -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()
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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))
|
||||||
|
@ -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()
|
||||||
|
@ -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},
|
||||||
|
@ -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
|
||||||
|
@ -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://'))
|
||||||
|
@ -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)
|
||||||
|
@ -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__
|
||||||
|
|
||||||
|
@ -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 = []
|
||||||
|
@ -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:
|
||||||
"""
|
"""
|
||||||
|
@ -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)
|
||||||
|
@ -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'
|
||||||
|
@ -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",
|
||||||
|
Loading…
Reference in New Issue
Block a user