Introduce .analyze() function for Strategy
Fixing a few tests along the way
This commit is contained in:
parent
95f3ac08d4
commit
273aaaff12
@ -151,6 +151,8 @@ class FreqtradeBot:
|
||||
self.dataprovider.refresh(self.pairlists.create_pair_list(self.active_pair_whitelist),
|
||||
self.strategy.informative_pairs())
|
||||
|
||||
self.strategy.analyze(self.active_pair_whitelist)
|
||||
|
||||
with self._sell_lock:
|
||||
# Check and handle any timed out open orders
|
||||
self.check_handle_timedout()
|
||||
@ -420,9 +422,7 @@ class FreqtradeBot:
|
||||
return False
|
||||
|
||||
# running get_signal on historical data fetched
|
||||
(buy, sell) = self.strategy.get_signal(
|
||||
pair, self.strategy.timeframe,
|
||||
self.dataprovider.ohlcv(pair, self.strategy.timeframe))
|
||||
(buy, sell) = self.strategy.get_signal(pair, self.strategy.timeframe)
|
||||
|
||||
if buy and not sell:
|
||||
stake_amount = self.get_trade_stake_amount(pair)
|
||||
@ -697,9 +697,7 @@ class FreqtradeBot:
|
||||
|
||||
if (config_ask_strategy.get('use_sell_signal', True) or
|
||||
config_ask_strategy.get('ignore_roi_if_buy_signal', False)):
|
||||
(buy, sell) = self.strategy.get_signal(
|
||||
trade.pair, self.strategy.timeframe,
|
||||
self.dataprovider.ohlcv(trade.pair, self.strategy.timeframe))
|
||||
(buy, sell) = self.strategy.get_signal(trade.pair, self.strategy.timeframe)
|
||||
|
||||
if config_ask_strategy.get('use_order_book', False):
|
||||
order_book_min = config_ask_strategy.get('order_book_min', 1)
|
||||
|
@ -7,20 +7,19 @@ import warnings
|
||||
from abc import ABC, abstractmethod
|
||||
from datetime import datetime, timezone
|
||||
from enum import Enum
|
||||
from typing import Dict, NamedTuple, Optional, Tuple
|
||||
from typing import Dict, List, NamedTuple, Optional, Tuple
|
||||
|
||||
import arrow
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.constants import ListPairsWithTimeframes
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.exceptions import StrategyError
|
||||
from freqtrade.exchange import timeframe_to_minutes
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper
|
||||
from freqtrade.constants import ListPairsWithTimeframes
|
||||
from freqtrade.wallets import Wallets
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -289,6 +288,38 @@ class IStrategy(ABC):
|
||||
|
||||
return dataframe
|
||||
|
||||
def analyze_pair(self, pair: str):
|
||||
"""
|
||||
Fetch data for this pair from dataprovider and analyze.
|
||||
Stores the dataframe into the dataprovider.
|
||||
The analyzed dataframe is then accessible via `dp.get_analyzed_dataframe()`.
|
||||
:param pair: Pair to analyze.
|
||||
"""
|
||||
dataframe = self.dp.ohlcv(pair, self.timeframe)
|
||||
if not isinstance(dataframe, DataFrame) or dataframe.empty:
|
||||
logger.warning('Empty candle (OHLCV) data for pair %s', pair)
|
||||
return
|
||||
|
||||
try:
|
||||
df_len, df_close, df_date = self.preserve_df(dataframe)
|
||||
|
||||
dataframe = strategy_safe_wrapper(
|
||||
self._analyze_ticker_internal, message=""
|
||||
)(dataframe, {'pair': pair})
|
||||
|
||||
self.assert_df(dataframe, df_len, df_close, df_date)
|
||||
except StrategyError as error:
|
||||
logger.warning(f"Unable to analyze candle (OHLCV) data for pair {pair}: {error}")
|
||||
return
|
||||
|
||||
if dataframe.empty:
|
||||
logger.warning('Empty dataframe for pair %s', pair)
|
||||
return
|
||||
|
||||
def analyze(self, pairs: List[str]):
|
||||
for pair in pairs:
|
||||
self.analyze_pair(pair)
|
||||
|
||||
@staticmethod
|
||||
def preserve_df(dataframe: DataFrame) -> Tuple[int, float, datetime]:
|
||||
""" keep some data for dataframes """
|
||||
@ -309,30 +340,22 @@ class IStrategy(ABC):
|
||||
else:
|
||||
raise StrategyError(f"Dataframe returned from strategy has mismatching {message}.")
|
||||
|
||||
def get_signal(self, pair: str, interval: str, dataframe: DataFrame) -> Tuple[bool, bool]:
|
||||
def get_signal(self, pair: str, timeframe: str) -> Tuple[bool, bool]:
|
||||
"""
|
||||
Calculates current signal based several technical analysis indicators
|
||||
Used by Bot to get the latest signal
|
||||
:param pair: pair in format ANT/BTC
|
||||
:param interval: Interval to use (in min)
|
||||
:param timeframe: timeframe to use
|
||||
:param dataframe: Dataframe to analyze
|
||||
:return: (Buy, Sell) A bool-tuple indicating buy/sell signal
|
||||
"""
|
||||
|
||||
dataframe, _ = self.dp.get_analyzed_dataframe(pair, timeframe)
|
||||
|
||||
if not isinstance(dataframe, DataFrame) or dataframe.empty:
|
||||
logger.warning('Empty candle (OHLCV) data for pair %s', pair)
|
||||
return False, False
|
||||
|
||||
try:
|
||||
df_len, df_close, df_date = self.preserve_df(dataframe)
|
||||
dataframe = strategy_safe_wrapper(
|
||||
self._analyze_ticker_internal, message=""
|
||||
)(dataframe, {'pair': pair})
|
||||
self.assert_df(dataframe, df_len, df_close, df_date)
|
||||
except StrategyError as error:
|
||||
logger.warning(f"Unable to analyze candle (OHLCV) data for pair {pair}: {error}")
|
||||
|
||||
return False, False
|
||||
|
||||
if dataframe.empty:
|
||||
logger.warning('Empty dataframe for pair %s', pair)
|
||||
return False, False
|
||||
@ -343,9 +366,9 @@ class IStrategy(ABC):
|
||||
latest_date = arrow.get(latest_date)
|
||||
|
||||
# Check if dataframe is out of date
|
||||
interval_minutes = timeframe_to_minutes(interval)
|
||||
timeframe_minutes = timeframe_to_minutes(timeframe)
|
||||
offset = self.config.get('exchange', {}).get('outdated_offset', 5)
|
||||
if latest_date < (arrow.utcnow().shift(minutes=-(interval_minutes * 2 + offset))):
|
||||
if latest_date < (arrow.utcnow().shift(minutes=-(timeframe_minutes * 2 + offset))):
|
||||
logger.warning(
|
||||
'Outdated history for pair %s. Last tick is %s minutes old',
|
||||
pair,
|
||||
|
@ -163,7 +163,7 @@ def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False)) -> None:
|
||||
:param value: which value IStrategy.get_signal() must return
|
||||
:return: None
|
||||
"""
|
||||
freqtrade.strategy.get_signal = lambda e, s, t: value
|
||||
freqtrade.strategy.get_signal = lambda e, s: value
|
||||
freqtrade.exchange.refresh_latest_ohlcv = lambda p: None
|
||||
|
||||
|
||||
|
@ -912,6 +912,7 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None:
|
||||
refresh_latest_ohlcv=refresh_mock,
|
||||
)
|
||||
inf_pairs = MagicMock(return_value=[("BTC/ETH", '1m'), ("ETH/USDT", "1h")])
|
||||
mocker.patch('freqtrade.strategy.interface.IStrategy.get_signal', return_value=(False, False))
|
||||
mocker.patch('time.sleep', return_value=None)
|
||||
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
|
Loading…
Reference in New Issue
Block a user