Esempio n. 1
0
    def __init__(self,
                 low: Union[float_or_int, Sequence[float_or_int]],
                 high: Optional[float_or_int] = None,
                 *,
                 default: float_or_int,
                 space: Optional[str] = None,
                 optimize: bool = True,
                 load: bool = True,
                 **kwargs):
        """
        Initialize hyperopt-optimizable numeric parameter.
        Cannot be instantiated, but provides the validation for other numeric parameters
        :param low: Lower end (inclusive) of optimization space or [low, high].
        :param high: Upper end (inclusive) of optimization space.
                     Must be none of entire range is passed first parameter.
        :param default: A default value.
        :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if
                      parameter fieldname is prefixed with 'buy_' or 'sell_'.
        :param optimize: Include parameter in hyperopt optimizations.
        :param load: Load parameter value from {space}_params.
        :param kwargs: Extra parameters to skopt.space.*.
        """
        if high is not None and isinstance(low, Sequence):
            raise OperationalException(
                f'{self.__class__.__name__} space invalid.')
        if high is None or isinstance(low, Sequence):
            if not isinstance(low, Sequence) or len(low) != 2:
                raise OperationalException(
                    f'{self.__class__.__name__} space must be [low, high]')
            self.low, self.high = low
        else:
            self.low = low
            self.high = high

        super().__init__(default=default,
                         space=space,
                         optimize=optimize,
                         load=load,
                         **kwargs)
    def __init__(self, exchange, pairlistmanager, config: Dict[str, Any],
                 pairlistconfig: dict, pairlist_pos: int) -> None:
        super().__init__(exchange, pairlistmanager, config, pairlistconfig,
                         pairlist_pos)

        if 'number_assets' not in self._pairlistconfig:
            raise OperationalException(
                f'`number_assets` not specified. Please check your configuration '
                'for "pairlist.config.number_assets"')
        self._number_pairs = self._pairlistconfig['number_assets']
        self._sort_key = self._pairlistconfig.get('sort_key', 'quoteVolume')
        self._min_value = self._pairlistconfig.get('min_value', 0)
        self.refresh_period = self._pairlistconfig.get('refresh_period', 1800)

        if not self._exchange.exchange_has('fetchTickers'):
            raise OperationalException(
                'Exchange does not support dynamic whitelist.'
                'Please edit your config and restart the bot')
        if not self._validate_keys(self._sort_key):
            raise OperationalException(
                f'key {self._sort_key} not in {SORT_VALUES}')
        self._last_refresh = 0
Esempio n. 3
0
 def detect_parameters(cls, category: str) -> Iterator[Tuple[str, BaseParameter]]:
     """ Detect all parameters for 'category' """
     for attr_name in dir(cls):
         if not attr_name.startswith('__'):  # Ignore internals, not strictly necessary.
             attr = getattr(cls, attr_name)
             if issubclass(attr.__class__, BaseParameter):
                 if (attr_name.startswith(category + '_')
                         and attr.category is not None and attr.category != category):
                     raise OperationalException(
                         f'Inconclusive parameter name {attr_name}, category: {attr.category}.')
                 if (category == attr.category or
                         (attr_name.startswith(category + '_') and attr.category is None)):
                     yield attr_name, attr
Esempio n. 4
0
    def __init__(self, exchange, pairlistmanager, config: Dict[str, Any],
                 pairlistconfig: Dict[str, Any], pairlist_pos: int) -> None:
        super().__init__(exchange, pairlistmanager, config, pairlistconfig,
                         pairlist_pos)

        self._days = pairlistconfig.get('lookback_days', 10)
        self._min_volatility = pairlistconfig.get('min_volatility', 0)
        self._max_volatility = pairlistconfig.get('max_volatility',
                                                  sys.maxsize)
        self._refresh_period = pairlistconfig.get('refresh_period', 1440)

        self._pair_cache: TTLCache = TTLCache(maxsize=1000,
                                              ttl=self._refresh_period)

        if self._days < 1:
            raise OperationalException(
                "VolatilityFilter requires lookback_days to be >= 1")
        if self._days > exchange.ohlcv_candle_limit('1d'):
            raise OperationalException(
                "VolatilityFilter requires lookback_days to not "
                "exceed exchange max request size "
                f"({exchange.ohlcv_candle_limit('1d')})")
