Merge branch 'freqtrade:develop' into develop

This commit is contained in:
GeorgeMurAlkh 2021-07-21 00:09:35 +03:00 committed by GitHub
commit 759a350d73
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
18 changed files with 110 additions and 129 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -6,10 +6,13 @@ python -m pip install --upgrade pip
$pyv = python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" $pyv = python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"
if ($pyv -eq '3.7') { if ($pyv -eq '3.7') {
pip install build_helpers\TA_Lib-0.4.20-cp37-cp37m-win_amd64.whl pip install build_helpers\TA_Lib-0.4.21-cp37-cp37m-win_amd64.whl
} }
if ($pyv -eq '3.8') { if ($pyv -eq '3.8') {
pip install build_helpers\TA_Lib-0.4.20-cp38-cp38-win_amd64.whl pip install build_helpers\TA_Lib-0.4.21-cp38-cp38-win_amd64.whl
}
if ($pyv -eq '3.9') {
pip install build_helpers\TA_Lib-0.4.21-cp39-cp39-win_amd64.whl
} }
pip install -r requirements-dev.txt pip install -r requirements-dev.txt

View File

@ -42,7 +42,7 @@ docker build --cache-from freqtrade:${TAG_ARM} --build-arg sourceimage=${TAG_ARM
docker tag freqtrade:$TAG_PLOT_ARM ${CACHE_IMAGE}:$TAG_PLOT_ARM docker tag freqtrade:$TAG_PLOT_ARM ${CACHE_IMAGE}:$TAG_PLOT_ARM
# Run backtest # Run backtest
docker run --rm -v $(pwd)/config_bittrex.json.example:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG_ARM} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy DefaultStrategy docker run --rm -v $(pwd)/config_examples/config_bittrex.example.json:/freqtrade/config.json:ro -v $(pwd)/tests:/tests freqtrade:${TAG_ARM} backtesting --datadir /tests/testdata --strategy-path /tests/strategy/strats/ --strategy DefaultStrategy
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "failed running backtest" echo "failed running backtest"

View File

@ -1,4 +1,4 @@
mkdocs==1.2.1 mkdocs==1.2.2
mkdocs-material==7.1.10 mkdocs-material==7.1.11
mdx_truly_sane_lists==1.2 mdx_truly_sane_lists==1.2
pymdown-extensions==8.2 pymdown-extensions==8.2

View File

@ -23,7 +23,7 @@ git clone https://github.com/freqtrade/freqtrade.git
Install ta-lib according to the [ta-lib documentation](https://github.com/mrjbq7/ta-lib#windows). Install ta-lib according to the [ta-lib documentation](https://github.com/mrjbq7/ta-lib#windows).
As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), there is also a repository of unofficial pre-compiled windows Wheels [here](https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib), which needs to be downloaded and installed using `pip install TA_Lib0.4.20cp38cp38win_amd64.whl` (make sure to use the version matching your python version). As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), there is also a repository of unofficial pre-compiled windows Wheels [here](https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib), which need to be downloaded and installed using `pip install TA_Lib-0.4.21-cp38-cp38-win_amd64.whl` (make sure to use the version matching your python version).
Freqtrade provides these dependencies for the latest 2 Python versions (3.7 and 3.8) and for 64bit Windows. Freqtrade provides these dependencies for the latest 2 Python versions (3.7 and 3.8) and for 64bit Windows.
Other versions must be downloaded from the above link. Other versions must be downloaded from the above link.

View File

