Merge pull request #7216 from freqtrade/precise_calcs

Precise calcs
This commit is contained in:
Matthias
2022-08-17 14:32:02 +02:00
committed by GitHub
19 changed files with 279 additions and 203 deletions

View File

@@ -9,12 +9,13 @@ 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 (available_exchanges, ccxt_exchanges,
is_exchange_known_ccxt, is_exchange_officially_supported,
market_is_active, 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 import (amount_to_precision, available_exchanges, ccxt_exchanges,
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)
from freqtrade.exchange.ftx import Ftx
from freqtrade.exchange.gateio import Gateio
from freqtrade.exchange.hitbtc import Hitbtc

View File

@@ -681,45 +681,35 @@ class Exchange:
"""
return endpoint in self._api.has and self._api.has[endpoint]
def get_precision_amount(self, pair: str) -> Optional[float]:
"""
Returns the amount precision of the exchange.
:param pair: Pair to get precision for
:return: precision for amount or None. Must be used in combination with precisionMode
"""
return self.markets.get(pair, {}).get('precision', {}).get('amount', None)
def get_precision_price(self, pair: str) -> Optional[float]:
"""
Returns the price precision of the exchange.
:param pair: Pair to get precision for
:return: precision for price or None. Must be used in combination with precisionMode
"""
return self.markets.get(pair, {}).get('precision', {}).get('price', None)
def amount_to_precision(self, pair: str, amount: float) -> float:
"""
Returns the amount to buy or sell to a precision the Exchange accepts
Re-implementation of ccxt internal methods - ensuring we can test the result is correct
based on our definitions.
"""
if self.markets[pair]['precision']['amount'] is not None:
amount = float(decimal_to_precision(amount, rounding_mode=TRUNCATE,
precision=self.markets[pair]['precision']['amount'],
counting_mode=self.precisionMode,
))
return amount
"""
return amount_to_precision(amount, self.get_precision_amount(pair), self.precisionMode)
def price_to_precision(self, pair: str, price: float) -> float:
"""
Returns the price rounded up to the precision the Exchange accepts.
Partial Re-implementation of ccxt internal method decimal_to_precision(),
which does not support rounding up
TODO: If ccxt supports ROUND_UP for decimal_to_precision(), we could remove this and
align with amount_to_precision().
Rounds up
"""
if self.markets[pair]['precision']['price']:
# price = float(decimal_to_precision(price, rounding_mode=ROUND,
# precision=self.markets[pair]['precision']['price'],
# counting_mode=self.precisionMode,
# ))
if self.precisionMode == TICK_SIZE:
precision = FtPrecise(self.markets[pair]['precision']['price'])
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 = self.markets[pair]['precision']['price']
big_price = price * pow(10, symbol_prec)
price = ceil(big_price) / pow(10, symbol_prec)
return price
return price_to_precision(price, self.get_precision_price(pair), self.precisionMode)
def price_get_one_pip(self, pair: str, price: float) -> float:
"""
@@ -2862,3 +2852,61 @@ def market_is_active(market: Dict) -> bool:
# See https://github.com/ccxt/ccxt/issues/4874,
# https://github.com/ccxt/ccxt/issues/4075#issuecomment-434760520
return market.get('active', True) is not False
def amount_to_precision(amount: float, amount_precision: Optional[float],
precisionMode: Optional[int]) -> float:
"""
Returns the amount to buy or sell to a precision the Exchange accepts
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
:return: truncated amount
"""
if amount_precision is not None and precisionMode is not None:
precision = int(amount_precision) if precisionMode != TICK_SIZE else amount_precision
# precision must be an int for non-ticksize inputs.
amount = float(decimal_to_precision(amount, rounding_mode=TRUNCATE,
precision=precision,
counting_mode=precisionMode,
))
return amount
def price_to_precision(price: float, price_precision: Optional[float],
precisionMode: Optional[int]) -> float:
"""
Returns the price rounded up to the precision the Exchange accepts.
Partial Re-implementation of ccxt internal method decimal_to_precision(),
which does not support rounding up
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
: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:
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 price

View File

@@ -7,9 +7,8 @@ from freqtrade.constants import BuySell
from freqtrade.enums import MarginMode, TradingMode
from freqtrade.enums.candletype import CandleType
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
from freqtrade.exchange import Exchange
from freqtrade.exchange import Exchange, date_minus_candles
from freqtrade.exchange.common import retrier
from freqtrade.exchange.exchange import date_minus_candles
logger = logging.getLogger(__name__)