Merge branch 'develop' into feat/short
This commit is contained in:
@@ -318,17 +318,15 @@ def test_backtesting_init_no_timeframe(mocker, default_conf, caplog) -> None:
|
||||
patch_exchange(mocker)
|
||||
del default_conf['timeframe']
|
||||
default_conf['strategy_list'] = [CURRENT_TEST_STRATEGY,
|
||||
'SampleStrategy']
|
||||
# TODO: This refers to the sampleStrategy in user_data if it exists...
|
||||
'HyperoptableStrategy']
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', MagicMock(return_value=0.5))
|
||||
with pytest.raises(OperationalException):
|
||||
with pytest.raises(OperationalException,
|
||||
match=r"Timeframe needs to be set in either configuration"):
|
||||
Backtesting(default_conf)
|
||||
log_has("Ticker-interval needs to be set in either configuration "
|
||||
"or as cli argument `--ticker-interval 5m`", caplog)
|
||||
|
||||
|
||||
def test_data_with_fee(default_conf, mocker, testdatadir) -> None:
|
||||
def test_data_with_fee(default_conf, mocker) -> None:
|
||||
patch_exchange(mocker)
|
||||
default_conf['fee'] = 0.1234
|
||||
|
||||
|
@@ -6,7 +6,7 @@ from unittest.mock import MagicMock
|
||||
from freqtrade.commands.optimize_commands import setup_optimize_configuration, start_edge
|
||||
from freqtrade.enums import RunMode
|
||||
from freqtrade.optimize.edge_cli import EdgeCli
|
||||
from tests.conftest import (CURRENT_TEST_STRATEGY, get_args, log_has, log_has_re, patch_exchange,
|
||||
from tests.conftest import (CURRENT_TEST_STRATEGY, get_args, log_has, patch_exchange,
|
||||
patched_configuration_load_config_file)
|
||||
|
||||
|
||||
@@ -30,7 +30,6 @@ def test_setup_optimize_configuration_without_arguments(mocker, default_conf, ca
|
||||
assert 'datadir' in config
|
||||
assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog)
|
||||
assert 'timeframe' in config
|
||||
assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog)
|
||||
|
||||
assert 'timerange' not in config
|
||||
assert 'stoploss_range' not in config
|
||||
|
@@ -63,7 +63,6 @@ def test_setup_hyperopt_configuration_without_arguments(mocker, default_conf, ca
|
||||
assert 'datadir' in config
|
||||
assert log_has('Using data directory: {} ...'.format(config['datadir']), caplog)
|
||||
assert 'timeframe' in config
|
||||
assert not log_has_re('Parameter -i/--ticker-interval detected .*', caplog)
|
||||
|
||||
assert 'position_stacking' not in config
|
||||
assert not log_has('Parameter --enable-position-stacking detected ...', caplog)
|
||||
|
@@ -1219,6 +1219,7 @@ def test_rpc_forceentry(mocker, default_conf, ticker, fee, limit_buy_order_open)
|
||||
pair = 'LTC/BTC'
|
||||
trade = rpc._rpc_force_entry(pair, 0.0001, order_type='limit', stake_amount=0.05)
|
||||
assert trade.stake_amount == 0.05
|
||||
assert trade.buy_tag == 'forceentry'
|
||||
|
||||
# Test not buying
|
||||
pair = 'XRP/BTC'
|
||||
|
@@ -1,14 +1,13 @@
|
||||
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
||||
|
||||
import talib.abstract as ta
|
||||
from pandas import DataFrame
|
||||
from strategy_test_v2 import StrategyTestV2
|
||||
|
||||
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||
from freqtrade.strategy import (BooleanParameter, DecimalParameter, IntParameter, IStrategy,
|
||||
RealParameter)
|
||||
from freqtrade.strategy import BooleanParameter, DecimalParameter, IntParameter, RealParameter
|
||||
|
||||
|
||||
class HyperoptableStrategy(IStrategy):
|
||||
class HyperoptableStrategy(StrategyTestV2):
|
||||
"""
|
||||
Default Strategy provided by freqtrade bot.
|
||||
Please do not modify this strategy, it's intended for internal use only.
|
||||
@@ -16,38 +15,6 @@ class HyperoptableStrategy(IStrategy):
|
||||
or strategy repository https://github.com/freqtrade/freqtrade-strategies
|
||||
for samples and inspiration.
|
||||
"""
|
||||
INTERFACE_VERSION = 2
|
||||
|
||||
# Minimal ROI designed for the strategy
|
||||
minimal_roi = {
|
||||
"40": 0.0,
|
||||
"30": 0.01,
|
||||
"20": 0.02,
|
||||
"0": 0.04
|
||||
}
|
||||
|
||||
# Optimal stoploss designed for the strategy
|
||||
stoploss = -0.10
|
||||
|
||||
# Optimal ticker interval for the strategy
|
||||
timeframe = '5m'
|
||||
|
||||
# Optional order type mapping
|
||||
order_types = {
|
||||
'entry': 'limit',
|
||||
'exit': 'limit',
|
||||
'stoploss': 'limit',
|
||||
'stoploss_on_exchange': False
|
||||
}
|
||||
|
||||
# Number of candles the strategy requires before producing valid signals
|
||||
startup_candle_count: int = 20
|
||||
|
||||
# Optional time in force for orders
|
||||
order_time_in_force = {
|
||||
'entry': 'gtc',
|
||||
'exit': 'gtc',
|
||||
}
|
||||
|
||||
buy_params = {
|
||||
'buy_rsi': 35,
|
||||
@@ -91,55 +58,6 @@ class HyperoptableStrategy(IStrategy):
|
||||
"""
|
||||
return []
|
||||
|
||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> 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.
|
||||
:param dataframe: Dataframe with data from the exchange
|
||||
:param metadata: Additional information, like the currently traded pair
|
||||
:return: a Dataframe with all mandatory indicators for the strategies
|
||||
"""
|
||||
|
||||
# Momentum Indicator
|
||||
# ------------------------------------
|
||||
|
||||
# ADX
|
||||
dataframe['adx'] = ta.ADX(dataframe)
|
||||
|
||||
# MACD
|
||||
macd = ta.MACD(dataframe)
|
||||
dataframe['macd'] = macd['macd']
|
||||
dataframe['macdsignal'] = macd['macdsignal']
|
||||
dataframe['macdhist'] = macd['macdhist']
|
||||
|
||||
# Minus Directional Indicator / Movement
|
||||
dataframe['minus_di'] = ta.MINUS_DI(dataframe)
|
||||
|
||||
# Plus Directional Indicator / Movement
|
||||
dataframe['plus_di'] = ta.PLUS_DI(dataframe)
|
||||
|
||||
# RSI
|
||||
dataframe['rsi'] = ta.RSI(dataframe)
|
||||
|
||||
# Stoch fast
|
||||
stoch_fast = ta.STOCHF(dataframe)
|
||||
dataframe['fastd'] = stoch_fast['fastd']
|
||||
dataframe['fastk'] = stoch_fast['fastk']
|
||||
|
||||
# Bollinger bands
|
||||
bollinger = qtpylib.bollinger_bands(qtpylib.typical_price(dataframe), window=20, stds=2)
|
||||
dataframe['bb_lowerband'] = bollinger['lower']
|
||||
dataframe['bb_middleband'] = bollinger['mid']
|
||||
dataframe['bb_upperband'] = bollinger['upper']
|
||||
|
||||
# EMA - Exponential Moving Average
|
||||
dataframe['ema10'] = ta.EMA(dataframe, timeperiod=10)
|
||||
|
||||
return dataframe
|
||||
|
||||
def populate_buy_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
"""
|
||||
Based on TA indicators, populates the buy signal for the given dataframe
|
||||
|
@@ -31,9 +31,7 @@ class TestStrategyLegacyV1(IStrategy):
|
||||
# This attribute will be overridden if the config file contains "stoploss"
|
||||
stoploss = -0.10
|
||||
|
||||
# Optimal timeframe for the strategy
|
||||
# Keep the legacy value here to test compatibility
|
||||
ticker_interval = '5m'
|
||||
timeframe = '5m'
|
||||
|
||||
def populate_indicators(self, dataframe: DataFrame) -> DataFrame:
|
||||
"""
|
||||
|
@@ -113,7 +113,6 @@ def test_strategy_pre_v3(result, default_conf, strategy_name):
|
||||
assert default_conf['stoploss'] == -0.10
|
||||
|
||||
assert strategy.timeframe == '5m'
|
||||
assert strategy.ticker_interval == '5m'
|
||||
assert default_conf['timeframe'] == '5m'
|
||||
|
||||
df_indicators = strategy.advise_indicators(result, metadata=metadata)
|
||||
@@ -440,7 +439,6 @@ def test_call_deprecated_function(result, default_conf, caplog):
|
||||
assert strategy._sell_fun_len == 2
|
||||
assert strategy.INTERFACE_VERSION == 1
|
||||
assert strategy.timeframe == '5m'
|
||||
assert strategy.ticker_interval == '5m'
|
||||
|
||||
indicator_df = strategy.advise_indicators(result, metadata=metadata)
|
||||
assert isinstance(indicator_df, DataFrame)
|
||||
@@ -454,9 +452,6 @@ def test_call_deprecated_function(result, default_conf, caplog):
|
||||
assert isinstance(exitdf, DataFrame)
|
||||
assert 'exit_long' in exitdf
|
||||
|
||||
assert log_has("DEPRECATED: Please migrate to using 'timeframe' instead of 'ticker_interval'.",
|
||||
caplog)
|
||||
|
||||
|
||||
def test_strategy_interface_versioning(result, default_conf):
|
||||
default_conf.update({'strategy': 'StrategyTestV2'})
|
||||
|
@@ -112,17 +112,17 @@ def test_parse_args_strategy_path_invalid() -> None:
|
||||
|
||||
def test_parse_args_backtesting_invalid() -> None:
|
||||
with pytest.raises(SystemExit, match=r'2'):
|
||||
Arguments(['backtesting --ticker-interval']).get_parsed_arg()
|
||||
Arguments(['backtesting --timeframe']).get_parsed_arg()
|
||||
|
||||
with pytest.raises(SystemExit, match=r'2'):
|
||||
Arguments(['backtesting --ticker-interval', 'abc']).get_parsed_arg()
|
||||
Arguments(['backtesting --timeframe', 'abc']).get_parsed_arg()
|
||||
|
||||
|
||||
def test_parse_args_backtesting_custom() -> None:
|
||||
args = [
|
||||
'backtesting',
|
||||
'-c', 'test_conf.json',
|
||||
'--ticker-interval', '1m',
|
||||
'--timeframe', '1m',
|
||||
'--strategy-list',
|
||||
CURRENT_TEST_STRATEGY,
|
||||
'SampleStrategy'
|
||||
|
@@ -444,7 +444,7 @@ def test_setup_configuration_with_arguments(mocker, default_conf, caplog) -> Non
|
||||
'--strategy', CURRENT_TEST_STRATEGY,
|
||||
'--datadir', '/foo/bar',
|
||||
'--userdir', "/tmp/freqtrade",
|
||||
'--ticker-interval', '1m',
|
||||
'--timeframe', '1m',
|
||||
'--enable-position-stacking',
|
||||
'--disable-max-market-positions',
|
||||
'--timerange', ':100',
|
||||
@@ -495,7 +495,7 @@ def test_setup_configuration_with_stratlist(mocker, default_conf, caplog) -> Non
|
||||
arglist = [
|
||||
'backtesting',
|
||||
'--config', 'config.json',
|
||||
'--ticker-interval', '1m',
|
||||
'--timeframe', '1m',
|
||||
'--export', 'trades',
|
||||
'--strategy-list',
|
||||
CURRENT_TEST_STRATEGY,
|
||||
@@ -1381,22 +1381,14 @@ def test_process_removed_setting(mocker, default_conf, caplog):
|
||||
def test_process_deprecated_ticker_interval(default_conf, caplog):
|
||||
message = "DEPRECATED: Please use 'timeframe' instead of 'ticker_interval."
|
||||
config = deepcopy(default_conf)
|
||||
|
||||
process_temporary_deprecated_settings(config)
|
||||
assert not log_has(message, caplog)
|
||||
|
||||
del config['timeframe']
|
||||
config['ticker_interval'] = '15m'
|
||||
process_temporary_deprecated_settings(config)
|
||||
assert log_has(message, caplog)
|
||||
assert config['ticker_interval'] == '15m'
|
||||
|
||||
config = deepcopy(default_conf)
|
||||
# Have both timeframe and ticker interval in config
|
||||
# Can also happen when using ticker_interval in configuration, and --timeframe as cli argument
|
||||
config['timeframe'] = '5m'
|
||||
config['ticker_interval'] = '4h'
|
||||
with pytest.raises(OperationalException,
|
||||
match=r"Both 'timeframe' and 'ticker_interval' detected."):
|
||||
match=r"DEPRECATED: 'ticker_interval' detected. Please use.*"):
|
||||
process_temporary_deprecated_settings(config)
|
||||
|
||||
|
||||
|
@@ -4107,8 +4107,9 @@ def test_get_real_amount_quote(default_conf_usdt, trades_for_order, buy_order_fe
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
|
||||
caplog.clear()
|
||||
order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
|
||||
# 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, order_obj) == amount - (amount * 0.001)
|
||||
assert log_has(
|
||||
'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, is_short=False,'
|
||||
' leverage=1.0, open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992).',
|
||||
@@ -4134,8 +4135,9 @@ def test_get_real_amount_quote_dust(default_conf_usdt, trades_for_order, buy_ord
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
|
||||
walletmock.reset_mock()
|
||||
order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
|
||||
# Amount is kept as is
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) == amount
|
||||
assert walletmock.call_count == 1
|
||||
assert log_has_re(r'Fee amount for Trade.* was in base currency '
|
||||
'- Eating Fee 0.008 into dust', caplog)
|
||||
@@ -4156,8 +4158,9 @@ def test_get_real_amount_no_trade(default_conf_usdt, buy_order_fee, caplog, mock
|
||||
)
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
|
||||
order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
|
||||
# Amount is reduced by "fee"
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee) == amount
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) == amount
|
||||
assert log_has(
|
||||
'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
|
||||
'is_short=False, leverage=1.0, open_rate=0.24544100, open_since=closed) failed: '
|
||||
@@ -4213,7 +4216,8 @@ def test_get_real_amount(
|
||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', side_effect=ExchangeError)
|
||||
|
||||
caplog.clear()
|
||||
assert freqtrade.get_real_amount(trade, buy_order) == amount - fee_reduction_amount
|
||||
order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
|
||||
assert freqtrade.get_real_amount(trade, buy_order, order_obj) == amount - fee_reduction_amount
|
||||
|
||||
if expected_log:
|
||||
assert log_has(expected_log, caplog)
|
||||
@@ -4260,7 +4264,8 @@ def test_get_real_amount_multi(
|
||||
|
||||
# Amount is reduced by "fee"
|
||||
expected_amount = amount - (amount * fee_reduction_amount)
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee) == expected_amount
|
||||
order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) == expected_amount
|
||||
assert log_has(
|
||||
(
|
||||
'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
|
||||
@@ -4296,8 +4301,9 @@ def test_get_real_amount_invalid_order(default_conf_usdt, trades_for_order, buy_
|
||||
)
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
|
||||
order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
|
||||
# Amount does not change
|
||||
assert freqtrade.get_real_amount(trade, limit_buy_order_usdt) == amount
|
||||
assert freqtrade.get_real_amount(trade, limit_buy_order_usdt, order_obj) == amount
|
||||
|
||||
|
||||
def test_get_real_amount_fees_order(default_conf_usdt, market_buy_order_usdt_doublefee,
|
||||
@@ -4319,7 +4325,8 @@ def test_get_real_amount_fees_order(default_conf_usdt, market_buy_order_usdt_dou
|
||||
|
||||
# Amount does not change
|
||||
assert trade.fee_open == 0.0025
|
||||
assert freqtrade.get_real_amount(trade, market_buy_order_usdt_doublefee) == 30.0
|
||||
order_obj = Order.parse_from_ccxt_object(market_buy_order_usdt_doublefee, 'LTC/ETH', 'buy')
|
||||
assert freqtrade.get_real_amount(trade, market_buy_order_usdt_doublefee, order_obj) == 30.0
|
||||
assert tfo_mock.call_count == 0
|
||||
# Fetch fees from trades dict if available to get "proper" values
|
||||
assert round(trade.fee_open, 4) == 0.001
|
||||
@@ -4343,9 +4350,10 @@ def test_get_real_amount_wrong_amount(default_conf_usdt, trades_for_order, buy_o
|
||||
)
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
|
||||
order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
|
||||
# Amount does not change
|
||||
with pytest.raises(DependencyException, match=r"Half bought\? Amounts don't match"):
|
||||
freqtrade.get_real_amount(trade, limit_buy_order_usdt)
|
||||
freqtrade.get_real_amount(trade, limit_buy_order_usdt, order_obj)
|
||||
|
||||
|
||||
def test_get_real_amount_wrong_amount_rounding(default_conf_usdt, trades_for_order, buy_order_fee,
|
||||
@@ -4367,9 +4375,10 @@ def test_get_real_amount_wrong_amount_rounding(default_conf_usdt, trades_for_ord
|
||||
)
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
|
||||
order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
|
||||
# Amount changes by fee amount.
|
||||
assert isclose(
|
||||
freqtrade.get_real_amount(trade, limit_buy_order_usdt),
|
||||
freqtrade.get_real_amount(trade, limit_buy_order_usdt, order_obj),
|
||||
amount - (amount * 0.001),
|
||||
abs_tol=MATH_CLOSE_PREC,
|
||||
)
|
||||
@@ -4393,7 +4402,8 @@ def test_get_real_amount_open_trade_usdt(default_conf_usdt, fee, mocker):
|
||||
'side': 'buy',
|
||||
}
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
assert freqtrade.get_real_amount(trade, order) == amount
|
||||
order_obj = Order.parse_from_ccxt_object(order, 'LTC/ETH', 'buy')
|
||||
assert freqtrade.get_real_amount(trade, order, order_obj) == amount
|
||||
|
||||
|
||||
@pytest.mark.parametrize('amount,fee_abs,wallet,amount_exp', [
|
||||
|
Reference in New Issue
Block a user