@ -551,7 +551,7 @@ class Exchange:
amount_reserve_percent = 1.0 + self._config.get('amount_reserve_percent', amount_reserve_percent = 1.0 + self._config.get('amount_reserve_percent',
DEFAULT_AMOUNT_RESERVE_PERCENT) DEFAULT_AMOUNT_RESERVE_PERCENT)
amount_reserve_percent = ( amount_reserve_percent = (
amount_reserve_percent / (1 - abs(stoploss)) if abs(stoploss) != 1 else 1.5 amount_reserve_percent / (1 - abs(stoploss)) if abs(stoploss) != 1 else 1.5
) )
# it should not be more than 50% # it should not be more than 50%
amount_reserve_percent = max(min(amount_reserve_percent, 1.5), 1) amount_reserve_percent = max(min(amount_reserve_percent, 1.5), 1)
@ -999,94 +999,64 @@ class Exchange:
except ccxt.BaseError as e: except ccxt.BaseError as e:
raise OperationalException(e) from e raise OperationalException(e) from e
def get_buy_rate(self, pair: str, refresh: bool) -> float: def get_rate(self, pair: str, refresh: bool, side: str) -> float:
""" """
Calculates bid target between current ask price and last price Calculates bid/ask target
bid rate - between current ask price and last price
ask rate - either using ticker bid or first bid based on orderbook
or remain static in any other case since it's not updating.
:param pair: Pair to get rate for :param pair: Pair to get rate for
:param refresh: allow cached data :param refresh: allow cached data
:param side: "buy" or "sell"
:return: float: Price :return: float: Price
:raises PricingError if orderbook price could not be determined. :raises PricingError if orderbook price could not be determined.
""" """
cache_rate: TTLCache = self._buy_rate_cache if side == "buy" else self._sell_rate_cache
[strat_name, name] = ['bid_strategy', 'Buy'] if side == "buy" else ['ask_strategy', 'Sell']
if not refresh: if not refresh:
rate = self._buy_rate_cache.get(pair) rate = cache_rate.get(pair)
# Check if cache has been invalidated # Check if cache has been invalidated
if rate: if rate:
logger.debug(f"Using cached buy rate for {pair}.") logger.debug(f"Using cached {side} rate for {pair}.")
return rate return rate
bid_strategy = self._config.get('bid_strategy', {}) conf_strategy = self._config.get(strat_name, {})
if 'use_order_book' in bid_strategy and bid_strategy.get('use_order_book', False):
order_book_top = bid_strategy.get('order_book_top', 1) if conf_strategy.get('use_order_book', False) and ('use_order_book' in conf_strategy):
order_book_top = conf_strategy.get('order_book_top', 1)
order_book = self.fetch_l2_order_book(pair, order_book_top) order_book = self.fetch_l2_order_book(pair, order_book_top)
logger.debug('order_book %s', order_book) logger.debug('order_book %s', order_book)
# top 1 = index 0 # top 1 = index 0
try: try:
rate_from_l2 = order_book[f"{bid_strategy['price_side']}s"][order_book_top - 1][0] rate = order_book[f"{conf_strategy['price_side']}s"][order_book_top - 1][0]
except (IndexError, KeyError) as e: except (IndexError, KeyError) as e:
logger.warning( logger.warning(
"Buy Price from orderbook could not be determined." f"{name} Price at location {order_book_top} from orderbook could not be "
f"Orderbook: {order_book}"
)
raise PricingError from e
logger.info(f"Buy price from orderbook {bid_strategy['price_side'].capitalize()} side "
f"- top {order_book_top} order book buy rate {rate_from_l2:.8f}")
used_rate = rate_from_l2
else:
logger.info(f"Using Last {bid_strategy['price_side'].capitalize()} / Last Price")
ticker = self.fetch_ticker(pair)
ticker_rate = ticker[bid_strategy['price_side']]
if ticker['last'] and ticker_rate > ticker['last']:
balance = bid_strategy['ask_last_balance']
ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate)
used_rate = ticker_rate
self._buy_rate_cache[pair] = used_rate
return used_rate
def get_sell_rate(self, pair: str, refresh: bool) -> float:
"""
Get sell rate - either using ticker bid or first bid based on orderbook
or remain static in any other case since it's not updating.
:param pair: Pair to get rate for
:param refresh: allow cached data
:return: Bid rate
:raises PricingError if price could not be determined.
"""
if not refresh:
rate = self._sell_rate_cache.get(pair)
# Check if cache has been invalidated
if rate:
logger.debug(f"Using cached sell rate for {pair}.")
return rate
ask_strategy = self._config.get('ask_strategy', {})
if ask_strategy.get('use_order_book', False):
logger.debug(
f"Getting price from order book {ask_strategy['price_side'].capitalize()} side."
)
order_book_top = ask_strategy.get('order_book_top', 1)
order_book = self.fetch_l2_order_book(pair, order_book_top)
try:
rate = order_book[f"{ask_strategy['price_side']}s"][order_book_top - 1][0]
except (IndexError, KeyError) as e:
logger.warning(
f"Sell Price at location {order_book_top} from orderbook could not be "
f"determined. Orderbook: {order_book}" f"determined. Orderbook: {order_book}"
) )
raise PricingError from e raise PricingError from e
logger.info(f"{name} price from orderbook {conf_strategy['price_side'].capitalize()}"
f"side - top {order_book_top} order book {side} rate {rate:.8f}")
else: else:
logger.info(f"Using Last {conf_strategy['price_side'].capitalize()} / Last Price")
ticker = self.fetch_ticker(pair) ticker = self.fetch_ticker(pair)
ticker_rate = ticker[ask_strategy['price_side']] ticker_rate = ticker[conf_strategy['price_side']]
if ticker['last'] and ticker_rate < ticker['last']: if ticker['last']:
balance = ask_strategy.get('bid_last_balance', 0.0) if side == 'buy' and ticker_rate > ticker['last']:
ticker_rate = ticker_rate - balance * (ticker_rate - ticker['last']) balance = conf_strategy['ask_last_balance']
ticker_rate = ticker_rate + balance * (ticker['last'] - ticker_rate)
elif side == 'sell' and ticker_rate < ticker['last']:
balance = conf_strategy.get('bid_last_balance', 0.0)
ticker_rate = ticker_rate - balance * (ticker_rate - ticker['last'])
rate = ticker_rate rate = ticker_rate
if rate is None: if rate is None:
raise PricingError(f"Sell-Rate for {pair} was empty.") raise PricingError(f"{name}-Rate for {pair} was empty.")
self._sell_rate_cache[pair] = rate cache_rate[pair] = rate
return rate return rate
# Fee handling # Fee handling
@ -1318,8 +1288,8 @@ class Exchange:
self._pairs_last_refresh_time[(pair, timeframe)] = ticks[-1][0] // 1000 self._pairs_last_refresh_time[(pair, timeframe)] = ticks[-1][0] // 1000
# keeping parsed dataframe in cache # keeping parsed dataframe in cache
ohlcv_df = ohlcv_to_dataframe( ohlcv_df = ohlcv_to_dataframe(
ticks, timeframe, pair=pair, fill_missing=True, ticks, timeframe, pair=pair, fill_missing=True,
drop_incomplete=self._ohlcv_partial_candle) drop_incomplete=self._ohlcv_partial_candle)
results_df[(pair, timeframe)] = ohlcv_df results_df[(pair, timeframe)] = ohlcv_df
if cache: if cache:
self._klines[(pair, timeframe)] = ohlcv_df self._klines[(pair, timeframe)] = ohlcv_df

