Merge branch 'develop' into pr/wizrds/7303
This commit is contained in:
@@ -62,9 +62,9 @@ ARGS_BUILD_CONFIG = ["config"]
|
||||
|
||||
ARGS_BUILD_STRATEGY = ["user_data_dir", "strategy", "template"]
|
||||
|
||||
ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase"]
|
||||
ARGS_CONVERT_DATA = ["pairs", "format_from", "format_to", "erase", "exchange"]
|
||||
|
||||
ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes", "exchange", "trading_mode",
|
||||
ARGS_CONVERT_DATA_OHLCV = ARGS_CONVERT_DATA + ["timeframes", "trading_mode",
|
||||
"candle_types"]
|
||||
|
||||
ARGS_CONVERT_TRADES = ["pairs", "timeframes", "exchange", "dataformat_ohlcv", "dataformat_trades"]
|
||||
|
@@ -69,7 +69,7 @@ AVAILABLE_CLI_OPTIONS = {
|
||||
metavar='PATH',
|
||||
),
|
||||
"datadir": Arg(
|
||||
'-d', '--datadir',
|
||||
'-d', '--datadir', '--data-dir',
|
||||
help='Path to directory with historical backtesting data.',
|
||||
metavar='PATH',
|
||||
),
|
||||
|
@@ -36,24 +36,24 @@ def deploy_new_strategy(strategy_name: str, strategy_path: Path, subtemplate: st
|
||||
"""
|
||||
fallback = 'full'
|
||||
indicators = render_template_with_fallback(
|
||||
templatefile=f"subtemplates/indicators_{subtemplate}.j2",
|
||||
templatefallbackfile=f"subtemplates/indicators_{fallback}.j2",
|
||||
templatefile=f"strategy_subtemplates/indicators_{subtemplate}.j2",
|
||||
templatefallbackfile=f"strategy_subtemplates/indicators_{fallback}.j2",
|
||||
)
|
||||
buy_trend = render_template_with_fallback(
|
||||
templatefile=f"subtemplates/buy_trend_{subtemplate}.j2",
|
||||
templatefallbackfile=f"subtemplates/buy_trend_{fallback}.j2",
|
||||
templatefile=f"strategy_subtemplates/buy_trend_{subtemplate}.j2",
|
||||
templatefallbackfile=f"strategy_subtemplates/buy_trend_{fallback}.j2",
|
||||
)
|
||||
sell_trend = render_template_with_fallback(
|
||||
templatefile=f"subtemplates/sell_trend_{subtemplate}.j2",
|
||||
templatefallbackfile=f"subtemplates/sell_trend_{fallback}.j2",
|
||||
templatefile=f"strategy_subtemplates/sell_trend_{subtemplate}.j2",
|
||||
templatefallbackfile=f"strategy_subtemplates/sell_trend_{fallback}.j2",
|
||||
)
|
||||
plot_config = render_template_with_fallback(
|
||||
templatefile=f"subtemplates/plot_config_{subtemplate}.j2",
|
||||
templatefallbackfile=f"subtemplates/plot_config_{fallback}.j2",
|
||||
templatefile=f"strategy_subtemplates/plot_config_{subtemplate}.j2",
|
||||
templatefallbackfile=f"strategy_subtemplates/plot_config_{fallback}.j2",
|
||||
)
|
||||
additional_methods = render_template_with_fallback(
|
||||
templatefile=f"subtemplates/strategy_methods_{subtemplate}.j2",
|
||||
templatefallbackfile="subtemplates/strategy_methods_empty.j2",
|
||||
templatefile=f"strategy_subtemplates/strategy_methods_{subtemplate}.j2",
|
||||
templatefallbackfile="strategy_subtemplates/strategy_methods_empty.j2",
|
||||
)
|
||||
|
||||
strategy_text = render_template(templatefile='base_strategy.py.j2',
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.enums import RunMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.exchange import (available_exchanges, is_exchange_known_ccxt,
|
||||
@@ -10,7 +10,7 @@ from freqtrade.exchange import (available_exchanges, is_exchange_known_ccxt,
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def check_exchange(config: Dict[str, Any], check_for_bad: bool = True) -> bool:
|
||||
def check_exchange(config: Config, check_for_bad: bool = True) -> bool:
|
||||
"""
|
||||
Check if the exchange name in the config file is supported by Freqtrade
|
||||
:param check_for_bad: if True, check the exchange against the list of known 'bad'
|
||||
|
@@ -13,6 +13,7 @@ from freqtrade.configuration.deprecated_settings import process_temporary_deprec
|
||||
from freqtrade.configuration.directory_operations import create_datadir, create_userdata_dir
|
||||
from freqtrade.configuration.environment_vars import enironment_vars_to_dict
|
||||
from freqtrade.configuration.load_config import load_file, load_from_files
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.enums import NON_UTIL_MODES, TRADING_MODES, CandleType, RunMode, TradingMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.loggers import setup_logging
|
||||
@@ -30,10 +31,10 @@ class Configuration:
|
||||
|
||||
def __init__(self, args: Dict[str, Any], runmode: RunMode = None) -> None:
|
||||
self.args = args
|
||||
self.config: Optional[Dict[str, Any]] = None
|
||||
self.config: Optional[Config] = None
|
||||
self.runmode = runmode
|
||||
|
||||
def get_config(self) -> Dict[str, Any]:
|
||||
def get_config(self) -> Config:
|
||||
"""
|
||||
Return the config. Use this method to get the bot config
|
||||
:return: Dict: Bot config
|
||||
@@ -65,7 +66,7 @@ class Configuration:
|
||||
:return: Configuration dictionary
|
||||
"""
|
||||
# Load all configs
|
||||
config: Dict[str, Any] = load_from_files(self.args.get("config", []))
|
||||
config: Config = load_from_files(self.args.get("config", []))
|
||||
|
||||
# Load environment variables
|
||||
env_data = enironment_vars_to_dict()
|
||||
@@ -108,7 +109,7 @@ class Configuration:
|
||||
|
||||
return config
|
||||
|
||||
def _process_logging_options(self, config: Dict[str, Any]) -> None:
|
||||
def _process_logging_options(self, config: Config) -> None:
|
||||
"""
|
||||
Extract information for sys.argv and load logging configuration:
|
||||
the -v/--verbose, --logfile options
|
||||
@@ -121,7 +122,7 @@ class Configuration:
|
||||
|
||||
setup_logging(config)
|
||||
|
||||
def _process_trading_options(self, config: Dict[str, Any]) -> None:
|
||||
def _process_trading_options(self, config: Config) -> None:
|
||||
if config['runmode'] not in TRADING_MODES:
|
||||
return
|
||||
|
||||
@@ -137,7 +138,7 @@ class Configuration:
|
||||
|
||||
logger.info(f'Using DB: "{parse_db_uri_for_logging(config["db_url"])}"')
|
||||
|
||||
def _process_common_options(self, config: Dict[str, Any]) -> None:
|
||||
def _process_common_options(self, config: Config) -> None:
|
||||
|
||||
# Set strategy if not specified in config and or if it's non default
|
||||
if self.args.get('strategy') or not config.get('strategy'):
|
||||
@@ -161,7 +162,7 @@ class Configuration:
|
||||
if 'sd_notify' in self.args and self.args['sd_notify']:
|
||||
config['internals'].update({'sd_notify': True})
|
||||
|
||||
def _process_datadir_options(self, config: Dict[str, Any]) -> None:
|
||||
def _process_datadir_options(self, config: Config) -> None:
|
||||
"""
|
||||
Extract information for sys.argv and load directory configurations
|
||||
--user-data, --datadir
|
||||
@@ -195,7 +196,7 @@ class Configuration:
|
||||
config['exportfilename'] = (config['user_data_dir']
|
||||
/ 'backtest_results')
|
||||
|
||||
def _process_optimize_options(self, config: Dict[str, Any]) -> None:
|
||||
def _process_optimize_options(self, config: Config) -> None:
|
||||
|
||||
# This will override the strategy configuration
|
||||
self._args_to_config(config, argname='timeframe',
|
||||
@@ -380,7 +381,7 @@ class Configuration:
|
||||
self._args_to_config(config, argname="hyperopt_ignore_missing_space",
|
||||
logstring="Paramter --ignore-missing-space detected: {}")
|
||||
|
||||
def _process_plot_options(self, config: Dict[str, Any]) -> None:
|
||||
def _process_plot_options(self, config: Config) -> None:
|
||||
|
||||
self._args_to_config(config, argname='pairs',
|
||||
logstring='Using pairs {}')
|
||||
@@ -432,7 +433,7 @@ class Configuration:
|
||||
self._args_to_config(config, argname='show_timerange',
|
||||
logstring='Detected --show-timerange')
|
||||
|
||||
def _process_data_options(self, config: Dict[str, Any]) -> None:
|
||||
def _process_data_options(self, config: Config) -> None:
|
||||
self._args_to_config(config, argname='new_pairs_days',
|
||||
logstring='Detected --new-pairs-days: {}')
|
||||
self._args_to_config(config, argname='trading_mode',
|
||||
@@ -443,7 +444,7 @@ class Configuration:
|
||||
self._args_to_config(config, argname='candle_types',
|
||||
logstring='Detected --candle-types: {}')
|
||||
|
||||
def _process_analyze_options(self, config: Dict[str, Any]) -> None:
|
||||
def _process_analyze_options(self, config: Config) -> None:
|
||||
self._args_to_config(config, argname='analysis_groups',
|
||||
logstring='Analysis reason groups: {}')
|
||||
|
||||
@@ -456,7 +457,7 @@ class Configuration:
|
||||
self._args_to_config(config, argname='indicator_list',
|
||||
logstring='Analysis indicator list: {}')
|
||||
|
||||
def _process_runmode(self, config: Dict[str, Any]) -> None:
|
||||
def _process_runmode(self, config: Config) -> None:
|
||||
|
||||
self._args_to_config(config, argname='dry_run',
|
||||
logstring='Parameter --dry-run detected, '
|
||||
@@ -469,7 +470,7 @@ class Configuration:
|
||||
|
||||
config.update({'runmode': self.runmode})
|
||||
|
||||
def _process_freqai_options(self, config: Dict[str, Any]) -> None:
|
||||
def _process_freqai_options(self, config: Config) -> None:
|
||||
|
||||
self._args_to_config(config, argname='freqaimodel',
|
||||
logstring='Using freqaimodel class name: {}')
|
||||
@@ -479,7 +480,7 @@ class Configuration:
|
||||
|
||||
return
|
||||
|
||||
def _args_to_config(self, config: Dict[str, Any], argname: str,
|
||||
def _args_to_config(self, config: Config, argname: str,
|
||||
logstring: str, logfun: Optional[Callable] = None,
|
||||
deprecated_msg: Optional[str] = None) -> None:
|
||||
"""
|
||||
@@ -502,7 +503,7 @@ class Configuration:
|
||||
if deprecated_msg:
|
||||
warnings.warn(f"DEPRECATED: {deprecated_msg}", DeprecationWarning)
|
||||
|
||||
def _resolve_pairs_list(self, config: Dict[str, Any]) -> None:
|
||||
def _resolve_pairs_list(self, config: Config) -> None:
|
||||
"""
|
||||
Helper for download script.
|
||||
Takes first found:
|
||||
|
@@ -3,15 +3,16 @@ Functions to handle deprecated settings
|
||||
"""
|
||||
|
||||
import logging
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Optional
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.exceptions import OperationalException
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def check_conflicting_settings(config: Dict[str, Any],
|
||||
def check_conflicting_settings(config: Config,
|
||||
section_old: Optional[str], name_old: str,
|
||||
section_new: Optional[str], name_new: str) -> None:
|
||||
section_new_config = config.get(section_new, {}) if section_new else config
|
||||
@@ -28,7 +29,7 @@ def check_conflicting_settings(config: Dict[str, Any],
|
||||
)
|
||||
|
||||
|
||||
def process_removed_setting(config: Dict[str, Any],
|
||||
def process_removed_setting(config: Config,
|
||||
section1: str, name1: str,
|
||||
section2: Optional[str], name2: str) -> None:
|
||||
"""
|
||||
@@ -47,7 +48,7 @@ def process_removed_setting(config: Dict[str, Any],
|
||||
)
|
||||
|
||||
|
||||
def process_deprecated_setting(config: Dict[str, Any],
|
||||
def process_deprecated_setting(config: Config,
|
||||
section_old: Optional[str], name_old: str,
|
||||
section_new: Optional[str], name_new: str
|
||||
) -> None:
|
||||
@@ -69,7 +70,7 @@ def process_deprecated_setting(config: Dict[str, Any],
|
||||
del section_old_config[name_old]
|
||||
|
||||
|
||||
def process_temporary_deprecated_settings(config: Dict[str, Any]) -> None:
|
||||
def process_temporary_deprecated_settings(config: Config) -> None:
|
||||
|
||||
# Kept for future deprecated / moved settings
|
||||
# check_conflicting_settings(config, 'ask_strategy', 'use_sell_signal',
|
||||
|
@@ -1,16 +1,16 @@
|
||||
import logging
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional
|
||||
from typing import Optional
|
||||
|
||||
from freqtrade.constants import USER_DATA_FILES
|
||||
from freqtrade.constants import USER_DATA_FILES, Config
|
||||
from freqtrade.exceptions import OperationalException
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def create_datadir(config: Dict[str, Any], datadir: Optional[str] = None) -> Path:
|
||||
def create_datadir(config: Config, datadir: Optional[str] = None) -> Path:
|
||||
|
||||
folder = Path(datadir) if datadir else Path(f"{config['user_data_dir']}/data")
|
||||
if not datadir:
|
||||
|
@@ -10,7 +10,7 @@ from typing import Any, Dict, List
|
||||
|
||||
import rapidjson
|
||||
|
||||
from freqtrade.constants import MINIMAL_CONFIG
|
||||
from freqtrade.constants import MINIMAL_CONFIG, Config
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.misc import deep_merge_dicts
|
||||
|
||||
@@ -80,7 +80,7 @@ def load_from_files(files: List[str], base_path: Path = None, level: int = 0) ->
|
||||
Recursively load configuration files if specified.
|
||||
Sub-files are assumed to be relative to the initial config.
|
||||
"""
|
||||
config: Dict[str, Any] = {}
|
||||
config: Config = {}
|
||||
if level > 5:
|
||||
raise OperationalException("Config loop detected.")
|
||||
|
||||
|
@@ -3,7 +3,7 @@
|
||||
"""
|
||||
bot constants
|
||||
"""
|
||||
from typing import List, Literal, Tuple
|
||||
from typing import Any, Dict, List, Literal, Tuple
|
||||
|
||||
from freqtrade.enums import CandleType
|
||||
|
||||
@@ -547,6 +547,7 @@ CONF_SCHEMA = {
|
||||
"weight_factor": {"type": "number", "default": 0},
|
||||
"principal_component_analysis": {"type": "boolean", "default": False},
|
||||
"use_SVM_to_remove_outliers": {"type": "boolean", "default": False},
|
||||
"plot_feature_importance": {"type": "boolean", "default": False},
|
||||
"svm_params": {"type": "object",
|
||||
"properties": {
|
||||
"shuffle": {"type": "boolean", "default": False},
|
||||
@@ -646,3 +647,5 @@ LongShort = Literal['long', 'short']
|
||||
EntryExit = Literal['entry', 'exit']
|
||||
BuySell = Literal['buy', 'sell']
|
||||
MakerTaker = Literal['maker', 'taker']
|
||||
|
||||
Config = Dict[str, Any]
|
||||
|
@@ -5,12 +5,12 @@ import itertools
|
||||
import logging
|
||||
from datetime import datetime, timezone
|
||||
from operator import itemgetter
|
||||
from typing import Any, Dict, List
|
||||
from typing import Dict, List
|
||||
|
||||
import pandas as pd
|
||||
from pandas import DataFrame, to_datetime
|
||||
|
||||
from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS, TradeList
|
||||
from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS, Config, TradeList
|
||||
from freqtrade.enums import CandleType
|
||||
|
||||
|
||||
@@ -237,7 +237,7 @@ def trades_to_ohlcv(trades: TradeList, timeframe: str) -> DataFrame:
|
||||
return df_new.loc[:, DEFAULT_DATAFRAME_COLUMNS]
|
||||
|
||||
|
||||
def convert_trades_format(config: Dict[str, Any], convert_from: str, convert_to: str, erase: bool):
|
||||
def convert_trades_format(config: Config, convert_from: str, convert_to: str, erase: bool):
|
||||
"""
|
||||
Convert trades from one format to another format.
|
||||
:param config: Config dictionary
|
||||
@@ -263,7 +263,7 @@ def convert_trades_format(config: Dict[str, Any], convert_from: str, convert_to:
|
||||
|
||||
|
||||
def convert_ohlcv_format(
|
||||
config: Dict[str, Any],
|
||||
config: Config,
|
||||
convert_from: str,
|
||||
convert_to: str,
|
||||
erase: bool,
|
||||
|
@@ -12,7 +12,7 @@ from typing import Any, Dict, List, Optional, Tuple
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.constants import ListPairsWithTimeframes, PairWithTimeframe
|
||||
from freqtrade.constants import Config, ListPairsWithTimeframes, PairWithTimeframe
|
||||
from freqtrade.data.history import load_pair_history
|
||||
from freqtrade.enums import CandleType, RPCMessageType, RunMode
|
||||
from freqtrade.exceptions import ExchangeError, OperationalException
|
||||
@@ -31,7 +31,7 @@ class DataProvider:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: dict,
|
||||
config: Config,
|
||||
exchange: Optional[Exchange],
|
||||
pairlists=None,
|
||||
rpc: Optional[RPCManager] = None
|
||||
|
@@ -1,7 +1,5 @@
|
||||
import logging
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
from typing import Optional
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
@@ -20,26 +18,6 @@ class HDF5DataHandler(IDataHandler):
|
||||
|
||||
_columns = DEFAULT_DATAFRAME_COLUMNS
|
||||
|
||||
@classmethod
|
||||
def ohlcv_get_pairs(cls, datadir: Path, timeframe: str, candle_type: CandleType) -> List[str]:
|
||||
"""
|
||||
Returns a list of all pairs with ohlcv data available in this datadir
|
||||
for the specified timeframe
|
||||
:param datadir: Directory to search for ohlcv files
|
||||
:param timeframe: Timeframe to search pairs for
|
||||
:param candle_type: Any of the enum CandleType (must match trading mode!)
|
||||
:return: List of Pairs
|
||||
"""
|
||||
candle = ""
|
||||
if candle_type != CandleType.SPOT:
|
||||
datadir = datadir.joinpath('futures')
|
||||
candle = f"-{candle_type}"
|
||||
|
||||
_tmp = [re.search(r'^(\S+)(?=\-' + timeframe + candle + '.h5)', p.name)
|
||||
for p in datadir.glob(f"*{timeframe}{candle}.h5")]
|
||||
# Check if regex found something and only return these results
|
||||
return [cls.rebuild_pair_from_filename(match[0]) for match in _tmp if match]
|
||||
|
||||
def ohlcv_store(
|
||||
self, pair: str, timeframe: str, data: pd.DataFrame, candle_type: CandleType) -> None:
|
||||
"""
|
||||
@@ -121,18 +99,6 @@ class HDF5DataHandler(IDataHandler):
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@classmethod
|
||||
def trades_get_pairs(cls, datadir: Path) -> List[str]:
|
||||
"""
|
||||
Returns a list of all pairs for which trade data is available in this
|
||||
:param datadir: Directory to search for ohlcv files
|
||||
:return: List of Pairs
|
||||
"""
|
||||
_tmp = [re.search(r'^(\S+)(?=\-trades.h5)', p.name)
|
||||
for p in datadir.glob("*trades.h5")]
|
||||
# Check if regex found something and only return these results to avoid exceptions.
|
||||
return [cls.rebuild_pair_from_filename(match[0]) for match in _tmp if match]
|
||||
|
||||
def trades_store(self, pair: str, data: TradeList) -> None:
|
||||
"""
|
||||
Store trades data (list of Dicts) to file
|
||||
|
@@ -26,7 +26,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class IDataHandler(ABC):
|
||||
|
||||
_OHLCV_REGEX = r'^([a-zA-Z_-]+)\-(\d+[a-zA-Z]{1,2})\-?([a-zA-Z_]*)?(?=\.)'
|
||||
_OHLCV_REGEX = r'^([a-zA-Z_\d-]+)\-(\d+[a-zA-Z]{1,2})\-?([a-zA-Z_]*)?(?=\.)'
|
||||
|
||||
def __init__(self, datadir: Path) -> None:
|
||||
self._datadir = datadir
|
||||
@@ -61,7 +61,6 @@ class IDataHandler(ABC):
|
||||
) for match in _tmp if match and len(match.groups()) > 1]
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def ohlcv_get_pairs(cls, datadir: Path, timeframe: str, candle_type: CandleType) -> List[str]:
|
||||
"""
|
||||
Returns a list of all pairs with ohlcv data available in this datadir
|
||||
@@ -71,6 +70,15 @@ class IDataHandler(ABC):
|
||||
:param candle_type: Any of the enum CandleType (must match trading mode!)
|
||||
:return: List of Pairs
|
||||
"""
|
||||
candle = ""
|
||||
if candle_type != CandleType.SPOT:
|
||||
datadir = datadir.joinpath('futures')
|
||||
candle = f"-{candle_type}"
|
||||
ext = cls._get_file_extension()
|
||||
_tmp = [re.search(r'^(\S+)(?=\-' + timeframe + candle + f'.{ext})', p.name)
|
||||
for p in datadir.glob(f"*{timeframe}{candle}.{ext}")]
|
||||
# Check if regex found something and only return these results
|
||||
return [cls.rebuild_pair_from_filename(match[0]) for match in _tmp if match]
|
||||
|
||||
@abstractmethod
|
||||
def ohlcv_store(
|
||||
@@ -144,13 +152,17 @@ class IDataHandler(ABC):
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
@abstractmethod
|
||||
def trades_get_pairs(cls, datadir: Path) -> List[str]:
|
||||
"""
|
||||
Returns a list of all pairs for which trade data is available in this
|
||||
:param datadir: Directory to search for ohlcv files
|
||||
:return: List of Pairs
|
||||
"""
|
||||
_ext = cls._get_file_extension()
|
||||
_tmp = [re.search(r'^(\S+)(?=\-trades.' + _ext + ')', p.name)
|
||||
for p in datadir.glob(f"*trades.{_ext}")]
|
||||
# Check if regex found something and only return these results to avoid exceptions.
|
||||
return [cls.rebuild_pair_from_filename(match[0]) for match in _tmp if match]
|
||||
|
||||
@abstractmethod
|
||||
def trades_store(self, pair: str, data: TradeList) -> None:
|
||||
@@ -255,7 +267,7 @@ class IDataHandler(ABC):
|
||||
Rebuild pair name from filename
|
||||
Assumes a asset name of max. 7 length to also support BTC-PERP and BTC-PERP:USD names.
|
||||
"""
|
||||
res = re.sub(r'^(([A-Za-z]{1,10})|^([A-Za-z\-]{1,6}))(_)', r'\g<1>/', pair, 1)
|
||||
res = re.sub(r'^(([A-Za-z\d]{1,10})|^([A-Za-z\-]{1,6}))(_)', r'\g<1>/', pair, 1)
|
||||
res = re.sub('_', ':', res, 1)
|
||||
return res
|
||||
|
||||
|
@@ -1,7 +1,5 @@
|
||||
import logging
|
||||
import re
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
from typing import Optional
|
||||
|
||||
import numpy as np
|
||||
from pandas import DataFrame, read_json, to_datetime
|
||||
@@ -23,26 +21,6 @@ class JsonDataHandler(IDataHandler):
|
||||
_use_zip = False
|
||||
_columns = DEFAULT_DATAFRAME_COLUMNS
|
||||
|
||||
@classmethod
|
||||
def ohlcv_get_pairs(cls, datadir: Path, timeframe: str, candle_type: CandleType) -> List[str]:
|
||||
"""
|
||||
Returns a list of all pairs with ohlcv data available in this datadir
|
||||
for the specified timeframe
|
||||
:param datadir: Directory to search for ohlcv files
|
||||
:param timeframe: Timeframe to search pairs for
|
||||
:param candle_type: Any of the enum CandleType (must match trading mode!)
|
||||
:return: List of Pairs
|
||||
"""
|
||||
candle = ""
|
||||
if candle_type != CandleType.SPOT:
|
||||
datadir = datadir.joinpath('futures')
|
||||
candle = f"-{candle_type}"
|
||||
|
||||
_tmp = [re.search(r'^(\S+)(?=\-' + timeframe + candle + '.json)', p.name)
|
||||
for p in datadir.glob(f"*{timeframe}{candle}.{cls._get_file_extension()}")]
|
||||
# Check if regex found something and only return these results
|
||||
return [cls.rebuild_pair_from_filename(match[0]) for match in _tmp if match]
|
||||
|
||||
def ohlcv_store(
|
||||
self, pair: str, timeframe: str, data: DataFrame, candle_type: CandleType) -> None:
|
||||
"""
|
||||
@@ -119,18 +97,6 @@ class JsonDataHandler(IDataHandler):
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
@classmethod
|
||||
def trades_get_pairs(cls, datadir: Path) -> List[str]:
|
||||
"""
|
||||
Returns a list of all pairs for which trade data is available in this
|
||||
:param datadir: Directory to search for ohlcv files
|
||||
:return: List of Pairs
|
||||
"""
|
||||
_tmp = [re.search(r'^(\S+)(?=\-trades.json)', p.name)
|
||||
for p in datadir.glob(f"*trades.{cls._get_file_extension()}")]
|
||||
# Check if regex found something and only return these results to avoid exceptions.
|
||||
return [cls.rebuild_pair_from_filename(match[0]) for match in _tmp if match]
|
||||
|
||||
def trades_store(self, pair: str, data: TradeList) -> None:
|
||||
"""
|
||||
Store trades data (list of Dicts) to file
|
||||
|
@@ -11,7 +11,7 @@ import utils_find_1st as utf1st
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.constants import DATETIME_PRINT_FORMAT, UNLIMITED_STAKE_AMOUNT
|
||||
from freqtrade.constants import DATETIME_PRINT_FORMAT, UNLIMITED_STAKE_AMOUNT, Config
|
||||
from freqtrade.data.history import get_timerange, load_data, refresh_data
|
||||
from freqtrade.enums import CandleType, ExitType, RunMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
@@ -42,10 +42,9 @@ class Edge:
|
||||
Author: https://github.com/mishaker
|
||||
"""
|
||||
|
||||
config: Dict = {}
|
||||
_cached_pairs: Dict[str, Any] = {} # Keeps a list of pairs
|
||||
|
||||
def __init__(self, config: Dict[str, Any], exchange, strategy) -> None:
|
||||
def __init__(self, config: Config, exchange, strategy) -> None:
|
||||
|
||||
self.config = config
|
||||
self.exchange = exchange
|
||||
|
@@ -1,5 +1,4 @@
|
||||
""" Binance exchange subclass """
|
||||
import json
|
||||
import logging
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
@@ -12,7 +11,7 @@ from freqtrade.enums import CandleType, MarginMode, TradingMode
|
||||
from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError
|
||||
from freqtrade.exchange import Exchange
|
||||
from freqtrade.exchange.common import retrier
|
||||
from freqtrade.misc import deep_merge_dicts
|
||||
from freqtrade.misc import deep_merge_dicts, json_load
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -31,7 +30,7 @@ class Binance(Exchange):
|
||||
"ccxt_futures_name": "future"
|
||||
}
|
||||
_ft_has_futures: Dict = {
|
||||
"stoploss_order_types": {"limit": "stop"},
|
||||
"stoploss_order_types": {"limit": "limit", "market": "market"},
|
||||
"tickers_have_price": False,
|
||||
}
|
||||
|
||||
@@ -48,13 +47,12 @@ class Binance(Exchange):
|
||||
Returns True if adjustment is necessary.
|
||||
:param side: "buy" or "sell"
|
||||
"""
|
||||
|
||||
ordertype = 'stop' if self.trading_mode == TradingMode.FUTURES else 'stop_loss_limit'
|
||||
order_types = ('stop_loss_limit', 'stop', 'stop_market')
|
||||
|
||||
return (
|
||||
order.get('stopPrice', None) is None
|
||||
or (
|
||||
order['type'] == ordertype
|
||||
order['type'] in order_types
|
||||
and (
|
||||
(side == "sell" and stop_loss > float(order['stopPrice'])) or
|
||||
(side == "buy" and stop_loss < float(order['stopPrice']))
|
||||
@@ -201,7 +199,7 @@ class Binance(Exchange):
|
||||
Path(__file__).parent / 'binance_leverage_tiers.json'
|
||||
)
|
||||
with open(leverage_tiers_path) as json_file:
|
||||
return json.load(json_file)
|
||||
return json_load(json_file)
|
||||
else:
|
||||
try:
|
||||
return self._api.fetch_leverage_tiers()
|
||||
|
@@ -81,6 +81,104 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"1000LUNC/USDT": [
|
||||
{
|
||||
"tier": 1.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 0.0,
|
||||
"maxNotional": 5000.0,
|
||||
"maintenanceMarginRate": 0.01,
|
||||
"maxLeverage": 25.0,
|
||||
"info": {
|
||||
"bracket": "1",
|
||||
"initialLeverage": "25",
|
||||
"notionalCap": "5000",
|
||||
"notionalFloor": "0",
|
||||
"maintMarginRatio": "0.01",
|
||||
"cum": "0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 2.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 5000.0,
|
||||
"maxNotional": 25000.0,
|
||||
"maintenanceMarginRate": 0.025,
|
||||
"maxLeverage": 20.0,
|
||||
"info": {
|
||||
"bracket": "2",
|
||||
"initialLeverage": "20",
|
||||
"notionalCap": "25000",
|
||||
"notionalFloor": "5000",
|
||||
"maintMarginRatio": "0.025",
|
||||
"cum": "75.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 3.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 25000.0,
|
||||
"maxNotional": 100000.0,
|
||||
"maintenanceMarginRate": 0.05,
|
||||
"maxLeverage": 10.0,
|
||||
"info": {
|
||||
"bracket": "3",
|
||||
"initialLeverage": "10",
|
||||
"notionalCap": "100000",
|
||||
"notionalFloor": "25000",
|
||||
"maintMarginRatio": "0.05",
|
||||
"cum": "700.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 4.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 100000.0,
|
||||
"maxNotional": 250000.0,
|
||||
"maintenanceMarginRate": 0.1,
|
||||
"maxLeverage": 5.0,
|
||||
"info": {
|
||||
"bracket": "4",
|
||||
"initialLeverage": "5",
|
||||
"notionalCap": "250000",
|
||||
"notionalFloor": "100000",
|
||||
"maintMarginRatio": "0.1",
|
||||
"cum": "5700.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 5.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 250000.0,
|
||||
"maxNotional": 1000000.0,
|
||||
"maintenanceMarginRate": 0.125,
|
||||
"maxLeverage": 2.0,
|
||||
"info": {
|
||||
"bracket": "5",
|
||||
"initialLeverage": "2",
|
||||
"notionalCap": "1000000",
|
||||
"notionalFloor": "250000",
|
||||
"maintMarginRatio": "0.125",
|
||||
"cum": "11950.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 6.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 1000000.0,
|
||||
"maxNotional": 5000000.0,
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1.0,
|
||||
"info": {
|
||||
"bracket": "6",
|
||||
"initialLeverage": "1",
|
||||
"notionalCap": "5000000",
|
||||
"notionalFloor": "1000000",
|
||||
"maintMarginRatio": "0.5",
|
||||
"cum": "386950.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"1000SHIB/BUSD": [
|
||||
{
|
||||
"tier": 1.0,
|
||||
@@ -1109,6 +1207,88 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"AMB/BUSD": [
|
||||
{
|
||||
"tier": 1.0,
|
||||
"currency": "BUSD",
|
||||
"minNotional": 0.0,
|
||||
"maxNotional": 25000.0,
|
||||
"maintenanceMarginRate": 0.025,
|
||||
"maxLeverage": 20.0,
|
||||
"info": {
|
||||
"bracket": "1",
|
||||
"initialLeverage": "20",
|
||||
"notionalCap": "25000",
|
||||
"notionalFloor": "0",
|
||||
"maintMarginRatio": "0.025",
|
||||
"cum": "0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 2.0,
|
||||
"currency": "BUSD",
|
||||
"minNotional": 25000.0,
|
||||
"maxNotional": 100000.0,
|
||||
"maintenanceMarginRate": 0.05,
|
||||
"maxLeverage": 10.0,
|
||||
"info": {
|
||||
"bracket": "2",
|
||||
"initialLeverage": "10",
|
||||
"notionalCap": "100000",
|
||||
"notionalFloor": "25000",
|
||||
"maintMarginRatio": "0.05",
|
||||
"cum": "625.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 3.0,
|
||||
"currency": "BUSD",
|
||||
"minNotional": 100000.0,
|
||||
"maxNotional": 250000.0,
|
||||
"maintenanceMarginRate": 0.1,
|
||||
"maxLeverage": 5.0,
|
||||
"info": {
|
||||
"bracket": "3",
|
||||
"initialLeverage": "5",
|
||||
"notionalCap": "250000",
|
||||
"notionalFloor": "100000",
|
||||
"maintMarginRatio": "0.1",
|
||||
"cum": "5625.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 4.0,
|
||||
"currency": "BUSD",
|
||||
"minNotional": 250000.0,
|
||||
"maxNotional": 1000000.0,
|
||||
"maintenanceMarginRate": 0.125,
|
||||
"maxLeverage": 2.0,
|
||||
"info": {
|
||||
"bracket": "4",
|
||||
"initialLeverage": "2",
|
||||
"notionalCap": "1000000",
|
||||
"notionalFloor": "250000",
|
||||
"maintMarginRatio": "0.125",
|
||||
"cum": "11875.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 5.0,
|
||||
"currency": "BUSD",
|
||||
"minNotional": 1000000.0,
|
||||
"maxNotional": 5000000.0,
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1.0,
|
||||
"info": {
|
||||
"bracket": "5",
|
||||
"initialLeverage": "1",
|
||||
"notionalCap": "5000000",
|
||||
"notionalFloor": "1000000",
|
||||
"maintMarginRatio": "0.5",
|
||||
"cum": "386875.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"ANC/BUSD": [
|
||||
{
|
||||
"tier": 1.0,
|
||||
@@ -3300,13 +3480,13 @@
|
||||
"tier": 6.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 1000000.0,
|
||||
"maxNotional": 30000000.0,
|
||||
"maxNotional": 5000000.0,
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1.0,
|
||||
"info": {
|
||||
"bracket": "6",
|
||||
"initialLeverage": "1",
|
||||
"notionalCap": "30000000",
|
||||
"notionalCap": "5000000",
|
||||
"notionalFloor": "1000000",
|
||||
"maintMarginRatio": "0.5",
|
||||
"cum": "386950.0"
|
||||
@@ -4880,13 +5060,13 @@
|
||||
"tier": 6.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 1000000.0,
|
||||
"maxNotional": 30000000.0,
|
||||
"maxNotional": 5000000.0,
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1.0,
|
||||
"info": {
|
||||
"bracket": "6",
|
||||
"initialLeverage": "1",
|
||||
"notionalCap": "30000000",
|
||||
"notionalCap": "5000000",
|
||||
"notionalFloor": "1000000",
|
||||
"maintMarginRatio": "0.5",
|
||||
"cum": "386940.0"
|
||||
@@ -8333,6 +8513,104 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"FOOTBALL/USDT": [
|
||||
{
|
||||
"tier": 1.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 0.0,
|
||||
"maxNotional": 5000.0,
|
||||
"maintenanceMarginRate": 0.01,
|
||||
"maxLeverage": 25.0,
|
||||
"info": {
|
||||
"bracket": "1",
|
||||
"initialLeverage": "25",
|
||||
"notionalCap": "5000",
|
||||
"notionalFloor": "0",
|
||||
"maintMarginRatio": "0.01",
|
||||
"cum": "0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 2.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 5000.0,
|
||||
"maxNotional": 25000.0,
|
||||
"maintenanceMarginRate": 0.025,
|
||||
"maxLeverage": 20.0,
|
||||
"info": {
|
||||
"bracket": "2",
|
||||
"initialLeverage": "20",
|
||||
"notionalCap": "25000",
|
||||
"notionalFloor": "5000",
|
||||
"maintMarginRatio": "0.025",
|
||||
"cum": "75.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 3.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 25000.0,
|
||||
"maxNotional": 100000.0,
|
||||
"maintenanceMarginRate": 0.05,
|
||||
"maxLeverage": 10.0,
|
||||
"info": {
|
||||
"bracket": "3",
|
||||
"initialLeverage": "10",
|
||||
"notionalCap": "100000",
|
||||
"notionalFloor": "25000",
|
||||
"maintMarginRatio": "0.05",
|
||||
"cum": "700.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 4.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 100000.0,
|
||||
"maxNotional": 250000.0,
|
||||
"maintenanceMarginRate": 0.1,
|
||||
"maxLeverage": 5.0,
|
||||
"info": {
|
||||
"bracket": "4",
|
||||
"initialLeverage": "5",
|
||||
"notionalCap": "250000",
|
||||
"notionalFloor": "100000",
|
||||
"maintMarginRatio": "0.1",
|
||||
"cum": "5700.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 5.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 250000.0,
|
||||
"maxNotional": 1000000.0,
|
||||
"maintenanceMarginRate": 0.125,
|
||||
"maxLeverage": 2.0,
|
||||
"info": {
|
||||
"bracket": "5",
|
||||
"initialLeverage": "2",
|
||||
"notionalCap": "1000000",
|
||||
"notionalFloor": "250000",
|
||||
"maintMarginRatio": "0.125",
|
||||
"cum": "11950.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 6.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 1000000.0,
|
||||
"maxNotional": 5000000.0,
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1.0,
|
||||
"info": {
|
||||
"bracket": "6",
|
||||
"initialLeverage": "1",
|
||||
"notionalCap": "5000000",
|
||||
"notionalFloor": "1000000",
|
||||
"maintMarginRatio": "0.5",
|
||||
"cum": "386950.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"FTM/BUSD": [
|
||||
{
|
||||
"tier": 1.0,
|
||||
@@ -12123,6 +12401,104 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"LUNA2/USDT": [
|
||||
{
|
||||
"tier": 1.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 0.0,
|
||||
"maxNotional": 5000.0,
|
||||
"maintenanceMarginRate": 0.015,
|
||||
"maxLeverage": 25.0,
|
||||
"info": {
|
||||
"bracket": "1",
|
||||
"initialLeverage": "25",
|
||||
"notionalCap": "5000",
|
||||
"notionalFloor": "0",
|
||||
"maintMarginRatio": "0.015",
|
||||
"cum": "0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 2.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 5000.0,
|
||||
"maxNotional": 25000.0,
|
||||
"maintenanceMarginRate": 0.025,
|
||||
"maxLeverage": 20.0,
|
||||
"info": {
|
||||
"bracket": "2",
|
||||
"initialLeverage": "20",
|
||||
"notionalCap": "25000",
|
||||
"notionalFloor": "5000",
|
||||
"maintMarginRatio": "0.025",
|
||||
"cum": "50.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 3.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 25000.0,
|
||||
"maxNotional": 100000.0,
|
||||
"maintenanceMarginRate": 0.05,
|
||||
"maxLeverage": 10.0,
|
||||
"info": {
|
||||
"bracket": "3",
|
||||
"initialLeverage": "10",
|
||||
"notionalCap": "100000",
|
||||
"notionalFloor": "25000",
|
||||
"maintMarginRatio": "0.05",
|
||||
"cum": "675.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 4.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 100000.0,
|
||||
"maxNotional": 250000.0,
|
||||
"maintenanceMarginRate": 0.1,
|
||||
"maxLeverage": 5.0,
|
||||
"info": {
|
||||
"bracket": "4",
|
||||
"initialLeverage": "5",
|
||||
"notionalCap": "250000",
|
||||
"notionalFloor": "100000",
|
||||
"maintMarginRatio": "0.1",
|
||||
"cum": "5675.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 5.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 250000.0,
|
||||
"maxNotional": 1000000.0,
|
||||
"maintenanceMarginRate": 0.125,
|
||||
"maxLeverage": 2.0,
|
||||
"info": {
|
||||
"bracket": "5",
|
||||
"initialLeverage": "2",
|
||||
"notionalCap": "1000000",
|
||||
"notionalFloor": "250000",
|
||||
"maintMarginRatio": "0.125",
|
||||
"cum": "11925.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 6.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 1000000.0,
|
||||
"maxNotional": 5000000.0,
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1.0,
|
||||
"info": {
|
||||
"bracket": "6",
|
||||
"initialLeverage": "1",
|
||||
"notionalCap": "5000000",
|
||||
"notionalFloor": "1000000",
|
||||
"maintMarginRatio": "0.5",
|
||||
"cum": "386925.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"MANA/USDT": [
|
||||
{
|
||||
"tier": 1.0,
|
||||
@@ -13028,10 +13404,10 @@
|
||||
"minNotional": 0.0,
|
||||
"maxNotional": 5000.0,
|
||||
"maintenanceMarginRate": 0.01,
|
||||
"maxLeverage": 50.0,
|
||||
"maxLeverage": 25.0,
|
||||
"info": {
|
||||
"bracket": "1",
|
||||
"initialLeverage": "50",
|
||||
"initialLeverage": "25",
|
||||
"notionalCap": "5000",
|
||||
"notionalFloor": "0",
|
||||
"maintMarginRatio": "0.01",
|
||||
@@ -13805,6 +14181,88 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"PHB/BUSD": [
|
||||
{
|
||||
"tier": 1.0,
|
||||
"currency": "BUSD",
|
||||
"minNotional": 0.0,
|
||||
"maxNotional": 25000.0,
|
||||
"maintenanceMarginRate": 0.025,
|
||||
"maxLeverage": 20.0,
|
||||
"info": {
|
||||
"bracket": "1",
|
||||
"initialLeverage": "20",
|
||||
"notionalCap": "25000",
|
||||
"notionalFloor": "0",
|
||||
"maintMarginRatio": "0.025",
|
||||
"cum": "0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 2.0,
|
||||
"currency": "BUSD",
|
||||
"minNotional": 25000.0,
|
||||
"maxNotional": 100000.0,
|
||||
"maintenanceMarginRate": 0.05,
|
||||
"maxLeverage": 10.0,
|
||||
"info": {
|
||||
"bracket": "2",
|
||||
"initialLeverage": "10",
|
||||
"notionalCap": "100000",
|
||||
"notionalFloor": "25000",
|
||||
"maintMarginRatio": "0.05",
|
||||
"cum": "625.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 3.0,
|
||||
"currency": "BUSD",
|
||||
"minNotional": 100000.0,
|
||||
"maxNotional": 250000.0,
|
||||
"maintenanceMarginRate": 0.1,
|
||||
"maxLeverage": 5.0,
|
||||
"info": {
|
||||
"bracket": "3",
|
||||
"initialLeverage": "5",
|
||||
"notionalCap": "250000",
|
||||
"notionalFloor": "100000",
|
||||
"maintMarginRatio": "0.1",
|
||||
"cum": "5625.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 4.0,
|
||||
"currency": "BUSD",
|
||||
"minNotional": 250000.0,
|
||||
"maxNotional": 1000000.0,
|
||||
"maintenanceMarginRate": 0.125,
|
||||
"maxLeverage": 2.0,
|
||||
"info": {
|
||||
"bracket": "4",
|
||||
"initialLeverage": "2",
|
||||
"notionalCap": "1000000",
|
||||
"notionalFloor": "250000",
|
||||
"maintMarginRatio": "0.125",
|
||||
"cum": "11875.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 5.0,
|
||||
"currency": "BUSD",
|
||||
"minNotional": 1000000.0,
|
||||
"maxNotional": 5000000.0,
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1.0,
|
||||
"info": {
|
||||
"bracket": "5",
|
||||
"initialLeverage": "1",
|
||||
"notionalCap": "5000000",
|
||||
"notionalFloor": "1000000",
|
||||
"maintMarginRatio": "0.5",
|
||||
"cum": "386875.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"QTUM/USDT": [
|
||||
{
|
||||
"tier": 1.0,
|
||||
@@ -14008,10 +14466,10 @@
|
||||
"minNotional": 0.0,
|
||||
"maxNotional": 5000.0,
|
||||
"maintenanceMarginRate": 0.01,
|
||||
"maxLeverage": 50.0,
|
||||
"maxLeverage": 25.0,
|
||||
"info": {
|
||||
"bracket": "1",
|
||||
"initialLeverage": "50",
|
||||
"initialLeverage": "25",
|
||||
"notionalCap": "5000",
|
||||
"notionalFloor": "0",
|
||||
"maintMarginRatio": "0.01",
|
||||
@@ -14478,13 +14936,13 @@
|
||||
"tier": 6.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 1000000.0,
|
||||
"maxNotional": 30000000.0,
|
||||
"maxNotional": 5000000.0,
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1.0,
|
||||
"info": {
|
||||
"bracket": "6",
|
||||
"initialLeverage": "1",
|
||||
"notionalCap": "30000000",
|
||||
"notionalCap": "5000000",
|
||||
"notionalFloor": "1000000",
|
||||
"maintMarginRatio": "0.5",
|
||||
"cum": "386950.0"
|
||||
@@ -14576,13 +15034,13 @@
|
||||
"tier": 6.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 1000000.0,
|
||||
"maxNotional": 30000000.0,
|
||||
"maxNotional": 5000000.0,
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1.0,
|
||||
"info": {
|
||||
"bracket": "6",
|
||||
"initialLeverage": "1",
|
||||
"notionalCap": "30000000",
|
||||
"notionalCap": "5000000",
|
||||
"notionalFloor": "1000000",
|
||||
"maintMarginRatio": "0.5",
|
||||
"cum": "386950.0"
|
||||
@@ -15487,6 +15945,104 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"SPELL/USDT": [
|
||||
{
|
||||
"tier": 1.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 0.0,
|
||||
"maxNotional": 5000.0,
|
||||
"maintenanceMarginRate": 0.01,
|
||||
"maxLeverage": 25.0,
|
||||
"info": {
|
||||
"bracket": "1",
|
||||
"initialLeverage": "25",
|
||||
"notionalCap": "5000",
|
||||
"notionalFloor": "0",
|
||||
"maintMarginRatio": "0.01",
|
||||
"cum": "0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 2.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 5000.0,
|
||||
"maxNotional": 25000.0,
|
||||
"maintenanceMarginRate": 0.025,
|
||||
"maxLeverage": 20.0,
|
||||
"info": {
|
||||
"bracket": "2",
|
||||
"initialLeverage": "20",
|
||||
"notionalCap": "25000",
|
||||
"notionalFloor": "5000",
|
||||
"maintMarginRatio": "0.025",
|
||||
"cum": "75.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 3.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 25000.0,
|
||||
"maxNotional": 100000.0,
|
||||
"maintenanceMarginRate": 0.05,
|
||||
"maxLeverage": 10.0,
|
||||
"info": {
|
||||
"bracket": "3",
|
||||
"initialLeverage": "10",
|
||||
"notionalCap": "100000",
|
||||
"notionalFloor": "25000",
|
||||
"maintMarginRatio": "0.05",
|
||||
"cum": "700.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 4.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 100000.0,
|
||||
"maxNotional": 250000.0,
|
||||
"maintenanceMarginRate": 0.1,
|
||||
"maxLeverage": 5.0,
|
||||
"info": {
|
||||
"bracket": "4",
|
||||
"initialLeverage": "5",
|
||||
"notionalCap": "250000",
|
||||
"notionalFloor": "100000",
|
||||
"maintMarginRatio": "0.1",
|
||||
"cum": "5700.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 5.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 250000.0,
|
||||
"maxNotional": 1000000.0,
|
||||
"maintenanceMarginRate": 0.125,
|
||||
"maxLeverage": 2.0,
|
||||
"info": {
|
||||
"bracket": "5",
|
||||
"initialLeverage": "2",
|
||||
"notionalCap": "1000000",
|
||||
"notionalFloor": "250000",
|
||||
"maintMarginRatio": "0.125",
|
||||
"cum": "11950.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 6.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 1000000.0,
|
||||
"maxNotional": 5000000.0,
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1.0,
|
||||
"info": {
|
||||
"bracket": "6",
|
||||
"initialLeverage": "1",
|
||||
"notionalCap": "5000000",
|
||||
"notionalFloor": "1000000",
|
||||
"maintMarginRatio": "0.5",
|
||||
"cum": "386950.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"SRM/USDT": [
|
||||
{
|
||||
"tier": 1.0,
|
||||
@@ -15585,6 +16141,104 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"STG/USDT": [
|
||||
{
|
||||
"tier": 1.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 0.0,
|
||||
"maxNotional": 5000.0,
|
||||
"maintenanceMarginRate": 0.01,
|
||||
"maxLeverage": 25.0,
|
||||
"info": {
|
||||
"bracket": "1",
|
||||
"initialLeverage": "25",
|
||||
"notionalCap": "5000",
|
||||
"notionalFloor": "0",
|
||||
"maintMarginRatio": "0.01",
|
||||
"cum": "0.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 2.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 5000.0,
|
||||
"maxNotional": 25000.0,
|
||||
"maintenanceMarginRate": 0.025,
|
||||
"maxLeverage": 20.0,
|
||||
"info": {
|
||||
"bracket": "2",
|
||||
"initialLeverage": "20",
|
||||
"notionalCap": "25000",
|
||||
"notionalFloor": "5000",
|
||||
"maintMarginRatio": "0.025",
|
||||
"cum": "75.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 3.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 25000.0,
|
||||
"maxNotional": 100000.0,
|
||||
"maintenanceMarginRate": 0.05,
|
||||
"maxLeverage": 10.0,
|
||||
"info": {
|
||||
"bracket": "3",
|
||||
"initialLeverage": "10",
|
||||
"notionalCap": "100000",
|
||||
"notionalFloor": "25000",
|
||||
"maintMarginRatio": "0.05",
|
||||
"cum": "700.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 4.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 100000.0,
|
||||
"maxNotional": 250000.0,
|
||||
"maintenanceMarginRate": 0.1,
|
||||
"maxLeverage": 5.0,
|
||||
"info": {
|
||||
"bracket": "4",
|
||||
"initialLeverage": "5",
|
||||
"notionalCap": "250000",
|
||||
"notionalFloor": "100000",
|
||||
"maintMarginRatio": "0.1",
|
||||
"cum": "5700.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 5.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 250000.0,
|
||||
"maxNotional": 1000000.0,
|
||||
"maintenanceMarginRate": 0.125,
|
||||
"maxLeverage": 2.0,
|
||||
"info": {
|
||||
"bracket": "5",
|
||||
"initialLeverage": "2",
|
||||
"notionalCap": "1000000",
|
||||
"notionalFloor": "250000",
|
||||
"maintMarginRatio": "0.125",
|
||||
"cum": "11950.0"
|
||||
}
|
||||
},
|
||||
{
|
||||
"tier": 6.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 1000000.0,
|
||||
"maxNotional": 5000000.0,
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1.0,
|
||||
"info": {
|
||||
"bracket": "6",
|
||||
"initialLeverage": "1",
|
||||
"notionalCap": "5000000",
|
||||
"notionalFloor": "1000000",
|
||||
"maintMarginRatio": "0.5",
|
||||
"cum": "386950.0"
|
||||
}
|
||||
}
|
||||
],
|
||||
"STMX/USDT": [
|
||||
{
|
||||
"tier": 1.0,
|
||||
@@ -16176,13 +16830,13 @@
|
||||
"tier": 5.0,
|
||||
"currency": "BUSD",
|
||||
"minNotional": 1000000.0,
|
||||
"maxNotional": 30000000.0,
|
||||
"maxNotional": 5000000.0,
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1.0,
|
||||
"info": {
|
||||
"bracket": "5",
|
||||
"initialLeverage": "1",
|
||||
"notionalCap": "30000000",
|
||||
"notionalCap": "5000000",
|
||||
"notionalFloor": "1000000",
|
||||
"maintMarginRatio": "0.5",
|
||||
"cum": "386875.0"
|
||||
@@ -16470,13 +17124,13 @@
|
||||
"tier": 6.0,
|
||||
"currency": "USDT",
|
||||
"minNotional": 1000000.0,
|
||||
"maxNotional": 30000000.0,
|
||||
"maxNotional": 5000000.0,
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1.0,
|
||||
"info": {
|
||||
"bracket": "6",
|
||||
"initialLeverage": "1",
|
||||
"notionalCap": "30000000",
|
||||
"notionalCap": "5000000",
|
||||
"notionalFloor": "1000000",
|
||||
"maintMarginRatio": "0.5",
|
||||
"cum": "386950.0"
|
||||
|
@@ -21,7 +21,8 @@ from dateutil import parser
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.constants import (DEFAULT_AMOUNT_RESERVE_PERCENT, NON_OPEN_EXCHANGE_STATES, BuySell,
|
||||
EntryExit, ListPairsWithTimeframes, MakerTaker, PairWithTimeframe)
|
||||
Config, EntryExit, ListPairsWithTimeframes, MakerTaker,
|
||||
PairWithTimeframe)
|
||||
from freqtrade.data.converter import ohlcv_to_dataframe, trades_dict_to_list
|
||||
from freqtrade.enums import OPTIMIZE_MODES, CandleType, MarginMode, TradingMode
|
||||
from freqtrade.exceptions import (DDosProtection, ExchangeError, InsufficientFundsError,
|
||||
@@ -91,7 +92,7 @@ class Exchange:
|
||||
# TradingMode.SPOT always supported and not required in this list
|
||||
]
|
||||
|
||||
def __init__(self, config: Dict[str, Any], validate: bool = True,
|
||||
def __init__(self, config: Config, validate: bool = True,
|
||||
load_leverage_tiers: bool = False) -> None:
|
||||
"""
|
||||
Initializes this module with the given config,
|
||||
@@ -108,7 +109,7 @@ class Exchange:
|
||||
self._loop_lock = Lock()
|
||||
self.loop = asyncio.new_event_loop()
|
||||
asyncio.set_event_loop(self.loop)
|
||||
self._config: Dict = {}
|
||||
self._config: Config = {}
|
||||
|
||||
self._config.update(config)
|
||||
|
||||
@@ -2304,7 +2305,7 @@ class Exchange:
|
||||
updated = tiers.get('updated')
|
||||
if updated:
|
||||
updated_dt = parser.parse(updated)
|
||||
if updated_dt < datetime.now(timezone.utc) - timedelta(days=1):
|
||||
if updated_dt < datetime.now(timezone.utc) - timedelta(weeks=4):
|
||||
logger.info("Cached leverage tiers are outdated. Will update.")
|
||||
return None
|
||||
return tiers['data']
|
||||
|
@@ -16,6 +16,7 @@ from numpy.typing import NDArray
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.data.history import load_pair_history
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
|
||||
@@ -27,9 +28,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class pair_info(TypedDict):
|
||||
model_filename: str
|
||||
first: bool
|
||||
trained_timestamp: int
|
||||
priority: int
|
||||
data_path: str
|
||||
extras: dict
|
||||
|
||||
@@ -58,7 +57,7 @@ class FreqaiDataDrawer:
|
||||
Juha Nykänen @suikula, Wagner Costa @wagnercosta, Johan Vlugt @Jooopieeert
|
||||
"""
|
||||
|
||||
def __init__(self, full_path: Path, config: dict, follow_mode: bool = False):
|
||||
def __init__(self, full_path: Path, config: Config, follow_mode: bool = False):
|
||||
|
||||
self.config = config
|
||||
self.freqai_info = config.get("freqai", {})
|
||||
@@ -91,7 +90,7 @@ class FreqaiDataDrawer:
|
||||
self.old_DBSCAN_eps: Dict[str, float] = {}
|
||||
self.empty_pair_dict: pair_info = {
|
||||
"model_filename": "", "trained_timestamp": 0,
|
||||
"priority": 1, "first": True, "data_path": "", "extras": {}}
|
||||
"data_path": "", "extras": {}}
|
||||
|
||||
def load_drawer_from_disk(self):
|
||||
"""
|
||||
@@ -216,7 +215,6 @@ class FreqaiDataDrawer:
|
||||
self.pair_dict[pair] = self.empty_pair_dict.copy()
|
||||
model_filename = ""
|
||||
trained_timestamp = 0
|
||||
self.pair_dict[pair]["priority"] = len(self.pair_dict)
|
||||
|
||||
if not data_path_set and self.follow_mode:
|
||||
logger.warning(
|
||||
@@ -236,18 +234,9 @@ class FreqaiDataDrawer:
|
||||
return
|
||||
else:
|
||||
self.pair_dict[metadata["pair"]] = self.empty_pair_dict.copy()
|
||||
self.pair_dict[metadata["pair"]]["priority"] = len(self.pair_dict)
|
||||
|
||||
return
|
||||
|
||||
def pair_to_end_of_training_queue(self, pair: str) -> None:
|
||||
# march all pairs up in the queue
|
||||
with self.pair_dict_lock:
|
||||
for p in self.pair_dict:
|
||||
self.pair_dict[p]["priority"] -= 1
|
||||
# send pair to end of queue
|
||||
self.pair_dict[pair]["priority"] = len(self.pair_dict)
|
||||
|
||||
def set_initial_return_values(self, pair: str, pred_df: DataFrame) -> None:
|
||||
"""
|
||||
Set the initial return values to the historical predictions dataframe. This avoids needing
|
||||
|
@@ -18,6 +18,7 @@ from sklearn.model_selection import train_test_split
|
||||
from sklearn.neighbors import NearestNeighbors
|
||||
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.exchange import timeframe_to_seconds
|
||||
from freqtrade.strategy.interface import IStrategy
|
||||
@@ -57,7 +58,7 @@ class FreqaiDataKitchen:
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
config: Dict[str, Any],
|
||||
config: Config,
|
||||
live: bool = False,
|
||||
pair: str = "",
|
||||
):
|
||||
|
@@ -3,6 +3,7 @@ import shutil
|
||||
import threading
|
||||
import time
|
||||
from abc import ABC, abstractmethod
|
||||
from collections import deque
|
||||
from datetime import datetime, timezone
|
||||
from pathlib import Path
|
||||
from threading import Lock
|
||||
@@ -14,12 +15,13 @@ from numpy.typing import NDArray
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.constants import DATETIME_PRINT_FORMAT
|
||||
from freqtrade.constants import DATETIME_PRINT_FORMAT, Config
|
||||
from freqtrade.enums import RunMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.exchange import timeframe_to_seconds
|
||||
from freqtrade.freqai.data_drawer import FreqaiDataDrawer
|
||||
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
|
||||
from freqtrade.freqai.utils import plot_feature_importance
|
||||
from freqtrade.strategy.interface import IStrategy
|
||||
|
||||
|
||||
@@ -50,7 +52,7 @@ class IFreqaiModel(ABC):
|
||||
Juha Nykänen @suikula, Wagner Costa @wagnercosta, Johan Vlugt @Jooopieeert
|
||||
"""
|
||||
|
||||
def __init__(self, config: Dict[str, Any]) -> None:
|
||||
def __init__(self, config: Config) -> None:
|
||||
|
||||
self.config = config
|
||||
self.assert_config(self.config)
|
||||
@@ -80,6 +82,7 @@ class IFreqaiModel(ABC):
|
||||
self.pair_it = 0
|
||||
self.pair_it_train = 0
|
||||
self.total_pairs = len(self.config.get("exchange", {}).get("pair_whitelist"))
|
||||
self.train_queue = self._set_train_queue()
|
||||
self.last_trade_database_summary: DataFrame = {}
|
||||
self.current_trade_database_summary: DataFrame = {}
|
||||
self.analysis_lock = Lock()
|
||||
@@ -99,7 +102,7 @@ class IFreqaiModel(ABC):
|
||||
"""
|
||||
return ({})
|
||||
|
||||
def assert_config(self, config: Dict[str, Any]) -> None:
|
||||
def assert_config(self, config: Config) -> None:
|
||||
|
||||
if not config.get("freqai", {}):
|
||||
raise OperationalException("No freqai parameters found in configuration file.")
|
||||
@@ -181,29 +184,36 @@ class IFreqaiModel(ABC):
|
||||
"""
|
||||
while not self._stop_event.is_set():
|
||||
time.sleep(1)
|
||||
for pair in self.config.get("exchange", {}).get("pair_whitelist"):
|
||||
pair = self.train_queue[0]
|
||||
|
||||
(_, trained_timestamp, _) = self.dd.get_pair_dict_info(pair)
|
||||
# ensure pair is avaialble in dp
|
||||
if pair not in strategy.dp.current_whitelist():
|
||||
self.train_queue.popleft()
|
||||
logger.warning(f'{pair} not in current whitelist, removing from train queue.')
|
||||
continue
|
||||
|
||||
if self.dd.pair_dict[pair]["priority"] != 1:
|
||||
continue
|
||||
dk = FreqaiDataKitchen(self.config, self.live, pair)
|
||||
dk.set_paths(pair, trained_timestamp)
|
||||
(
|
||||
retrain,
|
||||
new_trained_timerange,
|
||||
data_load_timerange,
|
||||
) = dk.check_if_new_training_required(trained_timestamp)
|
||||
dk.set_paths(pair, new_trained_timerange.stopts)
|
||||
(_, trained_timestamp, _) = self.dd.get_pair_dict_info(pair)
|
||||
|
||||
if retrain:
|
||||
self.train_timer('start')
|
||||
self.extract_data_and_train_model(
|
||||
new_trained_timerange, pair, strategy, dk, data_load_timerange
|
||||
)
|
||||
self.train_timer('stop')
|
||||
dk = FreqaiDataKitchen(self.config, self.live, pair)
|
||||
dk.set_paths(pair, trained_timestamp)
|
||||
(
|
||||
retrain,
|
||||
new_trained_timerange,
|
||||
data_load_timerange,
|
||||
) = dk.check_if_new_training_required(trained_timestamp)
|
||||
dk.set_paths(pair, new_trained_timerange.stopts)
|
||||
|
||||
self.dd.save_historic_predictions_to_disk()
|
||||
if retrain:
|
||||
self.train_timer('start')
|
||||
self.extract_data_and_train_model(
|
||||
new_trained_timerange, pair, strategy, dk, data_load_timerange
|
||||
)
|
||||
self.train_timer('stop')
|
||||
|
||||
# only rotate the queue after the first has been trained.
|
||||
self.train_queue.rotate(-1)
|
||||
|
||||
self.dd.save_historic_predictions_to_disk()
|
||||
|
||||
def start_backtesting(
|
||||
self, dataframe: DataFrame, metadata: dict, dk: FreqaiDataKitchen
|
||||
@@ -557,11 +567,11 @@ class IFreqaiModel(ABC):
|
||||
|
||||
self.dd.pair_dict[pair]["trained_timestamp"] = new_trained_timerange.stopts
|
||||
dk.set_new_model_names(pair, new_trained_timerange)
|
||||
self.dd.pair_dict[pair]["first"] = False
|
||||
if self.dd.pair_dict[pair]["priority"] == 1 and self.scanning:
|
||||
self.dd.pair_to_end_of_training_queue(pair)
|
||||
self.dd.save_data(model, pair, dk)
|
||||
|
||||
if self.freqai_info["feature_parameters"].get("plot_feature_importance", False):
|
||||
plot_feature_importance(model, pair, dk)
|
||||
|
||||
if self.freqai_info.get("purge_old_models", False):
|
||||
self.dd.purge_old_models()
|
||||
|
||||
@@ -685,6 +695,32 @@ class IFreqaiModel(ABC):
|
||||
|
||||
return init_model
|
||||
|
||||
def _set_train_queue(self):
|
||||
"""
|
||||
Sets train queue from existing train timestamps if they exist
|
||||
otherwise it sets the train queue based on the provided whitelist.
|
||||
"""
|
||||
current_pairlist = self.config.get("exchange", {}).get("pair_whitelist")
|
||||
if not self.dd.pair_dict:
|
||||
logger.info('Set fresh train queue from whitelist. '
|
||||
f'Queue: {current_pairlist}')
|
||||
return deque(current_pairlist)
|
||||
|
||||
best_queue = deque()
|
||||
|
||||
pair_dict_sorted = sorted(self.dd.pair_dict.items(),
|
||||
key=lambda k: k[1]['trained_timestamp'])
|
||||
for pair in pair_dict_sorted:
|
||||
if pair[0] in current_pairlist:
|
||||
best_queue.append(pair[0])
|
||||
for pair in current_pairlist:
|
||||
if pair not in best_queue:
|
||||
best_queue.appendleft(pair)
|
||||
|
||||
logger.info('Set existing queue from trained timestamps. '
|
||||
f'Best approximation queue: {best_queue}')
|
||||
return best_queue
|
||||
|
||||
# Following methods which are overridden by user made prediction models.
|
||||
# See freqai/prediction_models/CatboostPredictionModel.py for an example.
|
||||
|
||||
|
@@ -1,19 +1,25 @@
|
||||
import logging
|
||||
from datetime import datetime, timezone
|
||||
from typing import Any
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.data.history.history_utils import refresh_backtest_ohlcv_data
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.exchange import timeframe_to_seconds
|
||||
from freqtrade.exchange.exchange import market_is_active
|
||||
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
|
||||
from freqtrade.plugins.pairlist.pairlist_helpers import dynamic_expand_pairlist
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def download_all_data_for_training(dp: DataProvider, config: dict) -> None:
|
||||
def download_all_data_for_training(dp: DataProvider, config: Config) -> None:
|
||||
"""
|
||||
Called only once upon start of bot to download the necessary data for
|
||||
populating indicators and training the model.
|
||||
@@ -47,9 +53,7 @@ def download_all_data_for_training(dp: DataProvider, config: dict) -> None:
|
||||
)
|
||||
|
||||
|
||||
def get_required_data_timerange(
|
||||
config: dict
|
||||
) -> TimeRange:
|
||||
def get_required_data_timerange(config: Config) -> TimeRange:
|
||||
"""
|
||||
Used to compute the required data download time range
|
||||
for auto data-download in FreqAI
|
||||
@@ -86,7 +90,7 @@ def get_required_data_timerange(
|
||||
|
||||
|
||||
# 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: Config) -> None:
|
||||
# """
|
||||
# Called only once upon start of bot to download the necessary data for
|
||||
# populating indicators and training a FreqAI model.
|
||||
@@ -132,3 +136,58 @@ def get_required_data_timerange(
|
||||
# trading_mode=config.get("trading_mode", "spot"),
|
||||
# prepend=config.get("prepend_data", False),
|
||||
# )
|
||||
|
||||
|
||||
def plot_feature_importance(model: Any, pair: str, dk: FreqaiDataKitchen,
|
||||
count_max: int = 25) -> None:
|
||||
"""
|
||||
Plot Best and worst features by importance for a single sub-train.
|
||||
:param model: Any = A model which was `fit` using a common library
|
||||
such as catboost or lightgbm
|
||||
:param pair: str = pair e.g. BTC/USD
|
||||
:param dk: FreqaiDataKitchen = non-persistent data container for current coin/loop
|
||||
:param count_max: int = the amount of features to be loaded per column
|
||||
"""
|
||||
from freqtrade.plot.plotting import go, make_subplots, store_plot_file
|
||||
|
||||
# Extract feature importance from model
|
||||
models = {}
|
||||
if 'FreqaiMultiOutputRegressor' in str(model.__class__):
|
||||
for estimator, label in zip(model.estimators_, dk.label_list):
|
||||
models[label] = estimator
|
||||
else:
|
||||
models[dk.label_list[0]] = model
|
||||
|
||||
for label in models:
|
||||
mdl = models[label]
|
||||
if "catboost.core" in str(mdl.__class__):
|
||||
feature_importance = mdl.get_feature_importance()
|
||||
elif "lightgbm.sklearn" or "xgb" in str(mdl.__class__):
|
||||
feature_importance = mdl.feature_importances_
|
||||
else:
|
||||
logger.info('Model type not support for generating feature importances.')
|
||||
return
|
||||
|
||||
# Data preparation
|
||||
fi_df = pd.DataFrame({
|
||||
"feature_names": np.array(dk.training_features_list),
|
||||
"feature_importance": np.array(feature_importance)
|
||||
})
|
||||
fi_df_top = fi_df.nlargest(count_max, "feature_importance")[::-1]
|
||||
fi_df_worst = fi_df.nsmallest(count_max, "feature_importance")[::-1]
|
||||
|
||||
# Plotting
|
||||
def add_feature_trace(fig, fi_df, col):
|
||||
return fig.add_trace(
|
||||
go.Bar(
|
||||
x=fi_df["feature_importance"],
|
||||
y=fi_df["feature_names"],
|
||||
orientation='h', showlegend=False
|
||||
), row=1, col=col
|
||||
)
|
||||
fig = make_subplots(rows=1, cols=2, horizontal_spacing=0.5)
|
||||
fig = add_feature_trace(fig, fi_df_top, 1)
|
||||
fig = add_feature_trace(fig, fi_df_worst, 2)
|
||||
fig.update_layout(title_text=f"Best and worst features by importance {pair}")
|
||||
label = label.replace('&', '').replace('%', '') # escape two FreqAI specific characters
|
||||
store_plot_file(fig, f"{dk.model_filename}-{label}.html", dk.data_path)
|
||||
|
@@ -13,7 +13,7 @@ from schedule import Scheduler
|
||||
|
||||
from freqtrade import __version__, constants
|
||||
from freqtrade.configuration import validate_config_consistency
|
||||
from freqtrade.constants import BuySell, LongShort
|
||||
from freqtrade.constants import BuySell, Config, LongShort
|
||||
from freqtrade.data.converter import order_book_to_dataframe
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.edge import Edge
|
||||
@@ -45,7 +45,7 @@ class FreqtradeBot(LoggingMixin):
|
||||
This is from here the bot start its logic.
|
||||
"""
|
||||
|
||||
def __init__(self, config: Dict[str, Any]) -> None:
|
||||
def __init__(self, config: Config) -> None:
|
||||
"""
|
||||
Init all variables and objects the bot needs to work
|
||||
:param config: configuration dict, you can use Configuration.get_config()
|
||||
@@ -1085,6 +1085,7 @@ class FreqtradeBot(LoggingMixin):
|
||||
order_obj = Order.parse_from_ccxt_object(stoploss_order, trade.pair, 'stoploss')
|
||||
trade.orders.append(order_obj)
|
||||
trade.stoploss_order_id = str(stoploss_order['id'])
|
||||
trade.stoploss_last_update = datetime.now(timezone.utc)
|
||||
return True
|
||||
except InsufficientFundsError as e:
|
||||
logger.warning(f"Unable to place stoploss order {e}.")
|
||||
@@ -1158,10 +1159,9 @@ class FreqtradeBot(LoggingMixin):
|
||||
if self.create_stoploss_order(trade=trade, stop_price=stop_price):
|
||||
# The above will return False if the placement failed and the trade was force-sold.
|
||||
# in which case the trade will be closed - which we must check below.
|
||||
trade.stoploss_last_update = datetime.utcnow()
|
||||
return False
|
||||
|
||||
# If stoploss order is canceled for some reason we add it
|
||||
# If stoploss order is canceled for some reason we add it again
|
||||
if (trade.is_open
|
||||
and stoploss_order
|
||||
and stoploss_order['status'] in ('canceled', 'cancelled')):
|
||||
@@ -1199,7 +1199,8 @@ class FreqtradeBot(LoggingMixin):
|
||||
if self.exchange.stoploss_adjust(stoploss_norm, order, side=trade.exit_side):
|
||||
# we check if the update is necessary
|
||||
update_beat = self.strategy.order_types.get('stoploss_on_exchange_interval', 60)
|
||||
if (datetime.utcnow() - trade.stoploss_last_update).total_seconds() >= update_beat:
|
||||
upd_req = datetime.now(timezone.utc) - timedelta(seconds=update_beat)
|
||||
if trade.stoploss_last_update_utc and upd_req >= trade.stoploss_last_update_utc:
|
||||
# cancelling the current stoploss on exchange first
|
||||
logger.info(f"Cancelling current stoploss on exchange for pair {trade.pair} "
|
||||
f"(orderid:{order['id']}) in order to add another one ...")
|
||||
|
@@ -2,8 +2,8 @@ import logging
|
||||
import sys
|
||||
from logging import Formatter
|
||||
from logging.handlers import BufferingHandler, RotatingFileHandler, SysLogHandler
|
||||
from typing import Any, Dict
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.exceptions import OperationalException
|
||||
|
||||
|
||||
@@ -73,7 +73,7 @@ def setup_logging_pre() -> None:
|
||||
)
|
||||
|
||||
|
||||
def setup_logging(config: Dict[str, Any]) -> None:
|
||||
def setup_logging(config: Config) -> None:
|
||||
"""
|
||||
Process -v/--verbose, --logfile options
|
||||
"""
|
||||
|
@@ -15,7 +15,7 @@ from pandas import DataFrame
|
||||
|
||||
from freqtrade import constants
|
||||
from freqtrade.configuration import TimeRange, validate_config_consistency
|
||||
from freqtrade.constants import DATETIME_PRINT_FORMAT, LongShort
|
||||
from freqtrade.constants import DATETIME_PRINT_FORMAT, Config, LongShort
|
||||
from freqtrade.data import history
|
||||
from freqtrade.data.btanalysis import find_existing_backtest_stats, trade_list_to_dataframe
|
||||
from freqtrade.data.converter import trim_dataframe, trim_dataframes
|
||||
@@ -70,7 +70,7 @@ class Backtesting:
|
||||
backtesting.start()
|
||||
"""
|
||||
|
||||
def __init__(self, config: Dict[str, Any]) -> None:
|
||||
def __init__(self, config: Config) -> None:
|
||||
|
||||
LoggingMixin.show_output = False
|
||||
self.config = config
|
||||
@@ -812,14 +812,6 @@ class Backtesting:
|
||||
return trade
|
||||
time_in_force = self.strategy.order_time_in_force['entry']
|
||||
|
||||
if not pos_adjust:
|
||||
# Confirm trade entry:
|
||||
if not strategy_safe_wrapper(self.strategy.confirm_trade_entry, default_retval=True)(
|
||||
pair=pair, order_type=order_type, amount=stake_amount, rate=propose_rate,
|
||||
time_in_force=time_in_force, current_time=current_time,
|
||||
entry_tag=entry_tag, side=direction):
|
||||
return trade
|
||||
|
||||
if stake_amount and (not min_stake_amount or stake_amount > min_stake_amount):
|
||||
self.order_id_counter += 1
|
||||
base_currency = self.exchange.get_pair_base_currency(pair)
|
||||
@@ -834,6 +826,15 @@ class Backtesting:
|
||||
# Backcalculate actual stake amount.
|
||||
stake_amount = amount * propose_rate / leverage
|
||||
|
||||
if not pos_adjust:
|
||||
# Confirm trade entry:
|
||||
if not strategy_safe_wrapper(
|
||||
self.strategy.confirm_trade_entry, default_retval=True)(
|
||||
pair=pair, order_type=order_type, amount=amount, rate=propose_rate,
|
||||
time_in_force=time_in_force, current_time=current_time,
|
||||
entry_tag=entry_tag, side=direction):
|
||||
return trade
|
||||
|
||||
is_short = (direction == 'short')
|
||||
# Necessary for Margin trading. Disabled until support is enabled.
|
||||
# interest_rate = self.exchange.get_interest_rate()
|
||||
|
@@ -4,10 +4,10 @@
|
||||
This module contains the edge backtesting interface
|
||||
"""
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
|
||||
from freqtrade import constants
|
||||
from freqtrade.configuration import TimeRange, validate_config_consistency
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.edge import Edge
|
||||
from freqtrade.optimize.optimize_reports import generate_edge_table
|
||||
@@ -26,7 +26,7 @@ class EdgeCli:
|
||||
edge.start()
|
||||
"""
|
||||
|
||||
def __init__(self, config: Dict[str, Any]) -> None:
|
||||
def __init__(self, config: Config) -> None:
|
||||
self.config = config
|
||||
|
||||
# Ensure using dry-run
|
||||
|
@@ -21,7 +21,7 @@ from joblib import Parallel, cpu_count, delayed, dump, load, wrap_non_picklable_
|
||||
from joblib.externals import cloudpickle
|
||||
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, Config
|
||||
from freqtrade.data.converter import trim_dataframes
|
||||
from freqtrade.data.history import get_timerange
|
||||
from freqtrade.enums import HyperoptState
|
||||
@@ -66,7 +66,7 @@ class Hyperopt:
|
||||
hyperopt.start()
|
||||
"""
|
||||
|
||||
def __init__(self, config: Dict[str, Any]) -> None:
|
||||
def __init__(self, config: Config) -> None:
|
||||
self.buy_space: List[Dimension] = []
|
||||
self.sell_space: List[Dimension] = []
|
||||
self.protection_space: List[Dimension] = []
|
||||
@@ -132,7 +132,7 @@ class Hyperopt:
|
||||
self.print_json = self.config.get('print_json', False)
|
||||
|
||||
@staticmethod
|
||||
def get_lock_filename(config: Dict[str, Any]) -> str:
|
||||
def get_lock_filename(config: Config) -> str:
|
||||
|
||||
return str(config['user_data_dir'] / 'hyperopt.lock')
|
||||
|
||||
|
@@ -10,6 +10,7 @@ from typing import Dict, List, Union
|
||||
from sklearn.base import RegressorMixin
|
||||
from skopt.space import Categorical, Dimension, Integer
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.exchange import timeframe_to_minutes
|
||||
from freqtrade.misc import round_dict
|
||||
from freqtrade.optimize.space import SKDecimal
|
||||
@@ -32,7 +33,7 @@ class IHyperOpt(ABC):
|
||||
timeframe: str
|
||||
strategy: IStrategy
|
||||
|
||||
def __init__(self, config: dict) -> None:
|
||||
def __init__(self, config: Config) -> None:
|
||||
self.config = config
|
||||
|
||||
# Assign timeframe to be used in hyperopt
|
||||
|
@@ -10,6 +10,7 @@ from typing import Any, Dict
|
||||
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.data.metrics import calculate_max_drawdown
|
||||
from freqtrade.optimize.hyperopt import IHyperOptLoss
|
||||
|
||||
@@ -27,7 +28,7 @@ class CalmarHyperOptLoss(IHyperOptLoss):
|
||||
trade_count: int,
|
||||
min_date: datetime,
|
||||
max_date: datetime,
|
||||
config: Dict,
|
||||
config: Config,
|
||||
processed: Dict[str, DataFrame],
|
||||
backtest_stats: Dict[str, Any],
|
||||
*args,
|
||||
|
@@ -4,10 +4,9 @@ MaxDrawDownRelativeHyperOptLoss
|
||||
This module defines the alternative HyperOptLoss class which can be used for
|
||||
Hyperoptimization.
|
||||
"""
|
||||
from typing import Dict
|
||||
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.data.metrics import calculate_underwater
|
||||
from freqtrade.optimize.hyperopt import IHyperOptLoss
|
||||
|
||||
@@ -22,7 +21,7 @@ class MaxDrawDownRelativeHyperOptLoss(IHyperOptLoss):
|
||||
"""
|
||||
|
||||
@staticmethod
|
||||
def hyperopt_loss_function(results: DataFrame, config: Dict,
|
||||
def hyperopt_loss_function(results: DataFrame, config: Config,
|
||||
*args, **kwargs) -> float:
|
||||
|
||||
"""
|
||||
|
@@ -9,6 +9,8 @@ from typing import Any, Dict
|
||||
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.constants import Config
|
||||
|
||||
|
||||
class IHyperOptLoss(ABC):
|
||||
"""
|
||||
@@ -21,7 +23,7 @@ class IHyperOptLoss(ABC):
|
||||
@abstractmethod
|
||||
def hyperopt_loss_function(*, results: DataFrame, trade_count: int,
|
||||
min_date: datetime, max_date: datetime,
|
||||
config: Dict, processed: Dict[str, DataFrame],
|
||||
config: Config, processed: Dict[str, DataFrame],
|
||||
backtest_stats: Dict[str, Any],
|
||||
**kwargs) -> float:
|
||||
"""
|
||||
|
@@ -12,7 +12,7 @@ import tabulate
|
||||
from colorama import Fore, Style
|
||||
from pandas import isna, json_normalize
|
||||
|
||||
from freqtrade.constants import FTHYPT_FILEVERSION, USERPATH_STRATEGIES
|
||||
from freqtrade.constants import FTHYPT_FILEVERSION, USERPATH_STRATEGIES, Config
|
||||
from freqtrade.enums import HyperoptState
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.misc import deep_merge_dicts, round_coin_value, round_dict, safe_value_fallback2
|
||||
@@ -45,7 +45,7 @@ class HyperoptStateContainer():
|
||||
class HyperoptTools():
|
||||
|
||||
@staticmethod
|
||||
def get_strategy_filename(config: Dict, strategy_name: str) -> Optional[Path]:
|
||||
def get_strategy_filename(config: Config, strategy_name: str) -> Optional[Path]:
|
||||
"""
|
||||
Get Strategy-location (filename) from strategy_name
|
||||
"""
|
||||
@@ -81,7 +81,7 @@ class HyperoptTools():
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def try_export_params(config: Dict[str, Any], strategy_name: str, params: Dict):
|
||||
def try_export_params(config: Config, strategy_name: str, params: Dict):
|
||||
if params.get(FTHYPT_FILEVERSION, 1) >= 2 and not config.get('disableparamexport', False):
|
||||
# Export parameters ...
|
||||
fn = HyperoptTools.get_strategy_filename(config, strategy_name)
|
||||
@@ -91,7 +91,7 @@ class HyperoptTools():
|
||||
logger.warning("Strategy not found, not exporting parameter file.")
|
||||
|
||||
@staticmethod
|
||||
def has_space(config: Dict[str, Any], space: str) -> bool:
|
||||
def has_space(config: Config, space: str) -> bool:
|
||||
"""
|
||||
Tell if the space value is contained in the configuration
|
||||
"""
|
||||
@@ -131,7 +131,7 @@ class HyperoptTools():
|
||||
return False
|
||||
|
||||
@staticmethod
|
||||
def load_filtered_results(results_file: Path, config: Dict[str, Any]) -> Tuple[List, int]:
|
||||
def load_filtered_results(results_file: Path, config: Config) -> Tuple[List, int]:
|
||||
filteroptions = {
|
||||
'only_best': config.get('hyperopt_list_best', False),
|
||||
'only_profitable': config.get('hyperopt_list_profitable', False),
|
||||
@@ -346,7 +346,7 @@ class HyperoptTools():
|
||||
return trials
|
||||
|
||||
@staticmethod
|
||||
def get_result_table(config: dict, results: list, total_epochs: int, highlight_best: bool,
|
||||
def get_result_table(config: Config, results: list, total_epochs: int, highlight_best: bool,
|
||||
print_colorized: bool, remove_header: int) -> str:
|
||||
"""
|
||||
Log result table
|
||||
@@ -444,7 +444,7 @@ class HyperoptTools():
|
||||
return table
|
||||
|
||||
@staticmethod
|
||||
def export_csv_file(config: dict, results: list, csv_file: str) -> None:
|
||||
def export_csv_file(config: Config, results: list, csv_file: str) -> None:
|
||||
"""
|
||||
Log result to csv-file
|
||||
"""
|
||||
|
@@ -7,7 +7,8 @@ from typing import Any, Dict, List, Union
|
||||
from pandas import DataFrame, to_datetime
|
||||
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)
|
||||
from freqtrade.data.metrics import (calculate_cagr, calculate_csum, calculate_market_change,
|
||||
calculate_max_drawdown)
|
||||
from freqtrade.misc import decimals_per_coin, file_dump_joblib, file_dump_json, round_coin_value
|
||||
@@ -898,7 +899,7 @@ def show_backtest_result(strategy: str, results: Dict[str, Any], stake_currency:
|
||||
print()
|
||||
|
||||
|
||||
def show_backtest_results(config: Dict, backtest_stats: Dict):
|
||||
def show_backtest_results(config: Config, backtest_stats: Dict):
|
||||
stake_currency = config['stake_currency']
|
||||
|
||||
for strategy, results in backtest_stats['strategy'].items():
|
||||
@@ -918,7 +919,7 @@ def show_backtest_results(config: Dict, backtest_stats: Dict):
|
||||
print('\nFor more details, please look at the detail tables above')
|
||||
|
||||
|
||||
def show_sorted_pairlist(config: Dict, backtest_stats: Dict):
|
||||
def show_sorted_pairlist(config: Config, backtest_stats: Dict):
|
||||
if config.get('backtest_show_pair_list', False):
|
||||
for strategy, results in backtest_stats['strategy'].items():
|
||||
print(f"Pairs for Strategy {strategy}: \n[")
|
||||
|
@@ -83,7 +83,7 @@ class Order(_DECL_BASE):
|
||||
|
||||
@property
|
||||
def safe_price(self) -> float:
|
||||
return self.average or self.price
|
||||
return self.average or self.price or self.stop_price
|
||||
|
||||
@property
|
||||
def safe_filled(self) -> float:
|
||||
@@ -376,6 +376,12 @@ class LocalTrade():
|
||||
def open_date_utc(self):
|
||||
return self.open_date.replace(tzinfo=timezone.utc)
|
||||
|
||||
@property
|
||||
def stoploss_last_update_utc(self):
|
||||
if self.stoploss_last_update:
|
||||
return self.stoploss_last_update.replace(tzinfo=timezone.utc)
|
||||
return None
|
||||
|
||||
@property
|
||||
def close_date_utc(self):
|
||||
return self.close_date.replace(tzinfo=timezone.utc)
|
||||
@@ -560,7 +566,6 @@ class LocalTrade():
|
||||
self.stop_loss = stop_loss_norm
|
||||
|
||||
self.stop_loss_pct = -1 * abs(percent)
|
||||
self.stoploss_last_update = datetime.utcnow()
|
||||
|
||||
def adjust_stop_loss(self, current_price: float, stoploss: float,
|
||||
initial: bool = False, refresh: bool = False) -> None:
|
||||
|
@@ -1,10 +1,11 @@
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.data.btanalysis import (analyze_trade_parallelism, extract_trades_of_period,
|
||||
load_trades)
|
||||
from freqtrade.data.converter import trim_dataframe
|
||||
@@ -618,7 +619,7 @@ def store_plot_file(fig, filename: str, directory: Path, auto_open: bool = False
|
||||
logger.info(f"Stored plot as {_filename}")
|
||||
|
||||
|
||||
def load_and_plot_trades(config: Dict[str, Any]):
|
||||
def load_and_plot_trades(config: Config):
|
||||
"""
|
||||
From configuration provided
|
||||
- Initializes plot-script
|
||||
@@ -666,7 +667,7 @@ def load_and_plot_trades(config: Dict[str, Any]):
|
||||
logger.info('End of plotting process. %s plots generated', pair_counter)
|
||||
|
||||
|
||||
def plot_profit(config: Dict[str, Any]) -> None:
|
||||
def plot_profit(config: Config) -> None:
|
||||
"""
|
||||
Plots the total profit for all pairs.
|
||||
Note, the profit calculation isn't realistic.
|
||||
|
@@ -8,7 +8,7 @@ from typing import Any, Dict, List, Optional
|
||||
import arrow
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.constants import ListPairsWithTimeframes
|
||||
from freqtrade.constants import Config, ListPairsWithTimeframes
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.misc import plural
|
||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||
@@ -21,7 +21,7 @@ logger = logging.getLogger(__name__)
|
||||
class AgeFilter(IPairList):
|
||||
|
||||
def __init__(self, exchange, pairlistmanager,
|
||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
||||
config: Config, pairlistconfig: Dict[str, Any],
|
||||
pairlist_pos: int) -> None:
|
||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||
|
||||
|
@@ -6,6 +6,7 @@ from abc import ABC, abstractmethod, abstractproperty
|
||||
from copy import deepcopy
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.exchange import Exchange, market_is_active
|
||||
from freqtrade.mixins import LoggingMixin
|
||||
@@ -17,7 +18,7 @@ logger = logging.getLogger(__name__)
|
||||
class IPairList(LoggingMixin, ABC):
|
||||
|
||||
def __init__(self, exchange: Exchange, pairlistmanager,
|
||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
||||
config: Config, pairlistconfig: Dict[str, Any],
|
||||
pairlist_pos: int) -> None:
|
||||
"""
|
||||
:param exchange: Exchange instance
|
||||
|
@@ -4,6 +4,7 @@ Offset pair list filter
|
||||
import logging
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||
|
||||
@@ -14,7 +15,7 @@ logger = logging.getLogger(__name__)
|
||||
class OffsetFilter(IPairList):
|
||||
|
||||
def __init__(self, exchange, pairlistmanager,
|
||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
||||
config: Config, pairlistconfig: Dict[str, Any],
|
||||
pairlist_pos: int) -> None:
|
||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||
|
||||
|
@@ -6,6 +6,7 @@ from typing import Any, Dict, List
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||
|
||||
@@ -16,7 +17,7 @@ logger = logging.getLogger(__name__)
|
||||
class PerformanceFilter(IPairList):
|
||||
|
||||
def __init__(self, exchange, pairlistmanager,
|
||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
||||
config: Config, pairlistconfig: Dict[str, Any],
|
||||
pairlist_pos: int) -> None:
|
||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||
|
||||
|
@@ -4,6 +4,7 @@ Precision pair list filter
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||
|
||||
@@ -14,7 +15,7 @@ logger = logging.getLogger(__name__)
|
||||
class PrecisionFilter(IPairList):
|
||||
|
||||
def __init__(self, exchange, pairlistmanager,
|
||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
||||
config: Config, pairlistconfig: Dict[str, Any],
|
||||
pairlist_pos: int) -> None:
|
||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||
|
||||
|
@@ -4,6 +4,7 @@ Price pair list filter
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||
|
||||
@@ -14,7 +15,7 @@ logger = logging.getLogger(__name__)
|
||||
class PriceFilter(IPairList):
|
||||
|
||||
def __init__(self, exchange, pairlistmanager,
|
||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
||||
config: Config, pairlistconfig: Dict[str, Any],
|
||||
pairlist_pos: int) -> None:
|
||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||
|
||||
|
@@ -5,6 +5,7 @@ import logging
|
||||
import random
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.enums import RunMode
|
||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||
|
||||
@@ -15,7 +16,7 @@ logger = logging.getLogger(__name__)
|
||||
class ShuffleFilter(IPairList):
|
||||
|
||||
def __init__(self, exchange, pairlistmanager,
|
||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
||||
config: Config, pairlistconfig: Dict[str, Any],
|
||||
pairlist_pos: int) -> None:
|
||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||
|
||||
|
@@ -4,6 +4,7 @@ Spread pair list filter
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||
|
||||
@@ -14,7 +15,7 @@ logger = logging.getLogger(__name__)
|
||||
class SpreadFilter(IPairList):
|
||||
|
||||
def __init__(self, exchange, pairlistmanager,
|
||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
||||
config: Config, pairlistconfig: Dict[str, Any],
|
||||
pairlist_pos: int) -> None:
|
||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||
|
||||
|
@@ -7,6 +7,7 @@ import logging
|
||||
from copy import deepcopy
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||
|
||||
|
||||
@@ -16,7 +17,7 @@ logger = logging.getLogger(__name__)
|
||||
class StaticPairList(IPairList):
|
||||
|
||||
def __init__(self, exchange, pairlistmanager,
|
||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
||||
config: Config, pairlistconfig: Dict[str, Any],
|
||||
pairlist_pos: int) -> None:
|
||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||
|
||||
|
@@ -11,7 +11,7 @@ import numpy as np
|
||||
from cachetools import TTLCache
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.constants import ListPairsWithTimeframes
|
||||
from freqtrade.constants import Config, ListPairsWithTimeframes
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.misc import plural
|
||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||
@@ -26,7 +26,7 @@ class VolatilityFilter(IPairList):
|
||||
"""
|
||||
|
||||
def __init__(self, exchange, pairlistmanager,
|
||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
||||
config: Config, pairlistconfig: Dict[str, Any],
|
||||
pairlist_pos: int) -> None:
|
||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||
|
||||
|
@@ -9,7 +9,7 @@ from typing import Any, Dict, List
|
||||
|
||||
from cachetools import TTLCache
|
||||
|
||||
from freqtrade.constants import ListPairsWithTimeframes
|
||||
from freqtrade.constants import Config, ListPairsWithTimeframes
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_prev_date
|
||||
from freqtrade.misc import format_ms_time
|
||||
@@ -25,7 +25,7 @@ SORT_VALUES = ['quoteVolume']
|
||||
class VolumePairList(IPairList):
|
||||
|
||||
def __init__(self, exchange, pairlistmanager,
|
||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
||||
config: Config, pairlistconfig: Dict[str, Any],
|
||||
pairlist_pos: int) -> None:
|
||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||
|
||||
|
@@ -1,5 +1,7 @@
|
||||
import re
|
||||
from typing import Any, Dict, List
|
||||
from typing import List
|
||||
|
||||
from freqtrade.constants import Config
|
||||
|
||||
|
||||
def expand_pairlist(wildcardpl: List[str], available_pairs: List[str],
|
||||
@@ -42,7 +44,7 @@ def expand_pairlist(wildcardpl: List[str], available_pairs: List[str],
|
||||
return result
|
||||
|
||||
|
||||
def dynamic_expand_pairlist(config: Dict[str, Any], markets: List[str]) -> List[str]:
|
||||
def dynamic_expand_pairlist(config: Config, markets: List[str]) -> List[str]:
|
||||
expanded_pairs = expand_pairlist(config['pairs'], markets)
|
||||
if config.get('freqai', {}).get('enabled', False):
|
||||
corr_pairlist = config['freqai']['feature_parameters']['include_corr_pairlist']
|
||||
|
@@ -9,7 +9,7 @@ import arrow
|
||||
from cachetools import TTLCache
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.constants import ListPairsWithTimeframes
|
||||
from freqtrade.constants import Config, ListPairsWithTimeframes
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.misc import plural
|
||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||
@@ -21,7 +21,7 @@ logger = logging.getLogger(__name__)
|
||||
class RangeStabilityFilter(IPairList):
|
||||
|
||||
def __init__(self, exchange, pairlistmanager,
|
||||
config: Dict[str, Any], pairlistconfig: Dict[str, Any],
|
||||
config: Config, pairlistconfig: Dict[str, Any],
|
||||
pairlist_pos: int) -> None:
|
||||
super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)
|
||||
|
||||
|
@@ -7,7 +7,7 @@ from typing import Dict, List
|
||||
|
||||
from cachetools import TTLCache, cached
|
||||
|
||||
from freqtrade.constants import ListPairsWithTimeframes
|
||||
from freqtrade.constants import Config, ListPairsWithTimeframes
|
||||
from freqtrade.enums import CandleType
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.mixins import LoggingMixin
|
||||
@@ -21,7 +21,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class PairListManager(LoggingMixin):
|
||||
|
||||
def __init__(self, exchange, config: dict) -> None:
|
||||
def __init__(self, exchange, config: Config) -> None:
|
||||
self._exchange = exchange
|
||||
self._config = config
|
||||
self._whitelist = self._config['exchange'].get('pair_whitelist')
|
||||
|
@@ -5,7 +5,7 @@ import logging
|
||||
from datetime import datetime, timezone
|
||||
from typing import Dict, List, Optional
|
||||
|
||||
from freqtrade.constants import LongShort
|
||||
from freqtrade.constants import Config, LongShort
|
||||
from freqtrade.persistence import PairLocks
|
||||
from freqtrade.persistence.models import PairLock
|
||||
from freqtrade.plugins.protections import IProtection
|
||||
@@ -17,7 +17,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class ProtectionManager():
|
||||
|
||||
def __init__(self, config: Dict, protections: List) -> None:
|
||||
def __init__(self, config: Config, protections: List) -> None:
|
||||
self._config = config
|
||||
|
||||
self._protection_handlers: List[IProtection] = []
|
||||
|
@@ -5,7 +5,7 @@ from dataclasses import dataclass
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from freqtrade.constants import LongShort
|
||||
from freqtrade.constants import Config, LongShort
|
||||
from freqtrade.exchange import timeframe_to_minutes
|
||||
from freqtrade.misc import plural
|
||||
from freqtrade.mixins import LoggingMixin
|
||||
@@ -30,7 +30,7 @@ class IProtection(LoggingMixin, ABC):
|
||||
# Can stop trading for one pair
|
||||
has_local_stop: bool = False
|
||||
|
||||
def __init__(self, config: Dict[str, Any], protection_config: Dict[str, Any]) -> None:
|
||||
def __init__(self, config: Config, protection_config: Dict[str, Any]) -> None:
|
||||
self._config = config
|
||||
self._protection_config = protection_config
|
||||
self._stop_duration_candles: Optional[int] = None
|
||||
|
@@ -3,7 +3,7 @@ import logging
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from freqtrade.constants import LongShort
|
||||
from freqtrade.constants import Config, LongShort
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.plugins.protections import IProtection, ProtectionReturn
|
||||
|
||||
@@ -16,7 +16,7 @@ class LowProfitPairs(IProtection):
|
||||
has_global_stop: bool = False
|
||||
has_local_stop: bool = True
|
||||
|
||||
def __init__(self, config: Dict[str, Any], protection_config: Dict[str, Any]) -> None:
|
||||
def __init__(self, config: Config, protection_config: Dict[str, Any]) -> None:
|
||||
super().__init__(config, protection_config)
|
||||
|
||||
self._trade_limit = protection_config.get('trade_limit', 1)
|
||||
|
@@ -5,7 +5,7 @@ from typing import Any, Dict, Optional
|
||||
|
||||
import pandas as pd
|
||||
|
||||
from freqtrade.constants import LongShort
|
||||
from freqtrade.constants import Config, LongShort
|
||||
from freqtrade.data.metrics import calculate_max_drawdown
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.plugins.protections import IProtection, ProtectionReturn
|
||||
@@ -19,7 +19,7 @@ class MaxDrawdown(IProtection):
|
||||
has_global_stop: bool = True
|
||||
has_local_stop: bool = False
|
||||
|
||||
def __init__(self, config: Dict[str, Any], protection_config: Dict[str, Any]) -> None:
|
||||
def __init__(self, config: Config, protection_config: Dict[str, Any]) -> None:
|
||||
super().__init__(config, protection_config)
|
||||
|
||||
self._trade_limit = protection_config.get('trade_limit', 1)
|
||||
|
@@ -3,7 +3,7 @@ import logging
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from freqtrade.constants import LongShort
|
||||
from freqtrade.constants import Config, LongShort
|
||||
from freqtrade.enums import ExitType
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.plugins.protections import IProtection, ProtectionReturn
|
||||
@@ -17,7 +17,7 @@ class StoplossGuard(IProtection):
|
||||
has_global_stop: bool = True
|
||||
has_local_stop: bool = True
|
||||
|
||||
def __init__(self, config: Dict[str, Any], protection_config: Dict[str, Any]) -> None:
|
||||
def __init__(self, config: Config, protection_config: Dict[str, Any]) -> None:
|
||||
super().__init__(config, protection_config)
|
||||
|
||||
self._trade_limit = protection_config.get('trade_limit', 10)
|
||||
|
@@ -4,6 +4,7 @@ This module loads custom exchanges
|
||||
import logging
|
||||
|
||||
import freqtrade.exchange as exchanges
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.exchange import MAP_EXCHANGE_CHILDCLASS, Exchange
|
||||
from freqtrade.resolvers import IResolver
|
||||
|
||||
@@ -18,7 +19,7 @@ class ExchangeResolver(IResolver):
|
||||
object_type = Exchange
|
||||
|
||||
@staticmethod
|
||||
def load_exchange(exchange_name: str, config: dict, validate: bool = True,
|
||||
def load_exchange(exchange_name: str, config: Config, validate: bool = True,
|
||||
load_leverage_tiers: bool = False) -> Exchange:
|
||||
"""
|
||||
Load the custom class from config parameter
|
||||
|
@@ -5,9 +5,8 @@ This module load a custom model for freqai
|
||||
"""
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
from freqtrade.constants import USERPATH_FREQAIMODELS
|
||||
from freqtrade.constants import USERPATH_FREQAIMODELS, Config
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.freqai.freqai_interface import IFreqaiModel
|
||||
from freqtrade.resolvers import IResolver
|
||||
@@ -29,7 +28,7 @@ class FreqaiModelResolver(IResolver):
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def load_freqaimodel(config: Dict) -> IFreqaiModel:
|
||||
def load_freqaimodel(config: Config) -> IFreqaiModel:
|
||||
"""
|
||||
Load the custom class from config parameter
|
||||
:param config: configuration dictionary
|
||||
|
@@ -5,9 +5,8 @@ This module load custom hyperopt
|
||||
"""
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
from freqtrade.constants import HYPEROPT_LOSS_BUILTIN, USERPATH_HYPEROPTS
|
||||
from freqtrade.constants import HYPEROPT_LOSS_BUILTIN, USERPATH_HYPEROPTS, Config
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss
|
||||
from freqtrade.resolvers import IResolver
|
||||
@@ -26,7 +25,7 @@ class HyperOptLossResolver(IResolver):
|
||||
initial_search_path = Path(__file__).parent.parent.joinpath('optimize/hyperopt_loss').resolve()
|
||||
|
||||
@staticmethod
|
||||
def load_hyperoptloss(config: Dict) -> IHyperOptLoss:
|
||||
def load_hyperoptloss(config: Config) -> IHyperOptLoss:
|
||||
"""
|
||||
Load the custom class from config parameter
|
||||
:param config: configuration dictionary
|
||||
|
@@ -10,6 +10,7 @@ import sys
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Iterator, List, Optional, Tuple, Type, Union
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.exceptions import OperationalException
|
||||
|
||||
|
||||
@@ -43,7 +44,7 @@ class IResolver:
|
||||
initial_search_path: Optional[Path]
|
||||
|
||||
@classmethod
|
||||
def build_search_paths(cls, config: Dict[str, Any], user_subdir: Optional[str] = None,
|
||||
def build_search_paths(cls, config: Config, user_subdir: Optional[str] = None,
|
||||
extra_dirs: List[str] = []) -> List[Path]:
|
||||
|
||||
abs_paths: List[Path] = []
|
||||
@@ -153,7 +154,7 @@ class IResolver:
|
||||
return None
|
||||
|
||||
@classmethod
|
||||
def load_object(cls, object_name: str, config: dict, *, kwargs: dict,
|
||||
def load_object(cls, object_name: str, config: Config, *, kwargs: dict,
|
||||
extra_dir: Optional[str] = None) -> Any:
|
||||
"""
|
||||
Search and loads the specified object as configured in hte child class.
|
||||
|
@@ -6,6 +6,7 @@ This module load custom pairlists
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.plugins.pairlist.IPairList import IPairList
|
||||
from freqtrade.resolvers import IResolver
|
||||
|
||||
@@ -24,7 +25,7 @@ class PairListResolver(IResolver):
|
||||
|
||||
@staticmethod
|
||||
def load_pairlist(pairlist_name: str, exchange, pairlistmanager,
|
||||
config: dict, pairlistconfig: dict, pairlist_pos: int) -> IPairList:
|
||||
config: Config, pairlistconfig: dict, pairlist_pos: int) -> IPairList:
|
||||
"""
|
||||
Load the pairlist with pairlist_name
|
||||
:param pairlist_name: Classname of the pairlist
|
||||
|
@@ -5,6 +5,7 @@ import logging
|
||||
from pathlib import Path
|
||||
from typing import Dict
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.plugins.protections import IProtection
|
||||
from freqtrade.resolvers import IResolver
|
||||
|
||||
@@ -22,7 +23,8 @@ class ProtectionResolver(IResolver):
|
||||
initial_search_path = Path(__file__).parent.parent.joinpath('plugins/protections').resolve()
|
||||
|
||||
@staticmethod
|
||||
def load_protection(protection_name: str, config: Dict, protection_config: Dict) -> IProtection:
|
||||
def load_protection(protection_name: str, config: Config,
|
||||
protection_config: Dict) -> IProtection:
|
||||
"""
|
||||
Load the protection with protection_name
|
||||
:param protection_name: Classname of the pairlist
|
||||
|
@@ -9,10 +9,10 @@ from base64 import urlsafe_b64decode
|
||||
from inspect import getfullargspec
|
||||
from os import walk
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional
|
||||
from typing import Any, List, Optional
|
||||
|
||||
from freqtrade.configuration.config_validation import validate_migrated_strategy_settings
|
||||
from freqtrade.constants import REQUIRED_ORDERTIF, REQUIRED_ORDERTYPES, USERPATH_STRATEGIES
|
||||
from freqtrade.constants import REQUIRED_ORDERTIF, REQUIRED_ORDERTYPES, USERPATH_STRATEGIES, Config
|
||||
from freqtrade.enums import TradingMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.resolvers import IResolver
|
||||
@@ -32,7 +32,7 @@ class StrategyResolver(IResolver):
|
||||
initial_search_path = None
|
||||
|
||||
@staticmethod
|
||||
def load_strategy(config: Dict[str, Any] = None) -> IStrategy:
|
||||
def load_strategy(config: Config = None) -> IStrategy:
|
||||
"""
|
||||
Load the custom class from config parameter
|
||||
:param config: configuration dictionary or None
|
||||
@@ -91,8 +91,7 @@ class StrategyResolver(IResolver):
|
||||
return strategy
|
||||
|
||||
@staticmethod
|
||||
def _override_attribute_helper(strategy, config: Dict[str, Any],
|
||||
attribute: str, default: Any):
|
||||
def _override_attribute_helper(strategy, config: Config, attribute: str, default: Any):
|
||||
"""
|
||||
Override attributes in the strategy.
|
||||
Prevalence:
|
||||
@@ -215,7 +214,7 @@ class StrategyResolver(IResolver):
|
||||
|
||||
@staticmethod
|
||||
def _load_strategy(strategy_name: str,
|
||||
config: dict, extra_dir: Optional[str] = None) -> IStrategy:
|
||||
config: Config, extra_dir: Optional[str] = None) -> IStrategy:
|
||||
"""
|
||||
Search and loads the specified strategy.
|
||||
:param strategy_name: name of the module to import
|
||||
|
@@ -12,6 +12,7 @@ from fastapi.middleware.cors import CORSMiddleware
|
||||
from janus import Queue as ThreadedQueue
|
||||
from starlette.responses import JSONResponse
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.rpc.api_server.uvicorn_threaded import UvicornServer
|
||||
from freqtrade.rpc.api_server.ws import ChannelManager
|
||||
@@ -42,10 +43,10 @@ class ApiServer(RPCHandler):
|
||||
_bt = None
|
||||
_bt_data = None
|
||||
_bt_timerange = None
|
||||
_bt_last_config: Dict[str, Any] = {}
|
||||
_bt_last_config: Config = {}
|
||||
_has_rpc: bool = False
|
||||
_bgtask_running: bool = False
|
||||
_config: Dict[str, Any] = {}
|
||||
_config: Config = {}
|
||||
# Exchange - only available in webserver mode.
|
||||
_exchange = None
|
||||
# websocket message queue stuff
|
||||
@@ -63,7 +64,7 @@ class ApiServer(RPCHandler):
|
||||
ApiServer.__initialized = False
|
||||
return ApiServer.__instance
|
||||
|
||||
def __init__(self, config: Dict[str, Any], standalone: bool = False) -> None:
|
||||
def __init__(self, config: Config, standalone: bool = False) -> None:
|
||||
ApiServer._config = config
|
||||
if self.__initialized and (standalone or self._standalone):
|
||||
return
|
||||
|
@@ -1,6 +1,6 @@
|
||||
import logging
|
||||
from typing import Any, Dict
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.enums import RPCMessageType
|
||||
from freqtrade.rpc import RPC
|
||||
from freqtrade.rpc.webhook import Webhook
|
||||
@@ -10,7 +10,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Discord(Webhook):
|
||||
def __init__(self, rpc: 'RPC', config: Dict[str, Any]):
|
||||
def __init__(self, rpc: 'RPC', config: Config):
|
||||
# super().__init__(rpc, config)
|
||||
self.rpc = rpc
|
||||
self.config = config
|
||||
|
@@ -16,7 +16,7 @@ from pandas import DataFrame, NaT
|
||||
|
||||
from freqtrade import __version__
|
||||
from freqtrade.configuration.timerange import TimeRange
|
||||
from freqtrade.constants import CANCEL_REASON, DATETIME_PRINT_FORMAT
|
||||
from freqtrade.constants import CANCEL_REASON, DATETIME_PRINT_FORMAT, Config
|
||||
from freqtrade.data.history import load_data
|
||||
from freqtrade.data.metrics import calculate_max_drawdown
|
||||
from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, SignalDirection, State,
|
||||
@@ -58,7 +58,7 @@ class RPCException(Exception):
|
||||
|
||||
class RPCHandler:
|
||||
|
||||
def __init__(self, rpc: 'RPC', config: Dict[str, Any]) -> None:
|
||||
def __init__(self, rpc: 'RPC', config: Config) -> None:
|
||||
"""
|
||||
Initializes RPCHandlers
|
||||
:param rpc: instance of RPC Helper class
|
||||
@@ -66,7 +66,7 @@ class RPCHandler:
|
||||
:return: None
|
||||
"""
|
||||
self._rpc = rpc
|
||||
self._config: Dict[str, Any] = config
|
||||
self._config: Config = config
|
||||
|
||||
@property
|
||||
def name(self) -> str:
|
||||
@@ -96,7 +96,7 @@ class RPC:
|
||||
:return: None
|
||||
"""
|
||||
self._freqtrade = freqtrade
|
||||
self._config: Dict[str, Any] = freqtrade.config
|
||||
self._config: Config = freqtrade.config
|
||||
if self._config.get('fiat_display_currency'):
|
||||
self._fiat_converter = CryptoToFiatConverter()
|
||||
|
||||
|
@@ -5,6 +5,7 @@ import logging
|
||||
from collections import deque
|
||||
from typing import Any, Dict, List
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.enums import RPCMessageType
|
||||
from freqtrade.rpc import RPC, RPCHandler
|
||||
|
||||
@@ -90,7 +91,7 @@ class RPCManager:
|
||||
'msg': msg,
|
||||
})
|
||||
|
||||
def startup_messages(self, config: Dict[str, Any], pairlist, protections) -> None:
|
||||
def startup_messages(self, config: Config, pairlist, protections) -> None:
|
||||
if config['dry_run']:
|
||||
self.send_msg({
|
||||
'type': RPCMessageType.WARNING,
|
||||
|
@@ -24,7 +24,7 @@ from telegram.ext import CallbackContext, CallbackQueryHandler, CommandHandler,
|
||||
from telegram.utils.helpers import escape_markdown
|
||||
|
||||
from freqtrade.__init__ import __version__
|
||||
from freqtrade.constants import DUST_PER_COIN
|
||||
from freqtrade.constants import DUST_PER_COIN, Config
|
||||
from freqtrade.enums import RPCMessageType, SignalDirection, TradingMode
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.misc import chunks, plural, round_coin_value
|
||||
@@ -88,7 +88,7 @@ def authorized_only(command_handler: Callable[..., None]) -> Callable[..., Any]:
|
||||
class Telegram(RPCHandler):
|
||||
""" This class handles all telegram communication """
|
||||
|
||||
def __init__(self, rpc: RPC, config: Dict[str, Any]) -> None:
|
||||
def __init__(self, rpc: RPC, config: Config) -> None:
|
||||
"""
|
||||
Init the Telegram call, and init the super class RPCHandler
|
||||
:param rpc: instance of RPC Helper class
|
||||
|
@@ -7,6 +7,7 @@ from typing import Any, Dict
|
||||
|
||||
from requests import RequestException, post
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.enums import RPCMessageType
|
||||
from freqtrade.rpc import RPC, RPCHandler
|
||||
|
||||
@@ -19,7 +20,7 @@ logger.debug('Included module rpc.webhook ...')
|
||||
class Webhook(RPCHandler):
|
||||
""" This class handles all webhook communication """
|
||||
|
||||
def __init__(self, rpc: RPC, config: Dict[str, Any]) -> None:
|
||||
def __init__(self, rpc: RPC, config: Config) -> None:
|
||||
"""
|
||||
Init the Webhook class, and init the super class RPCHandler
|
||||
:param rpc: instance of RPC Helper class
|
||||
|
@@ -6,6 +6,7 @@ import logging
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Iterator, List, Tuple, Type, Union
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.misc import deep_merge_dicts, json_load
|
||||
from freqtrade.optimize.hyperopt_tools import HyperoptTools
|
||||
@@ -21,7 +22,7 @@ class HyperStrategyMixin:
|
||||
strategy logic.
|
||||
"""
|
||||
|
||||
def __init__(self, config: Dict[str, Any], *args, **kwargs):
|
||||
def __init__(self, config: Config, *args, **kwargs):
|
||||
"""
|
||||
Initialize hyperoptable strategy mixin.
|
||||
"""
|
||||
|
@@ -10,7 +10,7 @@ from typing import Dict, List, Optional, Tuple, Union
|
||||
import arrow
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.constants import ListPairsWithTimeframes
|
||||
from freqtrade.constants import Config, ListPairsWithTimeframes
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.enums import (CandleType, ExitCheckTuple, ExitType, RunMode, SignalDirection,
|
||||
SignalTagType, SignalType, TradingMode)
|
||||
@@ -119,7 +119,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
||||
# Definition of plot_config. See plotting documentation for more details.
|
||||
plot_config: Dict = {}
|
||||
|
||||
def __init__(self, config: dict) -> None:
|
||||
def __init__(self, config: Config) -> None:
|
||||
self.config = config
|
||||
# Dict to determine if analysis is necessary
|
||||
self._last_candle_seen_per_pair: Dict[str, datetime] = {}
|
||||
@@ -614,6 +614,22 @@ class IStrategy(ABC, HyperStrategyMixin):
|
||||
# END - Intended to be overridden by strategy
|
||||
###
|
||||
|
||||
def __informative_pairs_freqai(self) -> ListPairsWithTimeframes:
|
||||
"""
|
||||
Create informative-pairs needed for FreqAI
|
||||
"""
|
||||
if self.config.get('freqai', {}).get('enabled', False):
|
||||
whitelist_pairs = self.dp.current_whitelist()
|
||||
candle_type = self.config.get('candle_type_def', CandleType.SPOT)
|
||||
corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"]
|
||||
informative_pairs = []
|
||||
for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]:
|
||||
for pair in set(whitelist_pairs + corr_pairs):
|
||||
informative_pairs.append((pair, tf, candle_type))
|
||||
return informative_pairs
|
||||
|
||||
return []
|
||||
|
||||
def gather_informative_pairs(self) -> ListPairsWithTimeframes:
|
||||
"""
|
||||
Internal method which gathers all informative pairs (user or automatically defined).
|
||||
@@ -638,6 +654,7 @@ class IStrategy(ABC, HyperStrategyMixin):
|
||||
else:
|
||||
for pair in self.dp.current_whitelist():
|
||||
informative_pairs.append((pair, inf_data.timeframe, candle_type))
|
||||
informative_pairs.extend(self.__informative_pairs_freqai())
|
||||
return list(set(informative_pairs))
|
||||
|
||||
def get_strategy_name(self) -> str:
|
||||
|
@@ -45,20 +45,7 @@ class FreqaiExampleStrategy(IStrategy):
|
||||
std_dev_multiplier_buy = CategoricalParameter(
|
||||
[0.75, 1, 1.25, 1.5, 1.75], default=1.25, space="buy", optimize=True)
|
||||
std_dev_multiplier_sell = CategoricalParameter(
|
||||
[0.1, 0.25, 0.4], space="sell", default=0.2, optimize=True)
|
||||
|
||||
def informative_pairs(self):
|
||||
whitelist_pairs = self.dp.current_whitelist()
|
||||
corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"]
|
||||
informative_pairs = []
|
||||
for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]:
|
||||
for pair in whitelist_pairs:
|
||||
informative_pairs.append((pair, tf))
|
||||
for pair in corr_pairs:
|
||||
if pair in whitelist_pairs:
|
||||
continue # avoid duplication
|
||||
informative_pairs.append((pair, tf))
|
||||
return informative_pairs
|
||||
[0.75, 1, 1.25, 1.5, 1.75], space="sell", default=1.25, optimize=True)
|
||||
|
||||
def populate_any_indicators(
|
||||
self, pair, df, tf, informative=None, set_generalized_indicators=False
|
||||
@@ -183,25 +170,31 @@ class FreqaiExampleStrategy(IStrategy):
|
||||
|
||||
dataframe = self.freqai.start(dataframe, metadata, self)
|
||||
for val in self.std_dev_multiplier_buy.range:
|
||||
dataframe[f'target_roi_{val}'] = dataframe["&-s_close_mean"] + \
|
||||
dataframe["&-s_close_std"] * val
|
||||
dataframe[f'target_roi_{val}'] = (
|
||||
dataframe["&-s_close_mean"] + dataframe["&-s_close_std"] * val
|
||||
)
|
||||
for val in self.std_dev_multiplier_sell.range:
|
||||
dataframe[f'sell_roi_{val}'] = dataframe["&-s_close_mean"] - \
|
||||
dataframe["&-s_close_std"] * val
|
||||
dataframe[f'sell_roi_{val}'] = (
|
||||
dataframe["&-s_close_mean"] - dataframe["&-s_close_std"] * val
|
||||
)
|
||||
return dataframe
|
||||
|
||||
def populate_entry_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
|
||||
|
||||
enter_long_conditions = [df["do_predict"] == 1, df["&-s_close"]
|
||||
> df[f"target_roi_{self.std_dev_multiplier_buy.value}"]]
|
||||
enter_long_conditions = [
|
||||
df["do_predict"] == 1,
|
||||
df["&-s_close"] > df[f"target_roi_{self.std_dev_multiplier_buy.value}"],
|
||||
]
|
||||
|
||||
if enter_long_conditions:
|
||||
df.loc[
|
||||
reduce(lambda x, y: x & y, enter_long_conditions), ["enter_long", "enter_tag"]
|
||||
] = (1, "long")
|
||||
|
||||
enter_short_conditions = [df["do_predict"] == 1, df["&-s_close"]
|
||||
< df[f"sell_roi_{self.std_dev_multiplier_sell.value}"]]
|
||||
enter_short_conditions = [
|
||||
df["do_predict"] == 1,
|
||||
df["&-s_close"] < df[f"sell_roi_{self.std_dev_multiplier_sell.value}"],
|
||||
]
|
||||
|
||||
if enter_short_conditions:
|
||||
df.loc[
|
||||
@@ -211,13 +204,17 @@ class FreqaiExampleStrategy(IStrategy):
|
||||
return df
|
||||
|
||||
def populate_exit_trend(self, df: DataFrame, metadata: dict) -> DataFrame:
|
||||
exit_long_conditions = [df["do_predict"] == 1, df["&-s_close"] <
|
||||
df[f"sell_roi_{self.std_dev_multiplier_sell.value}"] * 0.25]
|
||||
exit_long_conditions = [
|
||||
df["do_predict"] == 1,
|
||||
df["&-s_close"] < df[f"sell_roi_{self.std_dev_multiplier_sell.value}"] * 0.25,
|
||||
]
|
||||
if exit_long_conditions:
|
||||
df.loc[reduce(lambda x, y: x & y, exit_long_conditions), "exit_long"] = 1
|
||||
|
||||
exit_short_conditions = [df["do_predict"] == 1, df["&-s_close"] >
|
||||
df[f"target_roi_{self.std_dev_multiplier_buy.value}"] * 0.25]
|
||||
exit_short_conditions = [
|
||||
df["do_predict"] == 1,
|
||||
df["&-s_close"] > df[f"target_roi_{self.std_dev_multiplier_buy.value}"] * 0.25,
|
||||
]
|
||||
if exit_short_conditions:
|
||||
df.loc[reduce(lambda x, y: x & y, exit_short_conditions), "exit_short"] = 1
|
||||
|
||||
|
@@ -95,20 +95,6 @@ class FreqaiExampleHybridStrategy(IStrategy):
|
||||
short_rsi = IntParameter(low=51, high=100, default=70, space='sell', optimize=True, load=True)
|
||||
exit_short_rsi = IntParameter(low=1, high=50, default=30, space='buy', optimize=True, load=True)
|
||||
|
||||
# FreqAI required function, leave as is or add additional informatives to existing structure.
|
||||
def informative_pairs(self):
|
||||
whitelist_pairs = self.dp.current_whitelist()
|
||||
corr_pairs = self.config["freqai"]["feature_parameters"]["include_corr_pairlist"]
|
||||
informative_pairs = []
|
||||
for tf in self.config["freqai"]["feature_parameters"]["include_timeframes"]:
|
||||
for pair in whitelist_pairs:
|
||||
informative_pairs.append((pair, tf))
|
||||
for pair in corr_pairs:
|
||||
if pair in whitelist_pairs:
|
||||
continue # avoid duplication
|
||||
informative_pairs.append((pair, tf))
|
||||
return informative_pairs
|
||||
|
||||
# FreqAI required function, user can add or remove indicators, but general structure
|
||||
# must stay the same.
|
||||
def populate_any_indicators(
|
||||
|
@@ -1,21 +1,21 @@
|
||||
# pragma pylint: disable=missing-docstring, invalid-name, pointless-string-statement
|
||||
# flake8: noqa: F401
|
||||
|
||||
# isort: skip_file
|
||||
# --- Do not remove these libs ---
|
||||
import numpy as np # noqa
|
||||
import pandas as pd # noqa
|
||||
from pandas import DataFrame # noqa
|
||||
from datetime import datetime # noqa
|
||||
from typing import Optional, Union # noqa
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
from pandas import DataFrame
|
||||
from datetime import datetime
|
||||
from typing import Optional, Union
|
||||
|
||||
from freqtrade.strategy import (BooleanParameter, CategoricalParameter, DecimalParameter,
|
||||
IStrategy, IntParameter)
|
||||
IntParameter, IStrategy, merge_informative_pair)
|
||||
|
||||
# --------------------------------
|
||||
# Add your lib to import here
|
||||
import talib.abstract as ta
|
||||
import pandas_ta as pta
|
||||
import freqtrade.vendor.qtpylib.indicators as qtpylib
|
||||
from technical import qtpylib
|
||||
|
||||
|
||||
class {{ strategy }}(IStrategy):
|
||||
|
@@ -4,6 +4,7 @@ from typing import Dict
|
||||
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.constants import Config
|
||||
from freqtrade.optimize.hyperopt import IHyperOptLoss
|
||||
|
||||
|
||||
@@ -36,7 +37,7 @@ class SampleHyperOptLoss(IHyperOptLoss):
|
||||
@staticmethod
|
||||
def hyperopt_loss_function(results: DataFrame, trade_count: int,
|
||||
min_date: datetime, max_date: datetime,
|
||||
config: Dict, processed: Dict[str, DataFrame],
|
||||
config: Config, processed: Dict[str, DataFrame],
|
||||
*args, **kwargs) -> float:
|
||||
"""
|
||||
Objective function, returns smaller number for better results
|
||||
|
@@ -7,7 +7,7 @@ from typing import Dict, NamedTuple, Optional
|
||||
|
||||
import arrow
|
||||
|
||||
from freqtrade.constants import UNLIMITED_STAKE_AMOUNT
|
||||
from freqtrade.constants import UNLIMITED_STAKE_AMOUNT, Config
|
||||
from freqtrade.enums import RunMode, TradingMode
|
||||
from freqtrade.exceptions import DependencyException
|
||||
from freqtrade.exchange import Exchange
|
||||
@@ -35,7 +35,7 @@ class PositionWallet(NamedTuple):
|
||||
|
||||
class Wallets:
|
||||
|
||||
def __init__(self, config: dict, exchange: Exchange, log: bool = True) -> None:
|
||||
def __init__(self, config: Config, exchange: Exchange, log: bool = True) -> None:
|
||||
self._config = config
|
||||
self._log = log
|
||||
self._exchange = exchange
|
||||
|
@@ -9,8 +9,9 @@ from typing import Any, Callable, Dict, Optional
|
||||
|
||||
import sdnotify
|
||||
|
||||
from freqtrade import __version__, constants
|
||||
from freqtrade import __version__
|
||||
from freqtrade.configuration import Configuration
|
||||
from freqtrade.constants import PROCESS_THROTTLE_SECS, RETRY_TIMEOUT, Config
|
||||
from freqtrade.enums import State
|
||||
from freqtrade.exceptions import OperationalException, TemporaryError
|
||||
from freqtrade.freqtradebot import FreqtradeBot
|
||||
@@ -24,7 +25,7 @@ class Worker:
|
||||
Freqtradebot worker class
|
||||
"""
|
||||
|
||||
def __init__(self, args: Dict[str, Any], config: Dict[str, Any] = None) -> None:
|
||||
def __init__(self, args: Dict[str, Any], config: Config = None) -> None:
|
||||
"""
|
||||
Init all variables and objects the bot needs to work
|
||||
"""
|
||||
@@ -53,7 +54,7 @@ class Worker:
|
||||
|
||||
internals_config = self._config.get('internals', {})
|
||||
self._throttle_secs = internals_config.get('process_throttle_secs',
|
||||
constants.PROCESS_THROTTLE_SECS)
|
||||
PROCESS_THROTTLE_SECS)
|
||||
self._heartbeat_interval = internals_config.get('heartbeat_interval', 60)
|
||||
|
||||
self._sd_notify = sdnotify.SystemdNotifier() if \
|
||||
@@ -151,8 +152,8 @@ class Worker:
|
||||
try:
|
||||
self.freqtrade.process()
|
||||
except TemporaryError as error:
|
||||
logger.warning(f"Error: {error}, retrying in {constants.RETRY_TIMEOUT} seconds...")
|
||||
time.sleep(constants.RETRY_TIMEOUT)
|
||||
logger.warning(f"Error: {error}, retrying in {RETRY_TIMEOUT} seconds...")
|
||||
time.sleep(RETRY_TIMEOUT)
|
||||
except OperationalException:
|
||||
tb = traceback.format_exc()
|
||||
hint = 'Issue `/start` if you think it is safe to restart.'
|
||||
|
Reference in New Issue
Block a user