Lock pair until a new candle arrives
This commit is contained in:
parent
8b767eedfd
commit
c272944834
@ -433,7 +433,9 @@ class FreqtradeBot:
|
||||
"""
|
||||
logger.debug(f"create_trade for pair {pair}")
|
||||
|
||||
if self.strategy.is_pair_locked(pair):
|
||||
analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(pair, self.strategy.timeframe)
|
||||
if self.strategy.is_pair_locked(
|
||||
pair, analyzed_df.iloc[-1]['date'] if len(analyzed_df) > 0 else None):
|
||||
logger.info(f"Pair {pair} is currently locked.")
|
||||
return False
|
||||
|
||||
@ -444,7 +446,6 @@ class FreqtradeBot:
|
||||
return False
|
||||
|
||||
# running get_signal on historical data fetched
|
||||
analyzed_df, _ = self.dataprovider.get_analyzed_dataframe(pair, self.strategy.timeframe)
|
||||
(buy, sell) = self.strategy.get_signal(pair, self.strategy.timeframe, analyzed_df)
|
||||
|
||||
if buy and not sell:
|
||||
|
@ -2,6 +2,7 @@
|
||||
IStrategy interface
|
||||
This module defines the interface to apply for strategies
|
||||
"""
|
||||
from freqtrade.exchange.exchange import timeframe_to_next_date
|
||||
import logging
|
||||
import warnings
|
||||
from abc import ABC, abstractmethod
|
||||
@ -297,13 +298,22 @@ class IStrategy(ABC):
|
||||
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, candle_date: datetime = None) -> bool:
|
||||
"""
|
||||
Checks if a pair is currently locked
|
||||
"""
|
||||
if pair not in self._pair_locked_until:
|
||||
return False
|
||||
return self._pair_locked_until[pair] >= datetime.now(timezone.utc)
|
||||
if not candle_date:
|
||||
return self._pair_locked_until[pair] >= datetime.now(timezone.utc)
|
||||
else:
|
||||
# Locking should happen until a new candle arrives
|
||||
lock_time = timeframe_to_next_date(self.timeframe, candle_date)
|
||||
# lock_time = candle_date + timedelta(minutes=timeframe_to_minutes(self.timeframe))
|
||||
res = self._pair_locked_until[pair] > lock_time
|
||||
logger.debug(f"pair time = {lock_time} - pair_lock = {self._pair_locked_until[pair]} "
|
||||
f"- res: {res}")
|
||||
return res
|
||||
|
||||
def analyze_ticker(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
@ -438,12 +448,6 @@ class IStrategy(ABC):
|
||||
)
|
||||
return False, False
|
||||
|
||||
# Check if dataframe has new candle
|
||||
if (arrow.utcnow() - latest_date).total_seconds() // 60 >= timeframe_minutes:
|
||||
logger.warning('Old candle for pair %s. Last candle is %s minutes old',
|
||||
pair, int((arrow.utcnow() - latest_date).total_seconds() // 60))
|
||||
return False, False
|
||||
|
||||
(buy, sell) = latest[SignalType.BUY.value] == 1, latest[SignalType.SELL.value] == 1
|
||||
logger.debug('trigger: %s (pair=%s) buy=%s sell=%s',
|
||||
latest['date'], pair, str(buy), str(sell))
|
||||
|
@ -1,6 +1,7 @@
|
||||
# pragma pylint: disable=missing-docstring, C0103
|
||||
|
||||
import logging
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import arrow
|
||||
@ -8,12 +9,12 @@ import pytest
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.data.history import load_data
|
||||
from freqtrade.exceptions import StrategyError
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.resolvers import StrategyResolver
|
||||
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from tests.conftest import log_has, log_has_re
|
||||
|
||||
from .strats.default_strategy import DefaultStrategy
|
||||
@ -87,21 +88,6 @@ def test_get_signal_exception_valueerror(default_conf, mocker, caplog, ohlcv_his
|
||||
assert log_has_re(r'Strategy caused the following exception: xyz.*', caplog)
|
||||
|
||||
|
||||
def test_get_signal_old_candle(default_conf, mocker, caplog, ohlcv_history):
|
||||
caplog.set_level(logging.INFO)
|
||||
# default_conf defines a 5m interval. we check interval of previous candle
|
||||
# this is necessary as the last candle is removed (partial candles) by default
|
||||
oldtime = arrow.utcnow().shift(minutes=-10)
|
||||
ticks = DataFrame([{'buy': 1, 'date': oldtime}])
|
||||
mocker.patch.object(
|
||||
_STRATEGY, '_analyze_ticker_internal',
|
||||
return_value=DataFrame(ticks)
|
||||
)
|
||||
assert (False, False) == _STRATEGY.get_signal('xyz', default_conf['timeframe'],
|
||||
ohlcv_history)
|
||||
assert log_has('Old candle for pair xyz. Last candle is 10 minutes old', caplog)
|
||||
|
||||
|
||||
def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history):
|
||||
# default_conf defines a 5m interval. we check interval * 2 + 5m
|
||||
# this is necessary as the last candle is removed (partial candles) by default
|
||||
@ -402,6 +388,31 @@ def test_is_pair_locked(default_conf):
|
||||
strategy.unlock_pair(pair)
|
||||
assert not strategy.is_pair_locked(pair)
|
||||
|
||||
pair = 'BTC/USDT'
|
||||
# Lock until 14:30
|
||||
lock_time = datetime(2020, 5, 1, 14, 30, 0, tzinfo=timezone.utc)
|
||||
strategy.lock_pair(pair, lock_time)
|
||||
# Lock is in the past ...
|
||||
assert not strategy.is_pair_locked(pair)
|
||||
# latest candle is from 14:20, lock goes to 14:30
|
||||
assert strategy.is_pair_locked(pair, lock_time + timedelta(minutes=-10))
|
||||
assert strategy.is_pair_locked(pair, lock_time + timedelta(minutes=-50))
|
||||
|
||||
# latest candle is from 14:25 (lock should be lifted)
|
||||
# Since this is the "new candle" available at 14:30
|
||||
assert not strategy.is_pair_locked(pair, lock_time + timedelta(minutes=-4))
|
||||
|
||||
# Should not be locked after time expired
|
||||
assert not strategy.is_pair_locked(pair, lock_time + timedelta(minutes=10))
|
||||
|
||||
# Change timeframe to 15m
|
||||
strategy.timeframe = '15m'
|
||||
# Candle from 14:14 - lock goes until 14:30
|
||||
assert strategy.is_pair_locked(pair, lock_time + timedelta(minutes=-16))
|
||||
assert strategy.is_pair_locked(pair, lock_time + timedelta(minutes=-15, seconds=-2))
|
||||
# Candle from 14:15 - lock goes until 14:30
|
||||
assert not strategy.is_pair_locked(pair, lock_time + timedelta(minutes=-15))
|
||||
|
||||
|
||||
def test_is_informative_pairs_callback(default_conf):
|
||||
default_conf.update({'strategy': 'TestStrategyLegacy'})
|
||||
|
Loading…
Reference in New Issue
Block a user