Merge pull request #2695 from freqtrade/custom_pairlock

Improve pairlocking mechanism to allow usage from within strategy
This commit is contained in:
hroff-1902 2019-12-22 15:03:24 +03:00 committed by GitHub
commit 98eed4f2ed
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 72 additions and 6 deletions

View File

@ -455,6 +455,51 @@ Sample return value: ETH/BTC had 5 trades, with a total profit of 1.5% (ratio of
!!! Warning !!! Warning
Trade history is not available during backtesting or hyperopt. 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 <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 ### Print created dataframe
To inspect the created dataframe, you can issue a print-statement in either `populate_buy_trend()` or `populate_sell_trend()`. 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). 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 ### Specify custom strategy location
If you want to use a strategy from a different directory you can pass `--strategy-path` If you want to use a strategy from a different directory you can pass `--strategy-path`

View File

@ -168,12 +168,25 @@ class IStrategy(ABC):
""" """
Locks pair until a given timestamp happens. Locks pair until a given timestamp happens.
Locked pairs are not analyzed, and are prevented from opening new trades. 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 pair: Pair to lock
:param until: datetime in UTC until the pair should be blocked from opening new trades. :param until: datetime in UTC until the pair should be blocked from opening new trades.
Needs to be timezone aware `datetime.now(timezone.utc)` Needs to be timezone aware `datetime.now(timezone.utc)`
""" """
if pair not in self._pair_locked_until or self._pair_locked_until[pair] < until:
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: def is_pair_locked(self, pair: str) -> bool:
""" """
Checks if a pair is currently locked Checks if a pair is currently locked

View File

@ -302,6 +302,19 @@ def test_is_pair_locked(default_conf):
# ETH/BTC locked for 4 minutes # ETH/BTC locked for 4 minutes
assert strategy.is_pair_locked(pair) 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 # XRP/BTC should not be locked now
pair = 'XRP/BTC' pair = 'XRP/BTC'
assert not strategy.is_pair_locked(pair) 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)