From 1132fa609342e744819c70a8341dffaf0c9184e1 Mon Sep 17 00:00:00 2001 From: ASU Date: Thu, 9 Mar 2023 02:11:31 +0200 Subject: [PATCH 01/40] feat: Added price_rounding modes in config --- freqtrade/constants.py | 8 ++- freqtrade/exchange/exchange.py | 17 +++-- freqtrade/exchange/exchange_utils.py | 44 ++++++++----- tests/exchange/test_exchange.py | 95 ++++++++++++++++++++-------- 4 files changed, 113 insertions(+), 51 deletions(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 1727da92e..ec2958c46 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -5,6 +5,8 @@ bot constants """ from typing import Any, Dict, List, Literal, Tuple +from ccxt import ROUND, ROUND_DOWN, ROUND_UP, TRUNCATE + from freqtrade.enums import CandleType, PriceType, RPCMessageType @@ -50,6 +52,8 @@ DEFAULT_DATAFRAME_COLUMNS = ['date', 'open', 'high', 'low', 'close', 'volume'] # it has wide consequences for stored trades files DEFAULT_TRADES_COLUMNS = ['timestamp', 'id', 'type', 'side', 'price', 'amount', 'cost'] TRADING_MODES = ['spot', 'margin', 'futures'] +PRICE_ROUND_MODES = [TRUNCATE, ROUND, ROUND_UP, ROUND_DOWN] +DEFAULT_PRICE_ROUND_MODE = ROUND_UP MARGIN_MODES = ['cross', 'isolated', ''] LAST_BT_RESULT_FN = '.last_result.json' @@ -476,7 +480,9 @@ CONF_SCHEMA = { 'outdated_offset': {'type': 'integer', 'minimum': 1}, 'markets_refresh_interval': {'type': 'integer'}, 'ccxt_config': {'type': 'object'}, - 'ccxt_async_config': {'type': 'object'} + 'ccxt_async_config': {'type': 'object'}, + 'price_rounding_mode': {'type': 'integer', 'enum': PRICE_ROUND_MODES, + 'default': DEFAULT_PRICE_ROUND_MODE} }, 'required': ['name'] }, diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index cdbda1506..cf7837a9a 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -19,9 +19,9 @@ from ccxt import TICK_SIZE from dateutil import parser from pandas import DataFrame, concat -from freqtrade.constants import (DEFAULT_AMOUNT_RESERVE_PERCENT, NON_OPEN_EXCHANGE_STATES, BidAsk, - BuySell, Config, EntryExit, ListPairsWithTimeframes, MakerTaker, - OBLiteral, PairWithTimeframe) +from freqtrade.constants import (DEFAULT_AMOUNT_RESERVE_PERCENT, DEFAULT_PRICE_ROUND_MODE, + NON_OPEN_EXCHANGE_STATES, BidAsk, BuySell, Config, EntryExit, + ListPairsWithTimeframes, MakerTaker, OBLiteral, PairWithTimeframe) 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.pricetype import PriceType @@ -143,7 +143,9 @@ class Exchange: if config.get('margin_mode') else MarginMode.NONE ) - self.liquidation_buffer = config.get('liquidation_buffer', 0.05) + self.liquidation_buffer = config.get("liquidation_buffer", 0.05) + self._price_rounding_mode = exchange_config.get("price_rounding_mode", + DEFAULT_PRICE_ROUND_MODE) # Deep merge ft_has with default ft_has options self._ft_has = deep_merge_dicts(self._ft_has, deepcopy(self._ft_has_default)) @@ -732,10 +734,11 @@ class Exchange: def price_to_precision(self, pair: str, price: float) -> 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_UP """ - return price_to_precision(price, self.get_precision_price(pair), self.precisionMode) + return price_to_precision(price, self.get_precision_price(pair), + self.precisionMode, self._price_rounding_mode) def price_get_one_pip(self, pair: str, price: float) -> float: """ diff --git a/freqtrade/exchange/exchange_utils.py b/freqtrade/exchange/exchange_utils.py index 6d3371a59..cf2b7986d 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,48 @@ 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_UP, +) -> 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 + :param rounding_mode: rounding mode to use. Defaults to ROUND_UP :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/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 940319a45..545d36a32 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 @@ -312,35 +313,47 @@ 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), ]) -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) == expected @pytest.mark.parametrize("price,precision_mode,precision,expected", [ @@ -5307,3 +5320,29 @@ 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.00000002 + + +@pytest.mark.parametrize( + "rounding_mode, price, expected_price", + [ + (TRUNCATE, 1.0000000199, 1.00000001), + (ROUND, 1.0000000149, 1.00000001), + (ROUND, 1.0000000151, 1.00000002), + (ROUND_UP, 1.0000000101, 1.00000002), + ], +) +def test_price_to_precision_rounding_mode_from_conf( + default_conf, mocker, rounding_mode, price, expected_price +): + conf = copy.deepcopy(default_conf) + conf["exchange"]["price_rounding_mode"] = rounding_mode + patched_ex = get_patched_exchange(mocker, conf) + prec_price = patched_ex.price_to_precision("XRP/USDT", price) + assert prec_price == expected_price From 01dfb1cba8a1e2f154eca49e990ded5c080e79ed Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 26 Mar 2023 10:17:01 +0200 Subject: [PATCH 02/40] Revert having price_rounding_mode as configuration --- freqtrade/constants.py | 8 +------- freqtrade/exchange/exchange.py | 11 +++++------ tests/exchange/test_exchange.py | 18 ------------------ 3 files changed, 6 insertions(+), 31 deletions(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index ec2958c46..1727da92e 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -5,8 +5,6 @@ bot constants """ from typing import Any, Dict, List, Literal, Tuple -from ccxt import ROUND, ROUND_DOWN, ROUND_UP, TRUNCATE - from freqtrade.enums import CandleType, PriceType, RPCMessageType @@ -52,8 +50,6 @@ DEFAULT_DATAFRAME_COLUMNS = ['date', 'open', 'high', 'low', 'close', 'volume'] # it has wide consequences for stored trades files DEFAULT_TRADES_COLUMNS = ['timestamp', 'id', 'type', 'side', 'price', 'amount', 'cost'] TRADING_MODES = ['spot', 'margin', 'futures'] -PRICE_ROUND_MODES = [TRUNCATE, ROUND, ROUND_UP, ROUND_DOWN] -DEFAULT_PRICE_ROUND_MODE = ROUND_UP MARGIN_MODES = ['cross', 'isolated', ''] LAST_BT_RESULT_FN = '.last_result.json' @@ -480,9 +476,7 @@ CONF_SCHEMA = { 'outdated_offset': {'type': 'integer', 'minimum': 1}, 'markets_refresh_interval': {'type': 'integer'}, 'ccxt_config': {'type': 'object'}, - 'ccxt_async_config': {'type': 'object'}, - 'price_rounding_mode': {'type': 'integer', 'enum': PRICE_ROUND_MODES, - 'default': DEFAULT_PRICE_ROUND_MODE} + 'ccxt_async_config': {'type': 'object'} }, 'required': ['name'] }, diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index cf7837a9a..049de176f 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -143,9 +143,7 @@ class Exchange: if config.get('margin_mode') else MarginMode.NONE ) - self.liquidation_buffer = config.get("liquidation_buffer", 0.05) - self._price_rounding_mode = exchange_config.get("price_rounding_mode", - DEFAULT_PRICE_ROUND_MODE) + self.liquidation_buffer = config.get('liquidation_buffer', 0.05) # Deep merge ft_has with default ft_has options self._ft_has = deep_merge_dicts(self._ft_has, deepcopy(self._ft_has_default)) @@ -732,13 +730,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) -> float: """ Returns the price rounded to the precision the Exchange accepts. - The default price_rounding_mode in conf is ROUND_UP + The default price_rounding_mode in conf is ROUND. + Must use ROUND_UP / ROUND_DOWN for stoploss calculations. """ return price_to_precision(price, self.get_precision_price(pair), - self.precisionMode, self._price_rounding_mode) + self.precisionMode, rounding_mode) def price_get_one_pip(self, pair: str, price: float) -> float: """ diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 545d36a32..784cc0508 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -5328,21 +5328,3 @@ def test_price_to_precision_with_default_conf(default_conf, mocker): prec_price = patched_ex.price_to_precision("XRP/USDT", 1.0000000101) assert prec_price == 1.00000002 - -@pytest.mark.parametrize( - "rounding_mode, price, expected_price", - [ - (TRUNCATE, 1.0000000199, 1.00000001), - (ROUND, 1.0000000149, 1.00000001), - (ROUND, 1.0000000151, 1.00000002), - (ROUND_UP, 1.0000000101, 1.00000002), - ], -) -def test_price_to_precision_rounding_mode_from_conf( - default_conf, mocker, rounding_mode, price, expected_price -): - conf = copy.deepcopy(default_conf) - conf["exchange"]["price_rounding_mode"] = rounding_mode - patched_ex = get_patched_exchange(mocker, conf) - prec_price = patched_ex.price_to_precision("XRP/USDT", price) - assert prec_price == expected_price From d0d0cbe1d18cd03693ff15837828710537e50964 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 26 Mar 2023 10:37:18 +0200 Subject: [PATCH 03/40] Implement price_to_precision logic for stoploss --- freqtrade/exchange/__init__.py | 18 ++++++++-------- freqtrade/exchange/exchange.py | 31 ++++++++++++++-------------- freqtrade/exchange/exchange_utils.py | 10 +++++---- freqtrade/exchange/kraken.py | 6 ++++-- freqtrade/freqtradebot.py | 6 ++++-- freqtrade/persistence/trade_model.py | 6 ++++-- 6 files changed, 43 insertions(+), 34 deletions(-) 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 1a5488b0f..24d3d97f7 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -19,9 +19,9 @@ from ccxt import TICK_SIZE from dateutil import parser from pandas import DataFrame, concat -from freqtrade.constants import (DEFAULT_AMOUNT_RESERVE_PERCENT, DEFAULT_PRICE_ROUND_MODE, - NON_OPEN_EXCHANGE_STATES, BidAsk, BuySell, Config, EntryExit, - ListPairsWithTimeframes, MakerTaker, OBLiteral, PairWithTimeframe) +from freqtrade.constants import (DEFAULT_AMOUNT_RESERVE_PERCENT, NON_OPEN_EXCHANGE_STATES, BidAsk, + BuySell, Config, EntryExit, ListPairsWithTimeframes, MakerTaker, + OBLiteral, PairWithTimeframe) 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.pricetype import PriceType @@ -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) @@ -730,11 +731,11 @@ class Exchange: """ 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. 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), self.precisionMode, rounding_mode) @@ -1177,12 +1178,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, 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, 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 cf2b7986d..8d6bb3838 100644 --- a/freqtrade/exchange/exchange_utils.py +++ b/freqtrade/exchange/exchange_utils.py @@ -224,19 +224,21 @@ def price_to_precision( price: float, price_precision: Optional[float], precisionMode: Optional[int], - rounding_mode: int = ROUND_UP, + 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(). :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_UP + :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: diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index b1a19fa69..e2bcd9a90 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, round_mode) else: 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']: dry_order = self.create_dry_run_order( diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 4482f37bf..da56e9a39 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 @@ -1230,7 +1231,8 @@ 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, 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..c9fdf745b 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, + 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 From 0cb28f3d82af569988a57859ee444b2f7beb656d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 26 Mar 2023 10:49:50 +0200 Subject: [PATCH 04/40] Use kwarg for rounding_mode, update tests with additional parameter --- freqtrade/exchange/exchange.py | 8 ++++---- freqtrade/exchange/exchange_utils.py | 1 + freqtrade/exchange/kraken.py | 4 ++-- freqtrade/freqtradebot.py | 3 ++- freqtrade/persistence/trade_model.py | 5 +++-- freqtrade/plugins/pairlist/PrecisionFilter.py | 6 ++++-- tests/exchange/test_binance.py | 4 ++-- tests/exchange/test_exchange.py | 7 ++++--- tests/exchange/test_huobi.py | 4 ++-- tests/exchange/test_kraken.py | 6 +++--- tests/exchange/test_kucoin.py | 4 ++-- tests/test_freqtradebot.py | 2 +- 12 files changed, 30 insertions(+), 24 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 24d3d97f7..ea0896aa1 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -731,14 +731,14 @@ class Exchange: """ return amount_to_precision(amount, self.get_precision_amount(pair), self.precisionMode) - def price_to_precision(self, pair: str, price: float, rounding_mode: int = ROUND) -> 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. 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, rounding_mode) + self.precisionMode, rounding_mode=rounding_mode) def price_get_one_pip(self, pair: str, price: float) -> float: """ @@ -1179,11 +1179,11 @@ class Exchange: user_order_type = order_types.get('stoploss', 'market') 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, round_mode) + 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, round_mode) + 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 8d6bb3838..83d2a214d 100644 --- a/freqtrade/exchange/exchange_utils.py +++ b/freqtrade/exchange/exchange_utils.py @@ -224,6 +224,7 @@ def price_to_precision( price: float, price_precision: Optional[float], precisionMode: Optional[int], + *, rounding_mode: int = ROUND, ) -> float: """ diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index e2bcd9a90..c41bb6d56 100644 --- a/freqtrade/exchange/kraken.py +++ b/freqtrade/exchange/kraken.py @@ -118,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, round_mode) + 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, round_mode) + 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 da56e9a39..ee4286c33 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1232,7 +1232,8 @@ class FreqtradeBot(LoggingMixin): :return: None """ stoploss_norm = self.exchange.price_to_precision( - trade.pair, trade.stoploss_or_liquidation, ROUND_DOWN if trade.is_short else ROUND_UP) + 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 c9fdf745b..e20a2b477 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -599,7 +599,7 @@ class LocalTrade(): Method used internally to set self.stop_loss. """ stop_loss_norm = price_to_precision(stop_loss, self.price_precision, self.precision_mode, - ROUND_DOWN if self.is_short else ROUND_UP) + 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 @@ -630,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 273860e15..eaec3d70f 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 a6138f6db..9aa04ff1f 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -353,7 +353,8 @@ def test_amount_to_precision(amount, precision_mode, precision, expected,): (234.26, TICK_SIZE, 0.5, 234.5, ROUND), ]) def test_price_to_precision(price, precision_mode, precision, expected, rounding_mode): - assert price_to_precision(price, precision, precision_mode, rounding_mode) == expected + assert price_to_precision( + price, precision, precision_mode, rounding_mode=rounding_mode) == expected @pytest.mark.parametrize("price,precision_mode,precision,expected", [ @@ -5277,7 +5278,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) @@ -5303,5 +5304,5 @@ 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.00000002 + 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 ff10cd2f0..5b4335652 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1602,7 +1602,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( From 159090c0e783a59c54eeb450655cb9a39ce8d6a2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 26 Mar 2023 11:11:19 +0200 Subject: [PATCH 05/40] Add explicit tests for TRUNCATE mode --- tests/exchange/test_exchange.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 9aa04ff1f..f05e1a77b 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -351,6 +351,12 @@ def test_amount_to_precision(amount, precision_mode, precision, expected,): (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, rounding_mode): assert price_to_precision( @@ -5305,4 +5311,3 @@ def test_price_to_precision_with_default_conf(default_conf, mocker): patched_ex = get_patched_exchange(mocker, conf) prec_price = patched_ex.price_to_precision("XRP/USDT", 1.0000000101) assert prec_price == 1.00000001 - From c330c493d5bde0c7a42c8fdf958629216abf4cc2 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 28 Mar 2023 19:48:25 +0200 Subject: [PATCH 06/40] test for Handle stop on exchange partial filled part of #8374 --- tests/test_freqtradebot.py | 58 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 01aa730cb..56f77585e 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1290,6 +1290,64 @@ def test_handle_stoploss_on_exchange(mocker, default_conf_usdt, fee, caplog, is_ assert trade.exit_reason == str(ExitType.EMERGENCY_EXIT) +@pytest.mark.parametrize("is_short", [False, True]) +def test_handle_stoploss_on_exchange_partial( + mocker, default_conf_usdt, fee, is_short, limit_order) -> None: + stop_order_dict = {'id': "101", "status": "open"} + stoploss = MagicMock(return_value=stop_order_dict) + enter_order = limit_order[entry_side(is_short)] + exit_order = limit_order[exit_side(is_short)] + patch_RPCManager(mocker) + patch_exchange(mocker) + mocker.patch.multiple( + EXMS, + fetch_ticker=MagicMock(return_value={ + 'bid': 1.9, + 'ask': 2.2, + 'last': 1.9 + }), + create_order=MagicMock(side_effect=[ + enter_order, + exit_order, + ]), + get_fee=fee, + create_stoploss=stoploss + ) + freqtrade = FreqtradeBot(default_conf_usdt) + patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short) + + freqtrade.enter_positions() + trade = Trade.session.scalars(select(Trade)).first() + trade.is_short = is_short + trade.is_open = True + trade.open_order_id = None + trade.stoploss_order_id = None + + assert freqtrade.handle_stoploss_on_exchange(trade) is False + assert stoploss.call_count == 1 + assert trade.stoploss_order_id == "101" + assert trade.amount == 30 + stop_order_dict.update({'id': "102"}) + # Stoploss on exchange is cancelled on exchange, but filled partially. + # Must update trade amount to guarantee successful exit. + stoploss_order_hit = MagicMock(return_value={ + 'id': "101", + 'status': 'canceled', + 'type': 'stop_loss_limit', + 'price': 3, + 'average': 2, + 'filled': trade.amount / 2, + 'remaining': trade.amount / 2, + 'amount': enter_order['amount'], + }) + mocker.patch(f'{EXMS}.fetch_stoploss_order', stoploss_order_hit) + assert freqtrade.handle_stoploss_on_exchange(trade) is False + # Stoploss filled partially ... + assert trade.amount == 15 + + assert trade.stoploss_order_id == "102" + + @pytest.mark.parametrize("is_short", [False, True]) def test_handle_sle_cancel_cant_recreate(mocker, default_conf_usdt, fee, caplog, is_short, limit_order) -> None: From e062a74e70c4700e1ee8a33d0a2b38b0cfb53776 Mon Sep 17 00:00:00 2001 From: Matthias Date: Tue, 28 Mar 2023 19:59:04 +0200 Subject: [PATCH 07/40] Add test for partial stop order canceling part of #8374 --- tests/test_freqtradebot.py | 73 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 56f77585e..e1c5b6c3c 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -1348,6 +1348,79 @@ def test_handle_stoploss_on_exchange_partial( assert trade.stoploss_order_id == "102" +@pytest.mark.parametrize("is_short", [False, True]) +def test_handle_stoploss_on_exchange_partial_cancel_here( + mocker, default_conf_usdt, fee, is_short, limit_order, caplog) -> None: + stop_order_dict = {'id': "101", "status": "open"} + default_conf_usdt['trailing_stop'] = True + stoploss = MagicMock(return_value=stop_order_dict) + enter_order = limit_order[entry_side(is_short)] + exit_order = limit_order[exit_side(is_short)] + patch_RPCManager(mocker) + patch_exchange(mocker) + mocker.patch.multiple( + EXMS, + fetch_ticker=MagicMock(return_value={ + 'bid': 1.9, + 'ask': 2.2, + 'last': 1.9 + }), + create_order=MagicMock(side_effect=[ + enter_order, + exit_order, + ]), + get_fee=fee, + create_stoploss=stoploss + ) + freqtrade = FreqtradeBot(default_conf_usdt) + patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short) + + freqtrade.enter_positions() + trade = Trade.session.scalars(select(Trade)).first() + trade.is_short = is_short + trade.is_open = True + trade.open_order_id = None + trade.stoploss_order_id = None + + assert freqtrade.handle_stoploss_on_exchange(trade) is False + assert stoploss.call_count == 1 + assert trade.stoploss_order_id == "101" + assert trade.amount == 30 + stop_order_dict.update({'id': "102"}) + # Stoploss on exchange is open. + # Freqtrade cancels the stop - but cancel returns a partial filled order. + stoploss_order_hit = MagicMock(return_value={ + 'id': "101", + 'status': 'open', + 'type': 'stop_loss_limit', + 'price': 3, + 'average': 2, + 'filled': 0, + 'remaining': trade.amount, + 'amount': enter_order['amount'], + }) + stoploss_order_cancel = MagicMock(return_value={ + 'id': "101", + 'status': 'canceled', + 'type': 'stop_loss_limit', + 'price': 3, + 'average': 2, + 'filled': trade.amount / 2, + 'remaining': trade.amount / 2, + 'amount': enter_order['amount'], + }) + mocker.patch(f'{EXMS}.fetch_stoploss_order', stoploss_order_hit) + mocker.patch(f'{EXMS}.cancel_stoploss_order_with_result', stoploss_order_cancel) + trade.stoploss_last_update = arrow.utcnow().shift(minutes=-10).datetime + + assert freqtrade.handle_stoploss_on_exchange(trade) is False + # Canceled Stoploss filled partially ... + assert log_has_re('Cancelling current stoploss on exchange.*', caplog) + + assert trade.stoploss_order_id == "102" + assert trade.amount == 15 + + @pytest.mark.parametrize("is_short", [False, True]) def test_handle_sle_cancel_cant_recreate(mocker, default_conf_usdt, fee, caplog, is_short, limit_order) -> None: From 861c5771384fd4fe9c24197438dc51760cac8634 Mon Sep 17 00:00:00 2001 From: Matthias Date: Wed, 29 Mar 2023 07:05:39 +0200 Subject: [PATCH 08/40] Support partially filled stop orders closes #8374 --- freqtrade/freqtradebot.py | 6 ++++-- freqtrade/persistence/trade_model.py | 21 ++++++++++++--------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 9746ac3d8..1cc1a0bae 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -853,7 +853,8 @@ class FreqtradeBot(LoggingMixin): logger.info(f"Canceling stoploss on exchange for {trade}") co = self.exchange.cancel_stoploss_order_with_result( trade.stoploss_order_id, trade.pair, trade.amount) - trade.update_order(co) + self.update_trade_state(trade, trade.stoploss_order_id, co, stoploss_order=True) + # Reset stoploss order id. trade.stoploss_order_id = None except InvalidOrderException: @@ -1171,7 +1172,8 @@ class FreqtradeBot(LoggingMixin): logger.warning('Unable to fetch stoploss order: %s', exception) if stoploss_order: - trade.update_order(stoploss_order) + self.update_trade_state(trade, trade.stoploss_order_id, stoploss_order, + stoploss_order=True) # We check if stoploss order is fulfilled if stoploss_order and stoploss_order['status'] in ('closed', 'triggered'): diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index 17117d436..4b59cbdbe 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -692,21 +692,24 @@ class LocalTrade(): else: logger.warning( f'Got different open_order_id {self.open_order_id} != {order.order_id}') + + elif order.ft_order_side == 'stoploss' and order.status not in ('open', ): + self.stoploss_order_id = None + self.close_rate_requested = self.stop_loss + self.exit_reason = ExitType.STOPLOSS_ON_EXCHANGE.value + if self.is_open: + logger.info(f'{order.order_type.upper()} is hit for {self}.') + else: + raise ValueError(f'Unknown order type: {order.order_type}') + + if order.ft_order_side != self.entry_side: 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: self.recalc_trade_from_orders() - elif order.ft_order_side == 'stoploss' and order.status not in ('canceled', 'open'): - self.stoploss_order_id = None - self.close_rate_requested = self.stop_loss - self.exit_reason = ExitType.STOPLOSS_ON_EXCHANGE.value - if self.is_open: - logger.info(f'{order.order_type.upper()} is hit for {self}.') - self.close(order.safe_price) - else: - raise ValueError(f'Unknown order type: {order.order_type}') + Trade.commit() def close(self, rate: float, *, show_msg: bool = True) -> None: From 3ec7c72da104053fe0e680c19798a6c9d12123af Mon Sep 17 00:00:00 2001 From: Matthias Date: Thu, 30 Mar 2023 07:06:23 +0200 Subject: [PATCH 09/40] Bump develop version to 2023.4.dev --- freqtrade/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 6ba045adf..f8955b295 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ Freqtrade bot """ -__version__ = '2023.3.dev' +__version__ = '2023.4.dev' if 'dev' in __version__: from pathlib import Path From f8330800d15e9d4dfdbd6db44994cd9a6a91df32 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 31 Mar 2023 06:49:02 +0200 Subject: [PATCH 10/40] Improve docker arm builds --- build_helpers/publish_docker_arm64.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build_helpers/publish_docker_arm64.sh b/build_helpers/publish_docker_arm64.sh index a6ecdbee6..229325efb 100755 --- a/build_helpers/publish_docker_arm64.sh +++ b/build_helpers/publish_docker_arm64.sh @@ -42,9 +42,9 @@ if [ $? -ne 0 ]; then return 1 fi -docker build --cache-from freqtrade:${TAG_ARM} --build-arg sourceimage=${CACHE_IMAGE} --build-arg sourcetag=${TAG_ARM} -t freqtrade:${TAG_PLOT_ARM} -f docker/Dockerfile.plot . -docker build --cache-from freqtrade:${TAG_ARM} --build-arg sourceimage=${CACHE_IMAGE} --build-arg sourcetag=${TAG_ARM} -t freqtrade:${TAG_FREQAI_ARM} -f docker/Dockerfile.freqai . -docker build --cache-from freqtrade:${TAG_ARM} --build-arg sourceimage=${CACHE_IMAGE} --build-arg sourcetag=${TAG_ARM} -t freqtrade:${TAG_FREQAI_RL_ARM} -f docker/Dockerfile.freqai_rl . +docker build --build-arg sourceimage=freqtrade --build-arg sourcetag=${TAG_ARM} -t freqtrade:${TAG_PLOT_ARM} -f docker/Dockerfile.plot . +docker build --build-arg sourceimage=freqtrade --build-arg sourcetag=${TAG_ARM} -t freqtrade:${TAG_FREQAI_ARM} -f docker/Dockerfile.freqai . +docker build --build-arg sourceimage=freqtrade --build-arg sourcetag=${TAG_FREQAI_ARM} -t freqtrade:${TAG_FREQAI_RL_ARM} -f docker/Dockerfile.freqai_rl . # Tag image for upload and next build step docker tag freqtrade:$TAG_ARM ${CACHE_IMAGE}:$TAG_ARM From 6dfb1a1d144fc7f6a494a757c887eb002979d165 Mon Sep 17 00:00:00 2001 From: Matthias Date: Fri, 31 Mar 2023 06:49:12 +0200 Subject: [PATCH 11/40] Improve docker regular build caching --- build_helpers/publish_docker_multi.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/build_helpers/publish_docker_multi.sh b/build_helpers/publish_docker_multi.sh index 27fa06b95..72b20ac5d 100755 --- a/build_helpers/publish_docker_multi.sh +++ b/build_helpers/publish_docker_multi.sh @@ -58,9 +58,9 @@ fi # Tag image for upload and next build step docker tag freqtrade:$TAG ${CACHE_IMAGE}:$TAG -docker build --cache-from freqtrade:${TAG} --build-arg sourceimage=${CACHE_IMAGE} --build-arg sourcetag=${TAG} -t freqtrade:${TAG_PLOT} -f docker/Dockerfile.plot . -docker build --cache-from freqtrade:${TAG} --build-arg sourceimage=${CACHE_IMAGE} --build-arg sourcetag=${TAG} -t freqtrade:${TAG_FREQAI} -f docker/Dockerfile.freqai . -docker build --cache-from freqtrade:${TAG_FREQAI} --build-arg sourceimage=${CACHE_IMAGE} --build-arg sourcetag=${TAG_FREQAI} -t freqtrade:${TAG_FREQAI_RL} -f docker/Dockerfile.freqai_rl . +docker build --build-arg sourceimage=freqtrade --build-arg sourcetag=${TAG} -t freqtrade:${TAG_PLOT} -f docker/Dockerfile.plot . +docker build --build-arg sourceimage=freqtrade --build-arg sourcetag=${TAG} -t freqtrade:${TAG_FREQAI} -f docker/Dockerfile.freqai . +docker build --build-arg sourceimage=freqtrade --build-arg sourcetag=${TAG_FREQAI} -t freqtrade:${TAG_FREQAI_RL} -f docker/Dockerfile.freqai_rl . docker tag freqtrade:$TAG_PLOT ${CACHE_IMAGE}:$TAG_PLOT docker tag freqtrade:$TAG_FREQAI ${CACHE_IMAGE}:$TAG_FREQAI From 92f34f262ed9f3d1f5329863ae6fbcc960d60e69 Mon Sep 17 00:00:00 2001 From: robcaulk Date: Sat, 1 Apr 2023 10:05:58 +0200 Subject: [PATCH 12/40] make trade_type value more explicit, add profit to trade_history dict --- freqtrade/freqai/RL/Base3ActionRLEnv.py | 4 ++-- freqtrade/freqai/RL/Base4ActionRLEnv.py | 18 ++++-------------- freqtrade/freqai/RL/Base5ActionRLEnv.py | 20 +++++--------------- 3 files changed, 11 insertions(+), 31 deletions(-) diff --git a/freqtrade/freqai/RL/Base3ActionRLEnv.py b/freqtrade/freqai/RL/Base3ActionRLEnv.py index a108d776e..c0a7eedaa 100644 --- a/freqtrade/freqai/RL/Base3ActionRLEnv.py +++ b/freqtrade/freqai/RL/Base3ActionRLEnv.py @@ -66,7 +66,7 @@ class Base3ActionRLEnv(BaseEnvironment): elif action == Actions.Sell.value and not self.can_short: self._update_total_profit() self._position = Positions.Neutral - trade_type = "neutral" + trade_type = "exit" self._last_trade_tick = None else: print("case not defined") @@ -74,7 +74,7 @@ class Base3ActionRLEnv(BaseEnvironment): if trade_type is not None: self.trade_history.append( {'price': self.current_price(), 'index': self._current_tick, - 'type': trade_type}) + 'type': trade_type, 'profit': self.get_unrealized_profit()}) if (self._total_profit < self.max_drawdown or self._total_unrealized_profit < self.max_drawdown): diff --git a/freqtrade/freqai/RL/Base4ActionRLEnv.py b/freqtrade/freqai/RL/Base4ActionRLEnv.py index 4f093f06c..e883136b2 100644 --- a/freqtrade/freqai/RL/Base4ActionRLEnv.py +++ b/freqtrade/freqai/RL/Base4ActionRLEnv.py @@ -52,16 +52,6 @@ class Base4ActionRLEnv(BaseEnvironment): trade_type = None if self.is_tradesignal(action): - """ - Action: Neutral, position: Long -> Close Long - Action: Neutral, position: Short -> Close Short - - Action: Long, position: Neutral -> Open Long - Action: Long, position: Short -> Close Short and Open Long - - Action: Short, position: Neutral -> Open Short - Action: Short, position: Long -> Close Long and Open Short - """ if action == Actions.Neutral.value: self._position = Positions.Neutral @@ -69,16 +59,16 @@ class Base4ActionRLEnv(BaseEnvironment): self._last_trade_tick = None elif action == Actions.Long_enter.value: self._position = Positions.Long - trade_type = "long" + trade_type = "enter_long" self._last_trade_tick = self._current_tick elif action == Actions.Short_enter.value: self._position = Positions.Short - trade_type = "short" + trade_type = "enter_short" self._last_trade_tick = self._current_tick elif action == Actions.Exit.value: self._update_total_profit() self._position = Positions.Neutral - trade_type = "neutral" + trade_type = "exit" self._last_trade_tick = None else: print("case not defined") @@ -86,7 +76,7 @@ class Base4ActionRLEnv(BaseEnvironment): if trade_type is not None: self.trade_history.append( {'price': self.current_price(), 'index': self._current_tick, - 'type': trade_type}) + 'type': trade_type, 'profit': self.get_unrealized_profit()}) if (self._total_profit < self.max_drawdown or self._total_unrealized_profit < self.max_drawdown): diff --git a/freqtrade/freqai/RL/Base5ActionRLEnv.py b/freqtrade/freqai/RL/Base5ActionRLEnv.py index 490ef3601..816211cc2 100644 --- a/freqtrade/freqai/RL/Base5ActionRLEnv.py +++ b/freqtrade/freqai/RL/Base5ActionRLEnv.py @@ -53,16 +53,6 @@ class Base5ActionRLEnv(BaseEnvironment): trade_type = None if self.is_tradesignal(action): - """ - Action: Neutral, position: Long -> Close Long - Action: Neutral, position: Short -> Close Short - - Action: Long, position: Neutral -> Open Long - Action: Long, position: Short -> Close Short and Open Long - - Action: Short, position: Neutral -> Open Short - Action: Short, position: Long -> Close Long and Open Short - """ if action == Actions.Neutral.value: self._position = Positions.Neutral @@ -70,21 +60,21 @@ class Base5ActionRLEnv(BaseEnvironment): self._last_trade_tick = None elif action == Actions.Long_enter.value: self._position = Positions.Long - trade_type = "long" + trade_type = "enter_long" self._last_trade_tick = self._current_tick elif action == Actions.Short_enter.value: self._position = Positions.Short - trade_type = "short" + trade_type = "enter_short" self._last_trade_tick = self._current_tick elif action == Actions.Long_exit.value: self._update_total_profit() self._position = Positions.Neutral - trade_type = "neutral" + trade_type = "exit_long" self._last_trade_tick = None elif action == Actions.Short_exit.value: self._update_total_profit() self._position = Positions.Neutral - trade_type = "neutral" + trade_type = "exit_short" self._last_trade_tick = None else: print("case not defined") @@ -92,7 +82,7 @@ class Base5ActionRLEnv(BaseEnvironment): if trade_type is not None: self.trade_history.append( {'price': self.current_price(), 'index': self._current_tick, - 'type': trade_type}) + 'type': trade_type, 'profit': self.get_unrealized_profit()}) if (self._total_profit < self.max_drawdown or self._total_unrealized_profit < self.max_drawdown): From 367186cc34057923b369139ac197e01b4dab0920 Mon Sep 17 00:00:00 2001 From: Robert Caulk Date: Sat, 1 Apr 2023 11:42:54 +0200 Subject: [PATCH 13/40] Update freqai-feature-engineering.md The `metadata` section of `freqai-feature-engineering.md` had a misplaced whitespace in front of the title. This PR removes the whitespace. --- docs/freqai-feature-engineering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/freqai-feature-engineering.md b/docs/freqai-feature-engineering.md index 6389bd9e5..f379e98c7 100644 --- a/docs/freqai-feature-engineering.md +++ b/docs/freqai-feature-engineering.md @@ -182,7 +182,7 @@ In total, the number of features the user of the presented example strat has cre $= 3 * 3 * 3 * 2 * 2 = 108$. - ### Gain finer control over `feature_engineering_*` functions with `metadata` +### Gain finer control over `feature_engineering_*` functions with `metadata` All `feature_engineering_*` and `set_freqai_targets()` functions are passed a `metadata` dictionary which contains information about the `pair`, `tf` (timeframe), and `period` that FreqAI is automating for feature building. As such, a user can use `metadata` inside `feature_engineering_*` functions as criteria for blocking/reserving features for certain timeframes, periods, pairs etc. From 631cb44f5cdb6b6455ea1ae41f3eeade4bff9a12 Mon Sep 17 00:00:00 2001 From: Robert Caulk Date: Sat, 1 Apr 2023 11:50:06 +0200 Subject: [PATCH 14/40] ensure python code block renders --- docs/freqai-feature-engineering.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/freqai-feature-engineering.md b/docs/freqai-feature-engineering.md index f379e98c7..1ca25d15c 100644 --- a/docs/freqai-feature-engineering.md +++ b/docs/freqai-feature-engineering.md @@ -186,7 +186,7 @@ In total, the number of features the user of the presented example strat has cre All `feature_engineering_*` and `set_freqai_targets()` functions are passed a `metadata` dictionary which contains information about the `pair`, `tf` (timeframe), and `period` that FreqAI is automating for feature building. As such, a user can use `metadata` inside `feature_engineering_*` functions as criteria for blocking/reserving features for certain timeframes, periods, pairs etc. - ```py + ```python def feature_engineering_expand_all(self, dataframe, period, metadata, **kwargs): if metadata["tf"] == "1h": dataframe["%-roc-period"] = ta.ROC(dataframe, timeperiod=period) From dc7e834911cbf9e7b5b14d059fe5aad6bb90270d Mon Sep 17 00:00:00 2001 From: Matthias Date: Sat, 1 Apr 2023 20:10:52 +0200 Subject: [PATCH 15/40] Fix some type issues --- freqtrade/freqtradebot.py | 4 ++-- freqtrade/rpc/rpc_types.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index e806c3bfc..bd281bc79 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -947,7 +947,7 @@ class FreqtradeBot(LoggingMixin): return enter_limit_requested, stake_amount, leverage - def _notify_enter(self, trade: Trade, order: Order, order_type: Optional[str] = None, + def _notify_enter(self, trade: Trade, order: Order, order_type: str, fill: bool = False, sub_trade: bool = False) -> None: """ Sends rpc notification when a entry order occurred. @@ -1852,7 +1852,7 @@ class FreqtradeBot(LoggingMixin): self.handle_protections(trade.pair, trade.trade_direction) elif send_msg and not trade.open_order_id and not stoploss_order: # Enter fill - self._notify_enter(trade, order, fill=True, sub_trade=sub_trade) + self._notify_enter(trade, order, order.order_type, fill=True, sub_trade=sub_trade) def handle_protections(self, pair: str, side: LongShort) -> None: # Lock pair for one candle to prevent immediate rebuys diff --git a/freqtrade/rpc/rpc_types.py b/freqtrade/rpc/rpc_types.py index 3277a2d6e..23f3ed5a9 100644 --- a/freqtrade/rpc/rpc_types.py +++ b/freqtrade/rpc/rpc_types.py @@ -52,7 +52,7 @@ class __RPCBuyMsgBase(RPCSendMsgBase): direction: str limit: float open_rate: float - order_type: Optional[str] # TODO: why optional?? + order_type: str stake_amount: float stake_currency: str fiat_currency: Optional[str] From cccf4f305bba68f8d90b198616db6ec25f96491f Mon Sep 17 00:00:00 2001 From: initrv Date: Sun, 2 Apr 2023 03:42:05 +0300 Subject: [PATCH 16/40] fix randomize_starting_position typo --- freqtrade/constants.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/freqtrade/constants.py b/freqtrade/constants.py index ebb946221..1d12ed8c1 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -598,7 +598,7 @@ CONF_SCHEMA = { "model_type": {"type": "string", "default": "PPO"}, "policy_type": {"type": "string", "default": "MlpPolicy"}, "net_arch": {"type": "array", "default": [128, 128]}, - "randomize_startinng_position": {"type": "boolean", "default": False}, + "randomize_starting_position": {"type": "boolean", "default": False}, "model_reward_parameters": { "type": "object", "properties": { From 12a73bc1510baac6a10f5e31bf8340e55eae81ce Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 03:56:46 +0000 Subject: [PATCH 17/40] Bump websockets from 10.4 to 11.0 Bumps [websockets](https://github.com/aaugustin/websockets) from 10.4 to 11.0. - [Release notes](https://github.com/aaugustin/websockets/releases) - [Commits](https://github.com/aaugustin/websockets/compare/10.4...11.0) --- updated-dependencies: - dependency-name: websockets dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b888d9f6e..f11fc1c42 100644 --- a/requirements.txt +++ b/requirements.txt @@ -53,7 +53,7 @@ python-dateutil==2.8.2 schedule==1.1.0 #WS Messages -websockets==10.4 +websockets==11.0 janus==1.0.0 ast-comments==1.0.1 From b1e20bcd1e30bc4261aa0712cae15072581a5616 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 03:57:00 +0000 Subject: [PATCH 18/40] Bump sqlalchemy from 2.0.7 to 2.0.8 Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 2.0.7 to 2.0.8. - [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases) - [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES.rst) - [Commits](https://github.com/sqlalchemy/sqlalchemy/commits) --- updated-dependencies: - dependency-name: sqlalchemy dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b888d9f6e..78da706a6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -5,7 +5,7 @@ pandas-ta==0.3.14b ccxt==3.0.37 cryptography==40.0.1 aiohttp==3.8.4 -SQLAlchemy==2.0.7 +SQLAlchemy==2.0.8 python-telegram-bot==13.15 arrow==1.2.3 cachetools==4.2.2 From 26ed1ca07c77e0c3831df0edfa128ee3a6513113 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 03:57:05 +0000 Subject: [PATCH 19/40] Bump xgboost from 1.7.4 to 1.7.5 Bumps [xgboost](https://github.com/dmlc/xgboost) from 1.7.4 to 1.7.5. - [Release notes](https://github.com/dmlc/xgboost/releases) - [Changelog](https://github.com/dmlc/xgboost/blob/master/NEWS.md) - [Commits](https://github.com/dmlc/xgboost/compare/v1.7.4...v1.7.5) --- updated-dependencies: - dependency-name: xgboost dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-freqai.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-freqai.txt b/requirements-freqai.txt index e6eae667c..e3a811661 100644 --- a/requirements-freqai.txt +++ b/requirements-freqai.txt @@ -7,5 +7,5 @@ scikit-learn==1.1.3 joblib==1.2.0 catboost==1.1.1; platform_machine != 'aarch64' and 'arm' not in platform_machine and python_version < '3.11' lightgbm==3.3.5 -xgboost==1.7.4 +xgboost==1.7.5 tensorboard==2.12.0 From e289c10b6cc1b00d1ceee48a10ca965b82b858f2 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 03:57:10 +0000 Subject: [PATCH 20/40] Bump types-cachetools from 5.3.0.4 to 5.3.0.5 Bumps [types-cachetools](https://github.com/python/typeshed) from 5.3.0.4 to 5.3.0.5. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-cachetools dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index 3324c11e9..e43048cb9 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -25,7 +25,7 @@ httpx==0.23.3 nbconvert==7.2.10 # mypy types -types-cachetools==5.3.0.4 +types-cachetools==5.3.0.5 types-filelock==3.2.7 types-requests==2.28.11.16 types-tabulate==0.9.0.1 From 1b31c5416287a22abc399543054b0e139817a967 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 03:57:19 +0000 Subject: [PATCH 21/40] Bump ccxt from 3.0.37 to 3.0.50 Bumps [ccxt](https://github.com/ccxt/ccxt) from 3.0.37 to 3.0.50. - [Release notes](https://github.com/ccxt/ccxt/releases) - [Changelog](https://github.com/ccxt/ccxt/blob/master/CHANGELOG.md) - [Commits](https://github.com/ccxt/ccxt/compare/3.0.37...3.0.50) --- updated-dependencies: - dependency-name: ccxt dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index b888d9f6e..d3df857da 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ numpy==1.24.2 pandas==1.5.3 pandas-ta==0.3.14b -ccxt==3.0.37 +ccxt==3.0.50 cryptography==40.0.1 aiohttp==3.8.4 SQLAlchemy==2.0.7 From 2ea575cb310428c556698a3748b9efb1459dd7ae Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 03:57:30 +0000 Subject: [PATCH 22/40] Bump mkdocs-material from 9.1.4 to 9.1.5 Bumps [mkdocs-material](https://github.com/squidfunk/mkdocs-material) from 9.1.4 to 9.1.5. - [Release notes](https://github.com/squidfunk/mkdocs-material/releases) - [Changelog](https://github.com/squidfunk/mkdocs-material/blob/master/CHANGELOG) - [Commits](https://github.com/squidfunk/mkdocs-material/compare/9.1.4...9.1.5) --- updated-dependencies: - dependency-name: mkdocs-material dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- docs/requirements-docs.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 7f4215aef..c70415c85 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,6 +1,6 @@ markdown==3.3.7 mkdocs==1.4.2 -mkdocs-material==9.1.4 +mkdocs-material==9.1.5 mdx_truly_sane_lists==1.3 pymdown-extensions==9.10 jinja2==3.1.2 From 2715b2ccf0a43f38dd585af6ee190e27a2bf10fe Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 03:58:12 +0000 Subject: [PATCH 23/40] Bump pypa/gh-action-pypi-publish from 1.8.3 to 1.8.4 Bumps [pypa/gh-action-pypi-publish](https://github.com/pypa/gh-action-pypi-publish) from 1.8.3 to 1.8.4. - [Release notes](https://github.com/pypa/gh-action-pypi-publish/releases) - [Commits](https://github.com/pypa/gh-action-pypi-publish/compare/v1.8.3...v1.8.4) --- updated-dependencies: - dependency-name: pypa/gh-action-pypi-publish dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 5c80bc141..e856607fc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -425,7 +425,7 @@ jobs: python setup.py sdist bdist_wheel - name: Publish to PyPI (Test) - uses: pypa/gh-action-pypi-publish@v1.8.3 + uses: pypa/gh-action-pypi-publish@v1.8.4 if: (github.event_name == 'release') with: user: __token__ @@ -433,7 +433,7 @@ jobs: repository_url: https://test.pypi.org/legacy/ - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@v1.8.3 + uses: pypa/gh-action-pypi-publish@v1.8.4 if: (github.event_name == 'release') with: user: __token__ From 6f79d14c9ccfa8d9375f33acd7e936ccab3e8a0c Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 3 Apr 2023 06:37:15 +0200 Subject: [PATCH 24/40] pre-commit - bump cachetools --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4784055a9..142d8d50d 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -13,7 +13,7 @@ repos: - id: mypy exclude: build_helpers additional_dependencies: - - types-cachetools==5.3.0.4 + - types-cachetools==5.3.0.5 - types-filelock==3.2.7 - types-requests==2.28.11.16 - types-tabulate==0.9.0.1 From 78a1551798abb05687822050be8efa08774cce56 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 2 Apr 2023 16:45:42 +0200 Subject: [PATCH 25/40] Reorder get_stake_limit --- freqtrade/exchange/exchange.py | 36 ++++++++++++++-------------------- 1 file changed, 15 insertions(+), 21 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 437ed4289..2d2fa3354 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -788,27 +788,6 @@ class Exchange: except KeyError: raise ValueError(f"Can't get market information for symbol {pair}") - stake_limits = [] - limits = market['limits'] - if (limits['cost'][limit] is not None): - stake_limits.append( - self._contracts_to_amount( - pair, - limits['cost'][limit] - ) - ) - - if (limits['amount'][limit] is not None): - stake_limits.append( - self._contracts_to_amount( - pair, - limits['amount'][limit] * price - ) - ) - - if not stake_limits: - return None if isMin else float('inf') - # reserve some percent defined in config (5% default) + stoploss amount_reserve_percent = 1.0 + self._config.get('amount_reserve_percent', DEFAULT_AMOUNT_RESERVE_PERCENT) @@ -818,6 +797,21 @@ class Exchange: # it should not be more than 50% amount_reserve_percent = max(min(amount_reserve_percent, 1.5), 1) + stake_limits = [] + limits = market['limits'] + if (limits['cost'][limit] is not None): + stake_limits.append( + self._contracts_to_amount(pair, limits['cost'][limit]) + ) + + if (limits['amount'][limit] is not None): + stake_limits.append( + self._contracts_to_amount(pair, limits['amount'][limit] * price) + ) + + if not stake_limits: + return None if isMin else float('inf') + # The value returned should satisfy both limits: for amount (base currency) and # for cost (quote, stake currency), so max() is used here. # See also #2575 at github. From e6a125719e09acaf5d03948b24142cad8c49bb6b Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 2 Apr 2023 16:59:45 +0200 Subject: [PATCH 26/40] Slightly refactor _get_stake_amount_limit --- freqtrade/exchange/exchange.py | 8 ++++---- tests/exchange/test_exchange.py | 3 +++ 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 2d2fa3354..1c37ad638 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -765,12 +765,12 @@ class Exchange: return self._get_stake_amount_limit(pair, price, stoploss, 'min', leverage) def get_max_pair_stake_amount(self, pair: str, price: float, leverage: float = 1.0) -> float: - max_stake_amount = self._get_stake_amount_limit(pair, price, 0.0, 'max') + max_stake_amount = self._get_stake_amount_limit(pair, price, 0.0, 'max', leverage) if max_stake_amount is None: # * Should never be executed raise OperationalException(f'{self.name}.get_max_pair_stake_amount should' 'never set max_stake_amount to None') - return max_stake_amount / leverage + return max_stake_amount def _get_stake_amount_limit( self, @@ -816,9 +816,9 @@ class Exchange: # for cost (quote, stake currency), so max() is used here. # See also #2575 at github. return self._get_stake_amount_considering_leverage( - max(stake_limits) * amount_reserve_percent, + (max(stake_limits) * amount_reserve_percent) if isMin else min(stake_limits), leverage or 1.0 - ) if isMin else min(stake_limits) + ) def _get_stake_amount_considering_leverage(self, stake_amount: float, leverage: float) -> float: """ diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 8c9d83a96..0325e6204 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -496,6 +496,9 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None: result = exchange.get_max_pair_stake_amount('ETH/BTC', 2) assert result == 1000 + result = exchange.get_max_pair_stake_amount('ETH/BTC', 2, 12.0) + assert result == 1000 / 12 + markets["ETH/BTC"]["contractSize"] = '0.01' default_conf['trading_mode'] = 'futures' default_conf['margin_mode'] = 'isolated' From a3acdd52406b33e83dc17498b7366f300ee1c054 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 2 Apr 2023 17:10:04 +0200 Subject: [PATCH 27/40] apply stop-reserve to minimum limits only when necessary it's unnecessary for amount - but necessary for Cost / price limits. --- freqtrade/exchange/exchange.py | 26 +++++++++++++++----------- tests/exchange/test_exchange.py | 6 +++--- tests/test_freqtradebot.py | 2 +- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 1c37ad638..6c236106f 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -788,25 +788,29 @@ class Exchange: except KeyError: raise ValueError(f"Can't get market information for symbol {pair}") - # reserve some percent defined in config (5% default) + stoploss - amount_reserve_percent = 1.0 + self._config.get('amount_reserve_percent', - DEFAULT_AMOUNT_RESERVE_PERCENT) - amount_reserve_percent = ( - amount_reserve_percent / (1 - abs(stoploss)) if abs(stoploss) != 1 else 1.5 - ) - # it should not be more than 50% - amount_reserve_percent = max(min(amount_reserve_percent, 1.5), 1) + if isMin: + # reserve some percent defined in config (5% default) + stoploss + margin_reserve: float = 1.0 + self._config.get('amount_reserve_percent', + DEFAULT_AMOUNT_RESERVE_PERCENT) + stoploss_reserve = ( + margin_reserve / (1 - abs(stoploss)) if abs(stoploss) != 1 else 1.5 + ) + # it should not be more than 50% + stoploss_reserve = max(min(stoploss_reserve, 1.5), 1) + else: + margin_reserve = 1.0 + stoploss_reserve = 1.0 stake_limits = [] limits = market['limits'] if (limits['cost'][limit] is not None): stake_limits.append( - self._contracts_to_amount(pair, limits['cost'][limit]) + self._contracts_to_amount(pair, limits['cost'][limit]) * stoploss_reserve ) if (limits['amount'][limit] is not None): stake_limits.append( - self._contracts_to_amount(pair, limits['amount'][limit] * price) + self._contracts_to_amount(pair, limits['amount'][limit]) * price * margin_reserve ) if not stake_limits: @@ -816,7 +820,7 @@ class Exchange: # for cost (quote, stake currency), so max() is used here. # See also #2575 at github. return self._get_stake_amount_considering_leverage( - (max(stake_limits) * amount_reserve_percent) if isMin else min(stake_limits), + max(stake_limits) if isMin else min(stake_limits), leverage or 1.0 ) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 0325e6204..fcc3dd4f8 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -437,7 +437,7 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None: } mocker.patch(f'{EXMS}.markets', PropertyMock(return_value=markets)) result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss) - expected_result = 2 * 2 * (1 + 0.05) / (1 - abs(stoploss)) + expected_result = 2 * 2 * (1 + 0.05) assert pytest.approx(result) == expected_result # With Leverage result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 5.0) @@ -446,14 +446,14 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None: result = exchange.get_max_pair_stake_amount('ETH/BTC', 2) assert result == 20000 - # min amount and cost are set (cost is minimal) + # min amount and cost are set (cost is minimal and therefore ignored) markets["ETH/BTC"]["limits"] = { 'cost': {'min': 2, 'max': None}, 'amount': {'min': 2, 'max': None}, } mocker.patch(f'{EXMS}.markets', PropertyMock(return_value=markets)) result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss) - expected_result = max(2, 2 * 2) * (1 + 0.05) / (1 - abs(stoploss)) + expected_result = max(2, 2 * 2) * (1 + 0.05) assert pytest.approx(result) == expected_result # With Leverage result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 10) diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 5dc3a993c..ab5dd4af5 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -356,7 +356,7 @@ def test_create_trade_no_stake_amount(default_conf_usdt, ticker_usdt, fee, mocke @pytest.mark.parametrize("is_short", [False, True]) @pytest.mark.parametrize('stake_amount,create,amount_enough,max_open_trades', [ (5.0, True, True, 99), - (0.049, True, False, 99), # Amount will be adjusted to min - which is 0.051 + (0.042, True, False, 99), # Amount will be adjusted to min - which is 0.051 (0, False, True, 99), (UNLIMITED_STAKE_AMOUNT, False, True, 0), ]) From 372f1cb37f4beaa4f0cd115177b08cbf0dd74200 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 2 Apr 2023 17:29:32 +0200 Subject: [PATCH 28/40] Reduce verbosity for stop orders --- freqtrade/freqtradebot.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index bd281bc79..af4f42feb 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -1783,7 +1783,8 @@ class FreqtradeBot(LoggingMixin): return False # Update trade with order values - logger.info(f'Found open order for {trade}') + if not stoploss_order: + logger.info(f'Found open order for {trade}') try: order = action_order or self.exchange.fetch_order_or_stoploss_order(order_id, trade.pair, From c9b904eb0e047bacdb8f717a21c3f9eeafcd63be Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 3 Apr 2023 06:49:30 +0200 Subject: [PATCH 29/40] Fix typos in documentation --- docs/freqai-feature-engineering.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/freqai-feature-engineering.md b/docs/freqai-feature-engineering.md index 1ca25d15c..05c6db523 100644 --- a/docs/freqai-feature-engineering.md +++ b/docs/freqai-feature-engineering.md @@ -6,8 +6,8 @@ Low level feature engineering is performed in the user strategy within a set of | Function | Description | |---------------|-------------| -| `feature_engineering__expand_all()` | This optional function will automatically expand the defined features on the config defined `indicator_periods_candles`, `include_timeframes`, `include_shifted_candles`, and `include_corr_pairs`. -| `feature_engineering__expand_basic()` | This optional function will automatically expand the defined features on the config defined `include_timeframes`, `include_shifted_candles`, and `include_corr_pairs`. Note: this function does *not* expand across `include_periods_candles`. +| `feature_engineering_expand_all()` | This optional function will automatically expand the defined features on the config defined `indicator_periods_candles`, `include_timeframes`, `include_shifted_candles`, and `include_corr_pairs`. +| `feature_engineering_expand_basic()` | This optional function will automatically expand the defined features on the config defined `include_timeframes`, `include_shifted_candles`, and `include_corr_pairs`. Note: this function does *not* expand across `include_periods_candles`. | `feature_engineering_standard()` | This optional function will be called once with the dataframe of the base timeframe. This is the final function to be called, which means that the dataframe entering this function will contain all the features and columns from the base asset created by the other `feature_engineering_expand` functions. This function is a good place to do custom exotic feature extractions (e.g. tsfresh). This function is also a good place for any feature that should not be auto-expanded upon (e.g., day of the week). | `set_freqai_targets()` | Required function to set the targets for the model. All targets must be prepended with `&` to be recognized by the FreqAI internals. From 43496d79290cef622d59591d3fd2edae533bb0d3 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 3 Apr 2023 09:46:32 +0200 Subject: [PATCH 30/40] bump sqlalchemy pre-commit --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4784055a9..ca8609982 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -18,7 +18,7 @@ repos: - types-requests==2.28.11.16 - types-tabulate==0.9.0.1 - types-python-dateutil==2.8.19.10 - - SQLAlchemy==2.0.7 + - SQLAlchemy==2.0.8 # stages: [push] - repo: https://github.com/pycqa/isort From 8236bbfd482b070649a52031b467ad5466a35bb6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 07:48:43 +0000 Subject: [PATCH 31/40] Bump orjson from 3.8.8 to 3.8.9 Bumps [orjson](https://github.com/ijl/orjson) from 3.8.8 to 3.8.9. - [Release notes](https://github.com/ijl/orjson/releases) - [Changelog](https://github.com/ijl/orjson/blob/master/CHANGELOG.md) - [Commits](https://github.com/ijl/orjson/compare/3.8.8...3.8.9) --- updated-dependencies: - dependency-name: orjson dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 9129f0c2b..8899ed7e6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -28,7 +28,7 @@ py_find_1st==1.1.5 # Load ticker files 30% faster python-rapidjson==1.10 # Properly format api responses -orjson==3.8.8 +orjson==3.8.9 # Notify systemd sdnotify==0.3.2 From bf7936b0af51e1eaf58145a256bddb60c2129730 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 07:48:50 +0000 Subject: [PATCH 32/40] Bump plotly from 5.13.1 to 5.14.0 Bumps [plotly](https://github.com/plotly/plotly.py) from 5.13.1 to 5.14.0. - [Release notes](https://github.com/plotly/plotly.py/releases) - [Changelog](https://github.com/plotly/plotly.py/blob/master/CHANGELOG.md) - [Commits](https://github.com/plotly/plotly.py/compare/v5.13.1...v5.14.0) --- updated-dependencies: - dependency-name: plotly dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- requirements-plot.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-plot.txt b/requirements-plot.txt index ad7bade95..d87219c42 100644 --- a/requirements-plot.txt +++ b/requirements-plot.txt @@ -1,4 +1,4 @@ # Include all requirements to run the bot. -r requirements.txt -plotly==5.13.1 +plotly==5.14.0 From 2bd2058afa97ed0c889e08fe6ee366812bfa4a7e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 07:49:12 +0000 Subject: [PATCH 33/40] Bump ruff from 0.0.259 to 0.0.260 Bumps [ruff](https://github.com/charliermarsh/ruff) from 0.0.259 to 0.0.260. - [Release notes](https://github.com/charliermarsh/ruff/releases) - [Changelog](https://github.com/charliermarsh/ruff/blob/main/BREAKING_CHANGES.md) - [Commits](https://github.com/charliermarsh/ruff/compare/v0.0.259...v0.0.260) --- updated-dependencies: - dependency-name: ruff dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index e43048cb9..04cf5c7c9 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,7 +7,7 @@ -r docs/requirements-docs.txt coveralls==3.3.1 -ruff==0.0.259 +ruff==0.0.260 mypy==1.1.1 pre-commit==3.2.1 pytest==7.2.2 From 7779b82277c0be0495ac90b444c5718fe76aecfd Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 07:49:18 +0000 Subject: [PATCH 34/40] Bump tensorboard from 2.12.0 to 2.12.1 Bumps [tensorboard](https://github.com/tensorflow/tensorboard) from 2.12.0 to 2.12.1. - [Release notes](https://github.com/tensorflow/tensorboard/releases) - [Changelog](https://github.com/tensorflow/tensorboard/blob/2.12.1/RELEASE.md) - [Commits](https://github.com/tensorflow/tensorboard/compare/2.12.0...2.12.1) --- updated-dependencies: - dependency-name: tensorboard dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-freqai.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-freqai.txt b/requirements-freqai.txt index e3a811661..840598d23 100644 --- a/requirements-freqai.txt +++ b/requirements-freqai.txt @@ -8,4 +8,4 @@ joblib==1.2.0 catboost==1.1.1; platform_machine != 'aarch64' and 'arm' not in platform_machine and python_version < '3.11' lightgbm==3.3.5 xgboost==1.7.5 -tensorboard==2.12.0 +tensorboard==2.12.1 From 57deaad8065b7bd43a84e6f9cdfa9eae0a763ca0 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 07:49:21 +0000 Subject: [PATCH 35/40] Bump types-requests from 2.28.11.16 to 2.28.11.17 Bumps [types-requests](https://github.com/python/typeshed) from 2.28.11.16 to 2.28.11.17. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-requests dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index e43048cb9..b7a1b4062 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -27,6 +27,6 @@ nbconvert==7.2.10 # mypy types types-cachetools==5.3.0.5 types-filelock==3.2.7 -types-requests==2.28.11.16 +types-requests==2.28.11.17 types-tabulate==0.9.0.1 types-python-dateutil==2.8.19.10 From ff40ee655bbf87bc31fa5f054bffb51cbd4f8a84 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 07:49:24 +0000 Subject: [PATCH 36/40] Bump types-python-dateutil from 2.8.19.10 to 2.8.19.11 Bumps [types-python-dateutil](https://github.com/python/typeshed) from 2.8.19.10 to 2.8.19.11. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-python-dateutil dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index e43048cb9..95b1d2558 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -29,4 +29,4 @@ types-cachetools==5.3.0.5 types-filelock==3.2.7 types-requests==2.28.11.16 types-tabulate==0.9.0.1 -types-python-dateutil==2.8.19.10 +types-python-dateutil==2.8.19.11 From b48498f27fe8bb9fae12127d9696fdec2a3e7a70 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 3 Apr 2023 10:16:56 +0200 Subject: [PATCH 37/40] Types pre-commit --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 142d8d50d..425b862c9 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,7 +15,7 @@ repos: additional_dependencies: - types-cachetools==5.3.0.5 - types-filelock==3.2.7 - - types-requests==2.28.11.16 + - types-requests==2.28.11.17 - types-tabulate==0.9.0.1 - types-python-dateutil==2.8.19.10 - SQLAlchemy==2.0.7 From b96f6670e3845336f2c744f9f0fdb7e57ff79218 Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 3 Apr 2023 13:28:17 +0200 Subject: [PATCH 38/40] pre-commit dateutil --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 142d8d50d..a15c53dc2 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -17,7 +17,7 @@ repos: - types-filelock==3.2.7 - types-requests==2.28.11.16 - types-tabulate==0.9.0.1 - - types-python-dateutil==2.8.19.10 + - types-python-dateutil==2.8.19.11 - SQLAlchemy==2.0.7 # stages: [push] From 30fc24bd8c39805d729092cbe4917b9384c1ce93 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 3 Apr 2023 12:18:15 +0000 Subject: [PATCH 39/40] Bump types-tabulate from 0.9.0.1 to 0.9.0.2 Bumps [types-tabulate](https://github.com/python/typeshed) from 0.9.0.1 to 0.9.0.2. - [Release notes](https://github.com/python/typeshed/releases) - [Commits](https://github.com/python/typeshed/commits) --- updated-dependencies: - dependency-name: types-tabulate dependency-type: direct:development update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- requirements-dev.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements-dev.txt b/requirements-dev.txt index eec8f81b6..f36ef6def 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -28,5 +28,5 @@ nbconvert==7.2.10 types-cachetools==5.3.0.5 types-filelock==3.2.7 types-requests==2.28.11.17 -types-tabulate==0.9.0.1 +types-tabulate==0.9.0.2 types-python-dateutil==2.8.19.11 From 7fed0782d5df67e9e7a98a5c18d5c221ea8cb12a Mon Sep 17 00:00:00 2001 From: Matthias Date: Mon, 3 Apr 2023 14:19:11 +0200 Subject: [PATCH 40/40] pre-commit types-tabulate --- .pre-commit-config.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1f6594e6a..a5ac69ff4 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -16,7 +16,7 @@ repos: - types-cachetools==5.3.0.5 - types-filelock==3.2.7 - types-requests==2.28.11.17 - - types-tabulate==0.9.0.1 + - types-tabulate==0.9.0.2 - types-python-dateutil==2.8.19.11 - SQLAlchemy==2.0.8 # stages: [push]