View File

@ -475,7 +475,7 @@ class FreqtradeBot(LoggingMixin):
buy_limit_requested = price buy_limit_requested = price
else: else:
# Calculate price # Calculate price
buy_limit_requested = self.exchange.get_buy_rate(pair, True) buy_limit_requested = self.exchange.get_rate(pair, refresh=True, side="buy")
if not buy_limit_requested: if not buy_limit_requested:
raise PricingError('Could not determine buy price.') raise PricingError('Could not determine buy price.')
@ -609,7 +609,7 @@ class FreqtradeBot(LoggingMixin):
""" """
Sends rpc notification when a buy cancel occurred. Sends rpc notification when a buy cancel occurred.
""" """
current_rate = self.exchange.get_buy_rate(trade.pair, False) current_rate = self.exchange.get_rate(trade.pair, refresh=False, side="buy")
msg = { msg = {
'trade_id': trade.id, 'trade_id': trade.id,
@ -695,7 +695,7 @@ class FreqtradeBot(LoggingMixin):
(buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.timeframe, analyzed_df) (buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.timeframe, analyzed_df)
logger.debug('checking sell') logger.debug('checking sell')
sell_rate = self.exchange.get_sell_rate(trade.pair, True) sell_rate = self.exchange.get_rate(trade.pair, refresh=True, side="sell")
if self._check_and_execute_sell(trade, sell_rate, buy, sell): if self._check_and_execute_sell(trade, sell_rate, buy, sell):
return True return True
@ -1132,7 +1132,8 @@ class FreqtradeBot(LoggingMixin):
profit_rate = trade.close_rate if trade.close_rate else trade.close_rate_requested profit_rate = trade.close_rate if trade.close_rate else trade.close_rate_requested
profit_trade = trade.calc_profit(rate=profit_rate) profit_trade = trade.calc_profit(rate=profit_rate)
# Use cached rates here - it was updated seconds ago. # Use cached rates here - it was updated seconds ago.
current_rate = self.exchange.get_sell_rate(trade.pair, False) if not fill else None current_rate = self.exchange.get_rate(
trade.pair, refresh=False, side="sell") if not fill else None
profit_ratio = trade.calc_profit_ratio(profit_rate) profit_ratio = trade.calc_profit_ratio(profit_rate)
gain = "profit" if profit_ratio > 0 else "loss" gain = "profit" if profit_ratio > 0 else "loss"
@ -1177,7 +1178,7 @@ class FreqtradeBot(LoggingMixin):
profit_rate = trade.close_rate if trade.close_rate else trade.close_rate_requested profit_rate = trade.close_rate if trade.close_rate else trade.close_rate_requested
profit_trade = trade.calc_profit(rate=profit_rate) profit_trade = trade.calc_profit(rate=profit_rate)
current_rate = self.exchange.get_sell_rate(trade.pair, False) current_rate = self.exchange.get_rate(trade.pair, refresh=False, side="sell")
profit_ratio = trade.calc_profit_ratio(profit_rate) profit_ratio = trade.calc_profit_ratio(profit_rate)
gain = "profit" if profit_ratio > 0 else "loss" gain = "profit" if profit_ratio > 0 else "loss"

View File

@ -154,7 +154,8 @@ class RPC:
# calculate profit and send message to user # calculate profit and send message to user
if trade.is_open: if trade.is_open:
try: try:
current_rate = self._freqtrade.exchange.get_sell_rate(trade.pair, False) current_rate = self._freqtrade.exchange.get_rate(
trade.pair, refresh=False, side="sell")
except (ExchangeError, PricingError): except (ExchangeError, PricingError):
current_rate = NAN current_rate = NAN
else: else:
@ -213,7 +214,8 @@ class RPC:
for trade in trades: for trade in trades:
# calculate profit and send message to user # calculate profit and send message to user
try: try:
current_rate = self._freqtrade.exchange.get_sell_rate(trade.pair, False) current_rate = self._freqtrade.exchange.get_rate(
trade.pair, refresh=False, side="sell")
except (PricingError, ExchangeError): except (PricingError, ExchangeError):
current_rate = NAN current_rate = NAN
trade_percent = (100 * trade.calc_profit_ratio(current_rate)) trade_percent = (100 * trade.calc_profit_ratio(current_rate))
@ -272,10 +274,10 @@ class RPC:
'date': key, 'date': key,
'abs_profit': value["amount"], 'abs_profit': value["amount"],
'fiat_value': self._fiat_converter.convert_amount( 'fiat_value': self._fiat_converter.convert_amount(
value['amount'], value['amount'],
stake_currency, stake_currency,
fiat_display_currency fiat_display_currency
) if self._fiat_converter else 0, ) if self._fiat_converter else 0,
'trade_count': value["trades"], 'trade_count': value["trades"],
} }
for key, value in profit_days.items() for key, value in profit_days.items()
@ -372,7 +374,8 @@ class RPC:
else: else:
# Get current rate # Get current rate
try: try:
current_rate = self._freqtrade.exchange.get_sell_rate(trade.pair, False) current_rate = self._freqtrade.exchange.get_rate(
trade.pair, refresh=False, side="sell")
except (PricingError, ExchangeError): except (PricingError, ExchangeError):
current_rate = NAN current_rate = NAN
profit_ratio = trade.calc_profit_ratio(rate=current_rate) profit_ratio = trade.calc_profit_ratio(rate=current_rate)
@ -551,7 +554,8 @@ class RPC:
if not fully_canceled: if not fully_canceled:
# Get current rate and execute sell # Get current rate and execute sell
current_rate = self._freqtrade.exchange.get_sell_rate(trade.pair, False) current_rate = self._freqtrade.exchange.get_rate(
trade.pair, refresh=False, side="sell")
sell_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL) sell_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL)
self._freqtrade.execute_sell(trade, current_rate, sell_reason) self._freqtrade.execute_sell(trade, current_rate, sell_reason)
# ---- EOF def _exec_forcesell ---- # ---- EOF def _exec_forcesell ----