Esempio n. 5
0
 def load_previous_results(trials_file: Path) -> List:
     """
     Load data for epochs from the file if we have one
     """
     trials: List = []
     if trials_file.is_file() and trials_file.stat().st_size > 0:
         trials = Hyperopt._read_trials(trials_file)
         if trials[0].get('is_best') is None:
             raise OperationalException(
                 "The file with Hyperopt results is incompatible with this version "
                 "of Freqtrade and cannot be loaded.")
         logger.info(f"Loaded {len(trials)} previous evaluations from disk.")
     return trials
Esempio n. 6
0
def check_conflicting_settings(config: Dict[str, Any], section_old: str,
                               name_old: str, section_new: Optional[str],
                               name_new: str) -> None:
    section_new_config = config.get(section_new, {}) if section_new else config
    section_old_config = config.get(section_old, {})
    if name_new in section_new_config and name_old in section_old_config:
        new_name = f"{section_new}.{name_new}" if section_new else f"{name_new}"
        raise OperationalException(
            f"Conflicting settings `{new_name}` and `{section_old}.{name_old}` "
            "(DEPRECATED) detected in the configuration file. "
            "This deprecated setting will be removed in the next versions of Freqtrade. "
            f"Please delete it from your configuration and use the `{new_name}` "
            "setting instead.")
Esempio n. 7
0
    def stoploss_limit(self, pair: str, amount: float, stop_price: float,
                       rate: float) -> Dict:
        """
        creates a stoploss limit order.
        Since ccxt does not unify stoploss-limit orders yet, this needs to be implemented in each
        exchange's subclass.
        The exception below should never raise, since we disallow
        starting the bot in validate_ordertypes()
        Note: Changes to this interface need to be applied to all sub-classes too.
        """

        raise OperationalException(
            f"stoploss_limit is not implemented for {self.name}.")
