Implement price_to_precision logic for stoploss
This commit is contained in:
parent
02078456fc
commit
d0d0cbe1d1
@ -8,15 +8,15 @@ from freqtrade.exchange.bitpanda import Bitpanda
|
|||||||
from freqtrade.exchange.bittrex import Bittrex
|
from freqtrade.exchange.bittrex import Bittrex
|
||||||
from freqtrade.exchange.bybit import Bybit
|
from freqtrade.exchange.bybit import Bybit
|
||||||
from freqtrade.exchange.coinbasepro import Coinbasepro
|
from freqtrade.exchange.coinbasepro import Coinbasepro
|
||||||
from freqtrade.exchange.exchange_utils import (amount_to_contract_precision, amount_to_contracts,
|
from freqtrade.exchange.exchange_utils import (ROUND_DOWN, ROUND_UP, amount_to_contract_precision,
|
||||||
amount_to_precision, available_exchanges,
|
amount_to_contracts, amount_to_precision,
|
||||||
ccxt_exchanges, contracts_to_amount,
|
available_exchanges, ccxt_exchanges,
|
||||||
date_minus_candles, is_exchange_known_ccxt,
|
contracts_to_amount, date_minus_candles,
|
||||||
market_is_active, price_to_precision,
|
is_exchange_known_ccxt, market_is_active,
|
||||||
timeframe_to_minutes, timeframe_to_msecs,
|
price_to_precision, timeframe_to_minutes,
|
||||||
timeframe_to_next_date, timeframe_to_prev_date,
|
timeframe_to_msecs, timeframe_to_next_date,
|
||||||
timeframe_to_seconds, validate_exchange,
|
timeframe_to_prev_date, timeframe_to_seconds,
|
||||||
validate_exchanges)
|
validate_exchange, validate_exchanges)
|
||||||
from freqtrade.exchange.gate import Gate
|
from freqtrade.exchange.gate import Gate
|
||||||
from freqtrade.exchange.hitbtc import Hitbtc
|
from freqtrade.exchange.hitbtc import Hitbtc
|
||||||
from freqtrade.exchange.huobi import Huobi
|
from freqtrade.exchange.huobi import Huobi
|
||||||
|
@ -19,9 +19,9 @@ from ccxt import TICK_SIZE
|
|||||||
from dateutil import parser
|
from dateutil import parser
|
||||||
from pandas import DataFrame, concat
|
from pandas import DataFrame, concat
|
||||||
|
|
||||||
from freqtrade.constants import (DEFAULT_AMOUNT_RESERVE_PERCENT, DEFAULT_PRICE_ROUND_MODE,
|
from freqtrade.constants import (DEFAULT_AMOUNT_RESERVE_PERCENT, NON_OPEN_EXCHANGE_STATES, BidAsk,
|
||||||
NON_OPEN_EXCHANGE_STATES, BidAsk, BuySell, Config, EntryExit,
|
BuySell, Config, EntryExit, ListPairsWithTimeframes, MakerTaker,
|
||||||
ListPairsWithTimeframes, MakerTaker, OBLiteral, PairWithTimeframe)
|
OBLiteral, PairWithTimeframe)
|
||||||
from freqtrade.data.converter import clean_ohlcv_dataframe, ohlcv_to_dataframe, trades_dict_to_list
|
from freqtrade.data.converter import clean_ohlcv_dataframe, ohlcv_to_dataframe, trades_dict_to_list
|
||||||
from freqtrade.enums import OPTIMIZE_MODES, CandleType, MarginMode, TradingMode
|
from freqtrade.enums import OPTIMIZE_MODES, CandleType, MarginMode, TradingMode
|
||||||
from freqtrade.enums.pricetype import PriceType
|
from freqtrade.enums.pricetype import PriceType
|
||||||
@ -30,13 +30,14 @@ from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFun
|
|||||||
RetryableOrderError, TemporaryError)
|
RetryableOrderError, TemporaryError)
|
||||||
from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, remove_credentials, retrier,
|
from freqtrade.exchange.common import (API_FETCH_ORDER_RETRY_COUNT, remove_credentials, retrier,
|
||||||
retrier_async)
|
retrier_async)
|
||||||
from freqtrade.exchange.exchange_utils import (CcxtModuleType, amount_to_contract_precision,
|
from freqtrade.exchange.exchange_utils import (ROUND, ROUND_DOWN, ROUND_UP, CcxtModuleType,
|
||||||
amount_to_contracts, amount_to_precision,
|
amount_to_contract_precision, amount_to_contracts,
|
||||||
contracts_to_amount, date_minus_candles,
|
amount_to_precision, contracts_to_amount,
|
||||||
is_exchange_known_ccxt, market_is_active,
|
date_minus_candles, is_exchange_known_ccxt,
|
||||||
price_to_precision, timeframe_to_minutes,
|
market_is_active, price_to_precision,
|
||||||
timeframe_to_msecs, timeframe_to_next_date,
|
timeframe_to_minutes, timeframe_to_msecs,
|
||||||
timeframe_to_prev_date, timeframe_to_seconds)
|
timeframe_to_next_date, timeframe_to_prev_date,
|
||||||
|
timeframe_to_seconds)
|
||||||
from freqtrade.exchange.types import OHLCVResponse, OrderBook, Ticker, Tickers
|
from freqtrade.exchange.types import OHLCVResponse, OrderBook, Ticker, Tickers
|
||||||
from freqtrade.misc import (chunks, deep_merge_dicts, file_dump_json, file_load_json,
|
from freqtrade.misc import (chunks, deep_merge_dicts, file_dump_json, file_load_json,
|
||||||
safe_value_fallback2)
|
safe_value_fallback2)
|
||||||
@ -730,11 +731,11 @@ class Exchange:
|
|||||||
"""
|
"""
|
||||||
return amount_to_precision(amount, self.get_precision_amount(pair), self.precisionMode)
|
return amount_to_precision(amount, self.get_precision_amount(pair), self.precisionMode)
|
||||||
|
|
||||||
def price_to_precision(self, pair: str, price: float, rounding_mode: int) -> float:
|
def price_to_precision(self, pair: str, price: float, rounding_mode: int = ROUND) -> float:
|
||||||
"""
|
"""
|
||||||
Returns the price rounded to the precision the Exchange accepts.
|
Returns the price rounded to the precision the Exchange accepts.
|
||||||
The default price_rounding_mode in conf is ROUND.
|
The default price_rounding_mode in conf is ROUND.
|
||||||
Must use ROUND_UP / ROUND_DOWN for stoploss calculations.
|
For stoploss calculations, must use ROUND_UP for longs, and ROUND_DOWN for shorts.
|
||||||
"""
|
"""
|
||||||
return price_to_precision(price, self.get_precision_price(pair),
|
return price_to_precision(price, self.get_precision_price(pair),
|
||||||
self.precisionMode, rounding_mode)
|
self.precisionMode, rounding_mode)
|
||||||
@ -1177,12 +1178,12 @@ class Exchange:
|
|||||||
|
|
||||||
user_order_type = order_types.get('stoploss', 'market')
|
user_order_type = order_types.get('stoploss', 'market')
|
||||||
ordertype, user_order_type = self._get_stop_order_type(user_order_type)
|
ordertype, user_order_type = self._get_stop_order_type(user_order_type)
|
||||||
|
round_mode = ROUND_DOWN if side == 'buy' else ROUND_UP
|
||||||
stop_price_norm = self.price_to_precision(pair, stop_price)
|
stop_price_norm = self.price_to_precision(pair, stop_price, round_mode)
|
||||||
limit_rate = None
|
limit_rate = None
|
||||||
if user_order_type == 'limit':
|
if user_order_type == 'limit':
|
||||||
limit_rate = self._get_stop_limit_rate(stop_price, order_types, side)
|
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, round_mode)
|
||||||
|
|
||||||
if self._config['dry_run']:
|
if self._config['dry_run']:
|
||||||
dry_order = self.create_dry_run_order(
|
dry_order = self.create_dry_run_order(
|
||||||
|
@ -224,19 +224,21 @@ def price_to_precision(
|
|||||||
price: float,
|
price: float,
|
||||||
price_precision: Optional[float],
|
price_precision: Optional[float],
|
||||||
precisionMode: Optional[int],
|
precisionMode: Optional[int],
|
||||||
rounding_mode: int = ROUND_UP,
|
rounding_mode: int = ROUND,
|
||||||
) -> float:
|
) -> 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(),
|
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
|
TODO: If ccxt supports ROUND_UP for decimal_to_precision(), we could remove this and
|
||||||
align with amount_to_precision().
|
align with amount_to_precision().
|
||||||
:param price: price to convert
|
:param price: price to convert
|
||||||
:param price_precision: price precision to use. Used from markets[pair]['precision']['price']
|
:param price_precision: price precision to use. Used from markets[pair]['precision']['price']
|
||||||
:param precisionMode: precision mode to use. Should be used from precisionMode
|
:param precisionMode: precision mode to use. Should be used from precisionMode
|
||||||
one of ccxt's DECIMAL_PLACES, SIGNIFICANT_DIGITS, or TICK_SIZE
|
one of ccxt's DECIMAL_PLACES, SIGNIFICANT_DIGITS, or TICK_SIZE
|
||||||
:param rounding_mode: rounding mode to use. Defaults to ROUND_UP
|
:param rounding_mode: rounding mode to use. Defaults to ROUND
|
||||||
:return: price rounded up to the precision the Exchange accepts
|
:return: price rounded up to the precision the Exchange accepts
|
||||||
"""
|
"""
|
||||||
if price_precision is not None and precisionMode is not None:
|
if price_precision is not None and precisionMode is not None:
|
||||||
|
@ -12,6 +12,7 @@ from freqtrade.exceptions import (DDosProtection, InsufficientFundsError, Invali
|
|||||||
OperationalException, TemporaryError)
|
OperationalException, TemporaryError)
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.exchange.common import retrier
|
from freqtrade.exchange.common import retrier
|
||||||
|
from freqtrade.exchange.exchange_utils import ROUND_DOWN, ROUND_UP
|
||||||
from freqtrade.exchange.types import Tickers
|
from freqtrade.exchange.types import Tickers
|
||||||
|
|
||||||
|
|
||||||
@ -109,6 +110,7 @@ class Kraken(Exchange):
|
|||||||
if self.trading_mode == TradingMode.FUTURES:
|
if self.trading_mode == TradingMode.FUTURES:
|
||||||
params.update({'reduceOnly': True})
|
params.update({'reduceOnly': True})
|
||||||
|
|
||||||
|
round_mode = ROUND_DOWN if side == 'buy' else ROUND_UP
|
||||||
if order_types.get('stoploss', 'market') == 'limit':
|
if order_types.get('stoploss', 'market') == 'limit':
|
||||||
ordertype = "stop-loss-limit"
|
ordertype = "stop-loss-limit"
|
||||||
limit_price_pct = order_types.get('stoploss_on_exchange_limit_ratio', 0.99)
|
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
|
limit_rate = stop_price * limit_price_pct
|
||||||
else:
|
else:
|
||||||
limit_rate = stop_price * (2 - limit_price_pct)
|
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, round_mode)
|
||||||
else:
|
else:
|
||||||
ordertype = "stop-loss"
|
ordertype = "stop-loss"
|
||||||
|
|
||||||
stop_price = self.price_to_precision(pair, stop_price)
|
stop_price = self.price_to_precision(pair, stop_price, round_mode)
|
||||||
|
|
||||||
if self._config['dry_run']:
|
if self._config['dry_run']:
|
||||||
dry_order = self.create_dry_run_order(
|
dry_order = self.create_dry_run_order(
|
||||||
|
@ -21,7 +21,8 @@ from freqtrade.enums import (ExitCheckTuple, ExitType, RPCMessageType, RunMode,
|
|||||||
State, TradingMode)
|
State, TradingMode)
|
||||||
from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError,
|
from freqtrade.exceptions import (DependencyException, ExchangeError, InsufficientFundsError,
|
||||||
InvalidOrderException, PricingError)
|
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.misc import safe_value_fallback, safe_value_fallback2
|
||||||
from freqtrade.mixins import LoggingMixin
|
from freqtrade.mixins import LoggingMixin
|
||||||
from freqtrade.persistence import Order, PairLocks, Trade, init_db
|
from freqtrade.persistence import Order, PairLocks, Trade, init_db
|
||||||
@ -1230,7 +1231,8 @@ class FreqtradeBot(LoggingMixin):
|
|||||||
:param order: Current on exchange stoploss order
|
:param order: Current on exchange stoploss order
|
||||||
:return: None
|
: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, ROUND_DOWN if trade.is_short else ROUND_UP)
|
||||||
|
|
||||||
if self.exchange.stoploss_adjust(stoploss_norm, order, side=trade.exit_side):
|
if self.exchange.stoploss_adjust(stoploss_norm, order, side=trade.exit_side):
|
||||||
# we check if the update is necessary
|
# we check if the update is necessary
|
||||||
|
@ -15,7 +15,8 @@ from freqtrade.constants import (DATETIME_PRINT_FORMAT, MATH_CLOSE_PREC, NON_OPE
|
|||||||
BuySell, LongShort)
|
BuySell, LongShort)
|
||||||
from freqtrade.enums import ExitType, TradingMode
|
from freqtrade.enums import ExitType, TradingMode
|
||||||
from freqtrade.exceptions import DependencyException, OperationalException
|
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.leverage import interest
|
||||||
from freqtrade.persistence.base import ModelBase, SessionType
|
from freqtrade.persistence.base import ModelBase, SessionType
|
||||||
from freqtrade.util import FtPrecise
|
from freqtrade.util import FtPrecise
|
||||||
@ -597,7 +598,8 @@ class LocalTrade():
|
|||||||
"""
|
"""
|
||||||
Method used internally to set self.stop_loss.
|
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,
|
||||||
|
ROUND_DOWN if self.is_short else ROUND_UP)
|
||||||
if not self.stop_loss:
|
if not self.stop_loss:
|
||||||
self.initial_stop_loss = stop_loss_norm
|
self.initial_stop_loss = stop_loss_norm
|
||||||
self.stop_loss = stop_loss_norm
|
self.stop_loss = stop_loss_norm
|
||||||
|
Loading…
Reference in New Issue
Block a user