From 12fabba7841552092b49d3b9f88c6b65fe73b9c8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jan 2022 03:01:12 +0000 Subject: [PATCH 1/5] Bump pytest-asyncio from 0.17.1 to 0.17.2 Bumps [pytest-asyncio](https://github.com/pytest-dev/pytest-asyncio) from 0.17.1 to 0.17.2. - [Release notes](https://github.com/pytest-dev/pytest-asyncio/releases) - [Commits](https://github.com/pytest-dev/pytest-asyncio/compare/v0.17.1...v0.17.2) --- updated-dependencies: - dependency-name: pytest-asyncio 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 7773ff01a..ae9b68d74 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -8,7 +8,7 @@ flake8==4.0.1 flake8-tidy-imports==4.6.0 mypy==0.931 pytest==6.2.5 -pytest-asyncio==0.17.1 +pytest-asyncio==0.17.2 pytest-cov==3.0.0 pytest-mock==3.6.1 pytest-random-order==1.0.4 From 95c7d4868455a17204d980b9649332ae8157f20e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Jan 2022 03:01:22 +0000 Subject: [PATCH 2/5] Bump sqlalchemy from 1.4.29 to 1.4.31 Bumps [sqlalchemy](https://github.com/sqlalchemy/sqlalchemy) from 1.4.29 to 1.4.31. - [Release notes](https://github.com/sqlalchemy/sqlalchemy/releases) - [Changelog](https://github.com/sqlalchemy/sqlalchemy/blob/main/CHANGES) - [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 ddbda38ad..1cc6db7a4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,7 +7,7 @@ ccxt==1.68.20 # Pin cryptography for now due to rust build errors with piwheels cryptography==36.0.1 aiohttp==3.8.1 -SQLAlchemy==1.4.29 +SQLAlchemy==1.4.31 python-telegram-bot==13.10 arrow==1.2.1 cachetools==4.2.2 From e252830229e536328c0d5bdf3afc13ceb4d04ffb Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 23 Jan 2022 19:32:38 +0100 Subject: [PATCH 3/5] Add entry_tag to "entry" callbacks --- docs/strategy-callbacks.md | 9 +++++---- freqtrade/freqtradebot.py | 10 ++++++---- freqtrade/optimize/backtesting.py | 13 ++++++++----- freqtrade/strategy/interface.py | 10 +++++++--- .../subtemplates/strategy_methods_advanced.j2 | 7 +++++-- 5 files changed, 31 insertions(+), 18 deletions(-) diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index 5e03776c7..e83fee46f 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -362,8 +362,8 @@ class AwesomeStrategy(IStrategy): # ... populate_* methods - def custom_entry_price(self, pair: str, current_time: datetime, - proposed_rate, **kwargs) -> float: + def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float, + entry_tag: Optional[str], **kwargs) -> float: dataframe, last_updated = self.dp.get_analyzed_dataframe(pair=pair, timeframe=self.timeframe) @@ -502,7 +502,8 @@ class AwesomeStrategy(IStrategy): # ... populate_* methods def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, - time_in_force: str, current_time: datetime, **kwargs) -> bool: + time_in_force: str, current_time: datetime, entry_tag: Optional[str], + **kwargs) -> bool: """ Called right before placing a buy order. Timing for this function is critical, so avoid doing heavy computations or @@ -620,7 +621,7 @@ class DigDeeperStrategy(IStrategy): # This is called when placing the initial order (opening trade) def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, proposed_stake: float, min_stake: float, max_stake: float, - **kwargs) -> float: + entry_tag: Optional[str], **kwargs) -> float: # We need to leave most of the funds for possible further DCA orders # This also applies to fixed stakes diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index e8f24864f..972b6f6b7 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -531,7 +531,7 @@ class FreqtradeBot(LoggingMixin): pos_adjust = trade is not None enter_limit_requested, stake_amount = self.get_valid_enter_price_and_stake( - pair, price, stake_amount, trade) + pair, price, stake_amount, buy_tag, trade) if not stake_amount: return False @@ -550,7 +550,8 @@ class FreqtradeBot(LoggingMixin): if not pos_adjust and not strategy_safe_wrapper( self.strategy.confirm_trade_entry, default_retval=True)( pair=pair, order_type=order_type, amount=amount, rate=enter_limit_requested, - time_in_force=time_in_force, current_time=datetime.now(timezone.utc)): + time_in_force=time_in_force, current_time=datetime.now(timezone.utc), + entry_tag=buy_tag): logger.info(f"User requested abortion of buying {pair}") return False amount = self.exchange.amount_to_precision(pair, amount) @@ -660,6 +661,7 @@ class FreqtradeBot(LoggingMixin): def get_valid_enter_price_and_stake( self, pair: str, price: Optional[float], stake_amount: float, + entry_tag: Optional[str], trade: Optional[Trade]) -> Tuple[float, float]: if price: enter_limit_requested = price @@ -669,7 +671,7 @@ class FreqtradeBot(LoggingMixin): custom_entry_price = strategy_safe_wrapper(self.strategy.custom_entry_price, default_retval=proposed_enter_rate)( pair=pair, current_time=datetime.now(timezone.utc), - proposed_rate=proposed_enter_rate) + proposed_rate=proposed_enter_rate, entry_tag=entry_tag) enter_limit_requested = self.get_valid_price(custom_entry_price, proposed_enter_rate) if not enter_limit_requested: @@ -682,7 +684,7 @@ class FreqtradeBot(LoggingMixin): default_retval=stake_amount)( pair=pair, current_time=datetime.now(timezone.utc), current_rate=enter_limit_requested, proposed_stake=stake_amount, - min_stake=min_stake_amount, max_stake=max_stake_amount) + min_stake=min_stake_amount, max_stake=max_stake_amount, entry_tag=entry_tag) stake_amount = self.wallets.validate_stake_amount(pair, stake_amount, min_stake_amount) return enter_limit_requested, stake_amount diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 8d5dc77d6..8e52a62fa 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -462,12 +462,14 @@ class Backtesting: def _enter_trade(self, pair: str, row: Tuple, stake_amount: Optional[float] = None, trade: Optional[LocalTrade] = None) -> Optional[LocalTrade]: + current_time = row[DATE_IDX].to_pydatetime() + entry_tag = row[BUY_TAG_IDX] if len(row) >= BUY_TAG_IDX + 1 else None # let's call the custom entry price, using the open price as default price propose_rate = strategy_safe_wrapper(self.strategy.custom_entry_price, default_retval=row[OPEN_IDX])( pair=pair, current_time=current_time, - proposed_rate=row[OPEN_IDX]) # default value is the open rate + proposed_rate=row[OPEN_IDX], entry_tag=entry_tag) # default value is the open rate # Move rate to within the candle's low/high rate propose_rate = min(max(propose_rate, row[LOW_IDX]), row[HIGH_IDX]) @@ -485,7 +487,8 @@ class Backtesting: stake_amount = strategy_safe_wrapper(self.strategy.custom_stake_amount, default_retval=stake_amount)( pair=pair, current_time=current_time, current_rate=propose_rate, - proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=max_stake_amount) + proposed_stake=stake_amount, min_stake=min_stake_amount, max_stake=max_stake_amount, + entry_tag=entry_tag) stake_amount = self.wallets.validate_stake_amount(pair, stake_amount, min_stake_amount) @@ -500,14 +503,14 @@ class Backtesting: if not pos_adjust: if not strategy_safe_wrapper(self.strategy.confirm_trade_entry, default_retval=True)( pair=pair, order_type=order_type, amount=stake_amount, rate=propose_rate, - time_in_force=time_in_force, current_time=current_time): + time_in_force=time_in_force, current_time=current_time, + entry_tag=entry_tag): return None if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount): amount = round(stake_amount / propose_rate, 8) if trade is None: # Enter trade - has_buy_tag = len(row) >= BUY_TAG_IDX + 1 trade = LocalTrade( pair=pair, open_rate=propose_rate, @@ -517,7 +520,7 @@ class Backtesting: fee_open=self.fee, fee_close=self.fee, is_open=True, - buy_tag=row[BUY_TAG_IDX] if has_buy_tag else None, + buy_tag=entry_tag, exchange='backtesting', orders=[] ) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 12755d6bc..0ce53c5cc 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -238,7 +238,8 @@ class IStrategy(ABC, HyperStrategyMixin): return False def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, - time_in_force: str, current_time: datetime, **kwargs) -> bool: + time_in_force: str, current_time: datetime, entry_tag: Optional[str], + **kwargs) -> bool: """ Called right before placing a buy order. Timing for this function is critical, so avoid doing heavy computations or @@ -254,6 +255,7 @@ class IStrategy(ABC, HyperStrategyMixin): :param rate: Rate that's going to be used when using limit orders :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). :param current_time: datetime object, containing the current datetime + :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return bool: When True is returned, then the buy-order is placed on the exchange. False aborts the process @@ -311,7 +313,7 @@ class IStrategy(ABC, HyperStrategyMixin): return self.stoploss def custom_entry_price(self, pair: str, current_time: datetime, proposed_rate: float, - **kwargs) -> float: + entry_tag: Optional[str], **kwargs) -> float: """ Custom entry price logic, returning the new entry price. @@ -322,6 +324,7 @@ class IStrategy(ABC, HyperStrategyMixin): :param pair: Pair that's currently analyzed :param current_time: datetime object, containing the current datetime :param proposed_rate: Rate, calculated based on pricing settings in ask_strategy. + :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return float: New entry price value if provided """ @@ -373,7 +376,7 @@ class IStrategy(ABC, HyperStrategyMixin): def custom_stake_amount(self, pair: str, current_time: datetime, current_rate: float, proposed_stake: float, min_stake: float, max_stake: float, - **kwargs) -> float: + entry_tag: Optional[str], **kwargs) -> float: """ Customize stake size for each new trade. This method is not called when edge module is enabled. @@ -384,6 +387,7 @@ class IStrategy(ABC, HyperStrategyMixin): :param proposed_stake: A stake amount proposed by the bot. :param min_stake: Minimal stake size allowed by exchange. :param max_stake: Balance available for trading. + :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :return: A stake size, which is between min_stake and max_stake. """ return proposed_stake diff --git a/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 index fb467ecaa..aed57fd20 100644 --- a/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 +++ b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 @@ -14,7 +14,7 @@ def bot_loop_start(self, **kwargs) -> None: def custom_stake_amount(self, pair: str, current_time: 'datetime', current_rate: float, proposed_stake: float, min_stake: float, max_stake: float, - **kwargs) -> float: + entry_tag: Optional[str], **kwargs) -> float: """ Customize stake size for each new trade. This method is not called when edge module is enabled. @@ -25,6 +25,7 @@ def custom_stake_amount(self, pair: str, current_time: 'datetime', current_rate: :param proposed_stake: A stake amount proposed by the bot. :param min_stake: Minimal stake size allowed by exchange. :param max_stake: Balance available for trading. + :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :return: A stake size, which is between min_stake and max_stake. """ return proposed_stake @@ -78,7 +79,8 @@ def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', curre return None def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, - time_in_force: str, current_time: 'datetime', **kwargs) -> bool: + time_in_force: str, current_time: 'datetime', entry_tag: Optional[str], + **kwargs) -> bool: """ Called right before placing a buy order. Timing for this function is critical, so avoid doing heavy computations or @@ -94,6 +96,7 @@ def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: f :param rate: Rate that's going to be used when using limit orders :param time_in_force: Time in force. Defaults to GTC (Good-til-cancelled). :param current_time: datetime object, containing the current datetime + :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. :return bool: When True is returned, then the buy-order is placed on the exchange. False aborts the process From 194a5ce3cca52e3944af19ece509382efa29e8f6 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 23 Jan 2022 19:40:12 +0100 Subject: [PATCH 4/5] Update advanced strategy template with missing methods --- freqtrade/strategy/interface.py | 1 + .../subtemplates/strategy_methods_advanced.j2 | 65 ++++++++++++++++++- 2 files changed, 64 insertions(+), 2 deletions(-) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 0ce53c5cc..6f139026e 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -398,6 +398,7 @@ class IStrategy(ABC, HyperStrategyMixin): """ Custom trade adjustment logic, returning the stake amount that a trade should be increased. This means extra buy orders with additional fees. + Only called when `position_adjustment_enable` is set to True. For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/ diff --git a/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 index aed57fd20..db12094ed 100644 --- a/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 +++ b/freqtrade/templates/subtemplates/strategy_methods_advanced.j2 @@ -12,9 +12,47 @@ def bot_loop_start(self, **kwargs) -> None: """ pass +def custom_entry_price(self, pair: str, current_time: 'datetime', proposed_rate: float, + entry_tag: 'Optional[str]', **kwargs) -> float: + """ + Custom entry price logic, returning the new entry price. + + For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/ + + When not implemented by a strategy, returns None, orderbook is used to set entry price + + :param pair: Pair that's currently analyzed + :param current_time: datetime object, containing the current datetime + :param proposed_rate: Rate, calculated based on pricing settings in ask_strategy. + :param entry_tag: Optional entry_tag (buy_tag) if provided with the buy signal. + :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. + :return float: New entry price value if provided + """ + return proposed_rate + +def custom_exit_price(self, pair: str, trade: 'Trade', + current_time: 'datetime', proposed_rate: float, + current_profit: float, **kwargs) -> float: + """ + Custom exit price logic, returning the new exit price. + + For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/ + + When not implemented by a strategy, returns None, orderbook is used to set exit price + + :param pair: Pair that's currently analyzed + :param trade: trade object. + :param current_time: datetime object, containing the current datetime + :param proposed_rate: Rate, calculated based on pricing settings in ask_strategy. + :param current_profit: Current profit (as ratio), calculated based on current_rate. + :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. + :return float: New exit price value if provided + """ + return proposed_rate + def custom_stake_amount(self, pair: str, current_time: 'datetime', current_rate: float, proposed_stake: float, min_stake: float, max_stake: float, - entry_tag: Optional[str], **kwargs) -> float: + entry_tag: 'Optional[str]', **kwargs) -> float: """ Customize stake size for each new trade. This method is not called when edge module is enabled. @@ -79,7 +117,7 @@ def custom_sell(self, pair: str, trade: 'Trade', current_time: 'datetime', curre return None def confirm_trade_entry(self, pair: str, order_type: str, amount: float, rate: float, - time_in_force: str, current_time: 'datetime', entry_tag: Optional[str], + time_in_force: str, current_time: 'datetime', entry_tag: 'Optional[str]', **kwargs) -> bool: """ Called right before placing a buy order. @@ -170,3 +208,26 @@ def check_sell_timeout(self, pair: str, trade: 'Trade', order: dict, **kwargs) - :return bool: When True is returned, then the sell-order is cancelled. """ return False + +def adjust_trade_position(self, trade: 'Trade', current_time: 'datetime', + current_rate: float, current_profit: float, min_stake: float, + max_stake: float, **kwargs) -> 'Optional[float]': + """ + Custom trade adjustment logic, returning the stake amount that a trade should be increased. + This means extra buy orders with additional fees. + Only called when `position_adjustment_enable` is set to True. + + For full documentation please go to https://www.freqtrade.io/en/latest/strategy-advanced/ + + When not implemented by a strategy, returns None + + :param trade: trade object. + :param current_time: datetime object, containing the current datetime + :param current_rate: Current buy rate. + :param current_profit: Current profit (as ratio), calculated based on current_rate. + :param min_stake: Minimal stake size allowed by exchange. + :param max_stake: Balance available for trading. + :param **kwargs: Ensure to keep this here so updates to this won't break your strategy. + :return float: Stake amount to adjust your trade + """ + return None From 381bda1e4a5c12e099e9d799021fca0d8fab96c9 Mon Sep 17 00:00:00 2001 From: Matthias Date: Sun, 23 Jan 2022 19:43:41 +0100 Subject: [PATCH 5/5] Update test to add new argument --- tests/strategy/test_default_strategy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/strategy/test_default_strategy.py b/tests/strategy/test_default_strategy.py index 6426ebe5f..a2d7df720 100644 --- a/tests/strategy/test_default_strategy.py +++ b/tests/strategy/test_default_strategy.py @@ -37,7 +37,7 @@ def test_strategy_test_v2(result, fee): assert strategy.confirm_trade_entry(pair='ETH/BTC', order_type='limit', amount=0.1, rate=20000, time_in_force='gtc', - current_time=datetime.utcnow()) is True + current_time=datetime.utcnow(), entry_tag=None) is True assert strategy.confirm_trade_exit(pair='ETH/BTC', trade=trade, order_type='limit', amount=0.1, rate=20000, time_in_force='gtc', sell_reason='roi', current_time=datetime.utcnow()) is True