diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index b815fb3ee..df10e40e5 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -8,15 +8,15 @@ from freqtrade.exchange.bitpanda import Bitpanda from freqtrade.exchange.bittrex import Bittrex from freqtrade.exchange.bybit import Bybit from freqtrade.exchange.coinbasepro import Coinbasepro -from freqtrade.exchange.exchange_utils import (amount_to_contract_precision, amount_to_contracts, - amount_to_precision, available_exchanges, - ccxt_exchanges, contracts_to_amount, - date_minus_candles, is_exchange_known_ccxt, - market_is_active, price_to_precision, - timeframe_to_minutes, timeframe_to_msecs, - timeframe_to_next_date, timeframe_to_prev_date, - timeframe_to_seconds, validate_exchange, - validate_exchanges) +from freqtrade.exchange.exchange_utils import (ROUND_DOWN, ROUND_UP, amount_to_contract_precision, + amount_to_contracts, amount_to_precision, + available_exchanges, ccxt_exchanges, + contracts_to_amount, date_minus_candles, + is_exchange_known_ccxt, market_is_active, + price_to_precision, timeframe_to_minutes, + timeframe_to_msecs, timeframe_to_next_date, + timeframe_to_prev_date, timeframe_to_seconds, + validate_exchange, validate_exchanges) from freqtrade.exchange.gate import Gate from freqtrade.exchange.hitbtc import Hitbtc from freqtrade.exchange.huobi import Huobi diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index bbe9585ae..437ed4289 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -30,13 +30,14 @@ from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFun RetryableOrderError, TemporaryError) from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, remove_credentials, retrier, retrier_async) -from freqtrade.exchange.exchange_utils import (CcxtModuleType, amount_to_contract_precision, - amount_to_contracts, amount_to_precision, - contracts_to_amount, date_minus_candles, - is_exchange_known_ccxt, market_is_active, - price_to_precision, timeframe_to_minutes, - timeframe_to_msecs, timeframe_to_next_date, - timeframe_to_prev_date, timeframe_to_seconds) +from freqtrade.exchange.exchange_utils import (ROUND, ROUND_DOWN, ROUND_UP, CcxtModuleType, + amount_to_contract_precision, amount_to_contracts, + amount_to_precision, contracts_to_amount, + date_minus_candles, is_exchange_known_ccxt, + market_is_active, price_to_precision, + timeframe_to_minutes, timeframe_to_msecs, + timeframe_to_next_date, timeframe_to_prev_date, + timeframe_to_seconds) from freqtrade.exchange.types import OHLCVResponse, OrderBook, Ticker, Tickers from freqtrade.misc import (chunks, deep_merge_dicts, file_dump_json, file_load_json, safe_value_fallback2) @@ -734,12 +735,14 @@ class Exchange: """ return amount_to_precision(amount, self.get_precision_amount(pair), self.precisionMode) - def price_to_precision(self, pair: str, price: float) -> float: + def price_to_precision(self, pair: str, price: float, *, rounding_mode: int = ROUND) -> float: """ - Returns the price rounded up to the precision the Exchange accepts. - Rounds up + Returns the price rounded to the precision the Exchange accepts. + The default price_rounding_mode in conf is ROUND. + For stoploss calculations, must use ROUND_UP for longs, and ROUND_DOWN for shorts. """ - return price_to_precision(price, self.get_precision_price(pair), self.precisionMode) + return price_to_precision(price, self.get_precision_price(pair), + self.precisionMode, rounding_mode=rounding_mode) def price_get_one_pip(self, pair: str, price: float) -> float: """ @@ -1185,12 +1188,12 @@ class Exchange: user_order_type = order_types.get('stoploss', 'market') ordertype, user_order_type = self._get_stop_order_type(user_order_type) - - stop_price_norm = self.price_to_precision(pair, stop_price) + round_mode = ROUND_DOWN if side == 'buy' else ROUND_UP + stop_price_norm = self.price_to_precision(pair, stop_price, rounding_mode=round_mode) limit_rate = None if user_order_type == 'limit': limit_rate = self._get_stop_limit_rate(stop_price, order_types, side) - limit_rate = self.price_to_precision(pair, limit_rate) + limit_rate = self.price_to_precision(pair, limit_rate, rounding_mode=round_mode) if self._config['dry_run']: dry_order = self.create_dry_run_order( diff --git a/freqtrade/exchange/exchange_utils.py b/freqtrade/exchange/exchange_utils.py index 6d3371a59..83d2a214d 100644 --- a/freqtrade/exchange/exchange_utils.py +++ b/freqtrade/exchange/exchange_utils.py @@ -2,11 +2,12 @@ Exchange support utils """ from datetime import datetime, timedelta, timezone -from math import ceil +from math import ceil, floor from typing import Any, Dict, List, Optional, Tuple import ccxt -from ccxt import ROUND_DOWN, ROUND_UP, TICK_SIZE, TRUNCATE, decimal_to_precision +from ccxt import (DECIMAL_PLACES, ROUND, ROUND_DOWN, ROUND_UP, SIGNIFICANT_DIGITS, TICK_SIZE, + TRUNCATE, decimal_to_precision) from freqtrade.exchange.common import BAD_EXCHANGES, EXCHANGE_HAS_OPTIONAL, EXCHANGE_HAS_REQUIRED from freqtrade.util import FtPrecise @@ -219,35 +220,51 @@ def amount_to_contract_precision( return amount -def price_to_precision(price: float, price_precision: Optional[float], - precisionMode: Optional[int]) -> float: +def price_to_precision( + price: float, + price_precision: Optional[float], + precisionMode: Optional[int], + *, + rounding_mode: int = ROUND, +) -> float: """ - Returns the price rounded up to the precision the Exchange accepts. + Returns the price rounded to the precision the Exchange accepts. Partial Re-implementation of ccxt internal method decimal_to_precision(), - which does not support rounding up + which does not support rounding up. + For stoploss calculations, must use ROUND_UP for longs, and ROUND_DOWN for shorts. + TODO: If ccxt supports ROUND_UP for decimal_to_precision(), we could remove this and align with amount_to_precision(). - !!! Rounds up :param price: price to convert :param price_precision: price precision to use. Used from markets[pair]['precision']['price'] :param precisionMode: precision mode to use. Should be used from precisionMode one of ccxt's DECIMAL_PLACES, SIGNIFICANT_DIGITS, or TICK_SIZE + :param rounding_mode: rounding mode to use. Defaults to ROUND :return: price rounded up to the precision the Exchange accepts - """ if price_precision is not None and precisionMode is not None: - # price = float(decimal_to_precision(price, rounding_mode=ROUND, - # precision=price_precision, - # counting_mode=self.precisionMode, - # )) if precisionMode == TICK_SIZE: + if rounding_mode == ROUND: + ticks = price / price_precision + rounded_ticks = round(ticks) + return rounded_ticks * price_precision precision = FtPrecise(price_precision) price_str = FtPrecise(price) missing = price_str % precision if not missing == FtPrecise("0"): - price = round(float(str(price_str - missing + precision)), 14) - else: - symbol_prec = price_precision - big_price = price * pow(10, symbol_prec) - price = ceil(big_price) / pow(10, symbol_prec) + return round(float(str(price_str - missing + precision)), 14) + return price + elif precisionMode in (SIGNIFICANT_DIGITS, DECIMAL_PLACES): + ndigits = round(price_precision) + if rounding_mode == ROUND: + return round(price, ndigits) + ticks = price * (10**ndigits) + if rounding_mode == ROUND_UP: + return ceil(ticks) / (10**ndigits) + if rounding_mode == TRUNCATE: + return int(ticks) / (10**ndigits) + if rounding_mode == ROUND_DOWN: + return floor(ticks) / (10**ndigits) + raise ValueError(f"Unknown rounding_mode {rounding_mode}") + raise ValueError(f"Unknown precisionMode {precisionMode}") return price diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index b1a19fa69..c41bb6d56 100644 --- a/freqtrade/exchange/kraken.py +++ b/freqtrade/exchange/kraken.py @@ -12,6 +12,7 @@ from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, Invali OperationalException, TemporaryError) from freqtrade.exchange import Exchange from freqtrade.exchange.common import retrier +from freqtrade.exchange.exchange_utils import ROUND_DOWN, ROUND_UP from freqtrade.exchange.types import Tickers @@ -109,6 +110,7 @@ class Kraken(Exchange): if self.trading_mode == TradingMode.FUTURES: params.update({'reduceOnly': True}) + round_mode = ROUND_DOWN if side == 'buy' else ROUND_UP if order_types.get('stoploss', 'market') == 'limit': ordertype = "stop-loss-limit" limit_price_pct = order_types.get('stoploss_on_exchange_limit_ratio', 0.99) @@ -116,11 +118,11 @@ class Kraken(Exchange): limit_rate = stop_price * limit_price_pct else: limit_rate = stop_price * (2 - limit_price_pct) - params['price2'] = self.price_to_precision(pair, limit_rate) + params['price2'] = self.price_to_precision(pair, limit_rate, rounding_mode=round_mode) else: ordertype = "stop-loss" - stop_price = self.price_to_precision(pair, stop_price) + stop_price = self.price_to_precision(pair, stop_price, rounding_mode=round_mode) if self._config['dry_run']: dry_order = self.create_dry_run_order( diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 9746ac3d8..ebccae3e5 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -21,7 +21,8 @@ from freqtrade.enums import (ExitCheckTuple, ExitType, RPCMessageType, RunMode, State, TradingMode) from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError, InvalidOrderException, PricingError) -from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date, timeframe_to_seconds +from freqtrade.exchange import (ROUND_DOWN, ROUND_UP, timeframe_to_minutes, timeframe_to_next_date, + timeframe_to_seconds) from freqtrade.misc import safe_value_fallback, safe_value_fallback2 from freqtrade.mixins import LoggingMixin from freqtrade.persistence import Order, PairLocks, Trade, init_db @@ -1235,7 +1236,9 @@ class FreqtradeBot(LoggingMixin): :param order: Current on exchange stoploss order :return: None """ - stoploss_norm = self.exchange.price_to_precision(trade.pair, trade.stoploss_or_liquidation) + stoploss_norm = self.exchange.price_to_precision( + trade.pair, trade.stoploss_or_liquidation, + rounding_mode=ROUND_DOWN if trade.is_short else ROUND_UP) if self.exchange.stoploss_adjust(stoploss_norm, order, side=trade.exit_side): # we check if the update is necessary diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index 17117d436..e20a2b477 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -15,7 +15,8 @@ from freqtrade.constants import (DATETIME_PRINT_FORMAT, MATH_CLOSE_PREC, NON_OPE BuySell, LongShort) from freqtrade.enums import ExitType, TradingMode from freqtrade.exceptions import DependencyException, OperationalException -from freqtrade.exchange import amount_to_contract_precision, price_to_precision +from freqtrade.exchange import (ROUND_DOWN, ROUND_UP, amount_to_contract_precision, + price_to_precision) from freqtrade.leverage import interest from freqtrade.persistence.base import ModelBase, SessionType from freqtrade.util import FtPrecise @@ -597,7 +598,8 @@ class LocalTrade(): """ Method used internally to set self.stop_loss. """ - stop_loss_norm = price_to_precision(stop_loss, self.price_precision, self.precision_mode) + stop_loss_norm = price_to_precision(stop_loss, self.price_precision, self.precision_mode, + rounding_mode=ROUND_DOWN if self.is_short else ROUND_UP) if not self.stop_loss: self.initial_stop_loss = stop_loss_norm self.stop_loss = stop_loss_norm @@ -628,7 +630,8 @@ class LocalTrade(): if self.initial_stop_loss_pct is None or refresh: self.__set_stop_loss(new_loss, stoploss) self.initial_stop_loss = price_to_precision( - new_loss, self.price_precision, self.precision_mode) + new_loss, self.price_precision, self.precision_mode, + rounding_mode=ROUND_DOWN if self.is_short else ROUND_UP) self.initial_stop_loss_pct = -1 * abs(stoploss) # evaluate if the stop loss needs to be updated diff --git a/freqtrade/plugins/pairlist/PrecisionFilter.py b/freqtrade/plugins/pairlist/PrecisionFilter.py index 478eaec20..2e74aa293 100644 --- a/freqtrade/plugins/pairlist/PrecisionFilter.py +++ b/freqtrade/plugins/pairlist/PrecisionFilter.py @@ -6,6 +6,7 @@ from typing import Any, Dict, Optional from freqtrade.constants import Config from freqtrade.exceptions import OperationalException +from freqtrade.exchange import ROUND_UP from freqtrade.exchange.types import Ticker from freqtrade.plugins.pairlist.IPairList import IPairList @@ -61,9 +62,10 @@ class PrecisionFilter(IPairList): stop_price = ticker['last'] * self._stoploss # Adjust stop-prices to precision - sp = self._exchange.price_to_precision(pair, stop_price) + sp = self._exchange.price_to_precision(pair, stop_price, rounding_mode=ROUND_UP) - stop_gap_price = self._exchange.price_to_precision(pair, stop_price * 0.99) + stop_gap_price = self._exchange.price_to_precision(pair, stop_price * 0.99, + rounding_mode=ROUND_UP) logger.debug(f"{pair} - {sp} : {stop_gap_price}") if sp <= stop_gap_price: diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py index fda33b859..d44dae00d 100644 --- a/tests/exchange/test_binance.py +++ b/tests/exchange/test_binance.py @@ -48,7 +48,7 @@ def test_create_stoploss_order_binance(default_conf, mocker, limitratio, expecte default_conf['margin_mode'] = MarginMode.ISOLATED default_conf['trading_mode'] = trademode mocker.patch(f'{EXMS}.amount_to_precision', lambda s, x, y: y) - mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y: y) + mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y, **kwargs: y) exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') @@ -127,7 +127,7 @@ def test_create_stoploss_order_dry_run_binance(default_conf, mocker): order_type = 'stop_loss_limit' default_conf['dry_run'] = True mocker.patch(f'{EXMS}.amount_to_precision', lambda s, x, y: y) - mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y: y) + mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y, **kwargs: y) exchange = get_patched_exchange(mocker, default_conf, api_mock, 'binance') diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index e08815e61..8c9d83a96 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -8,6 +8,7 @@ from unittest.mock import MagicMock, Mock, PropertyMock, patch import arrow import ccxt import pytest +from ccxt import DECIMAL_PLACES, ROUND, ROUND_UP, TICK_SIZE, TRUNCATE from pandas import DataFrame from freqtrade.enums import CandleType, MarginMode, TradingMode @@ -315,35 +316,54 @@ def test_amount_to_precision(amount, precision_mode, precision, expected,): assert amount_to_precision(amount, precision, precision_mode) == expected -@pytest.mark.parametrize("price,precision_mode,precision,expected", [ - (2.34559, 2, 4, 2.3456), - (2.34559, 2, 5, 2.34559), - (2.34559, 2, 3, 2.346), - (2.9999, 2, 3, 3.000), - (2.9909, 2, 3, 2.991), - # Tests for Tick_size - (2.34559, 4, 0.0001, 2.3456), - (2.34559, 4, 0.00001, 2.34559), - (2.34559, 4, 0.001, 2.346), - (2.9999, 4, 0.001, 3.000), - (2.9909, 4, 0.001, 2.991), - (2.9909, 4, 0.005, 2.995), - (2.9973, 4, 0.005, 3.0), - (2.9977, 4, 0.005, 3.0), - (234.43, 4, 0.5, 234.5), - (234.53, 4, 0.5, 235.0), - (0.891534, 4, 0.0001, 0.8916), - (64968.89, 4, 0.01, 64968.89), - (0.000000003483, 4, 1e-12, 0.000000003483), - +@pytest.mark.parametrize("price,precision_mode,precision,expected,rounding_mode", [ + # Tests for DECIMAL_PLACES, ROUND_UP + (2.34559, 2, 4, 2.3456, ROUND_UP), + (2.34559, 2, 5, 2.34559, ROUND_UP), + (2.34559, 2, 3, 2.346, ROUND_UP), + (2.9999, 2, 3, 3.000, ROUND_UP), + (2.9909, 2, 3, 2.991, ROUND_UP), + # Tests for DECIMAL_PLACES, ROUND + (2.345600000000001, DECIMAL_PLACES, 4, 2.3456, ROUND), + (2.345551, DECIMAL_PLACES, 4, 2.3456, ROUND), + (2.49, DECIMAL_PLACES, 0, 2., ROUND), + (2.51, DECIMAL_PLACES, 0, 3., ROUND), + (5.1, DECIMAL_PLACES, -1, 10., ROUND), + (4.9, DECIMAL_PLACES, -1, 0., ROUND), + # Tests for TICK_SIZE, ROUND_UP + (2.34559, TICK_SIZE, 0.0001, 2.3456, ROUND_UP), + (2.34559, TICK_SIZE, 0.00001, 2.34559, ROUND_UP), + (2.34559, TICK_SIZE, 0.001, 2.346, ROUND_UP), + (2.9999, TICK_SIZE, 0.001, 3.000, ROUND_UP), + (2.9909, TICK_SIZE, 0.001, 2.991, ROUND_UP), + (2.9909, TICK_SIZE, 0.005, 2.995, ROUND_UP), + (2.9973, TICK_SIZE, 0.005, 3.0, ROUND_UP), + (2.9977, TICK_SIZE, 0.005, 3.0, ROUND_UP), + (234.43, TICK_SIZE, 0.5, 234.5, ROUND_UP), + (234.53, TICK_SIZE, 0.5, 235.0, ROUND_UP), + (0.891534, TICK_SIZE, 0.0001, 0.8916, ROUND_UP), + (64968.89, TICK_SIZE, 0.01, 64968.89, ROUND_UP), + (0.000000003483, TICK_SIZE, 1e-12, 0.000000003483, ROUND_UP), + # Tests for TICK_SIZE, ROUND + (2.49, TICK_SIZE, 1., 2., ROUND), + (2.51, TICK_SIZE, 1., 3., ROUND), + (2.000000051, TICK_SIZE, 0.0000001, 2.0000001, ROUND), + (2.000000049, TICK_SIZE, 0.0000001, 2., ROUND), + (2.9909, TICK_SIZE, 0.005, 2.990, ROUND), + (2.9973, TICK_SIZE, 0.005, 2.995, ROUND), + (2.9977, TICK_SIZE, 0.005, 3.0, ROUND), + (234.24, TICK_SIZE, 0.5, 234., ROUND), + (234.26, TICK_SIZE, 0.5, 234.5, ROUND), + # Tests for TRUNCATTE + (2.34559, 2, 4, 2.3455, TRUNCATE), + (2.34559, 2, 5, 2.34559, TRUNCATE), + (2.34559, 2, 3, 2.345, TRUNCATE), + (2.9999, 2, 3, 2.999, TRUNCATE), + (2.9909, 2, 3, 2.990, TRUNCATE), ]) -def test_price_to_precision(price, precision_mode, precision, expected): - # digits counting mode - # DECIMAL_PLACES = 2 - # SIGNIFICANT_DIGITS = 3 - # TICK_SIZE = 4 - - assert price_to_precision(price, precision, precision_mode) == expected +def test_price_to_precision(price, precision_mode, precision, expected, rounding_mode): + assert price_to_precision( + price, precision, precision_mode, rounding_mode=rounding_mode) == expected @pytest.mark.parametrize("price,precision_mode,precision,expected", [ @@ -5281,7 +5301,7 @@ def test_stoploss_contract_size(mocker, default_conf, contract_size, order_amoun }) default_conf['dry_run'] = False mocker.patch(f'{EXMS}.amount_to_precision', lambda s, x, y: y) - mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y: y) + mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y, **kwargs: y) exchange = get_patched_exchange(mocker, default_conf, api_mock) exchange.get_contract_size = MagicMock(return_value=contract_size) @@ -5301,3 +5321,10 @@ def test_stoploss_contract_size(mocker, default_conf, contract_size, order_amoun assert order['cost'] == 100 assert order['filled'] == 100 assert order['remaining'] == 100 + + +def test_price_to_precision_with_default_conf(default_conf, mocker): + conf = copy.deepcopy(default_conf) + patched_ex = get_patched_exchange(mocker, conf) + prec_price = patched_ex.price_to_precision("XRP/USDT", 1.0000000101) + assert prec_price == 1.00000001 diff --git a/tests/exchange/test_huobi.py b/tests/exchange/test_huobi.py index 85d2ced9d..8be8ef8b3 100644 --- a/tests/exchange/test_huobi.py +++ b/tests/exchange/test_huobi.py @@ -27,7 +27,7 @@ def test_create_stoploss_order_huobi(default_conf, mocker, limitratio, expected, }) default_conf['dry_run'] = False mocker.patch(f'{EXMS}.amount_to_precision', lambda s, x, y: y) - mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y: y) + mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y, **kwargs: y) exchange = get_patched_exchange(mocker, default_conf, api_mock, 'huobi') @@ -80,7 +80,7 @@ def test_create_stoploss_order_dry_run_huobi(default_conf, mocker): order_type = 'stop-limit' default_conf['dry_run'] = True mocker.patch(f'{EXMS}.amount_to_precision', lambda s, x, y: y) - mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y: y) + mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y, **kwargs: y) exchange = get_patched_exchange(mocker, default_conf, api_mock, 'huobi') diff --git a/tests/exchange/test_kraken.py b/tests/exchange/test_kraken.py index 40a5a5b38..8fc23b94e 100644 --- a/tests/exchange/test_kraken.py +++ b/tests/exchange/test_kraken.py @@ -29,7 +29,7 @@ def test_buy_kraken_trading_agreement(default_conf, mocker): default_conf['dry_run'] = False mocker.patch(f'{EXMS}.amount_to_precision', lambda s, x, y: y) - mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y: y) + mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y, **kwargs: y) exchange = get_patched_exchange(mocker, default_conf, api_mock, id="kraken") order = exchange.create_order( @@ -192,7 +192,7 @@ def test_create_stoploss_order_kraken(default_conf, mocker, ordertype, side, adj default_conf['dry_run'] = False mocker.patch(f'{EXMS}.amount_to_precision', lambda s, x, y: y) - mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y: y) + mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y, **kwargs: y) exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kraken') @@ -263,7 +263,7 @@ def test_create_stoploss_order_dry_run_kraken(default_conf, mocker, side): api_mock = MagicMock() default_conf['dry_run'] = True mocker.patch(f'{EXMS}.amount_to_precision', lambda s, x, y: y) - mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y: y) + mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y, **kwargs: y) exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kraken') diff --git a/tests/exchange/test_kucoin.py b/tests/exchange/test_kucoin.py index 07f3fb6a3..741ee27be 100644 --- a/tests/exchange/test_kucoin.py +++ b/tests/exchange/test_kucoin.py @@ -27,7 +27,7 @@ def test_create_stoploss_order_kucoin(default_conf, mocker, limitratio, expected }) default_conf['dry_run'] = False mocker.patch(f'{EXMS}.amount_to_precision', lambda s, x, y: y) - mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y: y) + mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y, **kwargs: y) exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kucoin') if order_type == 'limit': @@ -88,7 +88,7 @@ def test_stoploss_order_dry_run_kucoin(default_conf, mocker): order_type = 'market' default_conf['dry_run'] = True mocker.patch(f'{EXMS}.amount_to_precision', lambda s, x, y: y) - mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y: y) + mocker.patch(f'{EXMS}.price_to_precision', lambda s, x, y, **kwargs: y) exchange = get_patched_exchange(mocker, default_conf, api_mock, 'kucoin') diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 01aa730cb..d1487c114 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1671,7 +1671,7 @@ def test_stoploss_on_exchange_price_rounding( EXMS, get_fee=fee, ) - price_mock = MagicMock(side_effect=lambda p, s: int(s)) + price_mock = MagicMock(side_effect=lambda p, s, **kwargs: int(s)) stoploss_mock = MagicMock(return_value={'id': '13434334'}) adjust_mock = MagicMock(return_value=False) mocker.patch.multiple(