Merge pull request #1007 from freqtrade/remove-analyze

Remove Analyze
This commit is contained in:
Matthias 2018-07-19 10:12:36 +02:00 committed by GitHub
commit 64f933477d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
23 changed files with 580 additions and 650 deletions

View File

@ -1,271 +0,0 @@
"""
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, StrategyResolver
logger = logging.getLogger(__name__)
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) -> None:
"""
Init Analyze
:param config: Bot configuration (use the one from Configuration())
"""
self.config = config
self.strategy: IStrategy = StrategyResolver(self.config).strategy
@staticmethod
def parse_ticker_dataframe(ticker: list) -> DataFrame:
"""
Analyses the trend for the given ticker history
:param ticker: See exchange.get_ticker_history
:return: DataFrame
"""
cols = ['date', 'open', 'high', 'low', 'close', 'volume']
frame = DataFrame(ticker, columns=cols)
frame['date'] = to_datetime(frame['date'],
unit='ms',
utc=True,
infer_datetime_format=True)
# group by index and aggregate results to eliminate duplicate ticks
frame = frame.groupby(by='date', as_index=False, sort=True).agg({
'open': 'first',
'high': 'max',
'low': 'min',
'close': 'last',
'volume': 'max',
})
frame.drop(frame.tail(1).index, inplace=True) # eliminate partial candle
return frame
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
"""
Adds several different TA indicators to the given DataFrame
Performance Note: For the best performance be frugal on the number of indicators
you are using. Let uncomment only the indicator you are using in your strategies
or your hyperopt configuration, otherwise you will waste your memory and CPU usage.
"""
return self.strategy.populate_indicators(dataframe=dataframe)
def populate_buy_trend(self, dataframe: DataFrame) -> DataFrame:
"""
Based on TA indicators, populates the buy signal for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
return self.strategy.populate_buy_trend(dataframe=dataframe)
def populate_sell_trend(self, dataframe: DataFrame) -> DataFrame:
"""
Based on TA indicators, populates the sell signal for the given dataframe
:param dataframe: DataFrame
:return: DataFrame with buy column
"""
return self.strategy.populate_sell_trend(dataframe=dataframe)
def get_ticker_interval(self) -> str:
"""
Return ticker interval to use
:return: Ticker interval value to use
"""
return self.strategy.ticker_interval
def get_stoploss(self) -> float:
"""
Return stoploss to use
:return: Strategy stoploss value to use
"""
return self.strategy.stoploss
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 = self.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.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.populate_indicators(self.parse_ticker_dataframe(pair_data))
for pair, pair_data in tickerdata.items()}

View File

@ -0,0 +1,33 @@
"""
Functions to analyze ticker data with indicators and produce buy and sell signals
"""
import logging
from pandas import DataFrame, to_datetime
logger = logging.getLogger(__name__)
def parse_ticker_dataframe(ticker: list) -> DataFrame:
"""
Analyses the trend for the given ticker history
:param ticker: See exchange.get_ticker_history
:return: DataFrame
"""
cols = ['date', 'open', 'high', 'low', 'close', 'volume']
frame = DataFrame(ticker, columns=cols)
frame['date'] = to_datetime(frame['date'],
unit='ms',
utc=True,
infer_datetime_format=True)
# group by index and aggregate results to eliminate duplicate ticks
frame = frame.groupby(by='date', as_index=False, sort=True).agg({
'open': 'first',
'high': 'max',
'low': 'min',
'close': 'last',
'volume': 'max',
})
frame.drop(frame.tail(1).index, inplace=True) # eliminate partial candle
return frame

View File