Esempio n. 8
0
 async def _async_fetch_trades(self,
                               pair: str,
                               since: Optional[int] = None,
                               params: Optional[dict] = None) -> List[Dict]:
     """
     Asyncronously gets trade history using fetch_trades.
     Handles exchange errors, does one call to the exchange.
     :param pair: Pair to fetch trade data for
     :param since: Since as integer timestamp in milliseconds
     returns: List of dicts containing trades
     """
     try:
         # fetch trades asynchronously
         if params:
             logger.debug("Fetching trades for pair %s, params: %s ", pair,
                          params)
             trades = await self._api_async.fetch_trades(pair,
                                                         params=params,
                                                         limit=1000)
         else:
             logger.debug(
                 "Fetching trades for pair %s, since %s %s...", pair, since,
                 '(' + arrow.get(since // 1000).isoformat() +
                 ') ' if since is not None else '')
             trades = await self._api_async.fetch_trades(pair,
                                                         since=since,
                                                         limit=1000)
         return trades
     except ccxt.NotSupported as e:
         raise OperationalException(
             f'Exchange {self._api.name} does not support fetching historical trade data.'
             f'Message: {e}') from e
     except (ccxt.NetworkError, ccxt.ExchangeError) as e:
         raise TemporaryError(
             f'Could not load trade history due to {e.__class__.__name__}. '
             f'Message: {e}') from e
     except ccxt.BaseError as e:
         raise OperationalException(
             f'Could not fetch trade data. Msg: {e}') from e
Esempio n. 9
0
    def current_whitelist(self) -> List[str]:
        """
        fetch latest available whitelist.

        Useful when you have a large whitelist and need to call each pair as an informative pair.
        As available pairs does not show whitelist until after informative pairs have been cached.
        :return: list of pairs in whitelist
        """

        if self._pairlists:
            return self._pairlists.whitelist
        else:
            raise OperationalException("Dataprovider was not initialized with a pairlist provider.")
Esempio n. 10
0
 def fetch_ticker(self, pair: str) -> dict:
     try:
         if pair not in self._api.markets or not self._api.markets[
                 pair].get('active'):
             raise DependencyException(f"Pair {pair} not available")
         data = self._api.fetch_ticker(pair)
         return data
     except (ccxt.NetworkError, ccxt.ExchangeError) as e:
         raise TemporaryError(
             f'Could not load ticker due to {e.__class__.__name__}. Message: {e}'
         ) from e
     except ccxt.BaseError as e:
         raise OperationalException(e) from e
Esempio n. 11
0
    def stoploss(self, pair: str, amount: float, stop_price: float, order_types: Dict) -> Dict:
        """
        Creates a stoploss order.
        depending on order_types.stoploss configuration, uses 'market' or limit order.

        Limit orders are defined by having orderPrice set, otherwise a market order is used.
        """
        limit_price_pct = order_types.get('stoploss_on_exchange_limit_ratio', 0.99)
        limit_rate = stop_price * limit_price_pct

        ordertype = "stop"

        stop_price = self.price_to_precision(pair, stop_price)

        if self._config['dry_run']:
            dry_order = self.create_dry_run_order(
                pair, ordertype, "sell", amount, stop_price)
            return dry_order

        try:
            params = self._params.copy()
            if order_types.get('stoploss', 'market') == 'limit':
                # set orderPrice to place limit order, otherwise it's a market order
                params['orderPrice'] = limit_rate

            params['stopPrice'] = stop_price
            amount = self.amount_to_precision(pair, amount)

            order = self._api.create_order(symbol=pair, type=ordertype, side='sell',
                                           amount=amount, params=params)
            self._log_exchange_response('create_stoploss_order', order)
            logger.info('stoploss order added for %s. '
                        'stop price: %s.', pair, stop_price)
            return order
        except ccxt.InsufficientFunds as e:
            raise InsufficientFundsError(
                f'Insufficient funds to create {ordertype} sell order on market {pair}. '
                f'Tried to create stoploss with amount {amount} at stoploss {stop_price}. '
                f'Message: {e}') from e
        except ccxt.InvalidOrder as e:
            raise InvalidOrderException(
                f'Could not create {ordertype} sell order on market {pair}. '
                f'Tried to create stoploss with amount {amount} at stoploss {stop_price}. '
                f'Message: {e}') from e
        except ccxt.DDoSProtection as e:
            raise DDosProtection(e) from e
        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
            raise TemporaryError(
                f'Could not place sell order due to {e.__class__.__name__}. Message: {e}') from e
        except ccxt.BaseError as e:
            raise OperationalException(e) from e
Esempio n. 12
0
def load_backtest_data(filename: Union[Path, str],
                       strategy: Optional[str] = None) -> pd.DataFrame:
    """
    Load backtest data file.
    :param filename: pathlib.Path object, or string pointing to a file or directory
    :param strategy: Strategy to load - mainly relevant for multi-strategy backtests
                     Can also serve as protection to load the correct result.
    :return: a dataframe with the analysis results
    :raise: ValueError if loading goes wrong.
    """
    data = load_backtest_stats(filename)
    if not isinstance(data, list):
        # new, nested format
        if 'strategy' not in data:
            raise ValueError("Unknown dataformat.")

        if not strategy:
            if len(data['strategy']) == 1:
                strategy = list(data['strategy'].keys())[0]
            else:
                raise ValueError(
                    "Detected backtest result with more than one strategy. "
                    "Please specify a strategy.")

        if strategy not in data['strategy']:
            raise ValueError(
                f"Strategy {strategy} not available in the backtest result.")

        data = data['strategy'][strategy]['trades']
        df = pd.DataFrame(data)
        if not df.empty:
            df['open_date'] = pd.to_datetime(df['open_date'],
                                             utc=True,
                                             infer_datetime_format=True)
            df['close_date'] = pd.to_datetime(df['close_date'],
                                              utc=True,
                                              infer_datetime_format=True)
            # Compatibility support for pre short Columns
            if 'is_short' not in df.columns:
                df['is_short'] = 0
            if 'enter_tag' not in df.columns:
                df['enter_tag'] = df['buy_tag']
                df = df.drop(['buy_tag'], axis=1)

    else:
        # old format - only with lists.
        raise OperationalException(
            "Backtest-results with only trades data are no longer supported.")
    if not df.empty:
        df = df.sort_values("open_date").reset_index(drop=True)
    return df
Esempio n. 13
0
def generate_profit_graph(pairs: str, data: Dict[str, pd.DataFrame],
                          trades: pd.DataFrame, timeframe: str,
                          stake_currency: str) -> go.Figure:
    # Combine close-values for all pairs, rename columns to "pair"
    df_comb = combine_dataframes_with_mean(data, "close")

    # Trim trades to available OHLCV data
    trades = extract_trades_of_period(df_comb, trades, date_index=True)
    if len(trades) == 0:
        raise OperationalException('No trades found in selected timerange.')

    # Add combined cumulative profit
    df_comb = create_cum_profit(df_comb, trades, 'cum_profit', timeframe)

    # Plot the pairs average close prices, and total profit growth
    avgclose = go.Scatter(
        x=df_comb.index,
        y=df_comb['mean'],
        name='Avg close price',
    )

    fig = make_subplots(rows=3,
                        cols=1,
                        shared_xaxes=True,
                        row_width=[1, 1, 1],
                        vertical_spacing=0.05,
                        subplot_titles=[
                            "AVG Close Price", "Combined Profit",
                            "Profit per pair"
                        ])
    fig['layout'].update(title="Freqtrade Profit plot")
    fig['layout']['yaxis1'].update(title='Price')
    fig['layout']['yaxis2'].update(title=f'Profit {stake_currency}')
    fig['layout']['yaxis3'].update(title=f'Profit {stake_currency}')
    fig['layout']['xaxis']['rangeslider'].update(visible=False)
    fig.update_layout(modebar_add=["v1hovermode", "toggleSpikeLines"])

    fig.add_trace(avgclose, 1, 1)
    fig = add_profit(fig, 2, df_comb, 'cum_profit', 'Profit')
    fig = add_max_drawdown(fig, 2, trades, df_comb, timeframe)

    for pair in pairs:
        profit_col = f'cum_profit_{pair}'
        try:
            df_comb = create_cum_profit(df_comb,
                                        trades[trades['pair'] == pair],
                                        profit_col, timeframe)
            fig = add_profit(fig, 3, df_comb, profit_col, f"Profit {pair}")
        except ValueError:
            pass
    return fig
Esempio n. 14
0
def init_db(db_url: str, clean_open_orders: bool = False) -> None:
    """
    Initializes this module with the given config,
    registers all known command handlers
    and starts polling for message updates
    :param db_url: Database to use
    :param clean_open_orders: Remove open orders from the database.
        Useful for dry-run or if all orders have been reset on the exchange.
    :return: None
    """
    kwargs = {}

    # Take care of thread ownership if in-memory db
    if db_url == 'sqlite://':
        kwargs.update({
            'connect_args': {
                'check_same_thread': False
            },
            'poolclass': StaticPool,
            'echo': False,
        })

    try:
        engine = create_engine(db_url, **kwargs)
    except NoSuchModuleError:
        raise OperationalException(
            f"Given value for db_url: '{db_url}' "
            f"is no valid database URL! (See {_SQL_DOCS_URL})")

    # https://docs.sqlalchemy.org/en/13/orm/contextual.html#thread-local-scope
    # Scoped sessions proxy requests to the appropriate thread-local session.
    # We should use the scoped_session object - not a seperately initialized version
    Trade.session = scoped_session(
        sessionmaker(bind=engine, autoflush=True, autocommit=True))
    Trade.query = Trade.session.query_property()
    # Copy session attributes to order object too
    Order.session = Trade.session
    Order.query = Order.session.query_property()
    PairLock.session = Trade.session
    PairLock.query = PairLock.session.query_property()

    previous_tables = inspect(engine).get_table_names()
    _DECL_BASE.metadata.create_all(engine)
    check_migrate(engine,
                  decl_base=_DECL_BASE,
                  previous_tables=previous_tables)

    # Clean dry_run DB if the db is not in-memory
    if clean_open_orders and db_url != 'sqlite://':
        clean_dry_run_db()
Esempio n. 15
0
    def validate_timeframes(self, timeframe: Optional[str]) -> None:
        """
        Checks if ticker interval from config is a supported timeframe on the exchange
        """
        if not hasattr(self._api,
                       "timeframes") or self._api.timeframes is None:
            # If timeframes attribute is missing (or is None), the exchange probably
            # has no fetchOHLCV method.
            # Therefore we also show that.
            raise OperationalException(
                f"The ccxt library does not provide the list of timeframes "
                f"for the exchange \"{self.name}\" and this exchange "
                f"is therefore not supported. ccxt fetchOHLCV: {self.exchange_has('fetchOHLCV')}"
            )

        if timeframe and (timeframe not in self.timeframes):
            raise OperationalException(
                f"Invalid ticker interval '{timeframe}'. This exchange supports: {self.timeframes}"
            )

        if timeframe and timeframe_to_minutes(timeframe) < 1:
            raise OperationalException(
                f"Timeframes < 1m are currently not supported by Freqtrade.")
Esempio n. 16
0
    def __init__(self, exchange, pairlistmanager,
                 config: Dict[str, Any], pairlistconfig: Dict[str, Any],
                 pairlist_pos: int) -> None:
        super().__init__(exchange, pairlistmanager, config, pairlistconfig, pairlist_pos)

        if 'stoploss' not in self._config:
            raise OperationalException(
                'PrecisionFilter can only work with stoploss defined. Please add the '
                'stoploss key to your configuration (overwrites eventual strategy settings).')
        self._stoploss = self._config['stoploss']
        self._enabled = self._stoploss != 0

        # Precalculate sanitized stoploss value to avoid recalculation for every pair
        self._stoploss = 1 - abs(self._stoploss)
Esempio n. 17
0
 def load_previous_results(results_file: Path) -> List:
     """
     Load data for epochs from the file if we have one
     """
     epochs: List = []
     if results_file.is_file() and results_file.stat().st_size > 0:
         epochs = Hyperopt._read_results(results_file)
         # Detection of some old format, without 'is_best' field saved
         if epochs[0].get('is_best') is None:
             raise OperationalException(
                 "The file with Hyperopt results is incompatible with this version "
                 "of Freqtrade and cannot be loaded.")
         logger.info(f"Loaded {len(epochs)} previous evaluations from disk.")
     return epochs
Esempio n. 18
0
def process_removed_setting(config: Dict[str, Any], section1: str, name1: str,
                            section2: str, name2: str) -> None:
    """
    :param section1: Removed section
    :param name1: Removed setting name
    :param section2: new section for this key
    :param name2: new setting name
    """
    section1_config = config.get(section1, {})
    if name1 in section1_config:
        raise OperationalException(
            f"Setting `{section1}.{name1}` has been moved to `{section2}.{name2}. "
            f"Please delete it from your configuration and use the `{section2}.{name2}` "
            "setting instead.")
Esempio n. 19
0
    def _load_strategy(strategy_name: str,
                       config: dict, extra_dir: Optional[str] = None) -> IStrategy:
        """
        Search and loads the specified strategy.
        :param strategy_name: name of the module to import
        :param config: configuration for the strategy
        :param extra_dir: additional directory to search for the given strategy
        :return: Strategy instance or None
        """

        abs_paths = StrategyResolver.build_search_paths(config,
                                                        user_subdir=USERPATH_STRATEGIES,
                                                        extra_dir=extra_dir)

        if ":" in strategy_name:
            logger.info("loading base64 encoded strategy")
            strat = strategy_name.split(":")

            if len(strat) == 2:
                temp = Path(tempfile.mkdtemp("freq", "strategy"))
                name = strat[0] + ".py"

                temp.joinpath(name).write_text(urlsafe_b64decode(strat[1]).decode('utf-8'))
                temp.joinpath("__init__.py").touch()

                strategy_name = strat[0]

                # register temp path with the bot
                abs_paths.insert(0, temp.resolve())

        strategy = StrategyResolver._load_object(paths=abs_paths,
                                                 object_name=strategy_name,
                                                 add_source=True,
                                                 kwargs={'config': config},
                                                 )
        if strategy:
            strategy._populate_fun_len = len(getfullargspec(strategy.populate_indicators).args)
            strategy._buy_fun_len = len(getfullargspec(strategy.populate_buy_trend).args)
            strategy._sell_fun_len = len(getfullargspec(strategy.populate_sell_trend).args)
            if any([x == 2 for x in [strategy._populate_fun_len,
                                     strategy._buy_fun_len,
                                     strategy._sell_fun_len]]):
                strategy.INTERFACE_VERSION = 1

            return strategy

        raise OperationalException(
            f"Impossible to load Strategy '{strategy_name}'. This class does not exist "
            "or contains Python code errors."
        )
Esempio n. 20
0
def copy_sample_files(directory: Path, overwrite: bool = False) -> None:
    """
    Copy files from templates to User data directory.
    :param directory: Directory to copy data to
    :param overwrite: Overwrite existing sample files
    """
    if not directory.is_dir():
        raise OperationalException(f"Directory `{directory}` does not exist.")
    sourcedir = Path(__file__).parents[1] / "templates"
    for source, target in USER_DATA_FILES.items():
        targetdir = directory / target
        if not targetdir.is_dir():
            raise OperationalException(
                f"Directory `{targetdir}` does not exist.")
        targetfile = targetdir / source
        if targetfile.exists():
            if not overwrite:
                logger.warning(
                    f"File `{targetfile}` exists already, not deploying sample file."
                )
                continue
            logger.warning(f"File `{targetfile}` exists already, overwriting.")
        shutil.copy(str(sourcedir / source), str(targetfile))
Esempio n. 21
0
def setup_logging(config: Dict[str, Any]) -> None:
    """
    Process -v/--verbose, --logfile options
    """
    # Log level
    verbosity = config['verbosity']

    # Log to stderr
    log_handlers: List[logging.Handler] = [logging.StreamHandler(sys.stderr)]

    logfile = config.get('logfile')
    if logfile:
        s = logfile.split(':')
        if s[0] == 'syslog':
            # Address can be either a string (socket filename) for Unix domain socket or
            # a tuple (hostname, port) for UDP socket.
            # Address can be omitted (i.e. simple 'syslog' used as the value of
            # config['logfilename']), which defaults to '/dev/log', applicable for most
            # of the systems.
            address = (s[1], int(s[2])) if len(s) > 2 else s[1] if len(s) > 1 else '/dev/log'
            handler = SysLogHandler(address=address)
            # No datetime field for logging into syslog, to allow syslog
            # to perform reduction of repeating messages if this is set in the
            # syslog config. The messages should be equal for this.
            handler.setFormatter(Formatter('%(name)s - %(levelname)s - %(message)s'))
            log_handlers.append(handler)
        elif s[0] == 'journald':
            try:
                from systemd.journal import JournaldLogHandler
            except ImportError:
                raise OperationalException("You need the systemd python package be installed in "
                                           "order to use logging to journald.")
            handler = JournaldLogHandler()
            # No datetime field for logging into journald, to allow syslog
            # to perform reduction of repeating messages if this is set in the
            # syslog config. The messages should be equal for this.
            handler.setFormatter(Formatter('%(name)s - %(levelname)s - %(message)s'))
            log_handlers.append(handler)
        else:
            log_handlers.append(RotatingFileHandler(logfile,
                                                    maxBytes=1024 * 1024,  # 1Mb
                                                    backupCount=10))

    logging.basicConfig(
        level=logging.INFO if verbosity < 1 else logging.DEBUG,
        format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
        handlers=log_handlers
    )
    _set_loggers(verbosity)
    logger.info('Verbosity set to %s', verbosity)
Esempio n. 22
0
    def get_fee(self, symbol: str, type: str = '', side: str = '', amount: float = 1,
                price: float = 1, taker_or_maker: str = 'maker') -> float:
        try:
            # validate that markets are loaded before trying to get fee
            if self._api.markets is None or len(self._api.markets) == 0:
                self._api.load_markets()

            return self._api.calculate_fee(symbol=symbol, type=type, side=side, amount=amount,
                                           price=price, takerOrMaker=taker_or_maker)['rate']
        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
            raise TemporaryError(
                f'Could not get fee info due to {e.__class__.__name__}. Message: {e}') from e
        except ccxt.BaseError as e:
            raise OperationalException(e) from e
Esempio n. 23
0
    def cancel_order(self, order_id: str, pair: str) -> None:
        if self._config['dry_run']:
            return

        try:
            return self._api.cancel_order(order_id, pair)
        except ccxt.InvalidOrder as e:
            raise InvalidOrderException(
                f'Could not cancel order. Message: {e}') from e
        except (ccxt.NetworkError, ccxt.ExchangeError) as e:
            raise TemporaryError(
                f'Could not cancel order due to {e.__class__.__name__}. Message: {e}') from e
        except ccxt.BaseError as e:
            raise OperationalException(e) from e
Esempio n. 24
0
    def __init__(self, exchange, pairlistmanager, config: Dict[str, Any],
                 pairlistconfig: Dict[str, Any], pairlist_pos: int) -> None:
        super().__init__(exchange, pairlistmanager, config, pairlistconfig,
                         pairlist_pos)

        self._low_price_ratio = pairlistconfig.get('low_price_ratio', 0)
        if self._low_price_ratio < 0:
            raise OperationalException(
                "PriceFilter requires low_price_ratio to be >= 0")
        self._min_price = pairlistconfig.get('min_price', 0)
        if self._min_price < 0:
            raise OperationalException(
                "PriceFilter requires min_price to be >= 0")
        self._max_price = pairlistconfig.get('max_price', 0)
        if self._max_price < 0:
            raise OperationalException(
                "PriceFilter requires max_price to be >= 0")
        self._max_value = pairlistconfig.get('max_value', 0)
        if self._max_value < 0:
            raise OperationalException(
                "PriceFilter requires max_value to be >= 0")
        self._enabled = ((self._low_price_ratio > 0) or (self._min_price > 0)
                         or (self._max_price > 0) or (self._max_value > 0))
Esempio n. 25
0
def load_config_file(path: str) -> Dict[str, Any]:
    """
    Loads a config file from the given path
    :param path: path as str
    :return: configuration as dictionary
    """
    try:
        # Read config from stdin if requested in the options
        with open(path) if path != '-' else sys.stdin as file:
            config = rapidjson.load(file, parse_mode=CONFIG_PARSE_MODE)
    except FileNotFoundError:
        raise OperationalException(
            f'Config file "{path}" not found!'
            ' Please create a config file or check whether it exists.')
    except rapidjson.JSONDecodeError as e:
        err_range = log_config_error_range(path, str(e))
        raise OperationalException(
            f'{e}\n'
            f'Please verify the following segment of your configuration:\n{err_range}'
            if err_range else
            'Please verify your configuration file for syntax errors.')

    return config
Esempio n. 26
0
    def __init__(self, config: Dict[str, Any], exchange, strategy) -> None:

        self.config = config
        self.exchange = exchange
        self.strategy: IStrategy = strategy

        self.edge_config = self.config.get('edge', {})
        self._cached_pairs: Dict[str, Any] = {}  # Keeps a list of pairs
        self._final_pairs: list = []

        # checking max_open_trades. it should be -1 as with Edge
        # the number of trades is determined by position size
        if self.config['max_open_trades'] != float('inf'):
            logger.critical('max_open_trades should be -1 in config !')

        if self.config['stake_amount'] != UNLIMITED_STAKE_AMOUNT:
            raise OperationalException(
                'Edge works only with unlimited stake amount')

        self._capital_ratio: float = self.config['tradable_balance_ratio']
        self._allowed_risk: float = self.edge_config.get('allowed_risk')
        self._since_number_of_days: int = self.edge_config.get(
            'calculate_since_number_of_days', 14)
        self._last_updated: int = 0  # Timestamp of pairs last updated time
        self._refresh_pairs = True

        self._stoploss_range_min = float(
            self.edge_config.get('stoploss_range_min', -0.01))
        self._stoploss_range_max = float(
            self.edge_config.get('stoploss_range_max', -0.05))
        self._stoploss_range_step = float(
            self.edge_config.get('stoploss_range_step', -0.001))

        # calculating stoploss range
        self._stoploss_range = np.arange(self._stoploss_range_min,
                                         self._stoploss_range_max,
                                         self._stoploss_range_step)

        self._timerange: TimeRange = TimeRange.parse_timerange(
            "%s-" % arrow.now().shift(
                days=-1 * self._since_number_of_days).format('YYYYMMDD'))
        if config.get('fee'):
            self.fee = config['fee']
        else:
            try:
                self.fee = self.exchange.get_fee(symbol=expand_pairlist(
                    self.config['exchange']['pair_whitelist'],
                    list(self.exchange.markets))[0])
            except IndexError:
                self.fee = None
Esempio n. 27
0
    def load_filtered_results(results_file: Path,
                              config: Dict[str, Any]) -> Tuple[List, int]:
        filteroptions = {
            'only_best':
            config.get('hyperopt_list_best', False),
            'only_profitable':
            config.get('hyperopt_list_profitable', False),
            'filter_min_trades':
            config.get('hyperopt_list_min_trades', 0),
            'filter_max_trades':
            config.get('hyperopt_list_max_trades', 0),
            'filter_min_avg_time':
            config.get('hyperopt_list_min_avg_time', None),
            'filter_max_avg_time':
            config.get('hyperopt_list_max_avg_time', None),
            'filter_min_avg_profit':
            config.get('hyperopt_list_min_avg_profit', None),
            'filter_max_avg_profit':
            config.get('hyperopt_list_max_avg_profit', None),
            'filter_min_total_profit':
            config.get('hyperopt_list_min_total_profit', None),
            'filter_max_total_profit':
            config.get('hyperopt_list_max_total_profit', None),
            'filter_min_objective':
            config.get('hyperopt_list_min_objective', None),
            'filter_max_objective':
            config.get('hyperopt_list_max_objective', None),
        }
        if not HyperoptTools._test_hyperopt_results_exist(results_file):
            # No file found.
            return [], 0

        epochs = []
        total_epochs = 0
        for epochs_tmp in HyperoptTools._read_results(results_file):
            if total_epochs == 0 and epochs_tmp[0].get('is_best') is None:
                raise OperationalException(
                    "The file with HyperoptTools results is incompatible with this version "
                    "of Freqtrade and cannot be loaded.")
            total_epochs += len(epochs_tmp)
            epochs += hyperopt_filter_epochs(epochs_tmp,
                                             filteroptions,
                                             log=False)

        logger.info(f"Loaded {total_epochs} previous evaluations from disk.")

        # Final filter run ...
        epochs = hyperopt_filter_epochs(epochs, filteroptions, log=True)

        return epochs, total_epochs
    def init_backtest_detail(self):
        # Load detail timeframe if specified
        self.timeframe_detail = str(self.config.get('timeframe_detail', ''))
        if self.timeframe_detail:
            self.timeframe_detail_min = timeframe_to_minutes(
                self.timeframe_detail)
            if self.timeframe_min <= self.timeframe_detail_min:
                raise OperationalException(
                    "Detail timeframe must be smaller than strategy timeframe."
                )

        else:
            self.timeframe_detail_min = 0
        self.detail_data: Dict[str, DataFrame] = {}
Esempio n. 29
0
 def __init__(self,
              low: Union[float, Sequence[float]],
              high: Optional[float] = None,
              *,
              default: float,
              space: Optional[str] = None,
              optimize: bool = True,
              load: bool = True,
              **kwargs):
     """
     Initialize hyperopt-optimizable floating point parameter with unlimited precision.
     :param low: Lower end (inclusive) of optimization space or [low, high].
     :param high: Upper end (inclusive) of optimization space.
                  Must be none if entire range is passed first parameter.
     :param default: A default value.
     :param space: A parameter category. Can be 'buy' or 'sell'. This parameter is optional if
                   parameter fieldname is prefixed with 'buy_' or 'sell_'.
     :param optimize: Include parameter in hyperopt optimizations.
     :param load: Load parameter value from {space}_params.
     :param kwargs: Extra parameters to skopt.space.Real.
     """
     if high is not None and isinstance(low, Sequence):
         raise OperationalException(
             f'{self.__class__.__name__} space invalid.')
     if high is None or isinstance(low, Sequence):
         if not isinstance(low, Sequence) or len(low) != 2:
             raise OperationalException(
                 f'{self.__class__.__name__} space must be [low, high]')
         opt_range = low
     else:
         opt_range = [low, high]
     super().__init__(opt_range=opt_range,
                      default=default,
                      space=space,
                      optimize=optimize,
                      load=load,
                      **kwargs)
Esempio n. 30
0
    def _init_ccxt(self,
                   exchange_config: dict,
                   ccxt_module=ccxt,
                   ccxt_kwargs: dict = None) -> ccxt.Exchange:
        """
        Initialize ccxt with given config and return valid
        ccxt instance.
        """
        # Find matching class for the given exchange name
        name = exchange_config['name']

        if not is_exchange_known_ccxt(name, ccxt_module):
            raise OperationalException(
                f'Exchange {name} is not supported by ccxt')

        ex_config = {
            'apiKey': exchange_config.get('key'),
            'secret': exchange_config.get('secret'),
            'password': exchange_config.get('password'),
            'uid': exchange_config.get('uid', ''),
        }
        if ccxt_kwargs:
            logger.info('Applying additional ccxt config: %s', ccxt_kwargs)
            ex_config.update(ccxt_kwargs)
        try:

            api = getattr(ccxt_module, name.lower())(ex_config)
        except (KeyError, AttributeError) as e:
            raise OperationalException(
                f'Exchange {name} is not supported') from e
        except ccxt.BaseError as e:
            raise OperationalException(
                f"Initialization of ccxt failed. Reason: {e}") from e

        self.set_sandbox(api, exchange_config, name)

        return api