Merge branch 'develop' into tsl_on_exchange
This commit is contained in:
@@ -124,9 +124,6 @@ class Configuration(object):
|
||||
if self.args.db_url and self.args.db_url != constants.DEFAULT_DB_PROD_URL:
|
||||
config.update({'db_url': self.args.db_url})
|
||||
logger.info('Parameter --db-url detected ...')
|
||||
else:
|
||||
# Set default here
|
||||
config.update({'db_url': constants.DEFAULT_DB_PROD_URL})
|
||||
|
||||
if config.get('dry_run', False):
|
||||
logger.info('Dry run is enabled')
|
||||
|
@@ -59,7 +59,7 @@ class Edge():
|
||||
|
||||
# checking max_open_trades. it should be -1 as with Edge
|
||||
# the number of trades is determined by position size
|
||||
if self.config['max_open_trades'] != -1:
|
||||
if self.config['max_open_trades'] != float('inf'):
|
||||
logger.critical('max_open_trades should be -1 in config !')
|
||||
|
||||
if self.config['stake_amount'] != constants.UNLIMITED_STAKE_AMOUNT:
|
||||
|
@@ -38,60 +38,43 @@ class StrategyResolver(IResolver):
|
||||
self.strategy: IStrategy = self._load_strategy(strategy_name,
|
||||
config=config,
|
||||
extra_dir=config.get('strategy_path'))
|
||||
|
||||
# Set attributes
|
||||
# Check if we need to override configuration
|
||||
if 'minimal_roi' in config:
|
||||
self.strategy.minimal_roi = config['minimal_roi']
|
||||
logger.info("Override strategy 'minimal_roi' with value in config file: %s.",
|
||||
config['minimal_roi'])
|
||||
else:
|
||||
config['minimal_roi'] = self.strategy.minimal_roi
|
||||
attributes = ["minimal_roi",
|
||||
"ticker_interval",
|
||||
"stoploss",
|
||||
"trailing_stop",
|
||||
"trailing_stop_positive",
|
||||
"trailing_stop_positive_offset",
|
||||
"process_only_new_candles",
|
||||
"order_types",
|
||||
"order_time_in_force"
|
||||
]
|
||||
for attribute in attributes:
|
||||
self._override_attribute_helper(config, attribute)
|
||||
|
||||
if 'stoploss' in config:
|
||||
self.strategy.stoploss = config['stoploss']
|
||||
logger.info(
|
||||
"Override strategy 'stoploss' with value in config file: %s.", config['stoploss']
|
||||
)
|
||||
else:
|
||||
config['stoploss'] = self.strategy.stoploss
|
||||
# Loop this list again to have output combined
|
||||
for attribute in attributes:
|
||||
if attribute in config:
|
||||
logger.info("Strategy using %s: %s", attribute, config[attribute])
|
||||
|
||||
if 'ticker_interval' in config:
|
||||
self.strategy.ticker_interval = config['ticker_interval']
|
||||
logger.info(
|
||||
"Override strategy 'ticker_interval' with value in config file: %s.",
|
||||
config['ticker_interval']
|
||||
)
|
||||
else:
|
||||
config['ticker_interval'] = self.strategy.ticker_interval
|
||||
# Sort and apply type conversions
|
||||
self.strategy.minimal_roi = OrderedDict(sorted(
|
||||
{int(key): value for (key, value) in self.strategy.minimal_roi.items()}.items(),
|
||||
key=lambda t: t[0]))
|
||||
self.strategy.stoploss = float(self.strategy.stoploss)
|
||||
|
||||
if 'process_only_new_candles' in config:
|
||||
self.strategy.process_only_new_candles = config['process_only_new_candles']
|
||||
logger.info(
|
||||
"Override process_only_new_candles 'process_only_new_candles' "
|
||||
"with value in config file: %s.", config['process_only_new_candles']
|
||||
)
|
||||
else:
|
||||
config['process_only_new_candles'] = self.strategy.process_only_new_candles
|
||||
self._strategy_sanity_validations()
|
||||
|
||||
if 'order_types' in config:
|
||||
self.strategy.order_types = config['order_types']
|
||||
logger.info(
|
||||
"Override strategy 'order_types' with value in config file: %s.",
|
||||
config['order_types']
|
||||
)
|
||||
else:
|
||||
config['order_types'] = self.strategy.order_types
|
||||
|
||||
if 'order_time_in_force' in config:
|
||||
self.strategy.order_time_in_force = config['order_time_in_force']
|
||||
logger.info(
|
||||
"Override strategy 'order_time_in_force' with value in config file: %s.",
|
||||
config['order_time_in_force']
|
||||
)
|
||||
else:
|
||||
config['order_time_in_force'] = self.strategy.order_time_in_force
|
||||
def _override_attribute_helper(self, config, attribute: str):
|
||||
if attribute in config:
|
||||
setattr(self.strategy, attribute, config[attribute])
|
||||
logger.info("Override strategy '%s' with value in config file: %s.",
|
||||
attribute, config[attribute])
|
||||
elif hasattr(self.strategy, attribute):
|
||||
config[attribute] = getattr(self.strategy, attribute)
|
||||
|
||||
def _strategy_sanity_validations(self):
|
||||
if not all(k in self.strategy.order_types for k in constants.REQUIRED_ORDERTYPES):
|
||||
raise ImportError(f"Impossible to load Strategy '{self.strategy.__class__.__name__}'. "
|
||||
f"Order-types mapping is incomplete.")
|
||||
@@ -100,12 +83,6 @@ class StrategyResolver(IResolver):
|
||||
raise ImportError(f"Impossible to load Strategy '{self.strategy.__class__.__name__}'. "
|
||||
f"Order-time-in-force mapping is incomplete.")
|
||||
|
||||
# Sort and apply type conversions
|
||||
self.strategy.minimal_roi = OrderedDict(sorted(
|
||||
{int(key): value for (key, value) in self.strategy.minimal_roi.items()}.items(),
|
||||
key=lambda t: t[0]))
|
||||
self.strategy.stoploss = float(self.strategy.stoploss)
|
||||
|
||||
def _load_strategy(
|
||||
self, strategy_name: str, config: dict, extra_dir: Optional[str] = None) -> IStrategy:
|
||||
"""
|
||||
|
@@ -67,6 +67,11 @@ class IStrategy(ABC):
|
||||
# associated stoploss
|
||||
stoploss: float
|
||||
|
||||
# trailing stoploss
|
||||
trailing_stop: bool = False
|
||||
trailing_stop_positive: float
|
||||
trailing_stop_positive_offset: float
|
||||
|
||||
# associated ticker interval
|
||||
ticker_interval: str
|
||||
|
||||
|
@@ -150,6 +150,45 @@ def test_strategy_override_stoploss(caplog):
|
||||
) in caplog.record_tuples
|
||||
|
||||
|
||||
def test_strategy_override_trailing_stop(caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
config = {
|
||||
'strategy': 'DefaultStrategy',
|
||||
'trailing_stop': True
|
||||
}
|
||||
resolver = StrategyResolver(config)
|
||||
|
||||
assert resolver.strategy.trailing_stop
|
||||
assert isinstance(resolver.strategy.trailing_stop, bool)
|
||||
assert ('freqtrade.resolvers.strategy_resolver',
|
||||
logging.INFO,
|
||||
"Override strategy 'trailing_stop' with value in config file: True."
|
||||
) in caplog.record_tuples
|
||||
|
||||
|
||||
def test_strategy_override_trailing_stop_positive(caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
config = {
|
||||
'strategy': 'DefaultStrategy',
|
||||
'trailing_stop_positive': -0.1,
|
||||
'trailing_stop_positive_offset': -0.2
|
||||
|
||||
}
|
||||
resolver = StrategyResolver(config)
|
||||
|
||||
assert resolver.strategy.trailing_stop_positive == -0.1
|
||||
assert ('freqtrade.resolvers.strategy_resolver',
|
||||
logging.INFO,
|
||||
"Override strategy 'trailing_stop_positive' with value in config file: -0.1."
|
||||
) in caplog.record_tuples
|
||||
|
||||
assert resolver.strategy.trailing_stop_positive_offset == -0.2
|
||||
assert ('freqtrade.resolvers.strategy_resolver',
|
||||
logging.INFO,
|
||||
"Override strategy 'trailing_stop_positive' with value in config file: -0.1."
|
||||
) in caplog.record_tuples
|
||||
|
||||
|
||||
def test_strategy_override_ticker_interval(caplog):
|
||||
caplog.set_level(logging.INFO)
|
||||
|
||||
@@ -178,8 +217,7 @@ def test_strategy_override_process_only_new_candles(caplog):
|
||||
assert resolver.strategy.process_only_new_candles
|
||||
assert ('freqtrade.resolvers.strategy_resolver',
|
||||
logging.INFO,
|
||||
"Override process_only_new_candles 'process_only_new_candles' "
|
||||
"with value in config file: True."
|
||||
"Override strategy 'process_only_new_candles' with value in config file: True."
|
||||
) in caplog.record_tuples
|
||||
|
||||
|
||||
|
@@ -73,7 +73,6 @@ def test_load_config_max_open_trades_minus_one(default_conf, mocker, caplog) ->
|
||||
args = Arguments([], '').get_parsed_arg()
|
||||
configuration = Configuration(args)
|
||||
validated_conf = configuration.load_config()
|
||||
print(validated_conf)
|
||||
|
||||
assert validated_conf['max_open_trades'] > 999999999
|
||||
assert validated_conf['max_open_trades'] == float('inf')
|
||||
@@ -125,6 +124,43 @@ def test_load_config_with_params(default_conf, mocker) -> None:
|
||||
assert validated_conf.get('strategy_path') == '/some/path'
|
||||
assert validated_conf.get('db_url') == 'sqlite:///someurl'
|
||||
|
||||
# Test conf provided db_url prod
|
||||
conf = default_conf.copy()
|
||||
conf["dry_run"] = False
|
||||
conf["db_url"] = "sqlite:///path/to/db.sqlite"
|
||||
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
||||
read_data=json.dumps(conf)
|
||||
))
|
||||
|
||||
arglist = [
|
||||
'--strategy', 'TestStrategy',
|
||||
'--strategy-path', '/some/path'
|
||||
]
|
||||
args = Arguments(arglist, '').get_parsed_arg()
|
||||
|
||||
configuration = Configuration(args)
|
||||
validated_conf = configuration.load_config()
|
||||
assert validated_conf.get('db_url') == "sqlite:///path/to/db.sqlite"
|
||||
|
||||
# Test conf provided db_url dry_run
|
||||
conf = default_conf.copy()
|
||||
conf["dry_run"] = True
|
||||
conf["db_url"] = "sqlite:///path/to/db.sqlite"
|
||||
mocker.patch('freqtrade.configuration.open', mocker.mock_open(
|
||||
read_data=json.dumps(conf)
|
||||
))
|
||||
|
||||
arglist = [
|
||||
'--strategy', 'TestStrategy',
|
||||
'--strategy-path', '/some/path'
|
||||
]
|
||||
args = Arguments(arglist, '').get_parsed_arg()
|
||||
|
||||
configuration = Configuration(args)
|
||||
validated_conf = configuration.load_config()
|
||||
assert validated_conf.get('db_url') == "sqlite:///path/to/db.sqlite"
|
||||
|
||||
# Test args provided db_url prod
|
||||
conf = default_conf.copy()
|
||||
conf["dry_run"] = False
|
||||
del conf["db_url"]
|
||||
@@ -142,7 +178,7 @@ def test_load_config_with_params(default_conf, mocker) -> None:
|
||||
validated_conf = configuration.load_config()
|
||||
assert validated_conf.get('db_url') == DEFAULT_DB_PROD_URL
|
||||
|
||||
# Test dry=run with ProdURL
|
||||
# Test args provided db_url dry_run
|
||||
conf = default_conf.copy()
|
||||
conf["dry_run"] = True
|
||||
conf["db_url"] = DEFAULT_DB_PROD_URL
|
||||
|
Reference in New Issue
Block a user