View File

@ -1,19 +1,19 @@
numpy==1.21.0 numpy==1.21.1
pandas==1.3.0 pandas==1.3.0
ccxt==1.52.83 ccxt==1.53.25
# Pin cryptography for now due to rust build errors with piwheels # Pin cryptography for now due to rust build errors with piwheels
cryptography==3.4.7 cryptography==3.4.7
aiohttp==3.7.4.post0 aiohttp==3.7.4.post0
SQLAlchemy==1.4.20 SQLAlchemy==1.4.21
python-telegram-bot==13.7 python-telegram-bot==13.7
arrow==1.1.1 arrow==1.1.1
cachetools==4.2.2 cachetools==4.2.2
requests==2.25.1 requests==2.26.0
urllib3==1.26.6 urllib3==1.26.6
wrapt==1.12.1 wrapt==1.12.1
jsonschema==3.2.0 jsonschema==3.2.0
TA-Lib==0.4.20 TA-Lib==0.4.21
technical==1.3.0 technical==1.3.0
tabulate==0.8.9 tabulate==0.8.9
pycoingecko==2.2.0 pycoingecko==2.2.0
@ -39,5 +39,5 @@ aiofiles==0.7.0
# Support for colorized terminal output # Support for colorized terminal output
colorama==0.4.4 colorama==0.4.4
# Building config files interactively # Building config files interactively
questionary==1.9.0 questionary==1.10.0
prompt-toolkit==3.0.19 prompt-toolkit==3.0.19

