Merge branch 'freqtrade:develop' into develop

This commit is contained in:
GeorgeMurAlkh
2021-07-21 00:09:35 +03:00
committed by GitHub
18 changed files with 110 additions and 129 deletions
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
+5 -2
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
+1 -1
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"
+2 -2
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
+1 -1
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.
+35 -65
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
+6 -5
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"
+12 -8
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 ----
+6 -6
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
+15 -12
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() {
+13 -13
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
+3 -3
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
+1 -1
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")
+10 -10
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: