Changed max_open_trades type to int or inf
This commit is contained in:
parent
192f75254f
commit
b0f1d914c8
@ -681,3 +681,4 @@ MakerTaker = Literal['maker', 'taker']
|
|||||||
BidAsk = Literal['bid', 'ask']
|
BidAsk = Literal['bid', 'ask']
|
||||||
|
|
||||||
Config = Dict[str, Any]
|
Config = Dict[str, Any]
|
||||||
|
IntOrInf = float
|
||||||
|
@ -10,7 +10,7 @@ from typing import Any, Dict, List, Optional, Union
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
|
|
||||||
from freqtrade.constants import LAST_BT_RESULT_FN
|
from freqtrade.constants import LAST_BT_RESULT_FN, IntOrInf
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.misc import json_load
|
from freqtrade.misc import json_load
|
||||||
from freqtrade.optimize.backtest_caching import get_backtest_metadata_filename
|
from freqtrade.optimize.backtest_caching import get_backtest_metadata_filename
|
||||||
@ -332,7 +332,7 @@ def analyze_trade_parallelism(results: pd.DataFrame, timeframe: str) -> pd.DataF
|
|||||||
|
|
||||||
|
|
||||||
def evaluate_result_multi(results: pd.DataFrame, timeframe: str,
|
def evaluate_result_multi(results: pd.DataFrame, timeframe: str,
|
||||||
max_open_trades: int) -> pd.DataFrame:
|
max_open_trades: IntOrInf) -> pd.DataFrame:
|
||||||
"""
|
"""
|
||||||
Find overlapping trades by expanding each trade once per period it was open
|
Find overlapping trades by expanding each trade once per period it was open
|
||||||
and then counting overlaps
|
and then counting overlaps
|
||||||
|
@ -1237,8 +1237,8 @@ class Backtesting:
|
|||||||
if not self.config.get('use_max_market_positions', True):
|
if not self.config.get('use_max_market_positions', True):
|
||||||
logger.info(
|
logger.info(
|
||||||
'Ignoring max_open_trades (--disable-max-market-positions was used) ...')
|
'Ignoring max_open_trades (--disable-max-market-positions was used) ...')
|
||||||
self.strategy.max_open_trades = -1
|
self.strategy.max_open_trades = float('inf')
|
||||||
self.config.update({'max_open_trades': float('inf')})
|
self.config.update({'max_open_trades': self.strategy.max_open_trades})
|
||||||
|
|
||||||
# need to reprocess data every time to populate signals
|
# need to reprocess data every time to populate signals
|
||||||
preprocessed = self.strategy.advise_all_indicators(data)
|
preprocessed = self.strategy.advise_all_indicators(data)
|
||||||
|
@ -120,8 +120,8 @@ class Hyperopt:
|
|||||||
# Use max_open_trades for hyperopt as well, except --disable-max-market-positions is set
|
# Use max_open_trades for hyperopt as well, except --disable-max-market-positions is set
|
||||||
if not self.config.get('use_max_market_positions', True):
|
if not self.config.get('use_max_market_positions', True):
|
||||||
logger.debug('Ignoring max_open_trades (--disable-max-market-positions was used) ...')
|
logger.debug('Ignoring max_open_trades (--disable-max-market-positions was used) ...')
|
||||||
self.backtesting.strategy.max_open_trades = -1
|
self.backtesting.strategy.max_open_trades = float('inf')
|
||||||
config.update({'max_open_trades': float('inf')})
|
config.update({'max_open_trades': self.backtesting.strategy.max_open_trades})
|
||||||
|
|
||||||
if HyperoptTools.has_space(self.config, 'sell'):
|
if HyperoptTools.has_space(self.config, 'sell'):
|
||||||
# Make sure use_exit_signal is enabled
|
# Make sure use_exit_signal is enabled
|
||||||
@ -211,7 +211,8 @@ class Hyperopt:
|
|||||||
result['trailing'] = self.custom_hyperopt.generate_trailing_params(params)
|
result['trailing'] = self.custom_hyperopt.generate_trailing_params(params)
|
||||||
if HyperoptTools.has_space(self.config, 'trades'):
|
if HyperoptTools.has_space(self.config, 'trades'):
|
||||||
result['max_open_trades'] = {
|
result['max_open_trades'] = {
|
||||||
'max_open_trades': self.backtesting.strategy.max_open_trades}
|
'max_open_trades': self.backtesting.strategy.max_open_trades
|
||||||
|
if self.backtesting.strategy.max_open_trades != float('inf') else -1}
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
@ -344,16 +345,13 @@ class Hyperopt:
|
|||||||
# Ignore unlimited max open trades if stake amount is unlimited
|
# Ignore unlimited max open trades if stake amount is unlimited
|
||||||
params_dict.update({'max_open_trades': self.config['max_open_trades']})
|
params_dict.update({'max_open_trades': self.config['max_open_trades']})
|
||||||
|
|
||||||
updated_config_max_open_trades = int(params_dict['max_open_trades']) \
|
updated_max_open_trades = int(params_dict['max_open_trades']) \
|
||||||
if (params_dict['max_open_trades'] != -1
|
if (params_dict['max_open_trades'] != -1
|
||||||
and params_dict['max_open_trades'] != 0) else float('inf')
|
and params_dict['max_open_trades'] != 0) else float('inf')
|
||||||
|
|
||||||
updated_strategy_max_open_trades = int(updated_config_max_open_trades) \
|
self.config.update({'max_open_trades': updated_max_open_trades})
|
||||||
if updated_config_max_open_trades != float('inf') else -1
|
|
||||||
|
|
||||||
self.config.update({'max_open_trades': updated_config_max_open_trades})
|
self.backtesting.strategy.max_open_trades = updated_max_open_trades
|
||||||
|
|
||||||
self.backtesting.strategy.max_open_trades = updated_strategy_max_open_trades
|
|
||||||
|
|
||||||
with self.data_pickle_file.open('rb') as f:
|
with self.data_pickle_file.open('rb') as f:
|
||||||
processed = load(f, mmap_mode='r')
|
processed = load(f, mmap_mode='r')
|
||||||
|
@ -8,7 +8,7 @@ from pandas import DataFrame, to_datetime
|
|||||||
from tabulate import tabulate
|
from tabulate import tabulate
|
||||||
|
|
||||||
from freqtrade.constants import (DATETIME_PRINT_FORMAT, LAST_BT_RESULT_FN, UNLIMITED_STAKE_AMOUNT,
|
from freqtrade.constants import (DATETIME_PRINT_FORMAT, LAST_BT_RESULT_FN, UNLIMITED_STAKE_AMOUNT,
|
||||||
Config)
|
Config, IntOrInf)
|
||||||
from freqtrade.data.metrics import (calculate_cagr, calculate_calmar, calculate_csum,
|
from freqtrade.data.metrics import (calculate_cagr, calculate_calmar, calculate_csum,
|
||||||
calculate_expectancy, calculate_market_change,
|
calculate_expectancy, calculate_market_change,
|
||||||
calculate_max_drawdown, calculate_sharpe, calculate_sortino)
|
calculate_max_drawdown, calculate_sharpe, calculate_sortino)
|
||||||
@ -191,7 +191,7 @@ def generate_tag_metrics(tag_type: str,
|
|||||||
return []
|
return []
|
||||||
|
|
||||||
|
|
||||||
def generate_exit_reason_stats(max_open_trades: int, results: DataFrame) -> List[Dict]:
|
def generate_exit_reason_stats(max_open_trades: IntOrInf, results: DataFrame) -> List[Dict]:
|
||||||
"""
|
"""
|
||||||
Generate small table outlining Backtest results
|
Generate small table outlining Backtest results
|
||||||
:param max_open_trades: Max_open_trades parameter
|
:param max_open_trades: Max_open_trades parameter
|
||||||
|
@ -104,11 +104,7 @@ class StrategyResolver(IResolver):
|
|||||||
if (attribute in config
|
if (attribute in config
|
||||||
and not isinstance(getattr(type(strategy), attribute, None), property)):
|
and not isinstance(getattr(type(strategy), attribute, None), property)):
|
||||||
# Ensure Properties are not overwritten
|
# Ensure Properties are not overwritten
|
||||||
val = config[attribute]
|
setattr(strategy, attribute, config[attribute])
|
||||||
# max_open_trades set to float('inf') in the config will be copied as -1 in the strategy
|
|
||||||
if attribute == 'max_open_trades' and val == float('inf'):
|
|
||||||
val = -1
|
|
||||||
setattr(strategy, attribute, val)
|
|
||||||
logger.info("Override strategy '%s' with value in config file: %s.",
|
logger.info("Override strategy '%s' with value in config file: %s.",
|
||||||
attribute, config[attribute])
|
attribute, config[attribute])
|
||||||
elif hasattr(strategy, attribute):
|
elif hasattr(strategy, attribute):
|
||||||
@ -137,6 +133,8 @@ class StrategyResolver(IResolver):
|
|||||||
key=lambda t: t[0]))
|
key=lambda t: t[0]))
|
||||||
if hasattr(strategy, 'stoploss'):
|
if hasattr(strategy, 'stoploss'):
|
||||||
strategy.stoploss = float(strategy.stoploss)
|
strategy.stoploss = float(strategy.stoploss)
|
||||||
|
if hasattr(strategy, 'max_open_trades') and strategy.max_open_trades < 0:
|
||||||
|
strategy.max_open_trades = float('inf')
|
||||||
return strategy
|
return strategy
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
|
@ -3,7 +3,7 @@ from typing import Any, Dict, List, Optional, Union
|
|||||||
|
|
||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from freqtrade.constants import DATETIME_PRINT_FORMAT
|
from freqtrade.constants import DATETIME_PRINT_FORMAT, IntOrInf
|
||||||
from freqtrade.enums import OrderTypeValues, SignalDirection, TradingMode
|
from freqtrade.enums import OrderTypeValues, SignalDirection, TradingMode
|
||||||
|
|
||||||
|
|
||||||
@ -165,7 +165,7 @@ class ShowConfig(BaseModel):
|
|||||||
stake_amount: str
|
stake_amount: str
|
||||||
available_capital: Optional[float]
|
available_capital: Optional[float]
|
||||||
stake_currency_decimals: int
|
stake_currency_decimals: int
|
||||||
max_open_trades: int
|
max_open_trades: IntOrInf
|
||||||
minimal_roi: Dict[str, Any]
|
minimal_roi: Dict[str, Any]
|
||||||
stoploss: Optional[float]
|
stoploss: Optional[float]
|
||||||
trailing_stop: Optional[bool]
|
trailing_stop: Optional[bool]
|
||||||
@ -422,7 +422,7 @@ class BacktestRequest(BaseModel):
|
|||||||
timeframe: Optional[str]
|
timeframe: Optional[str]
|
||||||
timeframe_detail: Optional[str]
|
timeframe_detail: Optional[str]
|
||||||
timerange: Optional[str]
|
timerange: Optional[str]
|
||||||
max_open_trades: Optional[int]
|
max_open_trades: Optional[IntOrInf]
|
||||||
stake_amount: Optional[str]
|
stake_amount: Optional[str]
|
||||||
enable_protections: bool
|
enable_protections: bool
|
||||||
dry_run_wallet: Optional[float]
|
dry_run_wallet: Optional[float]
|
||||||
|
@ -673,6 +673,7 @@ class RPC:
|
|||||||
if self._freqtrade.state == State.RUNNING:
|
if self._freqtrade.state == State.RUNNING:
|
||||||
# Set 'max_open_trades' to 0
|
# Set 'max_open_trades' to 0
|
||||||
self._freqtrade.config['max_open_trades'] = 0
|
self._freqtrade.config['max_open_trades'] = 0
|
||||||
|
self._freqtrade.strategy.max_open_trades = 0
|
||||||
|
|
||||||
return {'status': 'No more entries will occur from now. Run /reload_config to reset.'}
|
return {'status': 'No more entries will occur from now. Run /reload_config to reset.'}
|
||||||
|
|
||||||
|
@ -10,7 +10,7 @@ from typing import Dict, List, Optional, Tuple, Union
|
|||||||
import arrow
|
import arrow
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade.constants import Config, ListPairsWithTimeframes
|
from freqtrade.constants import Config, IntOrInf, ListPairsWithTimeframes
|
||||||
from freqtrade.data.dataprovider import DataProvider
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, RunMode, SignalDirection,
|
from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, RunMode, SignalDirection,
|
||||||
SignalTagType, SignalType, TradingMode)
|
SignalTagType, SignalType, TradingMode)
|
||||||
@ -55,7 +55,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
stoploss: float
|
stoploss: float
|
||||||
|
|
||||||
# max open trades for the strategy
|
# max open trades for the strategy
|
||||||
max_open_trades: int
|
max_open_trades: IntOrInf
|
||||||
|
|
||||||
# trailing stoploss
|
# trailing stoploss
|
||||||
trailing_stop: bool = False
|
trailing_stop: bool = False
|
||||||
|
@ -1020,6 +1020,52 @@ def test_stake_amount_unlimited_max_open_trades(mocker, hyperopt_conf, tmpdir, f
|
|||||||
assert hyperopt.backtesting.strategy.max_open_trades == 1
|
assert hyperopt.backtesting.strategy.max_open_trades == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_max_open_trades_dump(mocker, hyperopt_conf, tmpdir, fee, capsys) -> None:
|
||||||
|
# This test is to ensure that after hyperopting, max_open_trades is never
|
||||||
|
# saved as inf in the output json params
|
||||||
|
patch_exchange(mocker)
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
|
(Path(tmpdir) / 'hyperopt_results').mkdir(parents=True)
|
||||||
|
hyperopt_conf.update({
|
||||||
|
'strategy': 'HyperoptableStrategy',
|
||||||
|
'user_data_dir': Path(tmpdir),
|
||||||
|
'hyperopt_random_state': 42,
|
||||||
|
'spaces': ['trades'],
|
||||||
|
})
|
||||||
|
hyperopt = Hyperopt(hyperopt_conf)
|
||||||
|
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._get_params_dict',
|
||||||
|
return_value={
|
||||||
|
'max_open_trades': -1
|
||||||
|
})
|
||||||
|
|
||||||
|
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
|
||||||
|
|
||||||
|
hyperopt.start()
|
||||||
|
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
|
||||||
|
assert 'max_open_trades = -1' in out
|
||||||
|
assert 'max_open_trades = inf' not in out
|
||||||
|
|
||||||
|
##############
|
||||||
|
|
||||||
|
hyperopt_conf.update({'print_json': True})
|
||||||
|
|
||||||
|
hyperopt = Hyperopt(hyperopt_conf)
|
||||||
|
mocker.patch('freqtrade.optimize.hyperopt.Hyperopt._get_params_dict',
|
||||||
|
return_value={
|
||||||
|
'max_open_trades': -1
|
||||||
|
})
|
||||||
|
|
||||||
|
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
|
||||||
|
|
||||||
|
hyperopt.start()
|
||||||
|
|
||||||
|
out, err = capsys.readouterr()
|
||||||
|
|
||||||
|
assert '"max_open_trades":-1' in out
|
||||||
|
|
||||||
|
|
||||||
def test_max_open_trades_consistency(mocker, hyperopt_conf, tmpdir, fee) -> None:
|
def test_max_open_trades_consistency(mocker, hyperopt_conf, tmpdir, fee) -> None:
|
||||||
# This test is to ensure that max_open_trades is the same across all functions needing it
|
# This test is to ensure that max_open_trades is the same across all functions needing it
|
||||||
# after it has been changed from the hyperopt
|
# after it has been changed from the hyperopt
|
||||||
|
@ -6,6 +6,7 @@ from pathlib import Path
|
|||||||
import pytest
|
import pytest
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
|
from freqtrade.configuration.configuration import Configuration
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.resolvers import StrategyResolver
|
from freqtrade.resolvers import StrategyResolver
|
||||||
from freqtrade.strategy.interface import IStrategy
|
from freqtrade.strategy.interface import IStrategy
|
||||||
@ -371,20 +372,26 @@ def test_strategy_max_open_trades_infinity_from_strategy(caplog, default_conf):
|
|||||||
strategy = StrategyResolver.load_strategy(default_conf)
|
strategy = StrategyResolver.load_strategy(default_conf)
|
||||||
|
|
||||||
# this test assumes -1 set to 'max_open_trades' in CURRENT_TEST_STRATEGY
|
# this test assumes -1 set to 'max_open_trades' in CURRENT_TEST_STRATEGY
|
||||||
assert strategy.max_open_trades == -1
|
assert strategy.max_open_trades == float('inf')
|
||||||
assert default_conf['max_open_trades'] == float('inf')
|
assert default_conf['max_open_trades'] == float('inf')
|
||||||
|
|
||||||
|
|
||||||
def test_strategy_max_open_trades_infinity_from_config(caplog, default_conf):
|
def test_strategy_max_open_trades_infinity_from_config(caplog, default_conf, mocker):
|
||||||
caplog.set_level(logging.INFO)
|
caplog.set_level(logging.INFO)
|
||||||
default_conf.update({
|
default_conf.update({
|
||||||
'strategy': CURRENT_TEST_STRATEGY,
|
'strategy': CURRENT_TEST_STRATEGY,
|
||||||
'max_open_trades': float('inf')
|
'max_open_trades': -1,
|
||||||
|
'exchange': 'binance'
|
||||||
})
|
})
|
||||||
|
|
||||||
strategy = StrategyResolver.load_strategy(default_conf)
|
configuration = Configuration(args=default_conf)
|
||||||
|
parsed_config = configuration.get_config()
|
||||||
|
|
||||||
assert strategy.max_open_trades == -1
|
assert parsed_config['max_open_trades'] == float('inf')
|
||||||
|
|
||||||
|
strategy = StrategyResolver.load_strategy(parsed_config)
|
||||||
|
|
||||||
|
assert strategy.max_open_trades == float('inf')
|
||||||
|
|
||||||
|
|
||||||
@ pytest.mark.filterwarnings("ignore:deprecated")
|
@ pytest.mark.filterwarnings("ignore:deprecated")
|
||||||
@ -476,3 +483,19 @@ def test_strategy_interface_versioning(dataframe_1m, default_conf):
|
|||||||
assert isinstance(exitdf, DataFrame)
|
assert isinstance(exitdf, DataFrame)
|
||||||
assert 'sell' not in exitdf
|
assert 'sell' not in exitdf
|
||||||
assert 'exit_long' in exitdf
|
assert 'exit_long' in exitdf
|
||||||
|
|
||||||
|
|
||||||
|
def test_strategy_ft_load_params_from_file(mocker, default_conf):
|
||||||
|
default_conf.update({'strategy': 'StrategyTestV2'})
|
||||||
|
del default_conf['max_open_trades']
|
||||||
|
mocker.patch('freqtrade.strategy.hyper.HyperStrategyMixin.load_params_from_file',
|
||||||
|
return_value={
|
||||||
|
'params': {
|
||||||
|
'max_open_trades': {
|
||||||
|
'max_open_trades': -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
strategy = StrategyResolver.load_strategy(default_conf)
|
||||||
|
assert strategy.max_open_trades == float('inf')
|
||||||
|
assert strategy.config['max_open_trades'] == float('inf')
|
||||||
|
Loading…
Reference in New Issue
Block a user