View File

@ -4,8 +4,12 @@
function check_installed_pip() { function check_installed_pip() {
${PYTHON} -m pip > /dev/null ${PYTHON} -m pip > /dev/null
if [ $? -ne 0 ]; then if [ $? -ne 0 ]; then
echo "pip not found (called as '${PYTHON} -m pip'). Please make sure that pip is available for ${PYTHON}." echo "-----------------------------"
exit 1 echo "Installing Pip for ${PYTHON}"
echo "-----------------------------"
curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py
${PYTHON} get-pip.py
rm get-pip.py
fi fi
} }
@ -17,13 +21,12 @@ function check_installed_python() {
exit 2 exit 2
fi fi
for v in 8 9 7 for v in 9 8 7
do do
PYTHON="python3.${v}" PYTHON="python3.${v}"
which $PYTHON which $PYTHON
if [ $? -eq 0 ]; then if [ $? -eq 0 ]; then
echo "using ${PYTHON}" echo "using ${PYTHON}"
check_installed_pip check_installed_pip
return return
fi fi
@ -136,7 +139,7 @@ function install_macos() {
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
fi fi
#Gets number after decimal in python version #Gets number after decimal in python version
version=$(egrep -o 3.\[0-9\]+ <<< $PYTHON | sed 's/3.//g' ) version=$(egrep -o 3.\[0-9\]+ <<< $PYTHON | sed 's/3.//g')
if [[ $version -ge 9 ]]; then #Checks if python version >= 3.9 if [[ $version -ge 9 ]]; then #Checks if python version >= 3.9
install_mac_newer_python_dependencies install_mac_newer_python_dependencies
@ -147,7 +150,7 @@ function install_macos() {
# Install bot Debian_ubuntu # Install bot Debian_ubuntu
function install_debian() { function install_debian() {
sudo apt-get update sudo apt-get update
sudo apt-get install -y build-essential autoconf libtool pkg-config make wget git libpython3-dev sudo apt-get install -y build-essential autoconf libtool pkg-config make wget git $(echo lib${PYTHON}-dev ${PYTHON}-venv)
install_talib install_talib
} }
@ -236,12 +239,12 @@ function install() {
} }
function plot() { function plot() {
echo " echo "
----------------------------------------- -----------------------------------------
Installing dependencies for Plotting scripts Installing dependencies for Plotting scripts
----------------------------------------- -----------------------------------------
" "
${PYTHON} -m pip install plotly --upgrade ${PYTHON} -m pip install plotly --upgrade
} }
function help() { function help() {

View File

@ -1783,14 +1783,14 @@ def test_get_buy_rate(mocker, default_conf, caplog, side, ask, bid,
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
return_value={'ask': ask, 'last': last, 'bid': bid}) return_value={'ask': ask, 'last': last, 'bid': bid})
assert exchange.get_buy_rate('ETH/BTC', True) == expected assert exchange.get_rate('ETH/BTC', refresh=True, side="buy") == expected
assert not log_has("Using cached buy rate for ETH/BTC.", caplog) assert not log_has("Using cached buy rate for ETH/BTC.", caplog)
assert exchange.get_buy_rate('ETH/BTC', False) == expected assert exchange.get_rate('ETH/BTC', refresh=False, side="buy") == expected
assert log_has("Using cached buy rate for ETH/BTC.", caplog) assert log_has("Using cached buy rate for ETH/BTC.", caplog)
# Running a 2nd time with Refresh on! # Running a 2nd time with Refresh on!
caplog.clear() caplog.clear()
assert exchange.get_buy_rate('ETH/BTC', True) == expected assert exchange.get_rate('ETH/BTC', refresh=True, side="buy") == expected
assert not log_has("Using cached buy rate for ETH/BTC.", caplog) assert not log_has("Using cached buy rate for ETH/BTC.", caplog)
@ -1825,12 +1825,12 @@ def test_get_sell_rate(default_conf, mocker, caplog, side, bid, ask,
# Test regular mode # Test regular mode
exchange = get_patched_exchange(mocker, default_conf) exchange = get_patched_exchange(mocker, default_conf)
rate = exchange.get_sell_rate(pair, True) rate = exchange.get_rate(pair, refresh=True, side="sell")
assert not log_has("Using cached sell rate for ETH/BTC.", caplog) assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
assert isinstance(rate, float) assert isinstance(rate, float)
assert rate == expected assert rate == expected
# Use caching # Use caching
rate = exchange.get_sell_rate(pair, False) rate = exchange.get_rate(pair, refresh=False, side="sell")
assert rate == expected assert rate == expected
assert log_has("Using cached sell rate for ETH/BTC.", caplog) assert log_has("Using cached sell rate for ETH/BTC.", caplog)
@ -1848,11 +1848,11 @@ def test_get_sell_rate_orderbook(default_conf, mocker, caplog, side, expected, o
pair = "ETH/BTC" pair = "ETH/BTC"
mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', order_book_l2) mocker.patch('freqtrade.exchange.Exchange.fetch_l2_order_book', order_book_l2)
exchange = get_patched_exchange(mocker, default_conf) exchange = get_patched_exchange(mocker, default_conf)
rate = exchange.get_sell_rate(pair, True) rate = exchange.get_rate(pair, refresh=True, side="sell")
assert not log_has("Using cached sell rate for ETH/BTC.", caplog) assert not log_has("Using cached sell rate for ETH/BTC.", caplog)
assert isinstance(rate, float) assert isinstance(rate, float)
assert rate == expected assert rate == expected
rate = exchange.get_sell_rate(pair, False) rate = exchange.get_rate(pair, refresh=False, side="sell")
assert rate == expected assert rate == expected
assert log_has("Using cached sell rate for ETH/BTC.", caplog) assert log_has("Using cached sell rate for ETH/BTC.", caplog)
@ -1868,7 +1868,7 @@ def test_get_sell_rate_orderbook_exception(default_conf, mocker, caplog):
return_value={'bids': [[]], 'asks': [[]]}) return_value={'bids': [[]], 'asks': [[]]})
exchange = get_patched_exchange(mocker, default_conf) exchange = get_patched_exchange(mocker, default_conf)
with pytest.raises(PricingError): with pytest.raises(PricingError):
exchange.get_sell_rate(pair, True) exchange.get_rate(pair, refresh=True, side="sell")
assert log_has_re(r"Sell Price at location 1 from orderbook could not be determined\..*", assert log_has_re(r"Sell Price at location 1 from orderbook could not be determined\..*",
caplog) caplog)
@ -1881,18 +1881,18 @@ def test_get_sell_rate_exception(default_conf, mocker, caplog):
return_value={'ask': None, 'bid': 0.12, 'last': None}) return_value={'ask': None, 'bid': 0.12, 'last': None})
exchange = get_patched_exchange(mocker, default_conf) exchange = get_patched_exchange(mocker, default_conf)
with pytest.raises(PricingError, match=r"Sell-Rate for ETH/BTC was empty."): with pytest.raises(PricingError, match=r"Sell-Rate for ETH/BTC was empty."):
exchange.get_sell_rate(pair, True) exchange.get_rate(pair, refresh=True, side="sell")
exchange._config['ask_strategy']['price_side'] = 'bid' exchange._config['ask_strategy']['price_side'] = 'bid'
assert exchange.get_sell_rate(pair, True) == 0.12 assert exchange.get_rate(pair, refresh=True, side="sell") == 0.12
# Reverse sides # Reverse sides
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
return_value={'ask': 0.13, 'bid': None, 'last': None}) return_value={'ask': 0.13, 'bid': None, 'last': None})
with pytest.raises(PricingError, match=r"Sell-Rate for ETH/BTC was empty."): with pytest.raises(PricingError, match=r"Sell-Rate for ETH/BTC was empty."):
exchange.get_sell_rate(pair, True) exchange.get_rate(pair, refresh=True, side="sell")
exchange._config['ask_strategy']['price_side'] = 'ask' exchange._config['ask_strategy']['price_side'] = 'ask'
assert exchange.get_sell_rate(pair, True) == 0.13 assert exchange.get_rate(pair, refresh=True, side="sell") == 0.13
def make_fetch_ohlcv_mock(data): def make_fetch_ohlcv_mock(data):
@ -2203,7 +2203,7 @@ def test_cancel_order_dry_run(default_conf, mocker, exchange_name):
({'status': 'canceled', 'filled': 10.0}, False), ({'status': 'canceled', 'filled': 10.0}, False),
({'status': 'unknown', 'filled': 10.0}, False), ({'status': 'unknown', 'filled': 10.0}, False),
({'result': 'testest123'}, False), ({'result': 'testest123'}, False),
]) ])
def test_check_order_canceled_empty(mocker, default_conf, exchange_name, order, result): def test_check_order_canceled_empty(mocker, default_conf, exchange_name, order, result):
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name) exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
assert exchange.check_order_canceled_empty(order) == result assert exchange.check_order_canceled_empty(order) == result

