From 9fbe573ccadfdb81f97c6790419eac442b743ca9 Mon Sep 17 00:00:00 2001 From: hroff-1902 Date: Tue, 9 Apr 2019 12:27:35 +0300 Subject: [PATCH] limit usage of ccxt to freqtrade/exchange only --- freqtrade/configuration.py | 8 +++-- freqtrade/data/converter.py | 4 ++- freqtrade/data/history.py | 7 ++-- freqtrade/exchange/__init__.py | 5 +++ freqtrade/exchange/exchange.py | 39 +++++++++++++++++++++-- freqtrade/freqtradebot.py | 4 +-- freqtrade/misc.py | 26 +-------------- freqtrade/optimize/backtesting.py | 4 ++- freqtrade/strategy/__init__.py | 3 +- freqtrade/strategy/interface.py | 3 +- freqtrade/tests/optimize/__init__.py | 2 +- freqtrade/tests/optimize/test_optimize.py | 2 +- scripts/get_market_pairs.py | 10 +++--- scripts/plot_profit.py | 4 ++- 14 files changed, 71 insertions(+), 50 deletions(-) diff --git a/freqtrade/configuration.py b/freqtrade/configuration.py index fdd71f2f5..e08969a7e 100644 --- a/freqtrade/configuration.py +++ b/freqtrade/configuration.py @@ -9,14 +9,15 @@ from argparse import Namespace from logging.handlers import RotatingFileHandler from typing import Any, Dict, List, Optional -import ccxt from jsonschema import Draft4Validator, validate from jsonschema.exceptions import ValidationError, best_match from freqtrade import OperationalException, constants +from freqtrade.exchange import is_exchange_supported, supported_exchanges from freqtrade.misc import deep_merge_dicts from freqtrade.state import RunMode + logger = logging.getLogger(__name__) @@ -374,10 +375,11 @@ class Configuration(object): :return: True or raised an exception if the exchange if not supported """ exchange = config.get('exchange', {}).get('name').lower() - if exchange not in ccxt.exchanges: + if not is_exchange_supported(exchange): exception_msg = f'Exchange "{exchange}" not supported.\n' \ - f'The following exchanges are supported: {", ".join(ccxt.exchanges)}' + f'The following exchanges are supported: ' \ + f'{", ".join(supported_exchanges())}' logger.critical(exception_msg) raise OperationalException( diff --git a/freqtrade/data/converter.py b/freqtrade/data/converter.py index 28749293b..77a3447da 100644 --- a/freqtrade/data/converter.py +++ b/freqtrade/data/converter.py @@ -2,9 +2,9 @@ Functions to convert data from one format to another """ import logging + import pandas as pd from pandas import DataFrame, to_datetime -from freqtrade.misc import timeframe_to_minutes logger = logging.getLogger(__name__) @@ -58,6 +58,8 @@ def ohlcv_fill_up_missing_data(dataframe: DataFrame, ticker_interval: str) -> Da using the previous close as price for "open", "high" "low" and "close", volume is set to 0 """ + from freqtrade.exchange import timeframe_to_minutes + ohlc_dict = { 'open': 'first', 'high': 'max', diff --git a/freqtrade/data/history.py b/freqtrade/data/history.py index 594c85b5f..07a7f0b66 100644 --- a/freqtrade/data/history.py +++ b/freqtrade/data/history.py @@ -1,10 +1,10 @@ """ Handle historic data (ohlcv). -includes: + +Includes: * load data for a pair (or a list of pairs) from disk * download data from exchange and store to disk """ - import logging from pathlib import Path from typing import Optional, List, Dict, Tuple, Any @@ -15,8 +15,7 @@ from pandas import DataFrame from freqtrade import misc, OperationalException from freqtrade.arguments import TimeRange from freqtrade.data.converter import parse_ticker_dataframe -from freqtrade.exchange import Exchange -from freqtrade.misc import timeframe_to_minutes +from freqtrade.exchange import Exchange, timeframe_to_minutes logger = logging.getLogger(__name__) diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index f6db04da6..3c90e69ee 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -1,3 +1,8 @@ from freqtrade.exchange.exchange import Exchange # noqa: F401 +from freqtrade.exchange.exchange import (is_exchange_supported, # noqa: F401 + supported_exchanges) +from freqtrade.exchange.exchange import (timeframe_to_seconds, # noqa: F401 + timeframe_to_minutes, + timeframe_to_msecs) from freqtrade.exchange.kraken import Kraken # noqa: F401 from freqtrade.exchange.binance import Binance # noqa: F401 diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index ffd574f90..dc0c197c0 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -1,5 +1,7 @@ # pragma pylint: disable=W0603 -""" Cryptocurrency Exchanges support """ +""" +Cryptocurrency Exchanges support +""" import logging import inspect from random import randint @@ -16,7 +18,6 @@ from pandas import DataFrame from freqtrade import (constants, DependencyException, OperationalException, TemporaryError, InvalidOrderException) from freqtrade.data.converter import parse_ticker_dataframe -from freqtrade.misc import timeframe_to_seconds, timeframe_to_msecs logger = logging.getLogger(__name__) @@ -138,7 +139,7 @@ class Exchange(object): # Find matching class for the given exchange name name = exchange_config['name'] - if name not in ccxt_module.exchanges: + if not is_exchange_supported(name, ccxt_module): raise OperationalException(f'Exchange {name} is not supported') ex_config = { @@ -690,3 +691,35 @@ class Exchange(object): f'Could not get fee info due to {e.__class__.__name__}. Message: {e}') except ccxt.BaseError as e: raise OperationalException(e) + + +def is_exchange_supported(exchange: str, ccxt_module=None) -> bool: + return exchange in supported_exchanges(ccxt_module) + + +def supported_exchanges(ccxt_module=None) -> str: + exchanges = ccxt_module.exchanges if ccxt_module is not None else ccxt.exchanges + return exchanges + + +def timeframe_to_seconds(ticker_interval: str) -> int: + """ + Translates the timeframe interval value written in the human readable + form ('1m', '5m', '1h', '1d', '1w', etc.) to the number + of seconds for one timeframe interval. + """ + return ccxt.Exchange.parse_timeframe(ticker_interval) + + +def timeframe_to_minutes(ticker_interval: str) -> int: + """ + Same as above, but returns minutes. + """ + return ccxt.Exchange.parse_timeframe(ticker_interval) // 60 + + +def timeframe_to_msecs(ticker_interval: str) -> int: + """ + Same as above, but returns milliseconds. + """ + return ccxt.Exchange.parse_timeframe(ticker_interval) * 1000 diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index a9676a64e..038510755 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -16,7 +16,7 @@ from freqtrade import (DependencyException, OperationalException, InvalidOrderEx from freqtrade.data.converter import order_book_to_dataframe from freqtrade.data.dataprovider import DataProvider from freqtrade.edge import Edge -from freqtrade.misc import timeframe_to_minutes +from freqtrade.exchange import timeframe_to_minutes from freqtrade.persistence import Trade from freqtrade.rpc import RPCManager, RPCMessageType from freqtrade.resolvers import ExchangeResolver, StrategyResolver, PairListResolver @@ -460,7 +460,7 @@ class FreqtradeBot(object): def get_real_amount(self, trade: Trade, order: Dict) -> float: """ Get real amount for the trade - Necessary for self.exchanges which charge fees in base currency (e.g. binance) + Necessary for exchanges which charge fees in base currency (e.g. binance) """ order_amount = order['amount'] # Only run for closed orders diff --git a/freqtrade/misc.py b/freqtrade/misc.py index d066878be..9d37214e4 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -1,18 +1,17 @@ """ Various tool function for Freqtrade and scripts """ - import gzip import logging import re from datetime import datetime from typing import Dict -from ccxt import Exchange import numpy as np from pandas import DataFrame import rapidjson + logger = logging.getLogger(__name__) @@ -132,26 +131,3 @@ def deep_merge_dicts(source, destination): destination[key] = value return destination - - -def timeframe_to_seconds(ticker_interval: str) -> int: - """ - Translates the timeframe interval value written in the human readable - form ('1m', '5m', '1h', '1d', '1w', etc.) to the number - of seconds for one timeframe interval. - """ - return Exchange.parse_timeframe(ticker_interval) - - -def timeframe_to_minutes(ticker_interval: str) -> int: - """ - Same as above, but returns minutes. - """ - return Exchange.parse_timeframe(ticker_interval) // 60 - - -def timeframe_to_msecs(ticker_interval: str) -> int: - """ - Same as above, but returns milliseconds. - """ - return Exchange.parse_timeframe(ticker_interval) * 1000 diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 3e4d642cb..9ee1cd5cd 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -19,12 +19,14 @@ from freqtrade.arguments import Arguments from freqtrade.configuration import Configuration from freqtrade.data import history from freqtrade.data.dataprovider import DataProvider -from freqtrade.misc import file_dump_json, timeframe_to_minutes +from freqtrade.exchange import timeframe_to_minutes +from freqtrade.misc import file_dump_json from freqtrade.persistence import Trade from freqtrade.resolvers import ExchangeResolver, StrategyResolver from freqtrade.state import RunMode from freqtrade.strategy.interface import SellType, IStrategy + logger = logging.getLogger(__name__) diff --git a/freqtrade/strategy/__init__.py b/freqtrade/strategy/__init__.py index b29e26ef9..c62bfe5dc 100644 --- a/freqtrade/strategy/__init__.py +++ b/freqtrade/strategy/__init__.py @@ -6,6 +6,7 @@ from freqtrade.strategy.interface import IStrategy # Import Default-Strategy to have hyperopt correctly resolve from freqtrade.strategy.default_strategy import DefaultStrategy # noqa: F401 + logger = logging.getLogger(__name__) @@ -16,7 +17,6 @@ def import_strategy(strategy: IStrategy, config: dict) -> IStrategy: """ # Copy all attributes from base class and class - comb = {**strategy.__class__.__dict__, **strategy.__dict__} # Delete '_abc_impl' from dict as deepcopy fails on 3.7 with @@ -26,6 +26,7 @@ def import_strategy(strategy: IStrategy, config: dict) -> IStrategy: del comb['_abc_impl'] attr = deepcopy(comb) + # Adjust module name attr['__module__'] = 'freqtrade.strategy' diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 646bd2a94..caf56f13e 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -13,10 +13,11 @@ import arrow from pandas import DataFrame from freqtrade.data.dataprovider import DataProvider -from freqtrade.misc import timeframe_to_minutes +from freqtrade.exchange import timeframe_to_minutes from freqtrade.persistence import Trade from freqtrade.wallets import Wallets + logger = logging.getLogger(__name__) diff --git a/freqtrade/tests/optimize/__init__.py b/freqtrade/tests/optimize/__init__.py index 227050770..457113cb7 100644 --- a/freqtrade/tests/optimize/__init__.py +++ b/freqtrade/tests/optimize/__init__.py @@ -3,7 +3,7 @@ from typing import NamedTuple, List import arrow from pandas import DataFrame -from freqtrade.misc import timeframe_to_minutes +from freqtrade.exchange import timeframe_to_minutes from freqtrade.strategy.interface import SellType ticker_start_time = arrow.get(2018, 10, 3) diff --git a/freqtrade/tests/optimize/test_optimize.py b/freqtrade/tests/optimize/test_optimize.py index 088743038..d746aa44f 100644 --- a/freqtrade/tests/optimize/test_optimize.py +++ b/freqtrade/tests/optimize/test_optimize.py @@ -2,7 +2,7 @@ from freqtrade import optimize from freqtrade.arguments import TimeRange from freqtrade.data import history -from freqtrade.misc import timeframe_to_minutes +from freqtrade.exchange import timeframe_to_minutes from freqtrade.strategy.default_strategy import DefaultStrategy from freqtrade.tests.conftest import log_has, patch_exchange diff --git a/scripts/get_market_pairs.py b/scripts/get_market_pairs.py index 1a4c593f9..00639c50b 100644 --- a/scripts/get_market_pairs.py +++ b/scripts/get_market_pairs.py @@ -1,6 +1,8 @@ import os import sys +from freqtrade.exchange import is_exchange_supported, supported_exchanges + root = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) sys.path.append(root + '/python') @@ -44,7 +46,7 @@ def dump(*args): def print_supported_exchanges(): - dump('Supported exchanges:', green(', '.join(ccxt.exchanges))) + dump('Supported exchanges:', green(', '.join(supported_exchanges()))) try: @@ -52,9 +54,7 @@ try: id = sys.argv[1] # get exchange id from command line arguments # check if the exchange is supported by ccxt - exchange_found = id in ccxt.exchanges - - if exchange_found: + if is_exchange_supported(id): dump('Instantiating', green(id), 'exchange') # instantiate the exchange by id @@ -79,9 +79,7 @@ try: for (k, v) in tuples: dump('{:<15} {:<15} {:<15} {:<15}'.format(v['id'], v['symbol'], v['base'], v['quote'])) - else: - dump('Exchange ' + red(id) + ' not found') print_supported_exchanges() diff --git a/scripts/plot_profit.py b/scripts/plot_profit.py index 500d9fcde..5f7d42c87 100755 --- a/scripts/plot_profit.py +++ b/scripts/plot_profit.py @@ -27,10 +27,12 @@ from plotly.offline import plot from freqtrade.arguments import Arguments from freqtrade.configuration import Configuration from freqtrade.data import history -from freqtrade.misc import common_datearray, timeframe_to_seconds +from freqtrade.exchange import timeframe_to_seconds +from freqtrade.misc import common_datearray from freqtrade.resolvers import StrategyResolver from freqtrade.state import RunMode + logger = logging.getLogger(__name__)