@ -15,12 +15,12 @@ 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
from freqtrade.rpc import RPCManager, RPCMessageType
from freqtrade.state import State
from freqtrade.strategy.resolver import IStrategy, StrategyResolver
logger = logging.getLogger(__name__)
@ -48,7 +48,7 @@ class FreqtradeBot(object):
# Init objects
self.config = config
self.analyze = Analyze(self.config)
self.strategy: IStrategy = StrategyResolver(self.config).strategy
self.fiat_converter = CryptoToFiatConverter()
self.rpc: RPCManager = RPCManager(self)
self.persistence = None
@ -297,8 +297,8 @@ class FreqtradeBot(object):
return None
amount_reserve_percent = 1 - 0.05 # reserve 5% + stoploss
if self.analyze.get_stoploss() is not None:
amount_reserve_percent += self.analyze.get_stoploss()
if self.strategy.stoploss is not None:
amount_reserve_percent += self.strategy.stoploss
# it should not be more than 50%
amount_reserve_percent = max(amount_reserve_percent, 0.5)
return min(min_stake_amounts)/amount_reserve_percent
@ -309,7 +309,7 @@ class FreqtradeBot(object):
if one pair triggers the buy_signal a new trade record gets created
:return: True if a trade object has been created and persisted, False otherwise
"""
interval = self.analyze.get_ticker_interval()
interval = self.strategy.ticker_interval
stake_amount = self._get_trade_stake_amount()
if not stake_amount:
@ -332,7 +332,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
@ -502,10 +502,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.analyze.get_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..')

View File

@ -15,12 +15,12 @@ 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
from freqtrade.misc import file_dump_json
from freqtrade.persistence import Trade
from freqtrade.strategy.resolver import IStrategy, StrategyResolver
logger = logging.getLogger(__name__)
@ -52,11 +52,11 @@ class Backtesting(object):
"""
def __init__(self, config: Dict[str, Any]) -> None:
self.config = config
self.analyze = Analyze(self.config)
self.ticker_interval = self.analyze.strategy.ticker_interval
self.tickerdata_to_dataframe = self.analyze.tickerdata_to_dataframe
self.populate_buy_trend = self.analyze.populate_buy_trend
self.populate_sell_trend = self.analyze.populate_sell_trend
self.strategy: IStrategy = StrategyResolver(self.config).strategy
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
# Reset keys for backtesting
self.config['exchange']['key'] = ''
@ -151,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),

View File

@ -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()

View File

@ -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)

View File

@ -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.exchange.exchange_helpers 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) -> None:
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()}

View File