View File

@ -109,7 +109,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
'exchange': 'binance', 'exchange': 'binance',
} }
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate', mocker.patch('freqtrade.exchange.Exchange.get_rate',
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available"))) MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
results = rpc._rpc_trade_status() results = rpc._rpc_trade_status()
assert isnan(results[0]['current_profit']) assert isnan(results[0]['current_profit'])
@ -217,7 +217,7 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
assert '-0.41% (-0.06)' == result[0][3] assert '-0.41% (-0.06)' == result[0][3]
assert '-0.06' == f'{fiat_profit_sum:.2f}' assert '-0.06' == f'{fiat_profit_sum:.2f}'
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate', mocker.patch('freqtrade.exchange.Exchange.get_rate',
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available"))) MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD') result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
assert 'instantly' == result[0][2] assert 'instantly' == result[0][2]
@ -427,7 +427,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
assert prec_satoshi(stats['best_rate'], 6.2) assert prec_satoshi(stats['best_rate'], 6.2)
# Test non-available pair # Test non-available pair
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate', mocker.patch('freqtrade.exchange.Exchange.get_rate',
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available"))) MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency) stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
assert stats['trade_count'] == 2 assert stats['trade_count'] == 2

View File

@ -879,7 +879,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
'exchange': 'binance', 'exchange': 'binance',
} }
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate', mocker.patch('freqtrade.exchange.Exchange.get_rate',
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available"))) MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
rc = client_get(client, f"{BASE_URI}/status") rc = client_get(client, f"{BASE_URI}/status")

