Changed max_open_trades type to int or inf

This commit is contained in:
Antonio Della Fortuna 2023-01-15 11:44:10 +01:00
parent 192f75254f
commit b0f1d914c8
11 changed files with 97 additions and 30 deletions

View File

@ -681,3 +681,4 @@ MakerTaker = Literal['maker', 'taker']
BidAsk = Literal['bid', 'ask']
Config = Dict[str, Any]
IntOrInf = float

View File

@ -10,7 +10,7 @@ from typing import Any, Dict, List, Optional, Union
import numpy as np
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.misc import json_load
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,
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
and then counting overlaps

View File

@ -1237,8 +1237,8 @@ class Backtesting:
if not self.config.get('use_max_market_positions', True):
logger.info(
'Ignoring max_open_trades (--disable-max-market-positions was used) ...')
self.strategy.max_open_trades = -1
self.config.update({'max_open_trades': float('inf')})
self.strategy.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
preprocessed = self.strategy.advise_all_indicators(data)

View File

@ -120,8 +120,8 @@ class Hyperopt:
# 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):
logger.debug('Ignoring max_open_trades (--disable-max-market-positions was used) ...')
self.backtesting.strategy.max_open_trades = -1
config.update({'max_open_trades': float('inf')})
self.backtesting.strategy.max_open_trades = float('inf')
config.update({'max_open_trades': self.backtesting.strategy.max_open_trades})
if HyperoptTools.has_space(self.config, 'sell'):
# Make sure use_exit_signal is enabled
@ -211,7 +211,8 @@ class Hyperopt:
result['trailing'] = self.custom_hyperopt.generate_trailing_params(params)
if HyperoptTools.has_space(self.config, '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
@ -344,16 +345,13 @@ class Hyperopt:
# Ignore unlimited max open trades if stake amount is unlimited
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
and params_dict['max_open_trades'] != 0) else float('inf')
updated_strategy_max_open_trades = int(updated_config_max_open_trades) \
if updated_config_max_open_trades != float('inf') else -1
self.config.update({'max_open_trades': updated_max_open_trades})
self.config.update({'max_open_trades': updated_config_max_open_trades})
self.backtesting.strategy.max_open_trades = updated_strategy_max_open_trades
self.backtesting.strategy.max_open_trades = updated_max_open_trades
with self.data_pickle_file.open('rb') as f:
processed = load(f, mmap_mode='r')

View File

@ -8,7 +8,7 @@ from pandas import DataFrame, to_datetime
from tabulate import tabulate
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,
calculate_expectancy, calculate_market_change,
calculate_max_drawdown, calculate_sharpe, calculate_sortino)
@ -191,7 +191,7 @@ def generate_tag_metrics(tag_type: str,
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
:param max_open_trades: Max_open_trades parameter

View File

@ -104,11 +104,7 @@ class StrategyResolver(IResolver):
if (attribute in config
and not isinstance(getattr(type(strategy), attribute, None), property)):
# Ensure Properties are not overwritten
val = 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)
setattr(strategy, attribute, config[attribute])
logger.info("Override strategy '%s' with value in config file: %s.",
attribute, config[attribute])
elif hasattr(strategy, attribute):
@ -137,6 +133,8 @@ class StrategyResolver(IResolver):
key=lambda t: t[0]))
if hasattr(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
@staticmethod

View File

@ -3,7 +3,7 @@ from typing import Any, Dict, List, Optional, Union
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
@ -165,7 +165,7 @@ class ShowConfig(BaseModel):
stake_amount: str
available_capital: Optional[float]
stake_currency_decimals: int
max_open_trades: int
max_open_trades: IntOrInf
minimal_roi: Dict[str, Any]
stoploss: Optional[float]
trailing_stop: Optional[bool]
@ -422,7 +422,7 @@ class BacktestRequest(BaseModel):
timeframe: Optional[str]
timeframe_detail: Optional[str]
timerange: Optional[str]
max_open_trades: Optional[int]
max_open_trades: Optional[IntOrInf]
stake_amount: Optional[str]
enable_protections: bool
dry_run_wallet: Optional[float]

View File

@ -673,6 +673,7 @@ class RPC:
if self._freqtrade.state == State.RUNNING:
# Set 'max_open_trades' to 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.'}

View File

@ -10,7 +10,7 @@ from typing import Dict, List, Optional, Tuple, Union
import arrow
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.enums import (CandleType, ExitCheckTuple, ExitType, RunMode, SignalDirection,
SignalTagType, SignalType, TradingMode)
@ -55,7 +55,7 @@ class IStrategy(ABC, HyperStrategyMixin):
stoploss: float
# max open trades for the strategy
max_open_trades: int
max_open_trades: IntOrInf
# trailing stoploss
trailing_stop: bool = False

View File

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

View File

@ -6,6 +6,7 @@ from pathlib import Path
import pytest
from pandas import DataFrame
from freqtrade.configuration.configuration import Configuration
from freqtrade.exceptions import OperationalException
from freqtrade.resolvers import StrategyResolver
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)
# 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')
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)
default_conf.update({
'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")
@ -476,3 +483,19 @@ def test_strategy_interface_versioning(dataframe_1m, default_conf):
assert isinstance(exitdf, DataFrame)
assert 'sell' not 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')