@ -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
@ -68,10 +69,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
"""
@ -87,10 +89,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)
@ -120,7 +122,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
@ -136,5 +138,5 @@ class StrategyResolver(object):
os.path.abspath(os.path.join(directory, entry)), strategy_name
)
if strategy:
return strategy()
return strategy(config)
return None

View File

@ -12,7 +12,7 @@ from jsonschema import validate
from telegram import Chat, Message, Update
from freqtrade import constants
from freqtrade.analyze import Analyze
from freqtrade.exchange.exchange_helpers import parse_ticker_dataframe
from freqtrade.exchange import Exchange
from freqtrade.freqtradebot import FreqtradeBot
@ -20,7 +20,7 @@ logging.getLogger('').setLevel(logging.INFO)
def log_has(line, logs):
# caplog mocker returns log as a tuple: ('freqtrade.analyze', logging.WARNING, 'foobar')
# caplog mocker returns log as a tuple: ('freqtrade.something', logging.WARNING, 'foobar')
# and we want to match line against foobar in the tuple
return reduce(lambda a, b: a or b,
filter(lambda x: x[2] == line, logs),
@ -52,13 +52,11 @@ 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.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())
return FreqtradeBot(config)
@ -617,7 +615,7 @@ def tickers():
@pytest.fixture
def result():
with open('freqtrade/tests/testdata/UNITTEST_BTC-1m.json') as data_file:
return Analyze.parse_ticker_dataframe(json.load(data_file))
return parse_ticker_dataframe(json.load(data_file))
# FIX:
# Create an fixture/function

View File

@ -0,0 +1,25 @@
# pragma pylint: disable=missing-docstring, C0103
"""
Unit test file for exchange_helpers.py
"""
from freqtrade.exchange.exchange_helpers import parse_ticker_dataframe
def test_dataframe_correct_length(result):
dataframe = parse_ticker_dataframe(result)
assert len(result.index) - 1 == len(dataframe.index) # last partial candle removed
def test_dataframe_correct_columns(result):
assert result.columns.tolist() == \
['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

View File

@ -13,11 +13,11 @@ 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)
from freqtrade.tests.conftest import log_has, patch_exchange
from freqtrade.strategy.default_strategy import DefaultStrategy
def get_args(args) -> List[str]:
@ -325,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)
@ -347,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)
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'])
@ -412,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)
@ -453,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)

View File

@ -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()

View File

@ -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

View File

@ -3,14 +3,14 @@ import json
import pytest
from pandas import DataFrame
from freqtrade.analyze import Analyze
from freqtrade.exchange.exchange_helpers import parse_ticker_dataframe
from freqtrade.strategy.default_strategy import DefaultStrategy
@pytest.fixture
def result():
with open('freqtrade/tests/testdata/ETH_BTC-1m.json') as data_file:
return Analyze.parse_ticker_dataframe(json.load(data_file))
return parse_ticker_dataframe(json.load(data_file))
def test_default_strategy_structure():
@ -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

View File

@ -0,0 +1,126 @@
# pragma pylint: disable=missing-docstring, C0103
"""
Unit test file for analyse.py
"""
import logging
from unittest.mock import MagicMock
import arrow
from pandas import 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
_STRATEGY = DefaultStrategy(config={})
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.object(
_STRATEGY, 'analyze_ticker',
return_value=DataFrame([{'buy': 1, 'sell': 0, 'date': arrow.utcnow()}])
)
assert _STRATEGY.get_signal(exchange, 'ETH/BTC', '5m') == (True, False)
mocker.patch.object(
_STRATEGY, 'analyze_ticker',
return_value=DataFrame([{'buy': 0, 'sell': 1, 'date': arrow.utcnow()}])
)
assert _STRATEGY.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.object(
_STRATEGY, 'analyze_ticker',
return_value=DataFrame([{'sell': 1, 'buy': 0, 'date': arrow.utcnow()}])
)
assert _STRATEGY.get_signal(exchange, 'ETH/BTC', '5m') == (False, True)
mocker.patch.object(
_STRATEGY, 'analyze_ticker',
return_value=DataFrame([{'sell': 0, 'buy': 1, 'date': arrow.utcnow()}])
)
assert _STRATEGY.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) == _STRATEGY.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.object(
_STRATEGY, 'analyze_ticker',
side_effect=ValueError('xyz')
)
assert (False, False) == _STRATEGY.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.object(
_STRATEGY, 'analyze_ticker',
return_value=DataFrame([])
)
assert (False, False) == _STRATEGY.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.object(
_STRATEGY, 'analyze_ticker',
return_value=DataFrame(ticks)
)
assert (False, False) == _STRATEGY.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.object(
_STRATEGY, 'analyze_ticker',
side_effect=Exception('invalid ticker history ')
)
assert _STRATEGY.get_signal(exchange, 'ETH/BTC', '5m') == (False, False)
def test_tickerdata_to_dataframe(default_conf) -> None:
"""
Test Analyze.tickerdata_to_dataframe() method
"""
strategy = DefaultStrategy(default_conf)
timerange = TimeRange(None, 'line', 0, -100)
tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m', timerange=timerange)
tickerlist = {'UNITTEST/BTC': tick}
data = strategy.tickerdata_to_dataframe(tickerlist)
assert len(data['UNITTEST/BTC']) == 99 # partial candle was removed

View File

@ -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):
@ -53,7 +64,7 @@ def test_load_strategy(result):
def test_load_strategy_invalid_directory(result, caplog):
resolver = StrategyResolver()
extra_dir = os.path.join('some', 'path')
resolver._load_strategy('TestStrategy', extra_dir)
resolver._load_strategy('TestStrategy', config={}, extra_dir=extra_dir)
assert (
'freqtrade.strategy.resolver',
@ -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):

View File

@ -1,198 +0,0 @@
# pragma pylint: disable=missing-docstring, C0103
"""
Unit test file for analyse.py
"""
import logging
from unittest.mock import MagicMock
import arrow
from pandas import DataFrame
from freqtrade.analyze import Analyze, SignalType
from freqtrade.arguments import TimeRange
from freqtrade.optimize.__init__ import load_tickerdata_file
from freqtrade.tests.conftest import get_patched_exchange, log_has
# Avoid to reinit the same object again and again
_ANALYZE = Analyze({'strategy': 'DefaultStrategy'})
def test_signaltype_object() -> None:
"""
Test the SignalType object has the mandatory Constants
:return: None
"""
assert hasattr(SignalType, 'BUY')
assert hasattr(SignalType, 'SELL')
def test_analyze_object() -> None:
"""
Test the Analyze object has the mandatory methods
:return: None
"""
assert hasattr(Analyze, 'parse_ticker_dataframe')
assert hasattr(Analyze, 'populate_indicators')
assert hasattr(Analyze, 'populate_buy_trend')
assert hasattr(Analyze, 'populate_sell_trend')
assert hasattr(Analyze, 'analyze_ticker')
assert hasattr(Analyze, 'get_signal')
assert hasattr(Analyze, 'should_sell')
assert hasattr(Analyze, 'min_roi_reached')
assert hasattr(Analyze, 'stop_loss_reached')
def test_dataframe_correct_length(result):
dataframe = Analyze.parse_ticker_dataframe(result)
assert len(result.index) - 1 == len(dataframe.index) # last partial candle removed
def test_dataframe_correct_columns(result):
assert result.columns.tolist() == \
['date', 'open', 'high', 'low', 'close', 'volume']
def test_populates_buy_trend(result):
# Load the default strategy for the unit test, because this logic is done in main.py
dataframe = _ANALYZE.populate_buy_trend(_ANALYZE.populate_indicators(result))
assert 'buy' in dataframe.columns
def test_populates_sell_trend(result):
# Load the default strategy for the unit test, because this logic is done in main.py
dataframe = _ANALYZE.populate_sell_trend(_ANALYZE.populate_indicators(result))
assert 'sell' in dataframe.columns
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)
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)
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_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_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 ')
)
)
assert _ANALYZE.get_signal(exchange, 'ETH/BTC', '5m') == (False, False)
def test_parse_ticker_dataframe(ticker_history):
columns = ['date', 'open', 'high', 'low', 'close', 'volume']
# Test file with BV data
dataframe = Analyze.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)
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

View File

@ -2,33 +2,31 @@
import pandas
from freqtrade.analyze import Analyze
from freqtrade.optimize import load_data
from freqtrade.strategy.resolver import StrategyResolver
_pairs = ['ETH/BTC']
def load_dataframe_pair(pairs):
def load_dataframe_pair(pairs, strategy):
ld = load_data(None, ticker_interval='5m', pairs=pairs)
assert isinstance(ld, dict)
assert isinstance(pairs[0], str)
dataframe = ld[pairs[0]]
analyze = Analyze({'strategy': 'DefaultStrategy'})
dataframe = analyze.analyze_ticker(dataframe)
dataframe = strategy.analyze_ticker(dataframe)
return dataframe
def test_dataframe_load():
StrategyResolver({'strategy': 'DefaultStrategy'})
dataframe = load_dataframe_pair(_pairs)
strategy = StrategyResolver({'strategy': 'DefaultStrategy'}).strategy
dataframe = load_dataframe_pair(_pairs, strategy)
assert isinstance(dataframe, pandas.core.frame.DataFrame)
def test_dataframe_columns_exists():
StrategyResolver({'strategy': 'DefaultStrategy'})
dataframe = load_dataframe_pair(_pairs)
strategy = StrategyResolver({'strategy': 'DefaultStrategy'}).strategy
dataframe = load_dataframe_pair(_pairs, strategy)
assert 'high' in dataframe.columns
assert 'low' in dataframe.columns
assert 'close' in dataframe.columns

View File

@ -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()
@ -316,9 +311,8 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
patch_RPCManager(mocker)
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
mocker.patch('freqtrade.freqtradebot.Analyze.get_stoploss', MagicMock(return_value=-0.05))
freqtrade = FreqtradeBot(default_conf)
freqtrade.strategy.stoploss = -0.05
# no pair found
mocker.patch(
'freqtrade.exchange.Exchange.get_markets',
@ -453,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(
@ -468,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()
@ -491,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(
@ -504,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()
@ -514,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']})
@ -530,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]
@ -541,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']})
@ -557,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
@ -567,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(
@ -584,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
@ -593,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(
@ -609,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()
@ -621,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(
@ -637,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()
@ -651,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(
@ -665,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()
@ -676,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(
@ -689,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
@ -717,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(
@ -730,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()
@ -739,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(
@ -750,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()
@ -763,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(
@ -776,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
@ -914,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',
@ -932,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()
@ -942,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']
@ -963,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(),
@ -977,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()
@ -986,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)
@ -994,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)
@ -1002,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)
@ -1010,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
@ -1024,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(
@ -1036,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()
@ -1048,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)
@ -1062,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(
@ -1073,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)
@ -1094,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(
@ -1106,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()
@ -1346,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(
@ -1358,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()
@ -1398,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)
@ -1410,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()
@ -1451,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(
@ -1462,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()
@ -1501,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(
@ -1512,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()
@ -1551,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(),
@ -1573,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
@ -1586,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(),
@ -1608,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
@ -1620,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(),
@ -1642,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
@ -1654,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(),
@ -1678,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
@ -1690,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(),
@ -1713,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
@ -1729,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(),
@ -1749,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()
@ -1766,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(),
@ -1786,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()
@ -1827,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(),
@ -1850,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
@ -1870,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))
@ -1883,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, '
@ -1897,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))
@ -1910,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, '
@ -1923,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))
@ -1937,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
@ -1949,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))
@ -1963,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
@ -1972,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))
@ -1986,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, '
@ -2000,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))
@ -2015,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, '
@ -2029,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))
@ -2043,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
@ -2054,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))
@ -2068,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
@ -2076,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))
@ -2094,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

View File

@ -7,10 +7,11 @@ Unit test file for misc.py
import datetime
from unittest.mock import MagicMock
from freqtrade.analyze import Analyze
from freqtrade.exchange.exchange_helpers 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
from freqtrade.strategy.default_strategy import DefaultStrategy
def test_shorten_date() -> None:
@ -28,7 +29,7 @@ def test_datesarray_to_datetimearray(ticker_history):
Test datesarray_to_datetimearray() function
:return: None
"""
dataframes = Analyze.parse_ticker_dataframe(ticker_history)
dataframes = parse_ticker_dataframe(ticker_history)
dates = datesarray_to_datetimearray(dataframes['date'])
assert isinstance(dates[0], datetime.datetime)
@ -47,10 +48,10 @@ def test_common_datearray(default_conf) -> None:
Test common_datearray()
:return: None
"""
analyze = Analyze(default_conf)
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)

View File

@ -40,11 +40,11 @@ from plotly.offline import plot
import freqtrade.optimize as optimize
from freqtrade import persistence
from freqtrade.analyze import Analyze
from freqtrade.arguments import Arguments, TimeRange
from freqtrade.exchange import Exchange
from freqtrade.optimize.backtesting import setup_configuration
from freqtrade.persistence import Trade
from freqtrade.strategy.resolver import StrategyResolver
logger = logging.getLogger(__name__)
_CONF: Dict[str, Any] = {}
@ -122,7 +122,7 @@ def plot_analyzed_dataframe(args: Namespace) -> None:
# Load the strategy
try:
analyze = Analyze(_CONF)
strategy = StrategyResolver(_CONF).strategy
exchange = Exchange(_CONF)
except AttributeError:
logger.critical(
@ -132,7 +132,7 @@ def plot_analyzed_dataframe(args: Namespace) -> None:
exit()
# Set the ticker to use
tick_interval = analyze.get_ticker_interval()
tick_interval = strategy.ticker_interval
# Load pair tickers
tickers = {}
@ -156,11 +156,11 @@ def plot_analyzed_dataframe(args: Namespace) -> None:
# Get trades already made from the DB
trades = load_trades(args, pair, timerange)
dataframes = analyze.tickerdata_to_dataframe(tickers)
dataframes = strategy.tickerdata_to_dataframe(tickers)
dataframe = dataframes[pair]
dataframe = analyze.populate_buy_trend(dataframe)
dataframe = analyze.populate_sell_trend(dataframe)
dataframe = strategy.populate_buy_trend(dataframe)
dataframe = strategy.populate_sell_trend(dataframe)
if len(dataframe.index) > args.plot_limit:
logger.warning('Ticker contained more than %s candles as defined '

View File

@ -26,9 +26,8 @@ import plotly.graph_objs as go
from freqtrade.arguments import Arguments
from freqtrade.configuration import Configuration
from freqtrade.analyze import Analyze
from freqtrade import constants
from freqtrade.strategy.resolver import StrategyResolver
import freqtrade.optimize as optimize
import freqtrade.misc as misc
@ -87,7 +86,8 @@ def plot_profit(args: Namespace) -> None:
# Init strategy
try:
analyze = Analyze({'strategy': config.get('strategy')})
strategy = StrategyResolver({'strategy': config.get('strategy')}).strategy
except AttributeError:
logger.critical(
'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"',
@ -113,7 +113,7 @@ def plot_profit(args: Namespace) -> None:
else:
filter_pairs = config['exchange']['pair_whitelist']
tick_interval = analyze.strategy.ticker_interval
tick_interval = strategy.ticker_interval
pairs = config['exchange']['pair_whitelist']
if filter_pairs:
@ -127,7 +127,7 @@ def plot_profit(args: Namespace) -> None:
refresh_pairs=False,
timerange=timerange
)
dataframes = analyze.tickerdata_to_dataframe(tickers)
dataframes = strategy.tickerdata_to_dataframe(tickers)
# NOTE: the dataframes are of unequal length,
# 'dates' is an merged date array of them all.

View File

@ -12,6 +12,7 @@ import numpy # noqa
# This class is a sample. Feel free to customize it.
class TestStrategy(IStrategy):
__test__ = False # pytest expects to find tests here because of the name
"""
This is a test strategy to inspire you.
More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md