Merge pull request #7092 from freqtrade/fix/hyperopt_inherit
hyperopt inherit fix
This commit is contained in:
commit
3eb2131d0b
@ -175,23 +175,11 @@ class Exchange:
|
|||||||
logger.info(f'Using Exchange "{self.name}"')
|
logger.info(f'Using Exchange "{self.name}"')
|
||||||
|
|
||||||
if validate:
|
if validate:
|
||||||
# Check if timeframe is available
|
|
||||||
self.validate_timeframes(config.get('timeframe'))
|
|
||||||
|
|
||||||
# Initial markets load
|
# Initial markets load
|
||||||
self._load_markets()
|
self._load_markets()
|
||||||
|
self.validate_config(config)
|
||||||
# Check if all pairs are available
|
|
||||||
self.validate_stakecurrency(config['stake_currency'])
|
|
||||||
if not exchange_config.get('skip_pair_validation'):
|
|
||||||
self.validate_pairs(config['exchange']['pair_whitelist'])
|
|
||||||
self.validate_ordertypes(config.get('order_types', {}))
|
|
||||||
self.validate_order_time_in_force(config.get('order_time_in_force', {}))
|
|
||||||
self.required_candle_call_count = self.validate_required_startup_candles(
|
self.required_candle_call_count = self.validate_required_startup_candles(
|
||||||
config.get('startup_candle_count', 0), config.get('timeframe', ''))
|
config.get('startup_candle_count', 0), config.get('timeframe', ''))
|
||||||
self.validate_trading_mode_and_margin_mode(self.trading_mode, self.margin_mode)
|
|
||||||
self.validate_pricing(config['exit_pricing'])
|
|
||||||
self.validate_pricing(config['entry_pricing'])
|
|
||||||
|
|
||||||
# Converts the interval provided in minutes in config to seconds
|
# Converts the interval provided in minutes in config to seconds
|
||||||
self.markets_refresh_interval: int = exchange_config.get(
|
self.markets_refresh_interval: int = exchange_config.get(
|
||||||
@ -214,6 +202,20 @@ class Exchange:
|
|||||||
logger.info("Closing async ccxt session.")
|
logger.info("Closing async ccxt session.")
|
||||||
self.loop.run_until_complete(self._api_async.close())
|
self.loop.run_until_complete(self._api_async.close())
|
||||||
|
|
||||||
|
def validate_config(self, config):
|
||||||
|
# Check if timeframe is available
|
||||||
|
self.validate_timeframes(config.get('timeframe'))
|
||||||
|
|
||||||
|
# Check if all pairs are available
|
||||||
|
self.validate_stakecurrency(config['stake_currency'])
|
||||||
|
if not config['exchange'].get('skip_pair_validation'):
|
||||||
|
self.validate_pairs(config['exchange']['pair_whitelist'])
|
||||||
|
self.validate_ordertypes(config.get('order_types', {}))
|
||||||
|
self.validate_order_time_in_force(config.get('order_time_in_force', {}))
|
||||||
|
self.validate_trading_mode_and_margin_mode(self.trading_mode, self.margin_mode)
|
||||||
|
self.validate_pricing(config['exit_pricing'])
|
||||||
|
self.validate_pricing(config['entry_pricing'])
|
||||||
|
|
||||||
def _init_ccxt(self, exchange_config: Dict[str, Any], ccxt_module: CcxtModuleType = ccxt,
|
def _init_ccxt(self, exchange_config: Dict[str, Any], ccxt_module: CcxtModuleType = ccxt,
|
||||||
ccxt_kwargs: Dict = {}) -> ccxt.Exchange:
|
ccxt_kwargs: Dict = {}) -> ccxt.Exchange:
|
||||||
"""
|
"""
|
||||||
|
@ -6,6 +6,7 @@ This module contains the hyperopt logic
|
|||||||
|
|
||||||
import logging
|
import logging
|
||||||
import random
|
import random
|
||||||
|
import sys
|
||||||
import warnings
|
import warnings
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
from math import ceil
|
from math import ceil
|
||||||
@ -17,6 +18,7 @@ import rapidjson
|
|||||||
from colorama import Fore, Style
|
from colorama import Fore, Style
|
||||||
from colorama import init as colorama_init
|
from colorama import init as colorama_init
|
||||||
from joblib import Parallel, cpu_count, delayed, dump, load, wrap_non_picklable_objects
|
from joblib import Parallel, cpu_count, delayed, dump, load, wrap_non_picklable_objects
|
||||||
|
from joblib.externals import cloudpickle
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
|
|
||||||
from freqtrade.constants import DATETIME_PRINT_FORMAT, FTHYPT_FILEVERSION, LAST_BT_RESULT_FN
|
from freqtrade.constants import DATETIME_PRINT_FORMAT, FTHYPT_FILEVERSION, LAST_BT_RESULT_FN
|
||||||
@ -87,6 +89,7 @@ class Hyperopt:
|
|||||||
self.backtesting._set_strategy(self.backtesting.strategylist[0])
|
self.backtesting._set_strategy(self.backtesting.strategylist[0])
|
||||||
self.custom_hyperopt.strategy = self.backtesting.strategy
|
self.custom_hyperopt.strategy = self.backtesting.strategy
|
||||||
|
|
||||||
|
self.hyperopt_pickle_magic(self.backtesting.strategy.__class__.__bases__)
|
||||||
self.custom_hyperoptloss: IHyperOptLoss = HyperOptLossResolver.load_hyperoptloss(
|
self.custom_hyperoptloss: IHyperOptLoss = HyperOptLossResolver.load_hyperoptloss(
|
||||||
self.config)
|
self.config)
|
||||||
self.calculate_loss = self.custom_hyperoptloss.hyperopt_loss_function
|
self.calculate_loss = self.custom_hyperoptloss.hyperopt_loss_function
|
||||||
@ -137,6 +140,17 @@ class Hyperopt:
|
|||||||
logger.info(f"Removing `{p}`.")
|
logger.info(f"Removing `{p}`.")
|
||||||
p.unlink()
|
p.unlink()
|
||||||
|
|
||||||
|
def hyperopt_pickle_magic(self, bases) -> None:
|
||||||
|
"""
|
||||||
|
Hyperopt magic to allow strategy inheritance across files.
|
||||||
|
For this to properly work, we need to register the module of the imported class
|
||||||
|
to pickle as value.
|
||||||
|
"""
|
||||||
|
for modules in bases:
|
||||||
|
if modules.__name__ != 'IStrategy':
|
||||||
|
cloudpickle.register_pickle_by_value(sys.modules[modules.__module__])
|
||||||
|
self.hyperopt_pickle_magic(modules.__bases__)
|
||||||
|
|
||||||
def _get_params_dict(self, dimensions: List[Dimension], raw_params: List[Any]) -> Dict:
|
def _get_params_dict(self, dimensions: List[Dimension], raw_params: List[Any]) -> Dict:
|
||||||
|
|
||||||
# Ensure the number of dimensions match
|
# Ensure the number of dimensions match
|
||||||
|
@ -112,11 +112,8 @@ def patch_exchange(
|
|||||||
mock_supported_modes=True
|
mock_supported_modes=True
|
||||||
) -> None:
|
) -> None:
|
||||||
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock(return_value={}))
|
mocker.patch('freqtrade.exchange.Exchange._load_async_markets', MagicMock(return_value={}))
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_pairs', MagicMock())
|
mocker.patch('freqtrade.exchange.Exchange.validate_config', MagicMock())
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_ordertypes', MagicMock())
|
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_stakecurrency', MagicMock())
|
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_pricing')
|
|
||||||
mocker.patch('freqtrade.exchange.Exchange.id', PropertyMock(return_value=id))
|
mocker.patch('freqtrade.exchange.Exchange.id', PropertyMock(return_value=id))
|
||||||
mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value=id.title()))
|
mocker.patch('freqtrade.exchange.Exchange.name', PropertyMock(return_value=id.title()))
|
||||||
mocker.patch('freqtrade.exchange.Exchange.precisionMode', PropertyMock(return_value=2))
|
mocker.patch('freqtrade.exchange.Exchange.precisionMode', PropertyMock(return_value=2))
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
# pragma pylint: disable=missing-docstring,W0212,C0103
|
# pragma pylint: disable=missing-docstring,W0212,C0103
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from unittest.mock import ANY, MagicMock
|
from unittest.mock import ANY, MagicMock, PropertyMock
|
||||||
|
|
||||||
import pandas as pd
|
import pandas as pd
|
||||||
import pytest
|
import pytest
|
||||||
@ -18,8 +18,8 @@ from freqtrade.optimize.hyperopt_tools import HyperoptTools
|
|||||||
from freqtrade.optimize.optimize_reports import generate_strategy_stats
|
from freqtrade.optimize.optimize_reports import generate_strategy_stats
|
||||||
from freqtrade.optimize.space import SKDecimal
|
from freqtrade.optimize.space import SKDecimal
|
||||||
from freqtrade.strategy import IntParameter
|
from freqtrade.strategy import IntParameter
|
||||||
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, get_markets, log_has, log_has_re,
|
||||||
patched_configuration_load_config_file)
|
patch_exchange, patched_configuration_load_config_file)
|
||||||
|
|
||||||
|
|
||||||
def generate_result_metrics():
|
def generate_result_metrics():
|
||||||
@ -855,7 +855,7 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None:
|
|||||||
'strategy': 'HyperoptableStrategy',
|
'strategy': 'HyperoptableStrategy',
|
||||||
'user_data_dir': Path(tmpdir),
|
'user_data_dir': Path(tmpdir),
|
||||||
'hyperopt_random_state': 42,
|
'hyperopt_random_state': 42,
|
||||||
'spaces': ['all']
|
'spaces': ['all'],
|
||||||
})
|
})
|
||||||
hyperopt = Hyperopt(hyperopt_conf)
|
hyperopt = Hyperopt(hyperopt_conf)
|
||||||
hyperopt.backtesting.exchange.get_max_leverage = MagicMock(return_value=1.0)
|
hyperopt.backtesting.exchange.get_max_leverage = MagicMock(return_value=1.0)
|
||||||
@ -883,6 +883,45 @@ def test_in_strategy_auto_hyperopt(mocker, hyperopt_conf, tmpdir, fee) -> None:
|
|||||||
hyperopt.get_optimizer([], 2)
|
hyperopt.get_optimizer([], 2)
|
||||||
|
|
||||||
|
|
||||||
|
def test_in_strategy_auto_hyperopt_with_parallel(mocker, hyperopt_conf, tmpdir, fee) -> None:
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.validate_config', MagicMock())
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange._load_markets')
|
||||||
|
mocker.patch('freqtrade.exchange.Exchange.markets',
|
||||||
|
PropertyMock(return_value=get_markets()))
|
||||||
|
(Path(tmpdir) / 'hyperopt_results').mkdir(parents=True)
|
||||||
|
# No hyperopt needed
|
||||||
|
hyperopt_conf.update({
|
||||||
|
'strategy': 'HyperoptableStrategy',
|
||||||
|
'user_data_dir': Path(tmpdir),
|
||||||
|
'hyperopt_random_state': 42,
|
||||||
|
'spaces': ['all'],
|
||||||
|
# Enforce parallelity
|
||||||
|
'epochs': 2,
|
||||||
|
'hyperopt_jobs': 2,
|
||||||
|
'fee': fee.return_value,
|
||||||
|
})
|
||||||
|
hyperopt = Hyperopt(hyperopt_conf)
|
||||||
|
hyperopt.backtesting.exchange.get_max_leverage = lambda *x, **xx: 1.0
|
||||||
|
hyperopt.backtesting.exchange.get_min_pair_stake_amount = lambda *x, **xx: 1.0
|
||||||
|
hyperopt.backtesting.exchange.get_max_pair_stake_amount = lambda *x, **xx: 100.0
|
||||||
|
|
||||||
|
assert isinstance(hyperopt.custom_hyperopt, HyperOptAuto)
|
||||||
|
assert isinstance(hyperopt.backtesting.strategy.buy_rsi, IntParameter)
|
||||||
|
assert hyperopt.backtesting.strategy.bot_loop_started is True
|
||||||
|
|
||||||
|
assert hyperopt.backtesting.strategy.buy_rsi.in_space is True
|
||||||
|
assert hyperopt.backtesting.strategy.buy_rsi.value == 35
|
||||||
|
assert hyperopt.backtesting.strategy.sell_rsi.value == 74
|
||||||
|
assert hyperopt.backtesting.strategy.protection_cooldown_lookback.value == 30
|
||||||
|
buy_rsi_range = hyperopt.backtesting.strategy.buy_rsi.range
|
||||||
|
assert isinstance(buy_rsi_range, range)
|
||||||
|
# Range from 0 - 50 (inclusive)
|
||||||
|
assert len(list(buy_rsi_range)) == 51
|
||||||
|
|
||||||
|
hyperopt.start()
|
||||||
|
|
||||||
|
|
||||||
def test_SKDecimal():
|
def test_SKDecimal():
|
||||||
space = SKDecimal(1, 2, decimals=2)
|
space = SKDecimal(1, 2, decimals=2)
|
||||||
assert 1.5 in space
|
assert 1.5 in space
|
||||||
|
Loading…
Reference in New Issue
Block a user