commit
64f933477d
@ -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()}
|
|
33
freqtrade/exchange/exchange_helpers.py
Normal file
33
freqtrade/exchange/exchange_helpers.py
Normal 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
|
@ -15,12 +15,12 @@ from cachetools import TTLCache, cached
|
|||||||
|
|
||||||
from freqtrade import (DependencyException, OperationalException,
|
from freqtrade import (DependencyException, OperationalException,
|
||||||
TemporaryError, __version__, constants, persistence)
|
TemporaryError, __version__, constants, persistence)
|
||||||
from freqtrade.analyze import Analyze
|
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.fiat_convert import CryptoToFiatConverter
|
from freqtrade.fiat_convert import CryptoToFiatConverter
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
from freqtrade.rpc import RPCManager, RPCMessageType
|
from freqtrade.rpc import RPCManager, RPCMessageType
|
||||||
from freqtrade.state import State
|
from freqtrade.state import State
|
||||||
|
from freqtrade.strategy.resolver import IStrategy, StrategyResolver
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -48,7 +48,7 @@ class FreqtradeBot(object):
|
|||||||
|
|
||||||
# Init objects
|
# Init objects
|
||||||
self.config = config
|
self.config = config
|
||||||
self.analyze = Analyze(self.config)
|
self.strategy: IStrategy = StrategyResolver(self.config).strategy
|
||||||
self.fiat_converter = CryptoToFiatConverter()
|
self.fiat_converter = CryptoToFiatConverter()
|
||||||
self.rpc: RPCManager = RPCManager(self)
|
self.rpc: RPCManager = RPCManager(self)
|
||||||
self.persistence = None
|
self.persistence = None
|
||||||
@ -297,8 +297,8 @@ class FreqtradeBot(object):
|
|||||||
return None
|
return None
|
||||||
|
|
||||||
amount_reserve_percent = 1 - 0.05 # reserve 5% + stoploss
|
amount_reserve_percent = 1 - 0.05 # reserve 5% + stoploss
|
||||||
if self.analyze.get_stoploss() is not None:
|
if self.strategy.stoploss is not None:
|
||||||
amount_reserve_percent += self.analyze.get_stoploss()
|
amount_reserve_percent += self.strategy.stoploss
|
||||||
# it should not be more than 50%
|
# it should not be more than 50%
|
||||||
amount_reserve_percent = max(amount_reserve_percent, 0.5)
|
amount_reserve_percent = max(amount_reserve_percent, 0.5)
|
||||||
return min(min_stake_amounts)/amount_reserve_percent
|
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
|
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
|
: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()
|
stake_amount = self._get_trade_stake_amount()
|
||||||
|
|
||||||
if not stake_amount:
|
if not stake_amount:
|
||||||
@ -332,7 +332,7 @@ class FreqtradeBot(object):
|
|||||||
|
|
||||||
# Pick pair based on buy signals
|
# Pick pair based on buy signals
|
||||||
for _pair in whitelist:
|
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:
|
if buy and not sell:
|
||||||
return self.execute_buy(_pair, stake_amount)
|
return self.execute_buy(_pair, stake_amount)
|
||||||
return False
|
return False
|
||||||
@ -502,10 +502,10 @@ class FreqtradeBot(object):
|
|||||||
(buy, sell) = (False, False)
|
(buy, sell) = (False, False)
|
||||||
experimental = self.config.get('experimental', {})
|
experimental = self.config.get('experimental', {})
|
||||||
if experimental.get('use_sell_signal') or experimental.get('ignore_roi_if_buy_signal'):
|
if experimental.get('use_sell_signal') or experimental.get('ignore_roi_if_buy_signal'):
|
||||||
(buy, sell) = self.analyze.get_signal(self.exchange,
|
(buy, sell) = self.strategy.get_signal(self.exchange,
|
||||||
trade.pair, self.analyze.get_ticker_interval())
|
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)
|
self.execute_sell(trade, current_rate)
|
||||||
return True
|
return True
|
||||||
logger.info('Found no sell signals for whitelisted currencies. Trying again..')
|
logger.info('Found no sell signals for whitelisted currencies. Trying again..')
|
||||||
|
@ -15,12 +15,12 @@ from tabulate import tabulate
|
|||||||
|
|
||||||
import freqtrade.optimize as optimize
|
import freqtrade.optimize as optimize
|
||||||
from freqtrade import DependencyException, constants
|
from freqtrade import DependencyException, constants
|
||||||
from freqtrade.analyze import Analyze
|
|
||||||
from freqtrade.arguments import Arguments
|
from freqtrade.arguments import Arguments
|
||||||
from freqtrade.configuration import Configuration
|
from freqtrade.configuration import Configuration
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.misc import file_dump_json
|
from freqtrade.misc import file_dump_json
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
|
from freqtrade.strategy.resolver import IStrategy, StrategyResolver
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@ -52,11 +52,11 @@ class Backtesting(object):
|
|||||||
"""
|
"""
|
||||||
def __init__(self, config: Dict[str, Any]) -> None:
|
def __init__(self, config: Dict[str, Any]) -> None:
|
||||||
self.config = config
|
self.config = config
|
||||||
self.analyze = Analyze(self.config)
|
self.strategy: IStrategy = StrategyResolver(self.config).strategy
|
||||||
self.ticker_interval = self.analyze.strategy.ticker_interval
|
self.ticker_interval = self.strategy.ticker_interval
|
||||||
self.tickerdata_to_dataframe = self.analyze.tickerdata_to_dataframe
|
self.tickerdata_to_dataframe = self.strategy.tickerdata_to_dataframe
|
||||||
self.populate_buy_trend = self.analyze.populate_buy_trend
|
self.populate_buy_trend = self.strategy.populate_buy_trend
|
||||||
self.populate_sell_trend = self.analyze.populate_sell_trend
|
self.populate_sell_trend = self.strategy.populate_sell_trend
|
||||||
|
|
||||||
# Reset keys for backtesting
|
# Reset keys for backtesting
|
||||||
self.config['exchange']['key'] = ''
|
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
|
trade_count_lock[sell_row.date] = trade_count_lock.get(sell_row.date, 0) + 1
|
||||||
|
|
||||||
buy_signal = sell_row.buy
|
buy_signal = sell_row.buy
|
||||||
if self.analyze.should_sell(trade, sell_row.open, sell_row.date, buy_signal,
|
if self.strategy.should_sell(trade, sell_row.open, sell_row.date, buy_signal,
|
||||||
sell_row.sell):
|
sell_row.sell):
|
||||||
|
|
||||||
return BacktestResult(pair=pair,
|
return BacktestResult(pair=pair,
|
||||||
profit_percent=trade.calc_profit_percent(rate=sell_row.open),
|
profit_percent=trade.calc_profit_percent(rate=sell_row.open),
|
||||||
|
@ -267,13 +267,13 @@ class Hyperopt(Backtesting):
|
|||||||
params = self.get_args(_params)
|
params = self.get_args(_params)
|
||||||
|
|
||||||
if self.has_space('roi'):
|
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'):
|
if self.has_space('buy'):
|
||||||
self.populate_buy_trend = self.buy_strategy_generator(params)
|
self.populate_buy_trend = self.buy_strategy_generator(params)
|
||||||
|
|
||||||
if self.has_space('stoploss'):
|
if self.has_space('stoploss'):
|
||||||
self.analyze.strategy.stoploss = params['stoploss']
|
self.strategy.stoploss = params['stoploss']
|
||||||
|
|
||||||
processed = load(TICKERDATA_PICKLE)
|
processed = load(TICKERDATA_PICKLE)
|
||||||
results = self.backtest(
|
results = self.backtest(
|
||||||
@ -351,7 +351,7 @@ class Hyperopt(Backtesting):
|
|||||||
)
|
)
|
||||||
|
|
||||||
if self.has_space('buy'):
|
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)
|
dump(self.tickerdata_to_dataframe(data), TICKERDATA_PICKLE)
|
||||||
self.exchange = None # type: ignore
|
self.exchange = None # type: ignore
|
||||||
self.load_previous_results()
|
self.load_previous_results()
|
||||||
|
@ -7,7 +7,7 @@ from freqtrade.strategy.interface import IStrategy
|
|||||||
logger = logging.getLogger(__name__)
|
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
|
Imports given Strategy instance to global scope
|
||||||
of freqtrade.strategy and returns an instance of it
|
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
|
# Modify global scope to declare class
|
||||||
globals()[name] = clazz
|
globals()[name] = clazz
|
||||||
|
|
||||||
return clazz()
|
return clazz(config)
|
||||||
|
@ -2,11 +2,30 @@
|
|||||||
IStrategy interface
|
IStrategy interface
|
||||||
This module defines the interface to apply for strategies
|
This module defines the interface to apply for strategies
|
||||||
"""
|
"""
|
||||||
|
import logging
|
||||||
from abc import ABC, abstractmethod
|
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 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):
|
class IStrategy(ABC):
|
||||||
"""
|
"""
|
||||||
@ -23,6 +42,9 @@ class IStrategy(ABC):
|
|||||||
stoploss: float
|
stoploss: float
|
||||||
ticker_interval: str
|
ticker_interval: str
|
||||||
|
|
||||||
|
def __init__(self, config: dict) -> None:
|
||||||
|
self.config = config
|
||||||
|
|
||||||
@abstractmethod
|
@abstractmethod
|
||||||
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
|
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
|
||||||
"""
|
"""
|
||||||
@ -46,3 +68,169 @@ class IStrategy(ABC):
|
|||||||
:param dataframe: DataFrame
|
:param dataframe: DataFrame
|
||||||
:return: DataFrame with sell column
|
:return: DataFrame with sell column
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
def analyze_ticker(self, ticker_history: List[Dict]) -> DataFrame:
|
||||||
|
"""
|
||||||
|
Parses the given ticker history and returns a populated DataFrame
|
||||||
|
add several TA indicators and buy signal to it
|
||||||
|
:return DataFrame with ticker data and indicator data
|
||||||
|
"""
|
||||||
|
dataframe = parse_ticker_dataframe(ticker_history)
|
||||||
|
dataframe = self.populate_indicators(dataframe)
|
||||||
|
dataframe = self.populate_buy_trend(dataframe)
|
||||||
|
dataframe = self.populate_sell_trend(dataframe)
|
||||||
|
return dataframe
|
||||||
|
|
||||||
|
def get_signal(self, exchange: Exchange, pair: str, interval: str) -> Tuple[bool, bool]:
|
||||||
|
"""
|
||||||
|
Calculates current signal based several technical analysis indicators
|
||||||
|
:param pair: pair in format ANT/BTC
|
||||||
|
:param interval: Interval to use (in min)
|
||||||
|
:return: (Buy, Sell) A bool-tuple indicating buy/sell signal
|
||||||
|
"""
|
||||||
|
ticker_hist = exchange.get_ticker_history(pair, interval)
|
||||||
|
if not ticker_hist:
|
||||||
|
logger.warning('Empty ticker history for pair %s', pair)
|
||||||
|
return False, False
|
||||||
|
|
||||||
|
try:
|
||||||
|
dataframe = self.analyze_ticker(ticker_hist)
|
||||||
|
except ValueError as error:
|
||||||
|
logger.warning(
|
||||||
|
'Unable to analyze ticker for pair %s: %s',
|
||||||
|
pair,
|
||||||
|
str(error)
|
||||||
|
)
|
||||||
|
return False, False
|
||||||
|
except Exception as error:
|
||||||
|
logger.exception(
|
||||||
|
'Unexpected error when analyzing ticker for pair %s: %s',
|
||||||
|
pair,
|
||||||
|
str(error)
|
||||||
|
)
|
||||||
|
return False, False
|
||||||
|
|
||||||
|
if dataframe.empty:
|
||||||
|
logger.warning('Empty dataframe for pair %s', pair)
|
||||||
|
return False, False
|
||||||
|
|
||||||
|
latest = dataframe.iloc[-1]
|
||||||
|
|
||||||
|
# Check if dataframe is out of date
|
||||||
|
signal_date = arrow.get(latest['date'])
|
||||||
|
interval_minutes = constants.TICKER_INTERVAL_MINUTES[interval]
|
||||||
|
if signal_date < (arrow.utcnow().shift(minutes=-(interval_minutes * 2 + 5))):
|
||||||
|
logger.warning(
|
||||||
|
'Outdated history for pair %s. Last tick is %s minutes old',
|
||||||
|
pair,
|
||||||
|
(arrow.utcnow() - signal_date).seconds // 60
|
||||||
|
)
|
||||||
|
return False, False
|
||||||
|
|
||||||
|
(buy, sell) = latest[SignalType.BUY.value] == 1, latest[SignalType.SELL.value] == 1
|
||||||
|
logger.debug(
|
||||||
|
'trigger: %s (pair=%s) buy=%s sell=%s',
|
||||||
|
latest['date'],
|
||||||
|
pair,
|
||||||
|
str(buy),
|
||||||
|
str(sell)
|
||||||
|
)
|
||||||
|
return buy, sell
|
||||||
|
|
||||||
|
def should_sell(self, trade: Trade, rate: float, date: datetime, buy: bool, sell: bool) -> bool:
|
||||||
|
"""
|
||||||
|
This function evaluate if on the condition required to trigger a sell has been reached
|
||||||
|
if the threshold is reached and updates the trade record.
|
||||||
|
:return: True if trade should be sold, False otherwise
|
||||||
|
"""
|
||||||
|
current_profit = trade.calc_profit_percent(rate)
|
||||||
|
if self.stop_loss_reached(current_rate=rate, trade=trade, current_time=date,
|
||||||
|
current_profit=current_profit):
|
||||||
|
return True
|
||||||
|
|
||||||
|
experimental = self.config.get('experimental', {})
|
||||||
|
|
||||||
|
if buy and experimental.get('ignore_roi_if_buy_signal', False):
|
||||||
|
logger.debug('Buy signal still active - not selling.')
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Check if minimal roi has been reached and no longer in buy conditions (avoiding a fee)
|
||||||
|
if self.min_roi_reached(trade=trade, current_profit=current_profit, current_time=date):
|
||||||
|
logger.debug('Required profit reached. Selling..')
|
||||||
|
return True
|
||||||
|
|
||||||
|
if experimental.get('sell_profit_only', False):
|
||||||
|
logger.debug('Checking if trade is profitable..')
|
||||||
|
if trade.calc_profit(rate=rate) <= 0:
|
||||||
|
return False
|
||||||
|
if sell and not buy and experimental.get('use_sell_signal', False):
|
||||||
|
logger.debug('Sell signal received. Selling..')
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def stop_loss_reached(self, current_rate: float, trade: Trade, current_time: datetime,
|
||||||
|
current_profit: float) -> bool:
|
||||||
|
"""
|
||||||
|
Based on current profit of the trade and configured (trailing) stoploss,
|
||||||
|
decides to sell or not
|
||||||
|
"""
|
||||||
|
|
||||||
|
trailing_stop = self.config.get('trailing_stop', False)
|
||||||
|
|
||||||
|
trade.adjust_stop_loss(trade.open_rate, self.stoploss, initial=True)
|
||||||
|
|
||||||
|
# evaluate if the stoploss was hit
|
||||||
|
if self.stoploss is not None and trade.stop_loss >= current_rate:
|
||||||
|
|
||||||
|
if trailing_stop:
|
||||||
|
logger.debug(
|
||||||
|
f"HIT STOP: current price at {current_rate:.6f}, "
|
||||||
|
f"stop loss is {trade.stop_loss:.6f}, "
|
||||||
|
f"initial stop loss was at {trade.initial_stop_loss:.6f}, "
|
||||||
|
f"trade opened at {trade.open_rate:.6f}")
|
||||||
|
logger.debug(f"trailing stop saved {trade.stop_loss - trade.initial_stop_loss:.6f}")
|
||||||
|
|
||||||
|
logger.debug('Stop loss hit.')
|
||||||
|
return True
|
||||||
|
|
||||||
|
# update the stop loss afterwards, after all by definition it's supposed to be hanging
|
||||||
|
if trailing_stop:
|
||||||
|
|
||||||
|
# check if we have a special stop loss for positive condition
|
||||||
|
# and if profit is positive
|
||||||
|
stop_loss_value = self.stoploss
|
||||||
|
if 'trailing_stop_positive' in self.config and current_profit > 0:
|
||||||
|
|
||||||
|
# Ignore mypy error check in configuration that this is a float
|
||||||
|
stop_loss_value = self.config.get('trailing_stop_positive') # type: ignore
|
||||||
|
logger.debug(f"using positive stop loss mode: {stop_loss_value} "
|
||||||
|
f"since we have profit {current_profit}")
|
||||||
|
|
||||||
|
trade.adjust_stop_loss(current_rate, stop_loss_value)
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def min_roi_reached(self, trade: Trade, current_profit: float, current_time: datetime) -> bool:
|
||||||
|
"""
|
||||||
|
Based an earlier trade and current price and ROI configuration, decides whether bot should
|
||||||
|
sell
|
||||||
|
:return True if bot should sell at current rate
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Check if time matches and current rate is above threshold
|
||||||
|
time_diff = (current_time.timestamp() - trade.open_date.timestamp()) / 60
|
||||||
|
for duration, threshold in self.minimal_roi.items():
|
||||||
|
if time_diff <= duration:
|
||||||
|
return False
|
||||||
|
if current_profit > threshold:
|
||||||
|
return True
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
def tickerdata_to_dataframe(self, tickerdata: Dict[str, List]) -> Dict[str, DataFrame]:
|
||||||
|
"""
|
||||||
|
Creates a dataframe and populates indicators for given ticker data
|
||||||
|
"""
|
||||||
|
return {pair: self.populate_indicators(parse_ticker_dataframe(pair_data))
|
||||||
|
for pair, pair_data in tickerdata.items()}
|
||||||
|
@ -34,6 +34,7 @@ class StrategyResolver(object):
|
|||||||
# Verify the strategy is in the configuration, otherwise fallback to the default strategy
|
# Verify the strategy is in the configuration, otherwise fallback to the default strategy
|
||||||
strategy_name = config.get('strategy') or constants.DEFAULT_STRATEGY
|
strategy_name = config.get('strategy') or constants.DEFAULT_STRATEGY
|
||||||
self.strategy: IStrategy = self._load_strategy(strategy_name,
|
self.strategy: IStrategy = self._load_strategy(strategy_name,
|
||||||
|
config=config,
|
||||||
extra_dir=config.get('strategy_path'))
|
extra_dir=config.get('strategy_path'))
|
||||||
|
|
||||||
# Set attributes
|
# Set attributes
|
||||||
@ -68,10 +69,11 @@ class StrategyResolver(object):
|
|||||||
self.strategy.stoploss = float(self.strategy.stoploss)
|
self.strategy.stoploss = float(self.strategy.stoploss)
|
||||||
|
|
||||||
def _load_strategy(
|
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.
|
Search and loads the specified strategy.
|
||||||
:param strategy_name: name of the module to import
|
: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
|
:param extra_dir: additional directory to search for the given strategy
|
||||||
:return: Strategy instance or None
|
:return: Strategy instance or None
|
||||||
"""
|
"""
|
||||||
@ -87,10 +89,10 @@ class StrategyResolver(object):
|
|||||||
|
|
||||||
for path in abs_paths:
|
for path in abs_paths:
|
||||||
try:
|
try:
|
||||||
strategy = self._search_strategy(path, strategy_name)
|
strategy = self._search_strategy(path, strategy_name=strategy_name, config=config)
|
||||||
if strategy:
|
if strategy:
|
||||||
logger.info('Using resolved strategy %s from \'%s\'', strategy_name, path)
|
logger.info('Using resolved strategy %s from \'%s\'', strategy_name, path)
|
||||||
return import_strategy(strategy)
|
return import_strategy(strategy, config=config)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
logger.warning('Path "%s" does not exist', path)
|
logger.warning('Path "%s" does not exist', path)
|
||||||
|
|
||||||
@ -120,7 +122,7 @@ class StrategyResolver(object):
|
|||||||
return next(valid_strategies_gen, None)
|
return next(valid_strategies_gen, None)
|
||||||
|
|
||||||
@staticmethod
|
@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
|
Search for the strategy_name in the given directory
|
||||||
:param directory: relative or absolute directory path
|
:param directory: relative or absolute directory path
|
||||||
@ -136,5 +138,5 @@ class StrategyResolver(object):
|
|||||||
os.path.abspath(os.path.join(directory, entry)), strategy_name
|
os.path.abspath(os.path.join(directory, entry)), strategy_name
|
||||||
)
|
)
|
||||||
if strategy:
|
if strategy:
|
||||||
return strategy()
|
return strategy(config)
|
||||||
return None
|
return None
|
||||||
|
@ -12,7 +12,7 @@ from jsonschema import validate
|
|||||||
from telegram import Chat, Message, Update
|
from telegram import Chat, Message, Update
|
||||||
|
|
||||||
from freqtrade import constants
|
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.exchange import Exchange
|
||||||
from freqtrade.freqtradebot import FreqtradeBot
|
from freqtrade.freqtradebot import FreqtradeBot
|
||||||
|
|
||||||
@ -20,7 +20,7 @@ logging.getLogger('').setLevel(logging.INFO)
|
|||||||
|
|
||||||
|
|
||||||
def log_has(line, logs):
|
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
|
# and we want to match line against foobar in the tuple
|
||||||
return reduce(lambda a, b: a or b,
|
return reduce(lambda a, b: a or b,
|
||||||
filter(lambda x: x[2] == line, logs),
|
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})
|
# mocker.patch('freqtrade.fiat_convert.Market', {'price_usd': 12345.0})
|
||||||
patch_coinmarketcap(mocker, {'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.RPCManager', MagicMock())
|
||||||
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
|
||||||
patch_exchange(mocker, None)
|
patch_exchange(mocker, None)
|
||||||
mocker.patch('freqtrade.freqtradebot.RPCManager._init', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.RPCManager._init', MagicMock())
|
||||||
mocker.patch('freqtrade.freqtradebot.RPCManager.send_msg', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.RPCManager.send_msg', MagicMock())
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.get_signal', MagicMock())
|
|
||||||
|
|
||||||
return FreqtradeBot(config)
|
return FreqtradeBot(config)
|
||||||
|
|
||||||
@ -617,7 +615,7 @@ def tickers():
|
|||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def result():
|
def result():
|
||||||
with open('freqtrade/tests/testdata/UNITTEST_BTC-1m.json') as data_file:
|
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:
|
# FIX:
|
||||||
# Create an fixture/function
|
# Create an fixture/function
|
||||||
|
25
freqtrade/tests/exchange/test_exchange_helpers.py
Normal file
25
freqtrade/tests/exchange/test_exchange_helpers.py
Normal 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
|
@ -13,11 +13,11 @@ import pytest
|
|||||||
from arrow import Arrow
|
from arrow import Arrow
|
||||||
|
|
||||||
from freqtrade import DependencyException, constants, optimize
|
from freqtrade import DependencyException, constants, optimize
|
||||||
from freqtrade.analyze import Analyze
|
|
||||||
from freqtrade.arguments import Arguments, TimeRange
|
from freqtrade.arguments import Arguments, TimeRange
|
||||||
from freqtrade.optimize.backtesting import (Backtesting, setup_configuration,
|
from freqtrade.optimize.backtesting import (Backtesting, setup_configuration,
|
||||||
start)
|
start)
|
||||||
from freqtrade.tests.conftest import log_has, patch_exchange
|
from freqtrade.tests.conftest import log_has, patch_exchange
|
||||||
|
from freqtrade.strategy.default_strategy import DefaultStrategy
|
||||||
|
|
||||||
|
|
||||||
def get_args(args) -> List[str]:
|
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))
|
get_fee = mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5))
|
||||||
backtesting = Backtesting(default_conf)
|
backtesting = Backtesting(default_conf)
|
||||||
assert backtesting.config == default_conf
|
assert backtesting.config == default_conf
|
||||||
assert isinstance(backtesting.analyze, Analyze)
|
|
||||||
assert backtesting.ticker_interval == '5m'
|
assert backtesting.ticker_interval == '5m'
|
||||||
assert callable(backtesting.tickerdata_to_dataframe)
|
assert callable(backtesting.tickerdata_to_dataframe)
|
||||||
assert callable(backtesting.populate_buy_trend)
|
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)
|
data = backtesting.tickerdata_to_dataframe(tickerlist)
|
||||||
assert len(data['UNITTEST/BTC']) == 99
|
assert len(data['UNITTEST/BTC']) == 99
|
||||||
|
|
||||||
# Load Analyze to compare the result between Backtesting function and Analyze are the same
|
# Load strategy to compare the result between Backtesting function and strategy are the same
|
||||||
analyze = Analyze(default_conf)
|
strategy = DefaultStrategy(default_conf)
|
||||||
data2 = analyze.tickerdata_to_dataframe(tickerlist)
|
data2 = strategy.tickerdata_to_dataframe(tickerlist)
|
||||||
assert data['UNITTEST/BTC'].equals(data2['UNITTEST/BTC'])
|
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):
|
def get_timeframe(input1, input2):
|
||||||
return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59)
|
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.optimize.load_data', mocked_load_data)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history')
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history')
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
@ -453,7 +451,6 @@ def test_backtesting_start_no_data(default_conf, mocker, caplog) -> None:
|
|||||||
def get_timeframe(input1, input2):
|
def get_timeframe(input1, input2):
|
||||||
return Arrow(2017, 11, 14, 21, 17), Arrow(2017, 11, 14, 22, 59)
|
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.optimize.load_data', MagicMock(return_value={}))
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history')
|
mocker.patch('freqtrade.exchange.Exchange.get_ticker_history')
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
|
@ -30,7 +30,6 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
|
|||||||
"""
|
"""
|
||||||
Test rpc_trade_status() method
|
Test rpc_trade_status() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -42,6 +41,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
|
|
||||||
freqtradebot.state = State.STOPPED
|
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
|
Test rpc_status_table() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -86,6 +85,7 @@ def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
|
|
||||||
freqtradebot.state = State.STOPPED
|
freqtradebot.state = State.STOPPED
|
||||||
@ -108,7 +108,6 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee,
|
|||||||
"""
|
"""
|
||||||
Test rpc_daily_profit() method
|
Test rpc_daily_profit() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -120,6 +119,7 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee,
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
stake_currency = default_conf['stake_currency']
|
stake_currency = default_conf['stake_currency']
|
||||||
fiat_display_currency = default_conf['fiat_display_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
|
Test rpc_trade_statistics() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.fiat_convert.Market',
|
'freqtrade.fiat_convert.Market',
|
||||||
ticker=MagicMock(return_value={'price_usd': 15000.0}),
|
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)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
stake_currency = default_conf['stake_currency']
|
stake_currency = default_conf['stake_currency']
|
||||||
fiat_display_currency = default_conf['fiat_display_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
|
Test rpc_trade_statistics() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.fiat_convert.Market',
|
'freqtrade.fiat_convert.Market',
|
||||||
ticker=MagicMock(return_value={'price_usd': 15000.0}),
|
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)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
stake_currency = default_conf['stake_currency']
|
stake_currency = default_conf['stake_currency']
|
||||||
fiat_display_currency = default_conf['fiat_display_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(
|
mocker.patch.multiple(
|
||||||
'freqtrade.fiat_convert.Market',
|
'freqtrade.fiat_convert.Market',
|
||||||
ticker=MagicMock(return_value={'price_usd': 15000.0}),
|
ticker=MagicMock(return_value={'price_usd': 15000.0}),
|
||||||
@ -323,6 +322,7 @@ def test_rpc_balance_handle(default_conf, mocker):
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
|
|
||||||
result = rpc._rpc_balance(default_conf['fiat_display_currency'])
|
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
|
Test rpc_start() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -352,6 +351,7 @@ def test_rpc_start(mocker, default_conf) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
freqtradebot.state = State.STOPPED
|
freqtradebot.state = State.STOPPED
|
||||||
|
|
||||||
@ -368,7 +368,6 @@ def test_rpc_stop(mocker, default_conf) -> None:
|
|||||||
"""
|
"""
|
||||||
Test rpc_stop() method
|
Test rpc_stop() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -378,6 +377,7 @@ def test_rpc_stop(mocker, default_conf) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
freqtradebot.state = State.RUNNING
|
freqtradebot.state = State.RUNNING
|
||||||
|
|
||||||
@ -395,7 +395,6 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker, markets) -> None:
|
|||||||
"""
|
"""
|
||||||
Test rpc_forcesell() method
|
Test rpc_forcesell() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
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)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
|
|
||||||
freqtradebot.state = State.STOPPED
|
freqtradebot.state = State.STOPPED
|
||||||
@ -499,7 +499,6 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
|||||||
"""
|
"""
|
||||||
Test rpc_performance() method
|
Test rpc_performance() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -512,6 +511,7 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
@ -538,7 +538,6 @@ def test_rpc_count(mocker, default_conf, ticker, fee, markets) -> None:
|
|||||||
"""
|
"""
|
||||||
Test rpc_count() method
|
Test rpc_count() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -551,6 +550,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee, markets) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
rpc = RPC(freqtradebot)
|
rpc = RPC(freqtradebot)
|
||||||
|
|
||||||
trades = rpc._rpc_count()
|
trades = rpc._rpc_count()
|
||||||
|
@ -102,7 +102,6 @@ def test_authorized_only(default_conf, mocker, caplog) -> None:
|
|||||||
"""
|
"""
|
||||||
Test authorized_only() method when we are authorized
|
Test authorized_only() method when we are authorized
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
patch_exchange(mocker, None)
|
patch_exchange(mocker, None)
|
||||||
|
|
||||||
@ -112,7 +111,9 @@ def test_authorized_only(default_conf, mocker, caplog) -> None:
|
|||||||
|
|
||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
conf['telegram']['enabled'] = False
|
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)
|
dummy.dummy_handler(bot=MagicMock(), update=update)
|
||||||
assert dummy.state['called'] is True
|
assert dummy.state['called'] is True
|
||||||
assert log_has(
|
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
|
Test authorized_only() method when we are unauthorized
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
patch_exchange(mocker, None)
|
patch_exchange(mocker, None)
|
||||||
chat = Chat(0xdeadbeef, 0)
|
chat = Chat(0xdeadbeef, 0)
|
||||||
@ -142,7 +142,9 @@ def test_authorized_only_unauthorized(default_conf, mocker, caplog) -> None:
|
|||||||
|
|
||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
conf['telegram']['enabled'] = False
|
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)
|
dummy.dummy_handler(bot=MagicMock(), update=update)
|
||||||
assert dummy.state['called'] is False
|
assert dummy.state['called'] is False
|
||||||
assert not log_has(
|
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
|
Test authorized_only() method when an exception is thrown
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
|
|
||||||
@ -172,7 +173,11 @@ def test_authorized_only_exception(default_conf, mocker, caplog) -> None:
|
|||||||
|
|
||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
conf['telegram']['enabled'] = False
|
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)
|
dummy.dummy_exception(bot=MagicMock(), update=update)
|
||||||
assert dummy.state['called'] is False
|
assert dummy.state['called'] is False
|
||||||
assert not log_has(
|
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']['enabled'] = False
|
||||||
conf['telegram']['chat_id'] = 123
|
conf['telegram']['chat_id'] = 123
|
||||||
|
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
|
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -233,6 +237,7 @@ def test_status(default_conf, update, mocker, fee, ticker, markets) -> None:
|
|||||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(conf)
|
freqtradebot = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
@ -252,7 +257,6 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No
|
|||||||
"""
|
"""
|
||||||
Test _status() method
|
Test _status() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'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())
|
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
|
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
freqtradebot.state = State.STOPPED
|
freqtradebot.state = State.STOPPED
|
||||||
@ -299,7 +305,6 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker)
|
|||||||
"""
|
"""
|
||||||
Test _status_table() method
|
Test _status_table() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
@ -320,6 +325,8 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker)
|
|||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
conf['stake_amount'] = 15.0
|
conf['stake_amount'] = 15.0
|
||||||
freqtradebot = FreqtradeBot(conf)
|
freqtradebot = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
|
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
freqtradebot.state = State.STOPPED
|
freqtradebot.state = State.STOPPED
|
||||||
@ -353,7 +360,6 @@ def test_daily_handle(default_conf, update, ticker, limit_buy_order, fee,
|
|||||||
"""
|
"""
|
||||||
Test _daily() method
|
Test _daily() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.fiat_convert.CryptoToFiatConverter._find_price',
|
'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())
|
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
@ -427,7 +434,6 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None:
|
|||||||
"""
|
"""
|
||||||
Test _daily() method
|
Test _daily() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
@ -443,6 +449,7 @@ def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None:
|
|||||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
# Try invalid data
|
# Try invalid data
|
||||||
@ -466,7 +473,6 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
|
|||||||
"""
|
"""
|
||||||
Test _profit() method
|
Test _profit() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||||
mocker.patch.multiple(
|
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())
|
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
telegram._profit(bot=MagicMock(), update=update)
|
telegram._profit(bot=MagicMock(), update=update)
|
||||||
@ -568,7 +575,6 @@ def test_telegram_balance_handle(default_conf, update, mocker) -> None:
|
|||||||
'last': 0.1,
|
'last': 0.1,
|
||||||
}
|
}
|
||||||
|
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
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_balances', return_value=mock_balance)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_ticker', side_effect=mock_ticker)
|
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)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
|
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
telegram._balance(bot=MagicMock(), update=update)
|
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
|
Test _balance() method when the Exchange platform returns nothing
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={})
|
mocker.patch('freqtrade.exchange.Exchange.get_balances', return_value={})
|
||||||
|
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
@ -609,6 +616,8 @@ def test_zero_balance_handle(default_conf, update, mocker) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
|
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
telegram._balance(bot=MagicMock(), update=update)
|
telegram._balance(bot=MagicMock(), update=update)
|
||||||
@ -732,7 +741,6 @@ def test_forcesell_handle(default_conf, update, ticker, fee,
|
|||||||
"""
|
"""
|
||||||
Test _forcesell() method
|
Test _forcesell() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=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())
|
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)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
@ -785,7 +794,6 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee,
|
|||||||
"""
|
"""
|
||||||
Test _forcesell() method
|
Test _forcesell() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=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())
|
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)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
@ -842,7 +851,6 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker
|
|||||||
"""
|
"""
|
||||||
Test _forcesell() method
|
Test _forcesell() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=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())
|
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)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
@ -891,7 +900,6 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:
|
|||||||
"""
|
"""
|
||||||
Test _forcesell() method
|
Test _forcesell() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||||
msg_mock = MagicMock()
|
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())
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||||
|
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
# Trader is not running
|
# Trader is not running
|
||||||
@ -934,7 +943,6 @@ def test_performance_handle(default_conf, update, ticker, fee,
|
|||||||
"""
|
"""
|
||||||
Test _performance() method
|
Test _performance() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -951,6 +959,7 @@ def test_performance_handle(default_conf, update, ticker, fee,
|
|||||||
)
|
)
|
||||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
@ -976,7 +985,6 @@ def test_performance_handle_invalid(default_conf, update, mocker) -> None:
|
|||||||
"""
|
"""
|
||||||
Test _performance() method
|
Test _performance() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
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())
|
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
# Trader is not running
|
# Trader is not running
|
||||||
@ -999,7 +1008,6 @@ def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> Non
|
|||||||
"""
|
"""
|
||||||
Test _count() method
|
Test _count() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker, (True, False))
|
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
msg_mock = MagicMock()
|
msg_mock = MagicMock()
|
||||||
mocker.patch.multiple(
|
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)
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
freqtradebot = FreqtradeBot(default_conf)
|
freqtradebot = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtradebot, (True, False))
|
||||||
telegram = Telegram(freqtradebot)
|
telegram = Telegram(freqtradebot)
|
||||||
|
|
||||||
freqtradebot.state = State.STOPPED
|
freqtradebot.state = State.STOPPED
|
||||||
|
@ -3,14 +3,14 @@ import json
|
|||||||
import pytest
|
import pytest
|
||||||
from pandas import DataFrame
|
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
|
from freqtrade.strategy.default_strategy import DefaultStrategy
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def result():
|
def result():
|
||||||
with open('freqtrade/tests/testdata/ETH_BTC-1m.json') as data_file:
|
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():
|
def test_default_strategy_structure():
|
||||||
@ -23,7 +23,7 @@ def test_default_strategy_structure():
|
|||||||
|
|
||||||
|
|
||||||
def test_default_strategy(result):
|
def test_default_strategy(result):
|
||||||
strategy = DefaultStrategy()
|
strategy = DefaultStrategy({})
|
||||||
|
|
||||||
assert type(strategy.minimal_roi) is dict
|
assert type(strategy.minimal_roi) is dict
|
||||||
assert type(strategy.stoploss) is float
|
assert type(strategy.stoploss) is float
|
||||||
|
126
freqtrade/tests/strategy/test_interface.py
Normal file
126
freqtrade/tests/strategy/test_interface.py
Normal 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
|
@ -12,14 +12,15 @@ from freqtrade.strategy.resolver import StrategyResolver
|
|||||||
|
|
||||||
def test_import_strategy(caplog):
|
def test_import_strategy(caplog):
|
||||||
caplog.set_level(logging.DEBUG)
|
caplog.set_level(logging.DEBUG)
|
||||||
|
default_config = {}
|
||||||
|
|
||||||
strategy = DefaultStrategy()
|
strategy = DefaultStrategy(default_config)
|
||||||
strategy.some_method = lambda *args, **kwargs: 42
|
strategy.some_method = lambda *args, **kwargs: 42
|
||||||
|
|
||||||
assert strategy.__module__ == 'freqtrade.strategy.default_strategy'
|
assert strategy.__module__ == 'freqtrade.strategy.default_strategy'
|
||||||
assert strategy.some_method() == 42
|
assert strategy.some_method() == 42
|
||||||
|
|
||||||
imported_strategy = import_strategy(strategy)
|
imported_strategy = import_strategy(strategy, default_config)
|
||||||
|
|
||||||
assert dir(strategy) == dir(imported_strategy)
|
assert dir(strategy) == dir(imported_strategy)
|
||||||
|
|
||||||
@ -35,13 +36,23 @@ def test_import_strategy(caplog):
|
|||||||
|
|
||||||
|
|
||||||
def test_search_strategy():
|
def test_search_strategy():
|
||||||
|
default_config = {}
|
||||||
default_location = os.path.join(os.path.dirname(
|
default_location = os.path.join(os.path.dirname(
|
||||||
os.path.realpath(__file__)), '..', '..', 'strategy'
|
os.path.realpath(__file__)), '..', '..', 'strategy'
|
||||||
)
|
)
|
||||||
assert isinstance(
|
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):
|
def test_load_strategy(result):
|
||||||
@ -53,7 +64,7 @@ def test_load_strategy(result):
|
|||||||
def test_load_strategy_invalid_directory(result, caplog):
|
def test_load_strategy_invalid_directory(result, caplog):
|
||||||
resolver = StrategyResolver()
|
resolver = StrategyResolver()
|
||||||
extra_dir = os.path.join('some', 'path')
|
extra_dir = os.path.join('some', 'path')
|
||||||
resolver._load_strategy('TestStrategy', extra_dir)
|
resolver._load_strategy('TestStrategy', config={}, extra_dir=extra_dir)
|
||||||
|
|
||||||
assert (
|
assert (
|
||||||
'freqtrade.strategy.resolver',
|
'freqtrade.strategy.resolver',
|
||||||
@ -70,7 +81,7 @@ def test_load_not_found_strategy():
|
|||||||
with pytest.raises(ImportError,
|
with pytest.raises(ImportError,
|
||||||
match=r'Impossible to load Strategy \'NotFoundStrategy\'.'
|
match=r'Impossible to load Strategy \'NotFoundStrategy\'.'
|
||||||
r' This class does not exist or contains Python code errors'):
|
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):
|
def test_strategy(result):
|
||||||
|
@ -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
|
|
@ -2,33 +2,31 @@
|
|||||||
|
|
||||||
import pandas
|
import pandas
|
||||||
|
|
||||||
from freqtrade.analyze import Analyze
|
|
||||||
from freqtrade.optimize import load_data
|
from freqtrade.optimize import load_data
|
||||||
from freqtrade.strategy.resolver import StrategyResolver
|
from freqtrade.strategy.resolver import StrategyResolver
|
||||||
|
|
||||||
_pairs = ['ETH/BTC']
|
_pairs = ['ETH/BTC']
|
||||||
|
|
||||||
|
|
||||||
def load_dataframe_pair(pairs):
|
def load_dataframe_pair(pairs, strategy):
|
||||||
ld = load_data(None, ticker_interval='5m', pairs=pairs)
|
ld = load_data(None, ticker_interval='5m', pairs=pairs)
|
||||||
assert isinstance(ld, dict)
|
assert isinstance(ld, dict)
|
||||||
assert isinstance(pairs[0], str)
|
assert isinstance(pairs[0], str)
|
||||||
dataframe = ld[pairs[0]]
|
dataframe = ld[pairs[0]]
|
||||||
|
|
||||||
analyze = Analyze({'strategy': 'DefaultStrategy'})
|
dataframe = strategy.analyze_ticker(dataframe)
|
||||||
dataframe = analyze.analyze_ticker(dataframe)
|
|
||||||
return dataframe
|
return dataframe
|
||||||
|
|
||||||
|
|
||||||
def test_dataframe_load():
|
def test_dataframe_load():
|
||||||
StrategyResolver({'strategy': 'DefaultStrategy'})
|
strategy = StrategyResolver({'strategy': 'DefaultStrategy'}).strategy
|
||||||
dataframe = load_dataframe_pair(_pairs)
|
dataframe = load_dataframe_pair(_pairs, strategy)
|
||||||
assert isinstance(dataframe, pandas.core.frame.DataFrame)
|
assert isinstance(dataframe, pandas.core.frame.DataFrame)
|
||||||
|
|
||||||
|
|
||||||
def test_dataframe_columns_exists():
|
def test_dataframe_columns_exists():
|
||||||
StrategyResolver({'strategy': 'DefaultStrategy'})
|
strategy = StrategyResolver({'strategy': 'DefaultStrategy'}).strategy
|
||||||
dataframe = load_dataframe_pair(_pairs)
|
dataframe = load_dataframe_pair(_pairs, strategy)
|
||||||
assert 'high' in dataframe.columns
|
assert 'high' in dataframe.columns
|
||||||
assert 'low' in dataframe.columns
|
assert 'low' in dataframe.columns
|
||||||
assert 'close' in dataframe.columns
|
assert 'close' in dataframe.columns
|
||||||
|
@ -31,7 +31,6 @@ def get_patched_freqtradebot(mocker, config) -> FreqtradeBot:
|
|||||||
:param config: Config to pass to the bot
|
:param config: Config to pass to the bot
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze', MagicMock())
|
|
||||||
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.RPCManager', MagicMock())
|
||||||
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
|
mocker.patch('freqtrade.freqtradebot.persistence.init', MagicMock())
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
@ -40,17 +39,13 @@ def get_patched_freqtradebot(mocker, config) -> FreqtradeBot:
|
|||||||
return FreqtradeBot(config)
|
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 IStrategy class
|
||||||
:param mocker: mocker to patch Analyze class
|
:param value: which value IStrategy.get_signal() must return
|
||||||
:param value: which value Analyze.get_signal() must return
|
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
mocker.patch(
|
freqtrade.strategy.get_signal = lambda e, s, t: value
|
||||||
'freqtrade.freqtradebot.Analyze.get_signal',
|
|
||||||
side_effect=lambda e, s, t: value
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def patch_RPCManager(mocker) -> MagicMock:
|
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
|
Test get_trade_stake_amount() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -285,6 +279,7 @@ def test_get_trade_stake_amount_unlimited_amount(default_conf,
|
|||||||
conf['max_open_trades'] = 2
|
conf['max_open_trades'] = 2
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# no open trades, order amount should be 'balance / max_open_trades'
|
# no open trades, order amount should be 'balance / max_open_trades'
|
||||||
result = freqtrade._get_trade_stake_amount()
|
result = freqtrade._get_trade_stake_amount()
|
||||||
@ -316,9 +311,8 @@ def test_get_min_pair_stake_amount(mocker, default_conf) -> None:
|
|||||||
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
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 = FreqtradeBot(default_conf)
|
||||||
|
freqtrade.strategy.stoploss = -0.05
|
||||||
# no pair found
|
# no pair found
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.exchange.Exchange.get_markets',
|
'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
|
Test create_trade() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
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
|
# Save state of current whitelist
|
||||||
whitelist = deepcopy(default_conf['exchange']['pair_whitelist'])
|
whitelist = deepcopy(default_conf['exchange']['pair_whitelist'])
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
freqtrade.create_trade()
|
freqtrade.create_trade()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
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
|
Test create_trade() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -504,6 +497,7 @@ def test_create_trade_no_stake_amount(default_conf, ticker, limit_buy_order,
|
|||||||
get_markets=markets
|
get_markets=markets
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
|
with pytest.raises(DependencyException, match=r'.*stake amount.*'):
|
||||||
freqtrade.create_trade()
|
freqtrade.create_trade()
|
||||||
@ -514,7 +508,6 @@ def test_create_trade_minimal_amount(default_conf, ticker, limit_buy_order,
|
|||||||
"""
|
"""
|
||||||
Test create_trade() method
|
Test create_trade() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
buy_mock = MagicMock(return_value={'id': limit_buy_order['id']})
|
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 = deepcopy(default_conf)
|
||||||
conf['stake_amount'] = 0.0005
|
conf['stake_amount'] = 0.0005
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
freqtrade.create_trade()
|
freqtrade.create_trade()
|
||||||
rate, amount = buy_mock.call_args[0][1], buy_mock.call_args[0][2]
|
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
|
Test create_trade() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
buy_mock = MagicMock(return_value={'id': limit_buy_order['id']})
|
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 = deepcopy(default_conf)
|
||||||
conf['stake_amount'] = 0.000000005
|
conf['stake_amount'] = 0.000000005
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
result = freqtrade.create_trade()
|
result = freqtrade.create_trade()
|
||||||
assert result is False
|
assert result is False
|
||||||
@ -567,7 +561,6 @@ def test_create_trade_limit_reached(default_conf, ticker, limit_buy_order,
|
|||||||
"""
|
"""
|
||||||
Test create_trade() method
|
Test create_trade() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
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
|
conf['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
assert freqtrade.create_trade() is False
|
assert freqtrade.create_trade() is False
|
||||||
assert freqtrade._get_trade_stake_amount() is None
|
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
|
Test create_trade() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
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_whitelist'] = ["ETH/BTC"]
|
||||||
conf['exchange']['pair_blacklist'] = ["ETH/BTC"]
|
conf['exchange']['pair_blacklist'] = ["ETH/BTC"]
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
freqtrade.create_trade()
|
freqtrade.create_trade()
|
||||||
|
|
||||||
@ -621,7 +615,6 @@ def test_create_trade_no_pairs_after_blacklist(default_conf, ticker,
|
|||||||
"""
|
"""
|
||||||
Test create_trade() method
|
Test create_trade() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
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_whitelist'] = ["ETH/BTC"]
|
||||||
conf['exchange']['pair_blacklist'] = ["ETH/BTC"]
|
conf['exchange']['pair_blacklist'] = ["ETH/BTC"]
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
freqtrade.create_trade()
|
freqtrade.create_trade()
|
||||||
|
|
||||||
@ -651,7 +645,6 @@ def test_create_trade_no_signal(default_conf, fee, mocker) -> None:
|
|||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
conf['dry_run'] = True
|
conf['dry_run'] = True
|
||||||
|
|
||||||
patch_get_signal(mocker, value=(False, False))
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -665,6 +658,7 @@ def test_create_trade_no_signal(default_conf, fee, mocker) -> None:
|
|||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
conf['stake_amount'] = 10
|
conf['stake_amount'] = 10
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade, value=(False, False))
|
||||||
|
|
||||||
Trade.query = MagicMock()
|
Trade.query = MagicMock()
|
||||||
Trade.query.filter = 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
|
Test the trade creation in _process() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -689,6 +682,7 @@ def test_process_trade_creation(default_conf, ticker, limit_buy_order,
|
|||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
|
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
|
||||||
assert not trades
|
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
|
Test _process() method when a RequestException happens
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||||
mocker.patch.multiple(
|
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)
|
sleep_mock = mocker.patch('time.sleep', side_effect=lambda _: None)
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
result = freqtrade._process()
|
result = freqtrade._process()
|
||||||
assert result is False
|
assert result is False
|
||||||
assert sleep_mock.has_calls()
|
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
|
Test _process() method when an OperationalException happens
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
msg_mock = patch_RPCManager(mocker)
|
msg_mock = patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -750,6 +744,8 @@ def test_process_operational_exception(default_conf, ticker, markets, mocker) ->
|
|||||||
buy=MagicMock(side_effect=OperationalException)
|
buy=MagicMock(side_effect=OperationalException)
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
assert freqtrade.state == State.RUNNING
|
assert freqtrade.state == State.RUNNING
|
||||||
|
|
||||||
result = freqtrade._process()
|
result = freqtrade._process()
|
||||||
@ -763,7 +759,6 @@ def test_process_trade_handling(
|
|||||||
"""
|
"""
|
||||||
Test _process()
|
Test _process()
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -776,6 +771,7 @@ def test_process_trade_handling(
|
|||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
|
trades = Trade.query.filter(Trade.is_open.is_(True)).all()
|
||||||
assert not trades
|
assert not trades
|
||||||
@ -914,7 +910,6 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order,
|
|||||||
"""
|
"""
|
||||||
Test check_handle() method
|
Test check_handle() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'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})
|
patch_coinmarketcap(mocker, value={'price_usd': 15000.0})
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
freqtrade.create_trade()
|
freqtrade.create_trade()
|
||||||
|
|
||||||
@ -942,7 +938,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order,
|
|||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
assert trade.is_open is True
|
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 freqtrade.handle_trade(trade) is True
|
||||||
assert trade.open_order_id == limit_sell_order['id']
|
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 = deepcopy(default_conf)
|
||||||
conf.update({'experimental': {'use_sell_signal': True}})
|
conf.update({'experimental': {'use_sell_signal': True}})
|
||||||
|
|
||||||
patch_get_signal(mocker, value=(True, True))
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
@ -977,6 +971,8 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order,
|
|||||||
)
|
)
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(conf)
|
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()
|
freqtrade.create_trade()
|
||||||
|
|
||||||
@ -986,7 +982,7 @@ def test_handle_overlpapping_signals(default_conf, ticker, limit_buy_order,
|
|||||||
assert nb_trades == 0
|
assert nb_trades == 0
|
||||||
|
|
||||||
# Buy is triggering, so buying ...
|
# Buy is triggering, so buying ...
|
||||||
patch_get_signal(mocker, value=(True, False))
|
patch_get_signal(freqtrade, value=(True, False))
|
||||||
freqtrade.create_trade()
|
freqtrade.create_trade()
|
||||||
trades = Trade.query.all()
|
trades = Trade.query.all()
|
||||||
nb_trades = len(trades)
|
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
|
assert trades[0].is_open is True
|
||||||
|
|
||||||
# Buy and Sell are not triggering, so doing nothing ...
|
# 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
|
assert freqtrade.handle_trade(trades[0]) is False
|
||||||
trades = Trade.query.all()
|
trades = Trade.query.all()
|
||||||
nb_trades = len(trades)
|
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
|
assert trades[0].is_open is True
|
||||||
|
|
||||||
# Buy and Sell are triggering, so doing nothing ...
|
# 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
|
assert freqtrade.handle_trade(trades[0]) is False
|
||||||
trades = Trade.query.all()
|
trades = Trade.query.all()
|
||||||
nb_trades = len(trades)
|
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
|
assert trades[0].is_open is True
|
||||||
|
|
||||||
# Sell is triggering, guess what : we are Selling!
|
# 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()
|
trades = Trade.query.all()
|
||||||
assert freqtrade.handle_trade(trades[0]) is True
|
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 = deepcopy(default_conf)
|
||||||
conf.update({'experimental': {'use_sell_signal': True}})
|
conf.update({'experimental': {'use_sell_signal': True}})
|
||||||
|
|
||||||
patch_get_signal(mocker, value=(True, False))
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -1036,8 +1031,10 @@ def test_handle_trade_roi(default_conf, ticker, limit_buy_order,
|
|||||||
get_markets=markets
|
get_markets=markets
|
||||||
)
|
)
|
||||||
|
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=True)
|
|
||||||
freqtrade = FreqtradeBot(conf)
|
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()
|
freqtrade.create_trade()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
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
|
# we might just want to check if we are in a sell condition without
|
||||||
# executing
|
# executing
|
||||||
# if ROI is reached we must sell
|
# 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 freqtrade.handle_trade(trade)
|
||||||
assert log_has('Required profit reached. Selling..', caplog.record_tuples)
|
assert log_has('Required profit reached. Selling..', caplog.record_tuples)
|
||||||
|
|
||||||
@ -1062,7 +1059,6 @@ def test_handle_trade_experimental(
|
|||||||
conf = deepcopy(default_conf)
|
conf = deepcopy(default_conf)
|
||||||
conf.update({'experimental': {'use_sell_signal': True}})
|
conf.update({'experimental': {'use_sell_signal': True}})
|
||||||
|
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -1073,18 +1069,19 @@ def test_handle_trade_experimental(
|
|||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
get_markets=markets
|
get_markets=markets
|
||||||
)
|
)
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False
|
||||||
freqtrade.create_trade()
|
freqtrade.create_trade()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.is_open = True
|
trade.is_open = True
|
||||||
|
|
||||||
patch_get_signal(mocker, value=(False, False))
|
patch_get_signal(freqtrade, value=(False, False))
|
||||||
assert not freqtrade.handle_trade(trade)
|
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 freqtrade.handle_trade(trade)
|
||||||
assert log_has('Sell signal received. Selling..', caplog.record_tuples)
|
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
|
Test check_handle() method
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -1106,6 +1102,7 @@ def test_close_trade(default_conf, ticker, limit_buy_order, limit_sell_order,
|
|||||||
get_markets=markets
|
get_markets=markets
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create trade and sell it
|
# Create trade and sell it
|
||||||
freqtrade.create_trade()
|
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
|
Test execute_sell() method with a ticker going UP
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch.multiple(
|
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)
|
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.create_trade()
|
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
|
Test execute_sell() method with a ticker going DOWN
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.fiat_convert.CryptoToFiatConverter._find_price', return_value=15000.0)
|
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
|
get_markets=markets
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.create_trade()
|
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
|
Test execute_sell() method with a ticker going DOWN and with a bot config empty
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -1462,6 +1458,7 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee,
|
|||||||
get_markets=markets
|
get_markets=markets
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.create_trade()
|
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
|
Test execute_sell() method with a ticker going DOWN and with a bot config empty
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
patch_coinmarketcap(mocker, value={'price_usd': 12345.0})
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -1512,6 +1508,7 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee,
|
|||||||
get_markets=markets
|
get_markets=markets
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
freqtrade.create_trade()
|
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
|
Test sell_profit_only feature when enabled
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
@ -1573,11 +1568,14 @@ def test_sell_profit_only_enable_profit(default_conf, limit_buy_order,
|
|||||||
'sell_profit_only': True,
|
'sell_profit_only': True,
|
||||||
}
|
}
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False
|
||||||
|
|
||||||
freqtrade.create_trade()
|
freqtrade.create_trade()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
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
|
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
|
Test sell_profit_only feature when disabled
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
@ -1608,11 +1604,13 @@ def test_sell_profit_only_disable_profit(default_conf, limit_buy_order,
|
|||||||
'sell_profit_only': False,
|
'sell_profit_only': False,
|
||||||
}
|
}
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False
|
||||||
freqtrade.create_trade()
|
freqtrade.create_trade()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
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
|
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
|
Test sell_profit_only feature when enabled and we have a loss
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.stop_loss_reached', return_value=False)
|
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
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,
|
'sell_profit_only': True,
|
||||||
}
|
}
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
freqtrade.strategy.stop_loss_reached = \
|
||||||
|
lambda current_rate, trade, current_time, current_profit: False
|
||||||
freqtrade.create_trade()
|
freqtrade.create_trade()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
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
|
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
|
Test sell_profit_only feature when enabled and we have a loss
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
@ -1678,11 +1675,14 @@ def test_sell_profit_only_disable_loss(default_conf, limit_buy_order, fee, marke
|
|||||||
}
|
}
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False
|
||||||
|
|
||||||
freqtrade.create_trade()
|
freqtrade.create_trade()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
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
|
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
|
Test sell_profit_only feature when enabled and we have a loss
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=True)
|
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
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)
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: True
|
||||||
|
|
||||||
freqtrade.create_trade()
|
freqtrade.create_trade()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
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
|
assert freqtrade.handle_trade(trade) is False
|
||||||
|
|
||||||
# Test if buy-signal is absent (should sell due to roi = true)
|
# 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
|
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
|
Test sell_profit_only feature when enabled and we have a loss
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
@ -1749,6 +1748,9 @@ def test_trailing_stop_loss(default_conf, limit_buy_order, fee, caplog, mocker)
|
|||||||
conf['trailing_stop'] = True
|
conf['trailing_stop'] = True
|
||||||
print(limit_buy_order)
|
print(limit_buy_order)
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False
|
||||||
|
|
||||||
freqtrade.create_trade()
|
freqtrade.create_trade()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
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
|
Test sell_profit_only feature when enabled and we have a loss
|
||||||
"""
|
"""
|
||||||
buy_price = limit_buy_order['price']
|
buy_price = limit_buy_order['price']
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=False)
|
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
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'] = True
|
||||||
conf['trailing_stop_positive'] = 0.01
|
conf['trailing_stop_positive'] = 0.01
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: False
|
||||||
freqtrade.create_trade()
|
freqtrade.create_trade()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
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
|
Test sell_profit_only feature when enabled and we have a loss
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.freqtradebot.Analyze.min_roi_reached', return_value=True)
|
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
validate_pairs=MagicMock(),
|
validate_pairs=MagicMock(),
|
||||||
@ -1850,16 +1850,19 @@ def test_disable_ignore_roi_if_buy_signal(default_conf, limit_buy_order,
|
|||||||
}
|
}
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(conf)
|
freqtrade = FreqtradeBot(conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
freqtrade.strategy.min_roi_reached = lambda trade, current_profit, current_time: True
|
||||||
|
|
||||||
freqtrade.create_trade()
|
freqtrade.create_trade()
|
||||||
|
|
||||||
trade = Trade.query.first()
|
trade = Trade.query.first()
|
||||||
trade.update(limit_buy_order)
|
trade.update(limit_buy_order)
|
||||||
# Sell due to min_roi_reached
|
# 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
|
assert freqtrade.handle_trade(trade) is True
|
||||||
|
|
||||||
# Test if buy-signal is absent
|
# 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
|
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)
|
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=trades_for_order)
|
||||||
|
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
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"
|
open_order_id="123456"
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Amount is reduced by "fee"
|
# Amount is reduced by "fee"
|
||||||
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001)
|
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, '
|
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=[])
|
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
|
||||||
|
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
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"
|
open_order_id="123456"
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Amount is reduced by "fee"
|
# Amount is reduced by "fee"
|
||||||
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
|
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, '
|
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'
|
trades_for_order[0]['fee']['currency'] = 'ETH'
|
||||||
|
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
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"
|
open_order_id="123456"
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Amount does not change
|
# Amount does not change
|
||||||
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
|
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']['currency'] = 'BNB'
|
||||||
trades_for_order[0]['fee']['cost'] = 0.00094518
|
trades_for_order[0]['fee']['cost'] = 0.00094518
|
||||||
|
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
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"
|
open_order_id="123456"
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Amount does not change
|
# Amount does not change
|
||||||
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
|
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)
|
Test get_real_amount with split trades (multiple trades for this order)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
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"
|
open_order_id="123456"
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Amount is reduced by "fee"
|
# Amount is reduced by "fee"
|
||||||
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount - (amount * 0.001)
|
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, '
|
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 = deepcopy(buy_order_fee)
|
||||||
limit_buy_order['fee'] = {'cost': 0.004, 'currency': 'LTC'}
|
limit_buy_order['fee'] = {'cost': 0.004, 'currency': 'LTC'}
|
||||||
|
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
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"
|
open_order_id="123456"
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Amount is reduced by "fee"
|
# Amount is reduced by "fee"
|
||||||
assert freqtrade.get_real_amount(trade, limit_buy_order) == amount - 0.004
|
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, '
|
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 = deepcopy(buy_order_fee)
|
||||||
limit_buy_order['fee'] = {'cost': 0.004}
|
limit_buy_order['fee'] = {'cost': 0.004}
|
||||||
|
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
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"
|
open_order_id="123456"
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Amount does not change
|
# Amount does not change
|
||||||
assert freqtrade.get_real_amount(trade, limit_buy_order) == amount
|
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
|
# Remove "Currency" from fee dict
|
||||||
trades_for_order[0]['fee'] = {'cost': 0.008}
|
trades_for_order[0]['fee'] = {'cost': 0.008}
|
||||||
|
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
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"
|
open_order_id="123456"
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
# Amount does not change
|
# Amount does not change
|
||||||
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
|
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'
|
Test get_real_amount condition trade.fee_open == 0 or order['status'] == 'open'
|
||||||
"""
|
"""
|
||||||
patch_get_signal(mocker)
|
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_coinmarketcap(mocker)
|
patch_coinmarketcap(mocker)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock(return_value=True))
|
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',
|
'status': 'open',
|
||||||
}
|
}
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
|
patch_get_signal(freqtrade)
|
||||||
assert freqtrade.get_real_amount(trade, order) == amount
|
assert freqtrade.get_real_amount(trade, order) == amount
|
||||||
|
@ -7,10 +7,11 @@ Unit test file for misc.py
|
|||||||
import datetime
|
import datetime
|
||||||
from unittest.mock import MagicMock
|
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,
|
from freqtrade.misc import (common_datearray, datesarray_to_datetimearray,
|
||||||
file_dump_json, format_ms_time, shorten_date)
|
file_dump_json, format_ms_time, shorten_date)
|
||||||
from freqtrade.optimize.__init__ import load_tickerdata_file
|
from freqtrade.optimize.__init__ import load_tickerdata_file
|
||||||
|
from freqtrade.strategy.default_strategy import DefaultStrategy
|
||||||
|
|
||||||
|
|
||||||
def test_shorten_date() -> None:
|
def test_shorten_date() -> None:
|
||||||
@ -28,7 +29,7 @@ def test_datesarray_to_datetimearray(ticker_history):
|
|||||||
Test datesarray_to_datetimearray() function
|
Test datesarray_to_datetimearray() function
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
dataframes = Analyze.parse_ticker_dataframe(ticker_history)
|
dataframes = parse_ticker_dataframe(ticker_history)
|
||||||
dates = datesarray_to_datetimearray(dataframes['date'])
|
dates = datesarray_to_datetimearray(dataframes['date'])
|
||||||
|
|
||||||
assert isinstance(dates[0], datetime.datetime)
|
assert isinstance(dates[0], datetime.datetime)
|
||||||
@ -47,10 +48,10 @@ def test_common_datearray(default_conf) -> None:
|
|||||||
Test common_datearray()
|
Test common_datearray()
|
||||||
:return: None
|
:return: None
|
||||||
"""
|
"""
|
||||||
analyze = Analyze(default_conf)
|
strategy = DefaultStrategy(default_conf)
|
||||||
tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m')
|
tick = load_tickerdata_file(None, 'UNITTEST/BTC', '1m')
|
||||||
tickerlist = {'UNITTEST/BTC': tick}
|
tickerlist = {'UNITTEST/BTC': tick}
|
||||||
dataframes = analyze.tickerdata_to_dataframe(tickerlist)
|
dataframes = strategy.tickerdata_to_dataframe(tickerlist)
|
||||||
|
|
||||||
dates = common_datearray(dataframes)
|
dates = common_datearray(dataframes)
|
||||||
|
|
||||||
|
@ -40,11 +40,11 @@ from plotly.offline import plot
|
|||||||
|
|
||||||
import freqtrade.optimize as optimize
|
import freqtrade.optimize as optimize
|
||||||
from freqtrade import persistence
|
from freqtrade import persistence
|
||||||
from freqtrade.analyze import Analyze
|
|
||||||
from freqtrade.arguments import Arguments, TimeRange
|
from freqtrade.arguments import Arguments, TimeRange
|
||||||
from freqtrade.exchange import Exchange
|
from freqtrade.exchange import Exchange
|
||||||
from freqtrade.optimize.backtesting import setup_configuration
|
from freqtrade.optimize.backtesting import setup_configuration
|
||||||
from freqtrade.persistence import Trade
|
from freqtrade.persistence import Trade
|
||||||
|
from freqtrade.strategy.resolver import StrategyResolver
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
_CONF: Dict[str, Any] = {}
|
_CONF: Dict[str, Any] = {}
|
||||||
@ -122,7 +122,7 @@ def plot_analyzed_dataframe(args: Namespace) -> None:
|
|||||||
|
|
||||||
# Load the strategy
|
# Load the strategy
|
||||||
try:
|
try:
|
||||||
analyze = Analyze(_CONF)
|
strategy = StrategyResolver(_CONF).strategy
|
||||||
exchange = Exchange(_CONF)
|
exchange = Exchange(_CONF)
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
logger.critical(
|
logger.critical(
|
||||||
@ -132,7 +132,7 @@ def plot_analyzed_dataframe(args: Namespace) -> None:
|
|||||||
exit()
|
exit()
|
||||||
|
|
||||||
# Set the ticker to use
|
# Set the ticker to use
|
||||||
tick_interval = analyze.get_ticker_interval()
|
tick_interval = strategy.ticker_interval
|
||||||
|
|
||||||
# Load pair tickers
|
# Load pair tickers
|
||||||
tickers = {}
|
tickers = {}
|
||||||
@ -156,11 +156,11 @@ def plot_analyzed_dataframe(args: Namespace) -> None:
|
|||||||
# Get trades already made from the DB
|
# Get trades already made from the DB
|
||||||
trades = load_trades(args, pair, timerange)
|
trades = load_trades(args, pair, timerange)
|
||||||
|
|
||||||
dataframes = analyze.tickerdata_to_dataframe(tickers)
|
dataframes = strategy.tickerdata_to_dataframe(tickers)
|
||||||
|
|
||||||
dataframe = dataframes[pair]
|
dataframe = dataframes[pair]
|
||||||
dataframe = analyze.populate_buy_trend(dataframe)
|
dataframe = strategy.populate_buy_trend(dataframe)
|
||||||
dataframe = analyze.populate_sell_trend(dataframe)
|
dataframe = strategy.populate_sell_trend(dataframe)
|
||||||
|
|
||||||
if len(dataframe.index) > args.plot_limit:
|
if len(dataframe.index) > args.plot_limit:
|
||||||
logger.warning('Ticker contained more than %s candles as defined '
|
logger.warning('Ticker contained more than %s candles as defined '
|
||||||
|
@ -26,9 +26,8 @@ import plotly.graph_objs as go
|
|||||||
|
|
||||||
from freqtrade.arguments import Arguments
|
from freqtrade.arguments import Arguments
|
||||||
from freqtrade.configuration import Configuration
|
from freqtrade.configuration import Configuration
|
||||||
from freqtrade.analyze import Analyze
|
|
||||||
from freqtrade import constants
|
from freqtrade import constants
|
||||||
|
from freqtrade.strategy.resolver import StrategyResolver
|
||||||
import freqtrade.optimize as optimize
|
import freqtrade.optimize as optimize
|
||||||
import freqtrade.misc as misc
|
import freqtrade.misc as misc
|
||||||
|
|
||||||
@ -87,7 +86,8 @@ def plot_profit(args: Namespace) -> None:
|
|||||||
|
|
||||||
# Init strategy
|
# Init strategy
|
||||||
try:
|
try:
|
||||||
analyze = Analyze({'strategy': config.get('strategy')})
|
strategy = StrategyResolver({'strategy': config.get('strategy')}).strategy
|
||||||
|
|
||||||
except AttributeError:
|
except AttributeError:
|
||||||
logger.critical(
|
logger.critical(
|
||||||
'Impossible to load the strategy. Please check the file "user_data/strategies/%s.py"',
|
'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:
|
else:
|
||||||
filter_pairs = config['exchange']['pair_whitelist']
|
filter_pairs = config['exchange']['pair_whitelist']
|
||||||
|
|
||||||
tick_interval = analyze.strategy.ticker_interval
|
tick_interval = strategy.ticker_interval
|
||||||
pairs = config['exchange']['pair_whitelist']
|
pairs = config['exchange']['pair_whitelist']
|
||||||
|
|
||||||
if filter_pairs:
|
if filter_pairs:
|
||||||
@ -127,7 +127,7 @@ def plot_profit(args: Namespace) -> None:
|
|||||||
refresh_pairs=False,
|
refresh_pairs=False,
|
||||||
timerange=timerange
|
timerange=timerange
|
||||||
)
|
)
|
||||||
dataframes = analyze.tickerdata_to_dataframe(tickers)
|
dataframes = strategy.tickerdata_to_dataframe(tickers)
|
||||||
|
|
||||||
# NOTE: the dataframes are of unequal length,
|
# NOTE: the dataframes are of unequal length,
|
||||||
# 'dates' is an merged date array of them all.
|
# 'dates' is an merged date array of them all.
|
||||||
|
@ -12,6 +12,7 @@ import numpy # noqa
|
|||||||
|
|
||||||
# This class is a sample. Feel free to customize it.
|
# This class is a sample. Feel free to customize it.
|
||||||
class TestStrategy(IStrategy):
|
class TestStrategy(IStrategy):
|
||||||
|
__test__ = False # pytest expects to find tests here because of the name
|
||||||
"""
|
"""
|
||||||
This is a test strategy to inspire you.
|
This is a test strategy to inspire you.
|
||||||
More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md
|
More information in https://github.com/freqtrade/freqtrade/blob/develop/docs/bot-optimization.md
|
||||||
|
Loading…
Reference in New Issue
Block a user