mirror of
https://github.com/freqtrade/freqtrade.git
synced 2024-11-10 10:21:59 +00:00
refactor Analyze class methods to base Strategy class
This commit is contained in:
parent
f6b8c2b40f
commit
aeb4102bcb
|
@ -2,18 +2,8 @@
|
|||
Functions to analyze ticker data with indicators and produce buy and sell signals
|
||||
"""
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
import arrow
|
||||
from pandas import DataFrame, to_datetime
|
||||
|
||||
from freqtrade import constants
|
||||
from freqtrade.exchange import Exchange
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.strategy.resolver import IStrategy
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
|
@ -41,191 +31,3 @@ def parse_ticker_dataframe(ticker: list) -> DataFrame:
|
|||
})
|
||||
frame.drop(frame.tail(1).index, inplace=True) # eliminate partial candle
|
||||
return frame
|
||||
|
||||
|
||||
class SignalType(Enum):
|
||||
"""
|
||||
Enum to distinguish between buy and sell signals
|
||||
"""
|
||||
BUY = "buy"
|
||||
SELL = "sell"
|
||||
|
||||
|
||||
class Analyze(object):
|
||||
"""
|
||||
Analyze class contains everything the bot need to determine if the situation is good for
|
||||
buying or selling.
|
||||
"""
|
||||
def __init__(self, config: dict, strategy: IStrategy) -> None:
|
||||
"""
|
||||
Init Analyze
|
||||
:param config: Bot configuration (use the one from Configuration())
|
||||
"""
|
||||
self.config = config
|
||||
self.strategy = strategy
|
||||
|
||||
def analyze_ticker(self, ticker_history: List[Dict]) -> DataFrame:
|
||||
"""
|
||||
Parses the given ticker history and returns a populated DataFrame
|
||||
add several TA indicators and buy signal to it
|
||||
:return DataFrame with ticker data and indicator data
|
||||
"""
|
||||
dataframe = parse_ticker_dataframe(ticker_history)
|
||||
dataframe = self.strategy.populate_indicators(dataframe)
|
||||
dataframe = self.strategy.populate_buy_trend(dataframe)
|
||||
dataframe = self.strategy.populate_sell_trend(dataframe)
|
||||
return dataframe
|
||||
|
||||
def get_signal(self, exchange: Exchange, pair: str, interval: str) -> Tuple[bool, bool]:
|
||||
"""
|
||||
Calculates current signal based several technical analysis indicators
|
||||
:param pair: pair in format ANT/BTC
|
||||
:param interval: Interval to use (in min)
|
||||
:return: (Buy, Sell) A bool-tuple indicating buy/sell signal
|
||||
"""
|
||||
ticker_hist = exchange.get_ticker_history(pair, interval)
|
||||
if not ticker_hist:
|
||||
logger.warning('Empty ticker history for pair %s', pair)
|
||||
return False, False
|
||||
|
||||
try:
|
||||
dataframe = self.analyze_ticker(ticker_hist)
|
||||
except ValueError as error:
|
||||
logger.warning(
|
||||
'Unable to analyze ticker for pair %s: %s',
|
||||
pair,
|
||||
str(error)
|
||||
)
|
||||
return False, False
|
||||
except Exception as error:
|
||||
logger.exception(
|
||||
'Unexpected error when analyzing ticker for pair %s: %s',
|
||||
pair,
|
||||
str(error)
|
||||
)
|
||||
return False, False
|
||||
|
||||
if dataframe.empty:
|
||||
logger.warning('Empty dataframe for pair %s', pair)
|
||||
return False, False
|
||||
|
||||
latest = dataframe.iloc[-1]
|
||||
|
||||
# Check if dataframe is out of date
|
||||
signal_date = arrow.get(latest['date'])
|
||||
interval_minutes = constants.TICKER_INTERVAL_MINUTES[interval]
|
||||
if signal_date < (arrow.utcnow().shift(minutes=-(interval_minutes * 2 + 5))):
|
||||
logger.warning(
|
||||
'Outdated history for pair %s. Last tick is %s minutes old',
|
||||
pair,
|
||||
(arrow.utcnow() - signal_date).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)
|
||||
)
|
||||
return buy, sell
|
||||
|
||||
def should_sell(self, trade: Trade, rate: float, date: datetime, buy: bool, sell: bool) -> bool:
|
||||
"""
|
||||
This function evaluate if on the condition required to trigger a sell has been reached
|
||||
if the threshold is reached and updates the trade record.
|
||||
:return: True if trade should be sold, False otherwise
|
||||
"""
|
||||
current_profit = trade.calc_profit_percent(rate)
|
||||
if self.stop_loss_reached(current_rate=rate, trade=trade, current_time=date,
|
||||
current_profit=current_profit):
|
||||
return True
|
||||
|
||||
experimental = self.config.get('experimental', {})
|
||||
|
||||
if buy and experimental.get('ignore_roi_if_buy_signal', False):
|
||||
logger.debug('Buy signal still active - not selling.')
|
||||
return False
|
||||
|
||||
# Check if minimal roi has been reached and no longer in buy conditions (avoiding a fee)
|
||||
if self.min_roi_reached(trade=trade, current_profit=current_profit, current_time=date):
|
||||
logger.debug('Required profit reached. Selling..')
|
||||
return True
|
||||
|
||||
if experimental.get('sell_profit_only', False):
|
||||
logger.debug('Checking if trade is profitable..')
|
||||
if trade.calc_profit(rate=rate) <= 0:
|
||||
return False
|
||||
if sell and not buy and experimental.get('use_sell_signal', False):
|
||||
logger.debug('Sell signal received. Selling..')
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def stop_loss_reached(self, current_rate: float, trade: Trade, current_time: datetime,
|
||||
current_profit: float) -> bool:
|
||||
"""
|
||||
Based on current profit of the trade and configured (trailing) stoploss,
|
||||
decides to sell or not
|
||||
"""
|
||||
|
||||
trailing_stop = self.config.get('trailing_stop', False)
|
||||
|
||||
trade.adjust_stop_loss(trade.open_rate, self.strategy.stoploss, initial=True)
|
||||
|
||||
# evaluate if the stoploss was hit
|
||||
if self.strategy.stoploss is not None and trade.stop_loss >= current_rate:
|
||||
|
||||
if trailing_stop:
|
||||
logger.debug(
|
||||
f"HIT STOP: current price at {current_rate:.6f}, "
|
||||
f"stop loss is {trade.stop_loss:.6f}, "
|
||||
f"initial stop loss was at {trade.initial_stop_loss:.6f}, "
|
||||
f"trade opened at {trade.open_rate:.6f}")
|
||||
logger.debug(f"trailing stop saved {trade.stop_loss - trade.initial_stop_loss:.6f}")
|
||||
|
||||
logger.debug('Stop loss hit.')
|
||||
return True
|
||||
|
||||
# update the stop loss afterwards, after all by definition it's supposed to be hanging
|
||||
if trailing_stop:
|
||||
|
||||
# check if we have a special stop loss for positive condition
|
||||
# and if profit is positive
|
||||
stop_loss_value = self.strategy.stoploss
|
||||
if 'trailing_stop_positive' in self.config and current_profit > 0:
|
||||
|
||||
# Ignore mypy error check in configuration that this is a float
|
||||
stop_loss_value = self.config.get('trailing_stop_positive') # type: ignore
|
||||
logger.debug(f"using positive stop loss mode: {stop_loss_value} "
|
||||
f"since we have profit {current_profit}")
|
||||
|
||||
trade.adjust_stop_loss(current_rate, stop_loss_value)
|
||||
|
||||
return False
|
||||
|
||||
def min_roi_reached(self, trade: Trade, current_profit: float, current_time: datetime) -> bool:
|
||||
"""
|
||||
Based an earlier trade and current price and ROI configuration, decides whether bot should
|
||||
sell
|
||||
:return True if bot should sell at current rate
|
||||
"""
|
||||
|
||||
# Check if time matches and current rate is above threshold
|
||||
time_diff = (current_time.timestamp() - trade.open_date.timestamp()) / 60
|
||||
for duration, threshold in self.strategy.minimal_roi.items():
|
||||
if time_diff <= duration:
|
||||
return False
|
||||
if current_profit > threshold:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def tickerdata_to_dataframe(self, tickerdata: Dict[str, List]) -> Dict[str, DataFrame]:
|
||||
"""
|
||||
Creates a dataframe and populates indicators for given ticker data
|
||||
"""
|
||||
return {pair: self.strategy.populate_indicators(parse_ticker_dataframe(pair_data))
|
||||
for pair, pair_data in tickerdata.items()}
|
||||
|
|
|
@ -15,7 +15,6 @@ from cachetools import TTLCache, cached
|
|||
|
||||
from freqtrade import (DependencyException, OperationalException,
|
||||
TemporaryError, __version__, constants, persistence)
|
||||
from freqtrade.analyze import Analyze
|
||||
from freqtrade.exchange import Exchange
|
||||
from freqtrade.fiat_convert import CryptoToFiatConverter
|
||||
from freqtrade.persistence import Trade
|
||||
|
@ -51,7 +50,7 @@ class FreqtradeBot(object):
|
|||
# Init objects
|
||||
self.config = config
|
||||
self.strategy: IStrategy = StrategyResolver(self.config).strategy
|
||||
self.analyze = Analyze(self.config, self.strategy)
|
||||
# self.analyze = Analyze(self.config, self.strategy)
|
||||
self.fiat_converter = CryptoToFiatConverter()
|
||||
self.rpc: RPCManager = RPCManager(self)
|
||||
self.persistence = None
|
||||
|
@ -330,7 +329,7 @@ class FreqtradeBot(object):
|
|||
|
||||
# Pick pair based on buy signals
|
||||
for _pair in whitelist:
|
||||
(buy, sell) = self.analyze.get_signal(self.exchange, _pair, interval)
|
||||
(buy, sell) = self.strategy.get_signal(self.exchange, _pair, interval)
|
||||
if buy and not sell:
|
||||
return self.execute_buy(_pair, stake_amount)
|
||||
return False
|
||||
|
@ -500,10 +499,10 @@ class FreqtradeBot(object):
|
|||
(buy, sell) = (False, False)
|
||||
experimental = self.config.get('experimental', {})
|
||||
if experimental.get('use_sell_signal') or experimental.get('ignore_roi_if_buy_signal'):
|
||||
(buy, sell) = self.analyze.get_signal(self.exchange,
|
||||
trade.pair, self.strategy.ticker_interval)
|
||||
(buy, sell) = self.strategy.get_signal(self.exchange,
|
||||
trade.pair, self.strategy.ticker_interval)
|
||||
|
||||
if self.analyze.should_sell(trade, current_rate, datetime.utcnow(), buy, sell):
|
||||
if self.strategy.should_sell(trade, current_rate, datetime.utcnow(), buy, sell):
|
||||
self.execute_sell(trade, current_rate)
|
||||
return True
|
||||
logger.info('Found no sell signals for whitelisted currencies. Trying again..')
|
||||
|
|
|
@ -15,7 +15,6 @@ from tabulate import tabulate
|
|||
|
||||
import freqtrade.optimize as optimize
|
||||
from freqtrade import DependencyException, constants
|
||||
from freqtrade.analyze import Analyze
|
||||
from freqtrade.arguments import Arguments
|
||||
from freqtrade.configuration import Configuration
|
||||
from freqtrade.exchange import Exchange
|
||||
|
@ -54,9 +53,8 @@ class Backtesting(object):
|
|||
def __init__(self, config: Dict[str, Any]) -> None:
|
||||
self.config = config
|
||||
self.strategy: IStrategy = StrategyResolver(self.config).strategy
|
||||
self.analyze = Analyze(self.config, self.strategy)
|
||||
self.ticker_interval = self.analyze.strategy.ticker_interval
|
||||
self.tickerdata_to_dataframe = self.analyze.tickerdata_to_dataframe
|
||||
self.ticker_interval = self.strategy.ticker_interval
|
||||
self.tickerdata_to_dataframe = self.strategy.tickerdata_to_dataframe
|
||||
self.populate_buy_trend = self.strategy.populate_buy_trend
|
||||
self.populate_sell_trend = self.strategy.populate_sell_trend
|
||||
|
||||
|
@ -153,8 +151,8 @@ class Backtesting(object):
|
|||
trade_count_lock[sell_row.date] = trade_count_lock.get(sell_row.date, 0) + 1
|
||||
|
||||
buy_signal = sell_row.buy
|
||||
if self.analyze.should_sell(trade, sell_row.open, sell_row.date, buy_signal,
|
||||
sell_row.sell):
|
||||
if self.strategy.should_sell(trade, sell_row.open, sell_row.date, buy_signal,
|
||||
sell_row.sell):
|
||||
|
||||
return BacktestResult(pair=pair,
|
||||
profit_percent=trade.calc_profit_percent(rate=sell_row.open),
|
||||
|
|
|
@ -267,13 +267,13 @@ class Hyperopt(Backtesting):
|
|||
params = self.get_args(_params)
|
||||
|
||||
if self.has_space('roi'):
|
||||
self.analyze.strategy.minimal_roi = self.generate_roi_table(params)
|
||||
self.strategy.minimal_roi = self.generate_roi_table(params)
|
||||
|
||||
if self.has_space('buy'):
|
||||
self.populate_buy_trend = self.buy_strategy_generator(params)
|
||||
|
||||
if self.has_space('stoploss'):
|
||||
self.analyze.strategy.stoploss = params['stoploss']
|
||||
self.strategy.stoploss = params['stoploss']
|
||||
|
||||
processed = load(TICKERDATA_PICKLE)
|
||||
results = self.backtest(
|
||||
|
@ -351,7 +351,7 @@ class Hyperopt(Backtesting):
|
|||
)
|
||||
|
||||
if self.has_space('buy'):
|
||||
self.analyze.populate_indicators = Hyperopt.populate_indicators # type: ignore
|
||||
self.strategy.populate_indicators = Hyperopt.populate_indicators # type: ignore
|
||||
dump(self.tickerdata_to_dataframe(data), TICKERDATA_PICKLE)
|
||||
self.exchange = None # type: ignore
|
||||
self.load_previous_results()
|
||||
|
|
|
@ -7,7 +7,7 @@ from freqtrade.strategy.interface import IStrategy
|
|||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def import_strategy(strategy: IStrategy) -> IStrategy:
|
||||
def import_strategy(strategy: IStrategy, config: dict) -> IStrategy:
|
||||
"""
|
||||
Imports given Strategy instance to global scope
|
||||
of freqtrade.strategy and returns an instance of it
|
||||
|
@ -29,4 +29,4 @@ def import_strategy(strategy: IStrategy) -> IStrategy:
|
|||
# Modify global scope to declare class
|
||||
globals()[name] = clazz
|
||||
|
||||
return clazz()
|
||||
return clazz(config)
|
||||
|
|
|
@ -2,11 +2,30 @@
|
|||
IStrategy interface
|
||||
This module defines the interface to apply for strategies
|
||||
"""
|
||||
import logging
|
||||
from abc import ABC, abstractmethod
|
||||
from typing import Dict
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from typing import Dict, List, Tuple
|
||||
|
||||
import arrow
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade import constants
|
||||
from freqtrade.analyze import parse_ticker_dataframe
|
||||
from freqtrade.exchange import Exchange
|
||||
from freqtrade.persistence import Trade
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class SignalType(Enum):
|
||||
"""
|
||||
Enum to distinguish between buy and sell signals
|
||||
"""
|
||||
BUY = "buy"
|
||||
SELL = "sell"
|
||||
|
||||
|
||||
class IStrategy(ABC):
|
||||
"""
|
||||
|
@ -23,6 +42,9 @@ class IStrategy(ABC):
|
|||
stoploss: float
|
||||
ticker_interval: str
|
||||
|
||||
def __init__(self, config: dict):
|
||||
self.config = config
|
||||
|
||||
@abstractmethod
|
||||
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
|
||||
"""
|
||||
|
@ -46,3 +68,169 @@ class IStrategy(ABC):
|
|||
:param dataframe: DataFrame
|
||||
:return: DataFrame with sell column
|
||||
"""
|
||||
|
||||
def analyze_ticker(self, ticker_history: List[Dict]) -> DataFrame:
|
||||
"""
|
||||
Parses the given ticker history and returns a populated DataFrame
|
||||
add several TA indicators and buy signal to it
|
||||
:return DataFrame with ticker data and indicator data
|
||||
"""
|
||||
dataframe = parse_ticker_dataframe(ticker_history)
|
||||
dataframe = self.populate_indicators(dataframe)
|
||||
dataframe = self.populate_buy_trend(dataframe)
|
||||
dataframe = self.populate_sell_trend(dataframe)
|
||||
return dataframe
|
||||
|
||||
def get_signal(self, exchange: Exchange, pair: str, interval: str) -> Tuple[bool, bool]:
|
||||
"""
|
||||
Calculates current signal based several technical analysis indicators
|
||||
:param pair: pair in format ANT/BTC
|
||||
:param interval: Interval to use (in min)
|
||||
:return: (Buy, Sell) A bool-tuple indicating buy/sell signal
|
||||
"""
|
||||
ticker_hist = exchange.get_ticker_history(pair, interval)
|
||||
if not ticker_hist:
|
||||
logger.warning('Empty ticker history for pair %s', pair)
|
||||
return False, False
|
||||
|
||||
try:
|
||||
dataframe = self.analyze_ticker(ticker_hist)
|
||||
except ValueError as error:
|
||||
logger.warning(
|
||||
'Unable to analyze ticker for pair %s: %s',
|
||||
pair,
|
||||
str(error)
|
||||
)
|
||||
return False, False
|
||||
except Exception as error:
|
||||
logger.exception(
|
||||
'Unexpected error when analyzing ticker for pair %s: %s',
|
||||
pair,
|
||||
str(error)
|
||||
)
|
||||
return False, False
|
||||
|
||||
if dataframe.empty:
|
||||
logger.warning('Empty dataframe for pair %s', pair)
|
||||
return False, False
|
||||
|
||||
latest = dataframe.iloc[-1]
|
||||
|
||||
# Check if dataframe is out of date
|
||||
signal_date = arrow.get(latest['date'])
|
||||
interval_minutes = constants.TICKER_INTERVAL_MINUTES[interval]
|
||||
if signal_date < (arrow.utcnow().shift(minutes=-(interval_minutes * 2 + 5))):
|
||||
logger.warning(
|
||||
'Outdated history for pair %s. Last tick is %s minutes old',
|
||||
pair,
|
||||
(arrow.utcnow() - signal_date).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)
|
||||
)
|
||||
return buy, sell
|
||||
|
||||
def should_sell(self, trade: Trade, rate: float, date: datetime, buy: bool, sell: bool) -> bool:
|
||||
"""
|
||||
This function evaluate if on the condition required to trigger a sell has been reached
|
||||
if the threshold is reached and updates the trade record.
|
||||
:return: True if trade should be sold, False otherwise
|
||||
"""
|
||||
current_profit = trade.calc_profit_percent(rate)
|
||||
if self.stop_loss_reached(current_rate=rate, trade=trade, current_time=date,
|
||||
current_profit=current_profit):
|
||||
return True
|
||||
|
||||
experimental = self.config.get('experimental', {})
|
||||
|
||||
if buy and experimental.get('ignore_roi_if_buy_signal', False):
|
||||
logger.debug('Buy signal still active - not selling.')
|
||||
return False
|
||||
|
||||
# Check if minimal roi has been reached and no longer in buy conditions (avoiding a fee)
|
||||
if self.min_roi_reached(trade=trade, current_profit=current_profit, current_time=date):
|
||||
logger.debug('Required profit reached. Selling..')
|
||||
return True
|
||||
|
||||
if experimental.get('sell_profit_only', False):
|
||||
logger.debug('Checking if trade is profitable..')
|
||||
if trade.calc_profit(rate=rate) <= 0:
|
||||
return False
|
||||
if sell and not buy and experimental.get('use_sell_signal', False):
|
||||
logger.debug('Sell signal received. Selling..')
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def stop_loss_reached(self, current_rate: float, trade: Trade, current_time: datetime,
|
||||
current_profit: float) -> bool:
|
||||
"""
|
||||
Based on current profit of the trade and configured (trailing) stoploss,
|
||||
decides to sell or not
|
||||
"""
|
||||
|
||||
trailing_stop = self.config.get('trailing_stop', False)
|
||||
|
||||
trade.adjust_stop_loss(trade.open_rate, self.stoploss, initial=True)
|
||||
|
||||
# evaluate if the stoploss was hit
|
||||
if self.stoploss is not None and trade.stop_loss >= current_rate:
|
||||
|
||||
if trailing_stop:
|
||||
logger.debug(
|
||||
f"HIT STOP: current price at {current_rate:.6f}, "
|
||||
f"stop loss is {trade.stop_loss:.6f}, "
|
||||
f"initial stop loss was at {trade.initial_stop_loss:.6f}, "
|
||||
f"trade opened at {trade.open_rate:.6f}")
|
||||
logger.debug(f"trailing stop saved {trade.stop_loss - trade.initial_stop_loss:.6f}")
|
||||
|
||||
logger.debug('Stop loss hit.')
|
||||
return True
|
||||
|
||||
# update the stop loss afterwards, after all by definition it's supposed to be hanging
|
||||
if trailing_stop:
|
||||
|
||||
# check if we have a special stop loss for positive condition
|
||||
# and if profit is positive
|
||||
stop_loss_value = self.stoploss
|
||||
if 'trailing_stop_positive' in self.config and current_profit > 0:
|
||||
|
||||
# Ignore mypy error check in configuration that this is a float
|
||||
stop_loss_value = self.config.get('trailing_stop_positive') # type: ignore
|
||||
logger.debug(f"using positive stop loss mode: {stop_loss_value} "
|
||||
f"since we have profit {current_profit}")
|
||||
|
||||
trade.adjust_stop_loss(current_rate, stop_loss_value)
|
||||
|
||||
return False
|
||||
|
||||
def min_roi_reached(self, trade: Trade, current_profit: float, current_time: datetime) -> bool:
|
||||
"""
|
||||
Based an earlier trade and current price and ROI configuration, decides whether bot should
|
||||
sell
|
||||
:return True if bot should sell at current rate
|
||||
"""
|
||||
|
||||
# Check if time matches and current rate is above threshold
|
||||
time_diff = (current_time.timestamp() - trade.open_date.timestamp()) / 60
|
||||
for duration, threshold in self.minimal_roi.items():
|
||||
if time_diff <= duration:
|
||||
return False
|
||||
if current_profit > threshold:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def tickerdata_to_dataframe(self, tickerdata: Dict[str, List]) -> Dict[str, DataFrame]:
|
||||
"""
|
||||
Creates a dataframe and populates indicators for given ticker data
|
||||
"""
|
||||
return {pair: self.populate_indicators(parse_ticker_dataframe(pair_data))
|
||||
for pair, pair_data in tickerdata.items()}
|
||||
|
|
|
@ -34,6 +34,7 @@ class StrategyResolver(object):
|
|||
# Verify the strategy is in the configuration, otherwise fallback to the default strategy
|
||||
strategy_name = config.get('strategy') or constants.DEFAULT_STRATEGY
|
||||
self.strategy: IStrategy = self._load_strategy(strategy_name,
|
||||
config=config,
|
||||
extra_dir=config.get('strategy_path'))
|
||||
|
||||
# Set attributes
|
||||
|
@ -62,10 +63,11 @@ class StrategyResolver(object):
|
|||
self.strategy.stoploss = float(self.strategy.stoploss)
|
||||
|
||||
def _load_strategy(
|
||||
self, strategy_name: str, extra_dir: Optional[str] = None) -> IStrategy:
|
||||
self, strategy_name: str, config: dict, extra_dir: Optional[str] = None) -> IStrategy:
|
||||
"""
|
||||
Search and loads the specified strategy.
|
||||
:param strategy_name: name of the module to import
|
||||
:param config: configuration for the strategy
|
||||
:param extra_dir: additional directory to search for the given strategy
|
||||
:return: Strategy instance or None
|
||||
"""
|
||||
|
@ -81,10 +83,10 @@ class StrategyResolver(object):
|
|||
|
||||
for path in abs_paths:
|
||||
try:
|
||||
strategy = self._search_strategy(path, strategy_name)
|
||||
strategy = self._search_strategy(path, strategy_name=strategy_name, config=config)
|
||||
if strategy:
|
||||
logger.info('Using resolved strategy %s from \'%s\'', strategy_name, path)
|
||||
return import_strategy(strategy)
|
||||
return import_strategy(strategy, config=config)
|
||||
except FileNotFoundError:
|
||||
logger.warning('Path "%s" does not exist', path)
|
||||
|
||||
|
@ -114,7 +116,7 @@ class StrategyResolver(object):
|
|||
return next(valid_strategies_gen, None)
|
||||
|
||||
@staticmethod
|
||||
def _search_strategy(directory: str, strategy_name: str) -> Optional[IStrategy]:
|
||||
def _search_strategy(directory: str, strategy_name: str, config: dict) -> Optional[IStrategy]:
|
||||
"""
|
||||
Search for the strategy_name in the given directory
|
||||
:param directory: relative or absolute directory path
|
||||
|
@ -130,5 +132,5 @@ class StrategyResolver(object):
|
|||
os.path.abspath(os.path.join(directory, entry)), strategy_name
|
||||
)
|
||||
if strategy:
|
||||
return strategy()
|
||||
return strategy(config)
|
||||
return None
|
||||
|
|
|
@ -51,13 +51,13 @@ def get_patched_freqtradebot(mocker, config) -> FreqtradeBot:
|
|||
"""
|
||||
# mocker.patch('freqtrade.fiat_convert.Market', {'price_usd': 12345.0})
|
||||
patch_coinmarketcap(mocker, {'price_usd': 12345.0})
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
|
||||
# mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
|
||||
patch_exchange(mocker, None)
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager._init', MagicMock())
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager.send_msg', MagicMock())
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze.get_signal', MagicMock())
|
||||
# mocker.patch('freqtrade.freqtradebot.Analyze.get_signal', MagicMock())
|
||||
|
||||
return FreqtradeBot(config)
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ import pytest
|
|||
from arrow import Arrow
|
||||
|
||||
from freqtrade import DependencyException, constants, optimize
|
||||
from freqtrade.analyze import Analyze
|
||||
from freqtrade.arguments import Arguments, TimeRange
|
||||
from freqtrade.optimize.backtesting import (Backtesting, setup_configuration,
|
||||
start)
|
||||
|
@ -326,7 +325,6 @@ def test_backtesting_init(mocker, default_conf) -> None:
|
|||
get_fee = mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5))
|
||||
backtesting = Backtesting(default_conf)
|
||||
assert backtesting.config == default_conf
|
||||
assert isinstance(backtesting.analyze, Analyze)
|
||||
assert backtesting.ticker_interval == '5m'
|
||||
assert callable(backtesting.tickerdata_to_dataframe)
|
||||
assert callable(backtesting.populate_buy_trend)
|
||||
|
@ -348,9 +346,9 @@ def test_tickerdata_to_dataframe(default_conf, mocker) -> None:
|
|||
data = backtesting.tickerdata_to_dataframe(tickerlist)
|
||||
assert len(data['UNITTEST/BTC']) == 99
|
||||
|
||||
# Load Analyze to compare the result between Backtesting function and Analyze are the same
|
||||
analyze = Analyze(default_conf, DefaultStrategy())
|
||||
data2 = analyze.tickerdata_to_dataframe(tickerlist)
|
||||
# Load strategy to compare the result between Backtesting function and strategy are the same
|
||||
strategy = DefaultStrategy(default_conf)
|
||||
data2 = strategy.tickerdata_to_dataframe(tickerlist)
|
||||
assert data['UNITTEST/BTC'].equals(data2['UNITTEST/BTC'])
|
||||
|
||||
|
||||
|
@ -413,7 +411,6 @@ def test_backtesting_start(default_conf, mocker, caplog) -> None:
|
|||
def get_timeframe(input1, input2):
|
||||
return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59)
|
||||
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
|
||||
mocker.patch('freqtrade.optimize.load_data', mocked_load_data)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history')
|
||||
patch_exchange(mocker)
|
||||
|
@ -454,7 +451,6 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog) -> None:
|
|||
def get_timeframe(input1, input2):
|
||||
return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59)
|
||||
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
|
||||
mocker.patch('freqtrade.optimize.load_data', MagicMock(return_value={}))
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history')
|
||||
patch_exchange(mocker)
|
||||
|
|
|
@ -30,7 +30,6 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
|
|||
"""
|
||||
Test rpc_trade_status() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
|
@ -42,6 +41,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
|
|||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
rpc = RPC(freqtradebot)
|
||||
|
||||
freqtradebot.state = State.STOPPED
|
||||
|
@ -74,7 +74,6 @@ def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None:
|
|||
"""
|
||||
Test rpc_status_table() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
|
@ -86,6 +85,7 @@ def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None:
|
|||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
rpc = RPC(freqtradebot)
|
||||
|
||||
freqtradebot.state = State.STOPPED
|
||||
|
@ -108,7 +108,6 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee,
|
|||
"""
|
||||
Test rpc_daily_profit() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
|
@ -120,6 +119,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee,
|
|||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
stake_currency = default_conf['stake_currency']
|
||||
fiat_display_currency = default_conf['fiat_display_currency']
|
||||
|
||||
|
@ -160,7 +160,6 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
|||
"""
|
||||
Test rpc_trade_statistics() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.fiat_convert.Market',
|
||||
ticker=MagicMock(return_value={'price_usd': 15000.0}),
|
||||
|
@ -176,6 +175,7 @@ def test_rpc_trade_statistics(default_conf, ticker, ticker_sell_up, fee,
|
|||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
stake_currency = default_conf['stake_currency']
|
||||
fiat_display_currency = default_conf['fiat_display_currency']
|
||||
|
||||
|
@ -237,7 +237,6 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, markets,
|
|||
"""
|
||||
Test rpc_trade_statistics() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.fiat_convert.Market',
|
||||
ticker=MagicMock(return_value={'price_usd': 15000.0}),
|
||||
|
@ -253,6 +252,7 @@ def test_rpc_trade_statistics_closed(mocker, default_conf, ticker, fee, markets,
|
|||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
stake_currency = default_conf['stake_currency']
|
||||
fiat_display_currency = default_conf['fiat_display_currency']
|
||||
|
||||
|
@ -309,7 +309,6 @@ def test_rpc_balance_handle(default_conf, mocker):
|
|||
}
|
||||
}
|
||||
|
||||
patch_get_signal(mocker, (True, False))
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.fiat_convert.Market',
|
||||
ticker=MagicMock(return_value={'price_usd': 15000.0}),
|
||||
|
@ -323,6 +322,7 @@ def test_rpc_balance_handle(default_conf, mocker):
|
|||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
rpc = RPC(freqtradebot)
|
||||
|
||||
result = rpc._rpc_balance(default_conf['fiat_display_currency'])
|
||||
|
@ -342,7 +342,6 @@ def test_rpc_start(mocker, default_conf) -> None:
|
|||
"""
|
||||
Test rpc_start() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
|
@ -352,6 +351,7 @@ def test_rpc_start(mocker, default_conf) -> None:
|
|||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
rpc = RPC(freqtradebot)
|
||||
freqtradebot.state = State.STOPPED
|
||||
|
||||
|
@ -368,7 +368,6 @@ def test_rpc_stop(mocker, default_conf) -> None:
|
|||
"""
|
||||
Test rpc_stop() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
|
@ -378,6 +377,7 @@ def test_rpc_stop(mocker, default_conf) -> None:
|
|||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
rpc = RPC(freqtradebot)
|
||||
freqtradebot.state = State.RUNNING
|
||||
|
||||
|
@ -395,7 +395,6 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker, markets) -> None:
|
|||
"""
|
||||
Test rpc_forcesell() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
|
||||
|
@ -417,6 +416,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker, markets) -> None:
|
|||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
rpc = RPC(freqtradebot)
|
||||
|
||||
freqtradebot.state = State.STOPPED
|
||||
|
@ -499,7 +499,6 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
|||
"""
|
||||
Test rpc_performance() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
|
@ -512,6 +511,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
|||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
rpc = RPC(freqtradebot)
|
||||
|
||||
# Create some test data
|
||||
|
@ -538,7 +538,6 @@ def test_rpc_count(mocker, default_conf, ticker, fee, markets) -> None:
|
|||
"""
|
||||
Test rpc_count() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
|
@ -551,6 +550,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee, markets) -> None:
|
|||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
rpc = RPC(freqtradebot)
|
||||
|
||||
trades = rpc._rpc_count()
|
||||
|
|
|
@ -102,7 +102,6 @@ def test_authorized_only(default_conf, mocker, caplog) -> None:
|
|||
"""
|
||||
Test authorized_only() method when we are authorized
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
patch_exchange(mocker, None)
|
||||
|
||||
|
@ -112,7 +111,9 @@ def test_authorized_only(default_conf, mocker, caplog) -> None:
|
|||
|
||||
conf = deepcopy(default_conf)
|
||||
conf['telegram']['enabled'] = False
|
||||
dummy = DummyCls(FreqtradeBot(conf))
|
||||
bot = FreqtradeBot(conf)
|
||||
patch_get_signal(bot, (True, False))
|
||||
dummy = DummyCls(bot)
|
||||
dummy.dummy_handler(bot=MagicMock(), update=update)
|
||||
assert dummy.state['called'] is True
|
||||
assert log_has(
|
||||
|
@ -133,7 +134,6 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None:
|
|||
"""
|
||||
Test authorized_only() method when we are unauthorized
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
patch_exchange(mocker, None)
|
||||
chat = Chat(0xdeadbeef, 0)
|
||||
|
@ -142,7 +142,9 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None:
|
|||
|
||||
conf = deepcopy(default_conf)
|
||||
conf['telegram']['enabled'] = False
|
||||
dummy = DummyCls(FreqtradeBot(conf))
|
||||
bot = FreqtradeBot(conf)
|
||||
patch_get_signal(bot, (True, False))
|
||||
dummy = DummyCls(bot)
|
||||
dummy.dummy_handler(bot=MagicMock(), update=update)
|
||||
assert dummy.state['called'] is False
|
||||
assert not log_has(
|
||||
|
@ -163,7 +165,6 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None:
|
|||
"""
|
||||
Test authorized_only() method when an exception is thrown
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
patch_exchange(mocker)
|
||||
|
||||
|
@ -172,7 +173,11 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None:
|
|||
|
||||
conf = deepcopy(default_conf)
|
||||
conf['telegram']['enabled'] = False
|
||||
dummy = DummyCls(FreqtradeBot(conf))
|
||||
|
||||
bot = FreqtradeBot(conf)
|
||||
patch_get_signal(bot, (True, False))
|
||||
dummy = DummyCls(bot)
|
||||
|
||||
dummy.dummy_exception(bot=MagicMock(), update=update)
|
||||
assert dummy.state['called'] is False
|
||||
assert not log_has(
|
||||
|
@ -198,7 +203,6 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None:
|
|||
conf['telegram']['enabled'] = False
|
||||
conf['telegram']['chat_id'] = 123
|
||||
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
|
||||
mocker.patch.multiple(
|
||||
|
@ -233,6 +237,7 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None:
|
|||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
|
||||
freqtradebot = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
# Create some test data
|
||||
|
@ -252,7 +257,6 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No
|
|||
"""
|
||||
Test _status() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
|
@ -272,6 +276,8 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No
|
|||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
freqtradebot.state = State.STOPPED
|
||||
|
@ -299,7 +305,6 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker)
|
|||
"""
|
||||
Test _status_table() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
|
@ -320,6 +325,8 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker)
|
|||
conf = deepcopy(default_conf)
|
||||
conf['stake_amount'] = 15.0
|
||||
freqtradebot = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
freqtradebot.state = State.STOPPED
|
||||
|
@ -353,7 +360,6 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
|
|||
"""
|
||||
Test _daily() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||
mocker.patch(
|
||||
'freqtrade.fiat_convert.CryptoToFiatConverter._find_price',
|
||||
|
@ -375,6 +381,7 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
|
|||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
# Create some test data
|
||||
|
@ -427,7 +434,6 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None:
|
|||
"""
|
||||
Test _daily() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
|
@ -443,6 +449,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None:
|
|||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
# Try invalid data
|
||||
|
@ -466,7 +473,6 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
|
|||
"""
|
||||
Test _profit() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
mocker.patch.multiple(
|
||||
|
@ -485,6 +491,7 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
|
|||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
telegram._profit(bot=MagicMock(), update=update)
|
||||
|
@ -568,7 +575,6 @@ def test_telegram_balance_handle(default_conf, update, mocker) -> None:
|
|||
'last': 0.1,
|
||||
}
|
||||
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value=mock_balance)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker', side_effect=mock_ticker)
|
||||
|
@ -581,6 +587,8 @@ def test_telegram_balance_handle(default_conf, update, mocker) -> None:
|
|||
)
|
||||
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
telegram._balance(bot=MagicMock(), update=update)
|
||||
|
@ -598,7 +606,6 @@ def test_zero_balance_handle(default_conf, update, mocker) -> None:
|
|||
"""
|
||||
Test _balance() method when the Exchange platform returns nothing
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={})
|
||||
|
||||
msg_mock = MagicMock()
|
||||
|
@ -609,6 +616,8 @@ def test_zero_balance_handle(default_conf, update, mocker) -> None:
|
|||
)
|
||||
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
telegram._balance(bot=MagicMock(), update=update)
|
||||
|
@ -732,7 +741,6 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
|
|||
"""
|
||||
Test _forcesell() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
|
||||
|
@ -746,6 +754,7 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
|
|||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
# Create some test data
|
||||
|
@ -785,7 +794,6 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
|
|||
"""
|
||||
Test _forcesell() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
|
||||
|
@ -799,6 +807,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
|
|||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
# Create some test data
|
||||
|
@ -842,7 +851,6 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker
|
|||
"""
|
||||
Test _forcesell() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
rpc_mock = mocker.patch('freqtrade.rpc.telegram.Telegram.send_msg', MagicMock())
|
||||
|
@ -857,6 +865,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker
|
|||
)
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
# Create some test data
|
||||
|
@ -891,7 +900,6 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:
|
|||
"""
|
||||
Test _forcesell() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
msg_mock = MagicMock()
|
||||
|
@ -903,6 +911,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:
|
|||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
# Trader is not running
|
||||
|
@ -934,7 +943,6 @@ def test_performance_handle(default_conf, update, ticker, fee,
|
|||
"""
|
||||
Test _performance() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
|
@ -951,6 +959,7 @@ def test_performance_handle(default_conf, update, ticker, fee,
|
|||
)
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
# Create some test data
|
||||
|
@ -976,7 +985,6 @@ def test_performance_handle_invalid(default_conf, update, mocker) -> None:
|
|||
"""
|
||||
Test _performance() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
|
@ -986,6 +994,7 @@ def test_performance_handle_invalid(default_conf, update, mocker) -> None:
|
|||
)
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
# Trader is not running
|
||||
|
@ -999,7 +1008,6 @@ def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> Non
|
|||
"""
|
||||
Test _count() method
|
||||
"""
|
||||
patch_get_signal(mocker, (True, False))
|
||||
patch_coinmarketcap(mocker)
|
||||
msg_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
|
@ -1016,6 +1024,7 @@ def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> Non
|
|||
)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
freqtradebot = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtradebot, (True, False))
|
||||
telegram = Telegram(freqtradebot)
|
||||
|
||||
freqtradebot.state = State.STOPPED
|
||||
|
|
|
@ -23,7 +23,7 @@ def test_default_strategy_structure():
|
|||
|
||||
|
||||
def test_default_strategy(result):
|
||||
strategy = DefaultStrategy()
|
||||
strategy = DefaultStrategy({})
|
||||
|
||||
assert type(strategy.minimal_roi) is dict
|
||||
assert type(strategy.stoploss) is float
|
||||
|
|
|
@ -12,14 +12,15 @@ from freqtrade.strategy.resolver import StrategyResolver
|
|||
|
||||
def test_import_strategy(caplog):
|
||||
caplog.set_level(logging.DEBUG)
|
||||
default_config = {}
|
||||
|
||||
strategy = DefaultStrategy()
|
||||
strategy = DefaultStrategy(default_config)
|
||||
strategy.some_method = lambda *args, **kwargs: 42
|
||||
|
||||
assert strategy.__module__ == 'freqtrade.strategy.default_strategy'
|
||||
assert strategy.some_method() == 42
|
||||
|
||||
imported_strategy = import_strategy(strategy)
|
||||
imported_strategy = import_strategy(strategy, default_config)
|
||||
|
||||
assert dir(strategy) == dir(imported_strategy)
|
||||
|
||||
|
@ -35,13 +36,23 @@ def test_import_strategy(caplog):
|
|||
|
||||
|
||||
def test_search_strategy():
|
||||
default_config = {}
|
||||
default_location = os.path.join(os.path.dirname(
|
||||
os.path.realpath(__file__)), '..', '..', 'strategy'
|
||||
)
|
||||
assert isinstance(
|
||||
StrategyResolver._search_strategy(default_location, 'DefaultStrategy'), IStrategy
|
||||
StrategyResolver._search_strategy(
|
||||
default_location,
|
||||
config=default_config,
|
||||
strategy_name='DefaultStrategy'
|
||||
),
|
||||
IStrategy
|
||||
)
|
||||
assert StrategyResolver._search_strategy(default_location, 'NotFoundStrategy') is None
|
||||
assert StrategyResolver._search_strategy(
|
||||
default_location,
|
||||
config=default_config,
|
||||
strategy_name='NotFoundStrategy'
|
||||
) is None
|
||||
|
||||
|
||||
def test_load_strategy(result):
|
||||
|
@ -70,7 +81,7 @@ def test_load_not_found_strategy():
|
|||
with pytest.raises(ImportError,
|
||||
match=r'Impossible to load Strategy \'NotFoundStrategy\'.'
|
||||
r' This class does not exist or contains Python code errors'):
|
||||
strategy._load_strategy('NotFoundStrategy')
|
||||
strategy._load_strategy(strategy_name='NotFoundStrategy', config={})
|
||||
|
||||
|
||||
def test_strategy(result):
|
||||
|
|
|
@ -10,14 +10,14 @@ from unittest.mock import MagicMock
|
|||
import arrow
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.analyze import Analyze, parse_ticker_dataframe
|
||||
from freqtrade.analyze import parse_ticker_dataframe
|
||||
from freqtrade.arguments import TimeRange
|
||||
from freqtrade.optimize.__init__ import load_tickerdata_file
|
||||
from freqtrade.tests.conftest import get_patched_exchange, log_has
|
||||
from freqtrade.strategy.default_strategy import DefaultStrategy
|
||||
|
||||
# Avoid to reinit the same object again and again
|
||||
_ANALYZE = Analyze({}, DefaultStrategy())
|
||||
#_ANALYZE = Analyze({}, DefaultStrategy())
|
||||
|
||||
|
||||
def test_dataframe_correct_length(result):
|
||||
|
@ -30,133 +30,133 @@ def test_dataframe_correct_columns(result):
|
|||
['date', 'open', 'high', 'low', 'close', 'volume']
|
||||
|
||||
|
||||
def test_returns_latest_buy_signal(mocker, default_conf):
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=MagicMock())
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.analyze.Analyze',
|
||||
analyze_ticker=MagicMock(
|
||||
return_value=DataFrame([{'buy': 1, 'sell': 0, 'date': arrow.utcnow()}])
|
||||
)
|
||||
)
|
||||
assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (True, False)
|
||||
# def test_returns_latest_buy_signal(mocker, default_conf):
|
||||
# mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=MagicMock())
|
||||
# exchange = get_patched_exchange(mocker, default_conf)
|
||||
# mocker.patch.multiple(
|
||||
# 'freqtrade.analyze.Analyze',
|
||||
# analyze_ticker=MagicMock(
|
||||
# return_value=DataFrame([{'buy': 1, 'sell': 0, 'date': arrow.utcnow()}])
|
||||
# )
|
||||
# )
|
||||
# assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (True, False)
|
||||
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.analyze.Analyze',
|
||||
analyze_ticker=MagicMock(
|
||||
return_value=DataFrame([{'buy': 0, 'sell': 1, 'date': arrow.utcnow()}])
|
||||
)
|
||||
)
|
||||
assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (False, True)
|
||||
# mocker.patch.multiple(
|
||||
# 'freqtrade.analyze.Analyze',
|
||||
# analyze_ticker=MagicMock(
|
||||
# return_value=DataFrame([{'buy': 0, 'sell': 1, 'date': arrow.utcnow()}])
|
||||
# )
|
||||
# )
|
||||
# assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (False, True)
|
||||
|
||||
|
||||
def test_returns_latest_sell_signal(mocker, default_conf):
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=MagicMock())
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.analyze.Analyze',
|
||||
analyze_ticker=MagicMock(
|
||||
return_value=DataFrame([{'sell': 1, 'buy': 0, 'date': arrow.utcnow()}])
|
||||
)
|
||||
)
|
||||
# def test_returns_latest_sell_signal(mocker, default_conf):
|
||||
# mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=MagicMock())
|
||||
# exchange = get_patched_exchange(mocker, default_conf)
|
||||
# mocker.patch.multiple(
|
||||
# 'freqtrade.analyze.Analyze',
|
||||
# analyze_ticker=MagicMock(
|
||||
# return_value=DataFrame([{'sell': 1, 'buy': 0, 'date': arrow.utcnow()}])
|
||||
# )
|
||||
# )
|
||||
|
||||
assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (False, True)
|
||||
# assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (False, True)
|
||||
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.analyze.Analyze',
|
||||
analyze_ticker=MagicMock(
|
||||
return_value=DataFrame([{'sell': 0, 'buy': 1, 'date': arrow.utcnow()}])
|
||||
)
|
||||
)
|
||||
assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (True, False)
|
||||
# mocker.patch.multiple(
|
||||
# 'freqtrade.analyze.Analyze',
|
||||
# analyze_ticker=MagicMock(
|
||||
# return_value=DataFrame([{'sell': 0, 'buy': 1, 'date': arrow.utcnow()}])
|
||||
# )
|
||||
# )
|
||||
# assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (True, False)
|
||||
|
||||
|
||||
def test_get_signal_empty(default_conf, mocker, caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=None)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
assert (False, False) == _ANALYZE.get_signal(exchange, 'foo', default_conf['ticker_interval'])
|
||||
assert log_has('Empty ticker history for pair foo', caplog.record_tuples)
|
||||
# def test_get_signal_empty(default_conf, mocker, caplog):
|
||||
# caplog.set_level(logging.INFO)
|
||||
# mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=None)
|
||||
# exchange = get_patched_exchange(mocker, default_conf)
|
||||
# assert (False, False) == _ANALYZE.get_signal(exchange, 'foo', default_conf['ticker_interval'])
|
||||
# assert log_has('Empty ticker history for pair foo', caplog.record_tuples)
|
||||
|
||||
|
||||
def test_get_signal_exception_valueerror(default_conf, mocker, caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=1)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.analyze.Analyze',
|
||||
analyze_ticker=MagicMock(
|
||||
side_effect=ValueError('xyz')
|
||||
)
|
||||
)
|
||||
assert (False, False) == _ANALYZE.get_signal(exchange, 'foo', default_conf['ticker_interval'])
|
||||
assert log_has('Unable to analyze ticker for pair foo: xyz', caplog.record_tuples)
|
||||
# def test_get_signal_exception_valueerror(default_conf, mocker, caplog):
|
||||
# caplog.set_level(logging.INFO)
|
||||
# mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=1)
|
||||
# exchange = get_patched_exchange(mocker, default_conf)
|
||||
# mocker.patch.multiple(
|
||||
# 'freqtrade.analyze.Analyze',
|
||||
# analyze_ticker=MagicMock(
|
||||
# side_effect=ValueError('xyz')
|
||||
# )
|
||||
# )
|
||||
# assert (False, False) == _ANALYZE.get_signal(exchange, 'foo', default_conf['ticker_interval'])
|
||||
# assert log_has('Unable to analyze ticker for pair foo: xyz', caplog.record_tuples)
|
||||
|
||||
|
||||
def test_get_signal_empty_dataframe(default_conf, mocker, caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=1)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.analyze.Analyze',
|
||||
analyze_ticker=MagicMock(
|
||||
return_value=DataFrame([])
|
||||
)
|
||||
)
|
||||
assert (False, False) == _ANALYZE.get_signal(exchange, 'xyz', default_conf['ticker_interval'])
|
||||
assert log_has('Empty dataframe for pair xyz', caplog.record_tuples)
|
||||
# def test_get_signal_empty_dataframe(default_conf, mocker, caplog):
|
||||
# caplog.set_level(logging.INFO)
|
||||
# mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=1)
|
||||
# exchange = get_patched_exchange(mocker, default_conf)
|
||||
# mocker.patch.multiple(
|
||||
# 'freqtrade.analyze.Analyze',
|
||||
# analyze_ticker=MagicMock(
|
||||
# return_value=DataFrame([])
|
||||
# )
|
||||
# )
|
||||
# assert (False, False) == _ANALYZE.get_signal(exchange, 'xyz', default_conf['ticker_interval'])
|
||||
# assert log_has('Empty dataframe for pair xyz', caplog.record_tuples)
|
||||
|
||||
|
||||
def test_get_signal_old_dataframe(default_conf, mocker, caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=1)
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
# default_conf defines a 5m interval. we check interval * 2 + 5m
|
||||
# this is necessary as the last candle is removed (partial candles) by default
|
||||
oldtime = arrow.utcnow().shift(minutes=-16)
|
||||
ticks = DataFrame([{'buy': 1, 'date': oldtime}])
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.analyze.Analyze',
|
||||
analyze_ticker=MagicMock(
|
||||
return_value=DataFrame(ticks)
|
||||
)
|
||||
)
|
||||
assert (False, False) == _ANALYZE.get_signal(exchange, 'xyz', default_conf['ticker_interval'])
|
||||
assert log_has(
|
||||
'Outdated history for pair xyz. Last tick is 16 minutes old',
|
||||
caplog.record_tuples
|
||||
)
|
||||
# def test_get_signal_old_dataframe(default_conf, mocker, caplog):
|
||||
# caplog.set_level(logging.INFO)
|
||||
# mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=1)
|
||||
# exchange = get_patched_exchange(mocker, default_conf)
|
||||
# # default_conf defines a 5m interval. we check interval * 2 + 5m
|
||||
# # this is necessary as the last candle is removed (partial candles) by default
|
||||
# oldtime = arrow.utcnow().shift(minutes=-16)
|
||||
# ticks = DataFrame([{'buy': 1, 'date': oldtime}])
|
||||
# mocker.patch.multiple(
|
||||
# 'freqtrade.analyze.Analyze',
|
||||
# analyze_ticker=MagicMock(
|
||||
# return_value=DataFrame(ticks)
|
||||
# )
|
||||
# )
|
||||
# assert (False, False) == _ANALYZE.get_signal(exchange, 'xyz', default_conf['ticker_interval'])
|
||||
# assert log_has(
|
||||
# 'Outdated history for pair xyz. Last tick is 16 minutes old',
|
||||
# caplog.record_tuples
|
||||
# )
|
||||
|
||||
|
||||
def test_get_signal_handles_exceptions(mocker, default_conf):
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=MagicMock())
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.analyze.Analyze',
|
||||
analyze_ticker=MagicMock(
|
||||
side_effect=Exception('invalid ticker history ')
|
||||
)
|
||||
)
|
||||
# def test_get_signal_handles_exceptions(mocker, default_conf):
|
||||
# mocker.patch('freqtrade.exchange.Exchange.get_ticker_history', return_value=MagicMock())
|
||||
# exchange = get_patched_exchange(mocker, default_conf)
|
||||
# mocker.patch.multiple(
|
||||
# 'freqtrade.analyze.Analyze',
|
||||
# analyze_ticker=MagicMock(
|
||||
# side_effect=Exception('invalid ticker history ')
|
||||
# )
|
||||
# )
|
||||
|
||||
assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (False, False)
|
||||
# assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (False, False)
|
||||
|
||||
|
||||
def test_parse_ticker_dataframe(ticker_history):
|
||||
columns = ['date', 'open', 'high', 'low', 'close', 'volume']
|
||||
# def test_parse_ticker_dataframe(ticker_history):
|
||||
# columns = ['date', 'open', 'high', 'low', 'close', 'volume']
|
||||
|
||||
# Test file with BV data
|
||||
dataframe = parse_ticker_dataframe(ticker_history)
|
||||
assert dataframe.columns.tolist() == columns
|
||||
# # Test file with BV data
|
||||
# dataframe = parse_ticker_dataframe(ticker_history)
|
||||
# assert dataframe.columns.tolist() == columns
|
||||
|
||||
|
||||
def test_tickerdata_to_dataframe(default_conf) -> None:
|
||||
"""
|
||||
Test Analyze.tickerdata_to_dataframe() method
|
||||
"""
|
||||
analyze = Analyze(default_conf, DefaultStrategy())
|
||||
# def test_tickerdata_to_dataframe(default_conf) -> None:
|
||||
# """
|
||||
# Test Analyze.tickerdata_to_dataframe() method
|
||||
# """
|
||||
# analyze = Analyze(default_conf, DefaultStrategy())
|
||||
|
||||
timerange = TimeRange(None, 'line', 0, -100)
|
||||
tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m', timerange=timerange)
|
||||
tickerlist = {'UNITTEST/BTC': tick}
|
||||
data = analyze.tickerdata_to_dataframe(tickerlist)
|
||||
assert len(data['UNITTEST/BTC']) == 99 # partial candle was removed
|
||||
# timerange = TimeRange(None, 'line', 0, -100)
|
||||
# tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m', timerange=timerange)
|
||||
# tickerlist = {'UNITTEST/BTC': tick}
|
||||
# data = analyze.tickerdata_to_dataframe(tickerlist)
|
||||
# assert len(data['UNITTEST/BTC']) == 99 # partial candle was removed
|
||||
|
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
import pandas
|
||||
|
||||
from freqtrade.analyze import Analyze
|
||||
from freqtrade.optimize import load_data
|
||||
from freqtrade.strategy.resolver import StrategyResolver
|
||||
|
||||
|
@ -15,8 +14,7 @@ def load_dataframe_pair(pairs, strategy):
|
|||
assert isinstance(pairs[0], str)
|
||||
dataframe = ld[pairs[0]]
|
||||
|
||||
analyze = Analyze({}, strategy)
|
||||
dataframe = analyze.analyze_ticker(dataframe)
|
||||
dataframe = strategy.analyze_ticker(dataframe)
|
||||
return dataframe
|
||||
|
||||
|
||||
|
|
|
@ -31,7 +31,6 @@ def get_patched_freqtradebot(mocker, config) -> FreqtradeBot:
|
|||
:param config: Config to pass to the bot
|
||||
:return: None
|
||||
"""
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
|
||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
|
||||
patch_exchange(mocker)
|
||||
|
@ -40,17 +39,13 @@ def get_patched_freqtradebot(mocker, config) -> FreqtradeBot:
|
|||
return FreqtradeBot(config)
|
||||
|
||||
|
||||
def patch_get_signal(mocker, value=(True, False)) -> None:
|
||||
def patch_get_signal(freqtrade: FreqtradeBot, value=(True, False)) -> None:
|
||||
"""
|
||||
|
||||
:param mocker: mocker to patch Analyze class
|
||||
:param value: which value Analyze.get_signal() must return
|
||||
:param mocker: mocker to patch IStrategy class
|
||||
:param value: which value IStrategy.get_signal() must return
|
||||
:return: None
|
||||
"""
|
||||
mocker.patch(
|
||||
'freqtrade.freqtradebot.Analyze.get_signal',
|
||||
side_effect=lambda e, s, t: value
|
||||
)
|
||||
freqtrade.strategy.get_signal = lambda e, s, t: value
|
||||
|
||||
|
||||
def patch_RPCManager(mocker) -> MagicMock:
|
||||
|
@ -267,7 +262,6 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf,
|
|||
"""
|
||||
Test get_trade_stake_amount() method
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch.multiple(
|
||||
|
@ -285,6 +279,7 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf,
|
|||
conf['max_open_trades'] = 2
|
||||
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
# no open trades, order amount should be 'balance / max_open_trades'
|
||||
result = freqtrade._get_trade_stake_amount()
|
||||
|
@ -452,7 +447,6 @@ def test_create_trade(default_conf, ticker, limit_buy_order, fee, markets, mocke
|
|||
"""
|
||||
Test create_trade() method
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch.multiple(
|
||||
|
@ -467,6 +461,7 @@ def test_create_trade(default_conf, ticker, limit_buy_order, fee, markets, mocke
|
|||
# Save state of current whitelist
|
||||
whitelist = deepcopy(default_conf['exchange']['pair_whitelist'])
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
freqtrade.create_trade()
|
||||
|
||||
trade = Trade.query.first()
|
||||
|
@ -490,7 +485,6 @@ def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order,
|
|||
"""
|
||||
Test create_trade() method
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch.multiple(
|
||||
|
@ -503,6 +497,7 @@ def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order,
|
|||
get_markets=markets
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
|
||||
freqtrade.create_trade()
|
||||
|
@ -513,7 +508,6 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order,
|
|||
"""
|
||||
Test create_trade() method
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
buy_mock = MagicMock(return_value={'id': limit_buy_order['id']})
|
||||
|
@ -529,6 +523,7 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order,
|
|||
conf = deepcopy(default_conf)
|
||||
conf['stake_amount'] = 0.0005
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
freqtrade.create_trade()
|
||||
rate, amount = buy_mock.call_args[0][1], buy_mock.call_args[0][2]
|
||||
|
@ -540,7 +535,6 @@ def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_ord
|
|||
"""
|
||||
Test create_trade() method
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
buy_mock = MagicMock(return_value={'id': limit_buy_order['id']})
|
||||
|
@ -556,6 +550,7 @@ def test_create_trade_too_small_stake_amount(default_conf, ticker, limit_buy_ord
|
|||
conf = deepcopy(default_conf)
|
||||
conf['stake_amount'] = 0.000000005
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
result = freqtrade.create_trade()
|
||||
assert result is False
|
||||
|
@ -566,7 +561,6 @@ def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order,
|
|||
"""
|
||||
Test create_trade() method
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch.multiple(
|
||||
|
@ -583,6 +577,7 @@ def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order,
|
|||
conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
|
||||
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
assert freqtrade.create_trade() is False
|
||||
assert freqtrade._get_trade_stake_amount() is None
|
||||
|
@ -592,7 +587,6 @@ def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, fee, marke
|
|||
"""
|
||||
Test create_trade() method
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch.multiple(
|
||||
|
@ -608,6 +602,7 @@ def test_create_trade_no_pairs(default_conf, ticker, limit_buy_order, fee, marke
|
|||
conf['exchange']['pair_whitelist'] = ["ETH/BTC"]
|
||||
conf['exchange']['pair_blacklist'] = ["ETH/BTC"]
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
freqtrade.create_trade()
|
||||
|
||||
|
@ -620,7 +615,6 @@ def test_create_trade_no_pairs_after_blacklist(default_conf, ticker,
|
|||
"""
|
||||
Test create_trade() method
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch.multiple(
|
||||
|
@ -636,6 +630,7 @@ def test_create_trade_no_pairs_after_blacklist(default_conf, ticker,
|
|||
conf['exchange']['pair_whitelist'] = ["ETH/BTC"]
|
||||
conf['exchange']['pair_blacklist'] = ["ETH/BTC"]
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
freqtrade.create_trade()
|
||||
|
||||
|
@ -650,7 +645,6 @@ def test_create_trade_no_signal(default_conf, fee, mocker) -> None:
|
|||
conf = deepcopy(default_conf)
|
||||
conf['dry_run'] = True
|
||||
|
||||
patch_get_signal(mocker, value=(False, False))
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch.multiple(
|
||||
|
@ -664,6 +658,7 @@ def test_create_trade_no_signal(default_conf, fee, mocker) -> None:
|
|||
conf = deepcopy(default_conf)
|
||||
conf['stake_amount'] = 10
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade, value=(False, False))
|
||||
|
||||
Trade.query = MagicMock()
|
||||
Trade.query.filter = MagicMock()
|
||||
|
@ -675,7 +670,6 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order,
|
|||
"""
|
||||
Test the trade creation in _process() method
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||
mocker.patch.multiple(
|
||||
|
@ -688,6 +682,7 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order,
|
|||
get_fee=fee,
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
|
||||
assert not trades
|
||||
|
@ -716,7 +711,6 @@ def test_process_exchange_failures(default_conf, ticker, markets, mocker) -> Non
|
|||
"""
|
||||
Test _process() method when a RequestException happens
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||
mocker.patch.multiple(
|
||||
|
@ -729,6 +723,8 @@ def test_process_exchange_failures(default_conf, ticker, markets, mocker) -> Non
|
|||
sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None)
|
||||
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
result = freqtrade._process()
|
||||
assert result is False
|
||||
assert sleep_mock.has_calls()
|
||||
|
@ -738,7 +734,6 @@ def test_process_operational_exception(default_conf, ticker, markets, mocker) ->
|
|||
"""
|
||||
Test _process() method when an OperationalException happens
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
msg_mock = patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||
mocker.patch.multiple(
|
||||
|
@ -749,6 +744,8 @@ def test_process_operational_exception(default_conf, ticker, markets, mocker) ->
|
|||
buy=MagicMock(side_effect=OperationalException)
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
assert freqtrade.state == State.RUNNING
|
||||
|
||||
result = freqtrade._process()
|
||||
|
@ -762,7 +759,6 @@ def test_process_trade_handling(
|
|||
"""
|
||||
Test _process()
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||
mocker.patch.multiple(
|
||||
|
@ -775,6 +771,7 @@ def test_process_trade_handling(
|
|||
get_fee=fee,
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
|
||||
assert not trades
|
||||
|
@ -913,7 +910,6 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order,
|
|||
"""
|
||||
Test check_handle() method
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
|
@ -931,6 +927,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order,
|
|||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
freqtrade.create_trade()
|
||||
|
||||
|
@ -941,7 +938,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order,
|
|||
trade.update(limit_buy_order)
|
||||
assert trade.is_open is True
|
||||
|
||||
patch_get_signal(mocker, value=(False, True))
|
||||
patch_get_signal(freqtrade, value=(False, True))
|
||||
assert freqtrade.handle_trade(trade) is True
|
||||
assert trade.open_order_id == limit_sell_order['id']
|
||||
|
||||
|
@ -962,10 +959,8 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order,
|
|||
conf = deepcopy(default_conf)
|
||||
conf.update({'experimental': {'use_sell_signal': True}})
|
||||
|
||||
patch_get_signal(mocker, value=(True, True))
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
|
@ -976,6 +971,8 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order,
|
|||
)
|
||||
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade, value=(True, True))
|
||||
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False
|
||||
|
||||
freqtrade.create_trade()
|
||||
|
||||
|
@ -985,7 +982,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order,
|
|||
assert nb_trades == 0
|
||||
|
||||
# Buy is triggering, so buying ...
|
||||
patch_get_signal(mocker, value=(True, False))
|
||||
patch_get_signal(freqtrade, value=(True, False))
|
||||
freqtrade.create_trade()
|
||||
trades = Trade.query.all()
|
||||
nb_trades = len(trades)
|
||||
|
@ -993,7 +990,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order,
|
|||
assert trades[0].is_open is True
|
||||
|
||||
# Buy and Sell are not triggering, so doing nothing ...
|
||||
patch_get_signal(mocker, value=(False, False))
|
||||
patch_get_signal(freqtrade, value=(False, False))
|
||||
assert freqtrade.handle_trade(trades[0]) is False
|
||||
trades = Trade.query.all()
|
||||
nb_trades = len(trades)
|
||||
|
@ -1001,7 +998,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order,
|
|||
assert trades[0].is_open is True
|
||||
|
||||
# Buy and Sell are triggering, so doing nothing ...
|
||||
patch_get_signal(mocker, value=(True, True))
|
||||
patch_get_signal(freqtrade, value=(True, True))
|
||||
assert freqtrade.handle_trade(trades[0]) is False
|
||||
trades = Trade.query.all()
|
||||
nb_trades = len(trades)
|
||||
|
@ -1009,7 +1006,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order,
|
|||
assert trades[0].is_open is True
|
||||
|
||||
# Sell is triggering, guess what : we are Selling!
|
||||
patch_get_signal(mocker, value=(False, True))
|
||||
patch_get_signal(freqtrade, value=(False, True))
|
||||
trades = Trade.query.all()
|
||||
assert freqtrade.handle_trade(trades[0]) is True
|
||||
|
||||
|
@ -1023,7 +1020,6 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order,
|
|||
conf = deepcopy(default_conf)
|
||||
conf.update({'experimental': {'use_sell_signal': True}})
|
||||
|
||||
patch_get_signal(mocker, value=(True, False))
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch.multiple(
|
||||
|
@ -1035,8 +1031,10 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order,
|
|||
get_markets=markets
|
||||
)
|
||||
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=True)
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade, value=(True, False))
|
||||
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: True
|
||||
|
||||
freqtrade.create_trade()
|
||||
|
||||
trade = Trade.query.first()
|
||||
|
@ -1047,7 +1045,7 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order,
|
|||
# we might just want to check if we are in a sell condition without
|
||||
# executing
|
||||
# if ROI is reached we must sell
|
||||
patch_get_signal(mocker, value=(False, True))
|
||||
patch_get_signal(freqtrade, value=(False, True))
|
||||
assert freqtrade.handle_trade(trade)
|
||||
assert log_has('Required profit reached. Selling..', caplog.record_tuples)
|
||||
|
||||
|
@ -1061,7 +1059,6 @@ def test_handle_trade_experimental(
|
|||
conf = deepcopy(default_conf)
|
||||
conf.update({'experimental': {'use_sell_signal': True}})
|
||||
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch.multiple(
|
||||
|
@ -1072,18 +1069,19 @@ def test_handle_trade_experimental(
|
|||
get_fee=fee,
|
||||
get_markets=markets
|
||||
)
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
||||
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False
|
||||
freqtrade.create_trade()
|
||||
|
||||
trade = Trade.query.first()
|
||||
trade.is_open = True
|
||||
|
||||
patch_get_signal(mocker, value=(False, False))
|
||||
patch_get_signal(freqtrade, value=(False, False))
|
||||
assert not freqtrade.handle_trade(trade)
|
||||
|
||||
patch_get_signal(mocker, value=(False, True))
|
||||
patch_get_signal(freqtrade, value=(False, True))
|
||||
assert freqtrade.handle_trade(trade)
|
||||
assert log_has('Sell signal received. Selling..', caplog.record_tuples)
|
||||
|
||||
|
@ -1093,7 +1091,6 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order,
|
|||
"""
|
||||
Test check_handle() method
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch.multiple(
|
||||
|
@ -1105,6 +1102,7 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order,
|
|||
get_markets=markets
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
# Create trade and sell it
|
||||
freqtrade.create_trade()
|
||||
|
@ -1345,7 +1343,6 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, moc
|
|||
"""
|
||||
Test execute_sell() method with a ticker going UP
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
rpc_mock = patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch.multiple(
|
||||
|
@ -1357,6 +1354,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, moc
|
|||
)
|
||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
# Create some test data
|
||||
freqtrade.create_trade()
|
||||
|
@ -1397,7 +1395,6 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets,
|
|||
"""
|
||||
Test execute_sell() method with a ticker going DOWN
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
rpc_mock = patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
|
@ -1409,6 +1406,7 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets,
|
|||
get_markets=markets
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
# Create some test data
|
||||
freqtrade.create_trade()
|
||||
|
@ -1450,7 +1448,6 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee,
|
|||
"""
|
||||
Test execute_sell() method with a ticker going DOWN and with a bot config empty
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
rpc_mock = patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||
mocker.patch.multiple(
|
||||
|
@ -1461,6 +1458,7 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee,
|
|||
get_markets=markets
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
# Create some test data
|
||||
freqtrade.create_trade()
|
||||
|
@ -1500,7 +1498,6 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee,
|
|||
"""
|
||||
Test execute_sell() method with a ticker going DOWN and with a bot config empty
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
rpc_mock = patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||
mocker.patch.multiple(
|
||||
|
@ -1511,6 +1508,7 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee,
|
|||
get_markets=markets
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
# Create some test data
|
||||
freqtrade.create_trade()
|
||||
|
@ -1550,10 +1548,8 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order,
|
|||
"""
|
||||
Test sell_profit_only feature when enabled
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
|
@ -1572,11 +1568,14 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order,
|
|||
'sell_profit_only': True,
|
||||
}
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False
|
||||
|
||||
freqtrade.create_trade()
|
||||
|
||||
trade = Trade.query.first()
|
||||
trade.update(limit_buy_order)
|
||||
patch_get_signal(mocker, value=(False, True))
|
||||
patch_get_signal(freqtrade, value=(False, True))
|
||||
assert freqtrade.handle_trade(trade) is True
|
||||
|
||||
|
||||
|
@ -1585,10 +1584,8 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order,
|
|||
"""
|
||||
Test sell_profit_only feature when disabled
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
|
@ -1607,11 +1604,13 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order,
|
|||
'sell_profit_only': False,
|
||||
}
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False
|
||||
freqtrade.create_trade()
|
||||
|
||||
trade = Trade.query.first()
|
||||
trade.update(limit_buy_order)
|
||||
patch_get_signal(mocker, value=(False, True))
|
||||
patch_get_signal(freqtrade, value=(False, True))
|
||||
assert freqtrade.handle_trade(trade) is True
|
||||
|
||||
|
||||
|
@ -1619,10 +1618,8 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, market
|
|||
"""
|
||||
Test sell_profit_only feature when enabled and we have a loss
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze.stop_loss_reached', return_value=False)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
|
@ -1641,11 +1638,14 @@ def test_sell_profit_only_enable_loss(default_conf, limit_buy_order, fee, market
|
|||
'sell_profit_only': True,
|
||||
}
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
freqtrade.strategy.stop_loss_reached = \
|
||||
lambda current_rate, trade, current_time, current_profit: False
|
||||
freqtrade.create_trade()
|
||||
|
||||
trade = Trade.query.first()
|
||||
trade.update(limit_buy_order)
|
||||
patch_get_signal(mocker, value=(False, True))
|
||||
patch_get_signal(freqtrade, value=(False, True))
|
||||
assert freqtrade.handle_trade(trade) is False
|
||||
|
||||
|
||||
|
@ -1653,10 +1653,8 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, marke
|
|||
"""
|
||||
Test sell_profit_only feature when enabled and we have a loss
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
|
@ -1677,11 +1675,14 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, marke
|
|||
}
|
||||
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False
|
||||
|
||||
freqtrade.create_trade()
|
||||
|
||||
trade = Trade.query.first()
|
||||
trade.update(limit_buy_order)
|
||||
patch_get_signal(mocker, value=(False, True))
|
||||
patch_get_signal(freqtrade, value=(False, True))
|
||||
assert freqtrade.handle_trade(trade) is True
|
||||
|
||||
|
||||
|
@ -1689,10 +1690,8 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, markets, m
|
|||
"""
|
||||
Test sell_profit_only feature when enabled and we have a loss
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=True)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
|
@ -1712,15 +1711,18 @@ def test_ignore_roi_if_buy_signal(default_conf, limit_buy_order, fee, markets, m
|
|||
}
|
||||
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: True
|
||||
|
||||
freqtrade.create_trade()
|
||||
|
||||
trade = Trade.query.first()
|
||||
trade.update(limit_buy_order)
|
||||
patch_get_signal(mocker, value=(True, True))
|
||||
patch_get_signal(freqtrade, value=(True, True))
|
||||
assert freqtrade.handle_trade(trade) is False
|
||||
|
||||
# Test if buy-signal is absent (should sell due to roi = true)
|
||||
patch_get_signal(mocker, value=(False, True))
|
||||
patch_get_signal(freqtrade, value=(False, True))
|
||||
assert freqtrade.handle_trade(trade) is True
|
||||
|
||||
|
||||
|
@ -1728,10 +1730,8 @@ def test_trailing_stop_loss(default_conf, limit_buy_order, fee, caplog, mocker)
|
|||
"""
|
||||
Test sell_profit_only feature when enabled and we have a loss
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
|
@ -1748,6 +1748,9 @@ def test_trailing_stop_loss(default_conf, limit_buy_order, fee, caplog, mocker)
|
|||
conf['trailing_stop'] = True
|
||||
print(limit_buy_order)
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False
|
||||
|
||||
freqtrade.create_trade()
|
||||
|
||||
trade = Trade.query.first()
|
||||
|
@ -1765,10 +1768,8 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, caplog,
|
|||
Test sell_profit_only feature when enabled and we have a loss
|
||||
"""
|
||||
buy_price = limit_buy_order['price']
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
|
@ -1785,6 +1786,8 @@ def test_trailing_stop_loss_positive(default_conf, limit_buy_order, fee, caplog,
|
|||
conf['trailing_stop'] = True
|
||||
conf['trailing_stop_positive'] = 0.01
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False
|
||||
freqtrade.create_trade()
|
||||
|
||||
trade = Trade.query.first()
|
||||
|
@ -1826,10 +1829,8 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order,
|
|||
"""
|
||||
Test sell_profit_only feature when enabled and we have a loss
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=True)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_pairs=MagicMock(),
|
||||
|
@ -1849,16 +1850,19 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order,
|
|||
}
|
||||
|
||||
freqtrade = FreqtradeBot(conf)
|
||||
patch_get_signal(freqtrade)
|
||||
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: True
|
||||
|
||||
freqtrade.create_trade()
|
||||
|
||||
trade = Trade.query.first()
|
||||
trade.update(limit_buy_order)
|
||||
# Sell due to min_roi_reached
|
||||
patch_get_signal(mocker, value=(True, True))
|
||||
patch_get_signal(freqtrade, value=(True, True))
|
||||
assert freqtrade.handle_trade(trade) is True
|
||||
|
||||
# Test if buy-signal is absent
|
||||
patch_get_signal(mocker, value=(False, True))
|
||||
patch_get_signal(freqtrade, value=(False, True))
|
||||
assert freqtrade.handle_trade(trade) is True
|
||||
|
||||
|
||||
|
@ -1869,7 +1873,6 @@ def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, ca
|
|||
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
|
||||
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||
|
@ -1882,6 +1885,8 @@ def test_get_real_amount_quote(default_conf, trades_for_order, buy_order_fee, ca
|
|||
open_order_id="123456"
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
# Amount is reduced by "fee"
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001)
|
||||
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
|
||||
|
@ -1896,7 +1901,6 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker):
|
|||
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
|
||||
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||
|
@ -1909,6 +1913,8 @@ def test_get_real_amount_no_trade(default_conf, buy_order_fee, caplog, mocker):
|
|||
open_order_id="123456"
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
# Amount is reduced by "fee"
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
|
||||
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
|
||||
|
@ -1922,7 +1928,6 @@ def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, mo
|
|||
"""
|
||||
trades_for_order[0]['fee']['currency'] = 'ETH'
|
||||
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||
|
@ -1936,6 +1941,8 @@ def test_get_real_amount_stake(default_conf, trades_for_order, buy_order_fee, mo
|
|||
open_order_id="123456"
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
# Amount does not change
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
|
||||
|
||||
|
@ -1948,7 +1955,6 @@ def test_get_real_amount_BNB(default_conf, trades_for_order, buy_order_fee, mock
|
|||
trades_for_order[0]['fee']['currency'] = 'BNB'
|
||||
trades_for_order[0]['fee']['cost'] = 0.00094518
|
||||
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||
|
@ -1962,6 +1968,8 @@ def test_get_real_amount_BNB(default_conf, trades_for_order, buy_order_fee, mock
|
|||
open_order_id="123456"
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
# Amount does not change
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
|
||||
|
||||
|
@ -1971,7 +1979,6 @@ def test_get_real_amount_multi(default_conf, trades_for_order2, buy_order_fee, c
|
|||
Test get_real_amount with split trades (multiple trades for this order)
|
||||
"""
|
||||
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||
|
@ -1985,6 +1992,8 @@ def test_get_real_amount_multi(default_conf, trades_for_order2, buy_order_fee, c
|
|||
open_order_id="123456"
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
# Amount is reduced by "fee"
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001)
|
||||
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
|
||||
|
@ -1999,7 +2008,6 @@ def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee
|
|||
limit_buy_order = deepcopy(buy_order_fee)
|
||||
limit_buy_order['fee'] = {'cost': 0.004, 'currency': 'LTC'}
|
||||
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||
|
@ -2014,6 +2022,8 @@ def test_get_real_amount_fromorder(default_conf, trades_for_order, buy_order_fee
|
|||
open_order_id="123456"
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
# Amount is reduced by "fee"
|
||||
assert freqtrade.get_real_amount(trade, limit_buy_order) == amount - 0.004
|
||||
assert log_has('Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
|
||||
|
@ -2028,7 +2038,6 @@ def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order
|
|||
limit_buy_order = deepcopy(buy_order_fee)
|
||||
limit_buy_order['fee'] = {'cost': 0.004}
|
||||
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||
|
@ -2042,6 +2051,8 @@ def test_get_real_amount_invalid_order(default_conf, trades_for_order, buy_order
|
|||
open_order_id="123456"
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
|
||||
# Amount does not change
|
||||
assert freqtrade.get_real_amount(trade, limit_buy_order) == amount
|
||||
|
||||
|
@ -2053,7 +2064,6 @@ def test_get_real_amount_invalid(default_conf, trades_for_order, buy_order_fee,
|
|||
# Remove "Currency" from fee dict
|
||||
trades_for_order[0]['fee'] = {'cost': 0.008}
|
||||
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||
|
@ -2067,6 +2077,7 @@ def test_get_real_amount_invalid(default_conf, trades_for_order, buy_order_fee,
|
|||
open_order_id="123456"
|
||||
)
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
# Amount does not change
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
|
||||
|
||||
|
@ -2075,7 +2086,6 @@ def test_get_real_amount_open_trade(default_conf, mocker):
|
|||
"""
|
||||
Test get_real_amount condition trade.fee_open == 0 or order['status'] == 'open'
|
||||
"""
|
||||
patch_get_signal(mocker)
|
||||
patch_RPCManager(mocker)
|
||||
patch_coinmarketcap(mocker)
|
||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
||||
|
@ -2093,4 +2103,5 @@ def test_get_real_amount_open_trade(default_conf, mocker):
|
|||
'status': 'open',
|
||||
}
|
||||
freqtrade = FreqtradeBot(default_conf)
|
||||
patch_get_signal(freqtrade)
|
||||
assert freqtrade.get_real_amount(trade, order) == amount
|
||||
|
|
|
@ -7,7 +7,7 @@ Unit test file for misc.py
|
|||
import datetime
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
from freqtrade.analyze import Analyze, parse_ticker_dataframe
|
||||
from freqtrade.analyze import parse_ticker_dataframe
|
||||
from freqtrade.misc import (common_datearray, datesarray_to_datetimearray,
|
||||
file_dump_json, format_ms_time, shorten_date)
|
||||
from freqtrade.optimize.__init__ import load_tickerdata_file
|
||||
|
@ -48,10 +48,10 @@ def test_common_datearray(default_conf) -> None:
|
|||
Test common_datearray()
|
||||
:return: None
|
||||
"""
|
||||
analyze = Analyze(default_conf, DefaultStrategy())
|
||||
strategy = DefaultStrategy(default_conf)
|
||||
tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m')
|
||||
tickerlist = {'UNITTEST/BTC': tick}
|
||||
dataframes = analyze.tickerdata_to_dataframe(tickerlist)
|
||||
dataframes = strategy.tickerdata_to_dataframe(tickerlist)
|
||||
|
||||
dates = common_datearray(dataframes)
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user