View File

@ -161,7 +161,7 @@ def test_get_trade_stake_amount(default_conf, ticker, mocker) -> None:
(True, 0.0022, 3, 0.5, [0.001, 0.001, 0.0]), (True, 0.0022, 3, 0.5, [0.001, 0.001, 0.0]),
(True, 0.0027, 3, 0.5, [0.001, 0.001, 0.000673]), (True, 0.0027, 3, 0.5, [0.001, 0.001, 0.000673]),
(True, 0.0022, 3, 1, [0.001, 0.001, 0.0]), (True, 0.0022, 3, 1, [0.001, 0.001, 0.0]),
]) ])
def test_check_available_stake_amount(default_conf, ticker, mocker, fee, limit_buy_order_open, def test_check_available_stake_amount(default_conf, ticker, mocker, fee, limit_buy_order_open,
amend_last, wallet, max_open, lsamr, expected) -> None: amend_last, wallet, max_open, lsamr, expected) -> None:
patch_RPCManager(mocker) patch_RPCManager(mocker)
@ -784,7 +784,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
buy_mm = MagicMock(return_value=limit_buy_order_open) buy_mm = MagicMock(return_value=limit_buy_order_open)
mocker.patch.multiple( mocker.patch.multiple(
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
get_buy_rate=buy_rate_mock, get_rate=buy_rate_mock,
fetch_ticker=MagicMock(return_value={ fetch_ticker=MagicMock(return_value={
'bid': 0.00001172, 'bid': 0.00001172,
'ask': 0.00001173, 'ask': 0.00001173,
@ -824,7 +824,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
limit_buy_order_open['id'] = '33' limit_buy_order_open['id'] = '33'
fix_price = 0.06 fix_price = 0.06
assert freqtrade.execute_buy(pair, stake_amount, fix_price) assert freqtrade.execute_buy(pair, stake_amount, fix_price)
# Make sure get_buy_rate wasn't called again # Make sure get_rate wasn't called again
assert buy_rate_mock.call_count == 0 assert buy_rate_mock.call_count == 0
assert buy_mm.call_count == 2 assert buy_mm.call_count == 2
@ -893,7 +893,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order, limit_buy_order
assert not freqtrade.execute_buy(pair, stake_amount) assert not freqtrade.execute_buy(pair, stake_amount)
# Fail to get price... # Fail to get price...
mocker.patch('freqtrade.exchange.Exchange.get_buy_rate', MagicMock(return_value=0.0)) mocker.patch('freqtrade.exchange.Exchange.get_rate', MagicMock(return_value=0.0))
with pytest.raises(PricingError, match="Could not determine buy price."): with pytest.raises(PricingError, match="Could not determine buy price."):
freqtrade.execute_buy(pair, stake_amount) freqtrade.execute_buy(pair, stake_amount)
@ -909,7 +909,7 @@ def test_execute_buy_confirm_error(mocker, default_conf, fee, limit_buy_order) -
'last': 0.00001172 'last': 0.00001172
}), }),
buy=MagicMock(return_value=limit_buy_order), buy=MagicMock(return_value=limit_buy_order),
get_buy_rate=MagicMock(return_value=0.11), get_rate=MagicMock(return_value=0.11),
get_min_pair_stake_amount=MagicMock(return_value=1), get_min_pair_stake_amount=MagicMock(return_value=1),
get_fee=fee, get_fee=fee,
) )
@ -2513,7 +2513,7 @@ def test_handle_cancel_sell_limit(mocker, default_conf, fee) -> None:
'freqtrade.exchange.Exchange', 'freqtrade.exchange.Exchange',
cancel_order=cancel_order_mock, cancel_order=cancel_order_mock,
) )
mocker.patch('freqtrade.exchange.Exchange.get_sell_rate', return_value=0.245441) mocker.patch('freqtrade.exchange.Exchange.get_rate', return_value=0.245441)
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
@ -3956,7 +3956,7 @@ def test_order_book_depth_of_market_high_delta(default_conf, ticker, limit_buy_o
def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None: def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None:
""" """
test if function get_buy_rate will return the order book price test if function get_rate will return the order book price
instead of the ask rate instead of the ask rate
""" """
patch_exchange(mocker) patch_exchange(mocker)
@ -3974,7 +3974,7 @@ def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None:
default_conf['telegram']['enabled'] = False default_conf['telegram']['enabled'] = False
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
assert freqtrade.exchange.get_buy_rate('ETH/BTC', True) == 0.043935 assert freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy") == 0.043935
assert ticker_mock.call_count == 0 assert ticker_mock.call_count == 0
@ -3996,8 +3996,8 @@ def test_order_book_bid_strategy_exception(mocker, default_conf, caplog) -> None
freqtrade = FreqtradeBot(default_conf) freqtrade = FreqtradeBot(default_conf)
# orderbook shall be used even if tickers would be lower. # orderbook shall be used even if tickers would be lower.
with pytest.raises(PricingError): with pytest.raises(PricingError):
freqtrade.exchange.get_buy_rate('ETH/BTC', refresh=True) freqtrade.exchange.get_rate('ETH/BTC', refresh=True, side="buy")
assert log_has_re(r'Buy Price from orderbook could not be determined.', caplog) assert log_has_re(r'Buy Price at location 1 from orderbook could not be determined.', caplog)
def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2) -> None: def test_check_depth_of_market_buy(default_conf, mocker, order_book_l2) -> None: