From 6636f17e0f587dfc4ee0505040cb5e9aa6b30ed8 Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 25 Aug 2022 07:08:22 +0200 Subject: [PATCH] Simplify usage of amount_to_contract precision --- freqtrade/exchange/__init__.py | 10 ++++---- freqtrade/exchange/exchange.py | 23 +++++++++++++++++++ freqtrade/optimize/backtesting.py | 19 +++++---------- freqtrade/persistence/trade_model.py | 14 ++++------- .../test_backtesting_adjust_position.py | 3 ++- 5 files changed, 41 insertions(+), 28 deletions(-) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 57114a342..ff7ec7e04 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -9,11 +9,11 @@ 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 import (amount_to_contracts, amount_to_precision, - available_exchanges, ccxt_exchanges, contracts_to_amount, - date_minus_candles, is_exchange_known_ccxt, - is_exchange_officially_supported, market_is_active, - price_to_precision, timeframe_to_minutes, +from freqtrade.exchange.exchange 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, is_exchange_officially_supported, + 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) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 7d2a2f5c5..3264b82e2 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -2943,6 +2943,29 @@ def amount_to_precision(amount: float, amount_precision: Optional[float], return amount +def amount_to_contract_precision( + amount, amount_precision: Optional[float], precisionMode: Optional[int], + contract_size: Optional[float]) -> float: + """ + Returns the amount to buy or sell to a precision the Exchange accepts + including calculation to and from contracts. + Re-implementation of ccxt internal methods - ensuring we can test the result is correct + based on our definitions. + :param amount: amount to truncate + :param amount_precision: amount precision to use. + should be retrieved from markets[pair]['precision']['amount'] + :param precisionMode: precision mode to use. Should be used from precisionMode + one of ccxt's DECIMAL_PLACES, SIGNIFICANT_DIGITS, or TICK_SIZE + :param contract_size: contract size - taken from exchange.get_contract_size(pair) + :return: truncated amount + """ + if amount_precision is not None and precisionMode is not None: + contracts = amount_to_contracts(amount, contract_size) + amount_p = amount_to_precision(contracts, amount_precision, precisionMode) + return contracts_to_amount(amount_p, contract_size) + return amount + + def price_to_precision(price: float, price_precision: Optional[float], precisionMode: Optional[int]) -> float: """ diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 725ff2406..248300fad 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -23,9 +23,8 @@ from freqtrade.data.dataprovider import DataProvider from freqtrade.enums import (BacktestState, CandleType, ExitCheckTuple, ExitType, RunMode, TradingMode) from freqtrade.exceptions import DependencyException, OperationalException -from freqtrade.exchange import timeframe_to_minutes, timeframe_to_seconds -from freqtrade.exchange.exchange import (amount_to_contracts, amount_to_precision, - contracts_to_amount, price_to_precision) +from freqtrade.exchange import (amount_to_contract_precision, price_to_precision, + timeframe_to_minutes, timeframe_to_seconds) from freqtrade.mixins import LoggingMixin from freqtrade.optimize.backtest_caching import get_strategy_run_id from freqtrade.optimize.bt_progress import BTProgress @@ -659,11 +658,8 @@ class Backtesting: exit_candle_time = sell_row[DATE_IDX].to_pydatetime() order_type = self.strategy.order_types['exit'] # amount = amount or trade.amount - amount = contracts_to_amount( - amount_to_precision( - amount_to_contracts(amount or trade.amount, trade.contract_size), - trade.amount_precision, self.precision_mode), - trade.contract_size) + amount = amount_to_contract_precision(amount or trade.amount, trade.amount_precision, + self.precision_mode, trade.contract_size) rate = price_to_precision(close_rate, trade.price_precision, self.precision_mode) order = Order( id=self.order_id_counter, @@ -835,11 +831,8 @@ class Backtesting: contract_size = self.exchange.get_contract_size(pair) precision_amount = self.exchange.get_precision_amount(pair) - amount = contracts_to_amount( - amount_to_precision( - amount_to_contracts(amount_p, contract_size), - precision_amount, self.precision_mode), - contract_size) + amount = amount_to_contract_precision(amount_p, precision_amount, self.precision_mode, + contract_size) # Backcalculate actual stake amount. stake_amount = amount * propose_rate / leverage diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index 9ab608321..23997f835 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -14,8 +14,7 @@ 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_precision, price_to_precision -from freqtrade.exchange.exchange import amount_to_contracts, contracts_to_amount +from freqtrade.exchange import amount_to_contract_precision, price_to_precision from freqtrade.leverage import interest from freqtrade.persistence.base import _DECL_BASE from freqtrade.util import FtPrecise @@ -625,11 +624,8 @@ class LocalTrade(): else: logger.warning( f'Got different open_order_id {self.open_order_id} != {order.order_id}') - amount_tr = contracts_to_amount( - amount_to_precision( - amount_to_contracts(self.amount, self.contract_size), - self.amount_precision, self.precision_mode), - self.contract_size) + amount_tr = amount_to_contract_precision(self.amount, self.amount_precision, + self.precision_mode, self.contract_size) if isclose(order.safe_amount_after_fee, amount_tr, abs_tol=MATH_CLOSE_PREC): self.close(order.safe_price) else: @@ -882,8 +878,8 @@ class LocalTrade(): self.realized_profit = close_profit_abs self.close_profit_abs = profit - current_amount_tr = amount_to_precision(float(current_amount), - self.amount_precision, self.precision_mode) + current_amount_tr = amount_to_contract_precision( + float(current_amount), self.amount_precision, self.precision_mode, self.contract_size) if current_amount_tr > 0.0: # Trade is still open # Leverage not updated, as we don't allow changing leverage through DCA at the moment. diff --git a/tests/optimize/test_backtesting_adjust_position.py b/tests/optimize/test_backtesting_adjust_position.py index f0829ea1e..71f8cdcea 100644 --- a/tests/optimize/test_backtesting_adjust_position.py +++ b/tests/optimize/test_backtesting_adjust_position.py @@ -18,7 +18,8 @@ from tests.conftest import patch_exchange def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) -> None: default_conf['use_exit_signal'] = False mocker.patch('freqtrade.exchange.Exchange.get_fee', fee) - mocker.patch('freqtrade.optimize.backtesting.amount_to_precision', lambda x, y, z: round(x, 8)) + mocker.patch('freqtrade.optimize.backtesting.amount_to_contract_precision', + lambda x, *args, **kwargs: round(x, 8)) mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001) mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf')) patch_exchange(mocker)