ensure spice_rack is backtestable. Ensure download-data knows about the spice_rack informative pair requirements
This commit is contained in:
parent
91e2a05aff
commit
7b390b8edb
@ -12,6 +12,7 @@ from freqtrade.data.history import (convert_trades_to_ohlcv, refresh_backtest_oh
|
|||||||
from freqtrade.enums import CandleType, RunMode, TradingMode
|
from freqtrade.enums import CandleType, RunMode, TradingMode
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import market_is_active, timeframe_to_minutes
|
from freqtrade.exchange import market_is_active, timeframe_to_minutes
|
||||||
|
from freqtrade.freqai.utils import setup_freqai_spice_rack
|
||||||
from freqtrade.plugins.pairlist.pairlist_helpers import dynamic_expand_pairlist, expand_pairlist
|
from freqtrade.plugins.pairlist.pairlist_helpers import dynamic_expand_pairlist, expand_pairlist
|
||||||
from freqtrade.resolvers import ExchangeResolver
|
from freqtrade.resolvers import ExchangeResolver
|
||||||
|
|
||||||
@ -48,6 +49,10 @@ def start_download_data(args: Dict[str, Any]) -> None:
|
|||||||
|
|
||||||
# Init exchange
|
# Init exchange
|
||||||
exchange = ExchangeResolver.load_exchange(config['exchange']['name'], config, validate=False)
|
exchange = ExchangeResolver.load_exchange(config['exchange']['name'], config, validate=False)
|
||||||
|
|
||||||
|
if config.get('freqai_spice_rack', False):
|
||||||
|
config = setup_freqai_spice_rack(config, exchange)
|
||||||
|
|
||||||
markets = [p for p, m in exchange.markets.items() if market_is_active(m)
|
markets = [p for p, m in exchange.markets.items() if market_is_active(m)
|
||||||
or config.get('include_inactive')]
|
or config.get('include_inactive')]
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import logging
|
import logging
|
||||||
from datetime import datetime, timezone
|
from datetime import datetime, timezone
|
||||||
|
from typing import Any, Dict, Optional
|
||||||
|
|
||||||
import numpy as np
|
import numpy as np
|
||||||
# for spice rack
|
# for spice rack
|
||||||
@ -12,7 +13,7 @@ from freqtrade.configuration import TimeRange
|
|||||||
from freqtrade.data.dataprovider import DataProvider
|
from freqtrade.data.dataprovider import DataProvider
|
||||||
from freqtrade.data.history.history_utils import refresh_backtest_ohlcv_data
|
from freqtrade.data.history.history_utils import refresh_backtest_ohlcv_data
|
||||||
from freqtrade.exceptions import OperationalException
|
from freqtrade.exceptions import OperationalException
|
||||||
from freqtrade.exchange import timeframe_to_seconds
|
from freqtrade.exchange import Exchange, timeframe_to_seconds
|
||||||
from freqtrade.exchange.exchange import market_is_active
|
from freqtrade.exchange.exchange import market_is_active
|
||||||
from freqtrade.plugins.pairlist.pairlist_helpers import dynamic_expand_pairlist
|
from freqtrade.plugins.pairlist.pairlist_helpers import dynamic_expand_pairlist
|
||||||
from freqtrade.strategy import merge_informative_pair
|
from freqtrade.strategy import merge_informative_pair
|
||||||
@ -170,6 +171,60 @@ def auto_populate_any_indicators(
|
|||||||
|
|
||||||
return df
|
return df
|
||||||
|
|
||||||
|
|
||||||
|
def setup_freqai_spice_rack(config: dict, exchange: Optional[Exchange]) -> Dict[str, Any]:
|
||||||
|
import difflib
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
auto_config = config.get('freqai_config', 'lightgbm_config.json')
|
||||||
|
with open(Path(__file__).parent / Path('spice_rack') / auto_config) as json_file:
|
||||||
|
freqai_config = json.load(json_file)
|
||||||
|
config['freqai'] = freqai_config['freqai']
|
||||||
|
config['freqai']['identifier'] = config['freqai_identifier']
|
||||||
|
corr_pairs = config['freqai']['feature_parameters']['include_corr_pairlist']
|
||||||
|
timeframes = config['freqai']['feature_parameters']['include_timeframes']
|
||||||
|
new_corr_pairs = []
|
||||||
|
new_tfs = []
|
||||||
|
|
||||||
|
if not exchange:
|
||||||
|
logger.warning('No dataprovider available.')
|
||||||
|
config['freqai']['enabled'] = False
|
||||||
|
return config
|
||||||
|
# find the closest pairs to what the default config wants
|
||||||
|
for pair in corr_pairs:
|
||||||
|
closest_pair = difflib.get_close_matches(
|
||||||
|
pair,
|
||||||
|
exchange.markets
|
||||||
|
)
|
||||||
|
if not closest_pair:
|
||||||
|
logger.warning(f'Could not find {pair} in markets, removing from '
|
||||||
|
f'corr_pairlist.')
|
||||||
|
else:
|
||||||
|
closest_pair = closest_pair[0]
|
||||||
|
|
||||||
|
new_corr_pairs.append(closest_pair)
|
||||||
|
logger.info(f'Spice rack will use {closest_pair} as informative in FreqAI model.')
|
||||||
|
|
||||||
|
# find the closest matching timeframes to what the default config wants
|
||||||
|
if timeframe_to_seconds(config['timeframe']) > timeframe_to_seconds('15m'):
|
||||||
|
logger.warning('Default spice rack is designed for lower base timeframes (e.g. > '
|
||||||
|
f'15m). But user passed {config["timeframe"]}.')
|
||||||
|
new_tfs.append(config['timeframe'])
|
||||||
|
|
||||||
|
list_tfs = [timeframe_to_seconds(tf) for tf
|
||||||
|
in exchange.timeframes]
|
||||||
|
for tf in timeframes:
|
||||||
|
tf_secs = timeframe_to_seconds(tf)
|
||||||
|
closest_index = min(range(len(list_tfs)), key=lambda i: abs(list_tfs[i] - tf_secs))
|
||||||
|
closest_tf = exchange.timeframes[closest_index]
|
||||||
|
logger.info(f'Spice rack will use {closest_tf} as informative tf in FreqAI model.')
|
||||||
|
new_tfs.append(closest_tf)
|
||||||
|
|
||||||
|
config['freqai']['feature_parameters'].update({'include_timeframes': new_tfs})
|
||||||
|
config['freqai']['feature_parameters'].update({'include_corr_pairlist': new_corr_pairs})
|
||||||
|
config.update({"freqaimodel": 'LightGBMRegressorMultiTarget'})
|
||||||
|
return config
|
||||||
|
|
||||||
# Keep below for when we wish to download heterogeneously lengthed data for FreqAI.
|
# Keep below for when we wish to download heterogeneously lengthed data for FreqAI.
|
||||||
# def download_all_data_for_training(dp: DataProvider, config: dict) -> None:
|
# def download_all_data_for_training(dp: DataProvider, config: dict) -> None:
|
||||||
# """
|
# """
|
||||||
|
@ -89,6 +89,10 @@ class Backtesting:
|
|||||||
self._exchange_name, self.config, load_leverage_tiers=True)
|
self._exchange_name, self.config, load_leverage_tiers=True)
|
||||||
self.dataprovider = DataProvider(self.config, self.exchange)
|
self.dataprovider = DataProvider(self.config, self.exchange)
|
||||||
|
|
||||||
|
if config.get('freqai_spice_rack', False):
|
||||||
|
from freqtrade.freqai.utils import setup_freqai_spice_rack
|
||||||
|
self.config = setup_freqai_spice_rack(self.config, self.exchange)
|
||||||
|
|
||||||
if self.config.get('strategy_list'):
|
if self.config.get('strategy_list'):
|
||||||
if self.config.get('freqai', {}).get('enabled', False):
|
if self.config.get('freqai', {}).get('enabled', False):
|
||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
|
@ -5,7 +5,7 @@ This module defines the interface to apply for strategies
|
|||||||
import logging
|
import logging
|
||||||
from abc import ABC, abstractmethod
|
from abc import ABC, abstractmethod
|
||||||
from datetime import datetime, timedelta, timezone
|
from datetime import datetime, timedelta, timezone
|
||||||
from typing import Any, Dict, List, Optional, Tuple, Union
|
from typing import Dict, List, Optional, Tuple, Union
|
||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
from pandas import DataFrame
|
from pandas import DataFrame
|
||||||
@ -147,9 +147,9 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
def load_freqAI_model(self) -> None:
|
def load_freqAI_model(self) -> None:
|
||||||
spice_rack = self.config.get('freqai_spice_rack', False)
|
spice_rack = self.config.get('freqai_spice_rack', False)
|
||||||
if self.config.get('freqai', {}).get('enabled', False) or spice_rack:
|
if self.config.get('freqai', {}).get('enabled', False) or spice_rack:
|
||||||
spice_rack = self.config.get('freqai_spice_rack', False)
|
|
||||||
if spice_rack:
|
if spice_rack:
|
||||||
self.config = self.setup_freqai_spice_rack(self.config)
|
from freqtrade.freqai.utils import setup_freqai_spice_rack
|
||||||
|
self.config = setup_freqai_spice_rack(self.config, self.dp._exchange)
|
||||||
# Import here to avoid importing this if freqAI is disabled
|
# Import here to avoid importing this if freqAI is disabled
|
||||||
from freqtrade.freqai.utils import download_all_data_for_training
|
from freqtrade.freqai.utils import download_all_data_for_training
|
||||||
from freqtrade.resolvers.freqaimodel_resolver import FreqaiModelResolver
|
from freqtrade.resolvers.freqaimodel_resolver import FreqaiModelResolver
|
||||||
@ -190,60 +190,6 @@ class IStrategy(ABC, HyperStrategyMixin):
|
|||||||
|
|
||||||
self.freqai = DummyClass() # type: ignore
|
self.freqai = DummyClass() # type: ignore
|
||||||
|
|
||||||
def setup_freqai_spice_rack(self, config: dict) -> Dict[str, Any]:
|
|
||||||
import difflib
|
|
||||||
import json
|
|
||||||
from pathlib import Path
|
|
||||||
auto_config = config.get('freqai_config', 'lightgbm_config.json')
|
|
||||||
with open(Path('freqtrade') / 'freqai' / 'spice_rack'
|
|
||||||
/ auto_config) as json_file:
|
|
||||||
freqai_config = json.load(json_file)
|
|
||||||
config['freqai'] = freqai_config['freqai']
|
|
||||||
config['freqai']['identifier'] = config['freqai_identifier']
|
|
||||||
corr_pairs = config['freqai']['feature_parameters']['include_corr_pairlist']
|
|
||||||
timeframes = config['freqai']['feature_parameters']['include_timeframes']
|
|
||||||
new_corr_pairs = []
|
|
||||||
new_tfs = []
|
|
||||||
|
|
||||||
if not self.dp:
|
|
||||||
logger.warning('No dataprovider available.')
|
|
||||||
config['freqai']['enabled'] = False
|
|
||||||
return config
|
|
||||||
# find the closest pairs to what the default config wants
|
|
||||||
for pair in corr_pairs:
|
|
||||||
closest_pair = difflib.get_close_matches(
|
|
||||||
pair,
|
|
||||||
self.dp._exchange.markets # type: ignore
|
|
||||||
)
|
|
||||||
if not closest_pair:
|
|
||||||
logger.warning(f'Could not find {pair} in markets, removing from '
|
|
||||||
f'corr_pairlist.')
|
|
||||||
else:
|
|
||||||
closest_pair = closest_pair[0]
|
|
||||||
|
|
||||||
new_corr_pairs.append(closest_pair)
|
|
||||||
logger.info(f'Spice rack will use {closest_pair} as informative in FreqAI model.')
|
|
||||||
|
|
||||||
# find the closest matching timeframes to what the default config wants
|
|
||||||
if timeframe_to_seconds(config['timeframe']) > timeframe_to_seconds('15m'):
|
|
||||||
logger.warning('Default spice rack is designed for lower base timeframes (e.g. > '
|
|
||||||
f'15m). But user passed {config["timeframe"]}.')
|
|
||||||
new_tfs.append(config['timeframe'])
|
|
||||||
|
|
||||||
list_tfs = [timeframe_to_seconds(tf) for tf
|
|
||||||
in self.dp._exchange.timeframes] # type: ignore
|
|
||||||
for tf in timeframes:
|
|
||||||
tf_secs = timeframe_to_seconds(tf)
|
|
||||||
closest_index = min(range(len(list_tfs)), key=lambda i: abs(list_tfs[i] - tf_secs))
|
|
||||||
closest_tf = self.dp._exchange.timeframes[closest_index] # type: ignore
|
|
||||||
logger.info(f'Spice rack will use {closest_tf} as informative tf in FreqAI model.')
|
|
||||||
new_tfs.append(closest_tf)
|
|
||||||
|
|
||||||
config['freqai']['feature_parameters'].update({'include_timeframes': new_tfs})
|
|
||||||
config['freqai']['feature_parameters'].update({'include_corr_pairlist': new_corr_pairs})
|
|
||||||
config.update({"freqaimodel": 'LightGBMRegressorMultiTarget'})
|
|
||||||
return config
|
|
||||||
|
|
||||||
def ft_bot_start(self, **kwargs) -> None:
|
def ft_bot_start(self, **kwargs) -> None:
|
||||||
"""
|
"""
|
||||||
Strategy init - runs after dataprovider has been added.
|
Strategy init - runs after dataprovider has been added.
|
||||||
|
Loading…
Reference in New Issue
Block a user