diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index 4efca7d2f..d59b097d7 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -455,6 +455,51 @@ Sample return value: ETH/BTC had 5 trades, with a total profit of 1.5% (ratio of !!! Warning Trade history is not available during backtesting or hyperopt. +### Prevent trades from happening for a specific pair + +Freqtrade locks pairs automatically for the current candle (until that candle is over) when a pair is sold, preventing an immediate re-buy of that pair. + +Locked pairs will show the message `Pair is currently locked.`. + +#### Locking pairs from within the strategy + +Sometimes it may be desired to lock a pair after certain events happen (e.g. multiple losing trades in a row). + +Freqtrade has an easy method to do this from within the strategy, by calling `self.lock_pair(pair, until)`. +`until` must be a datetime object in the future, after which trading will be reenabled for that pair. + +Locks can also be lifted manually, by calling `self.unlock_pair(pair)`. + +To verify if a pair is currently locked, use `self.is_pair_locked(pair)`. + +!!! Note + Locked pairs are not persisted, so a restart of the bot, or calling `/reload_conf` will reset locked pairs. + +!!! Warning + Locking pairs is not functioning during backtesting. + +##### Pair locking example + +``` python +from freqtrade.persistence import Trade +from datetime import timedelta, datetime, timezone +# Put the above lines a the top of the strategy file, next to all the other imports +# -------- + +# Within populate indicators (or populate_buy): +if self.config['runmode'] in ('live', 'dry_run'): + # fetch closed trades for the last 2 days + trades = Trade.get_trades([Trade.pair == metadata['pair'], + Trade.open_date > datetime.utcnow() - timedelta(days=2), + Trade.is_open == False, + ]).all() + # Analyze the conditions you'd like to lock the pair .... will probably be different for every strategy + sumprofit = sum(trade.close_profit for trade in trades) + if sumprofit < 0: + # Lock pair for 12 hours + self.lock_pair(metadata['pair'], until=datetime.now(timezone.utc) + timedelta(hours=12)) +``` + ### Print created dataframe To inspect the created dataframe, you can issue a print-statement in either `populate_buy_trend()` or `populate_sell_trend()`. @@ -479,11 +524,6 @@ def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame: Printing more than a few rows is also possible (simply use `print(dataframe)` instead of `print(dataframe.tail())`), however not recommended, as that will be very verbose (~500 lines per pair every 5 seconds). -### Where can i find a strategy template? - -The strategy template is located in the file -[user_data/strategies/sample_strategy.py](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/templates/sample_strategy.py). - ### Specify custom strategy location If you want to use a strategy from a different directory you can pass `--strategy-path` diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 985ff37de..4f2e990d2 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -168,11 +168,24 @@ class IStrategy(ABC): """ Locks pair until a given timestamp happens. Locked pairs are not analyzed, and are prevented from opening new trades. + Locks can only count up (allowing users to lock pairs for a longer period of time). + To remove a lock from a pair, use `unlock_pair()` :param pair: Pair to lock :param until: datetime in UTC until the pair should be blocked from opening new trades. Needs to be timezone aware `datetime.now(timezone.utc)` """ - self._pair_locked_until[pair] = until + if pair not in self._pair_locked_until or self._pair_locked_until[pair] < until: + self._pair_locked_until[pair] = until + + def unlock_pair(self, pair) -> None: + """ + Unlocks a pair previously locked using lock_pair. + Not used by freqtrade itself, but intended to be used if users lock pairs + manually from within the strategy, to allow an easy way to unlock pairs. + :param pair: Unlock pair to allow trading again + """ + if pair in self._pair_locked_until: + del self._pair_locked_until[pair] def is_pair_locked(self, pair: str) -> bool: """ diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 605622b8f..89c38bda1 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -302,6 +302,19 @@ def test_is_pair_locked(default_conf): # ETH/BTC locked for 4 minutes assert strategy.is_pair_locked(pair) + # Test lock does not change + lock = strategy._pair_locked_until[pair] + strategy.lock_pair(pair, arrow.utcnow().shift(minutes=2).datetime) + assert lock == strategy._pair_locked_until[pair] + # XRP/BTC should not be locked now pair = 'XRP/BTC' assert not strategy.is_pair_locked(pair) + + # Unlocking a pair that's not locked should not raise an error + strategy.unlock_pair(pair) + + # Unlock original pair + pair = 'ETH/BTC' + strategy.unlock_pair(pair) + assert not strategy.is_pair_locked(pair)