exchange refactor
This commit is contained in:
parent
447bcf98e1
commit
845af384de
@ -6,12 +6,12 @@ Common Interface for bot and strategy to access data.
|
||||
"""
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import List, Tuple
|
||||
from typing import List, Optional, Tuple
|
||||
|
||||
from pandas import DataFrame
|
||||
|
||||
from freqtrade.data.history import load_pair_history
|
||||
from freqtrade.exchange import Exchange
|
||||
from freqtrade.exchange import Exchange, get_exchange
|
||||
from freqtrade.state import RunMode
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -19,9 +19,9 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
class DataProvider():
|
||||
|
||||
def __init__(self, config: dict, exchange: Exchange) -> None:
|
||||
def __init__(self, config: dict, exchange_name: Optional[str] = None) -> None:
|
||||
self._config = config
|
||||
self._exchange = exchange
|
||||
self._exchange: Exchange = get_exchange(config, exchange_name)
|
||||
|
||||
def refresh(self,
|
||||
pairlist: List[Tuple[str, str]],
|
||||
|
@ -1,4 +1,6 @@
|
||||
from freqtrade.exchange.exchange import Exchange # noqa: F401
|
||||
from freqtrade.exchange.exchange import (get_exchange, # noqa: F401
|
||||
delete_exchange)
|
||||
from freqtrade.exchange.exchange import (get_exchange_bad_reason, # noqa: F401
|
||||
is_exchange_bad,
|
||||
is_exchange_available,
|
||||
|
@ -21,6 +21,7 @@ from freqtrade import (DependencyException, InvalidOrderException,
|
||||
from freqtrade.data.converter import parse_ticker_dataframe
|
||||
from freqtrade.misc import deep_merge_dicts
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -772,6 +773,39 @@ class Exchange(object):
|
||||
raise OperationalException(e) from e
|
||||
|
||||
|
||||
# Store Exchange objects created
|
||||
__exchanges: Dict[str, Exchange] = {}
|
||||
|
||||
|
||||
def get_exchange(config, exchange_name: Optional[str] = None) -> Exchange:
|
||||
"""
|
||||
Return Exchange object for exchange with name `exchange_name`.
|
||||
Creates Exchange if it was not created before.
|
||||
"""
|
||||
from freqtrade.resolvers import ExchangeResolver
|
||||
|
||||
name = exchange_name or config['exchange']['name']
|
||||
if name not in __exchanges:
|
||||
logger.info(f"Creating Exchange object for '{name}'...")
|
||||
__exchanges[name] = ExchangeResolver(name, config).exchange
|
||||
logger.info(f"Exchange object for '{name}' created.")
|
||||
|
||||
return __exchanges[name]
|
||||
|
||||
|
||||
def delete_exchange(config, exchange_name: Optional[str] = None):
|
||||
"""
|
||||
Delete Exchange object for exchange with name `exchange_name`.
|
||||
"""
|
||||
name = exchange_name or config['exchange']['name']
|
||||
logger.info(f"Deleting Exchange object for '{name}'...")
|
||||
if name in __exchanges:
|
||||
del __exchanges[name]
|
||||
logger.info(f"Exchange object for '{name}' deleted.")
|
||||
else:
|
||||
logger.warning(f"Exchange object for '{name}' was not found, cannot be deleted.")
|
||||
|
||||
|
||||
def is_exchange_bad(exchange_name: str) -> bool:
|
||||
return exchange_name in BAD_EXCHANGES
|
||||
|
||||
|
@ -17,10 +17,10 @@ from freqtrade.data.converter import order_book_to_dataframe
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.edge import Edge
|
||||
from freqtrade.configuration import validate_config_consistency
|
||||
from freqtrade.exchange import timeframe_to_minutes, timeframe_to_next_date
|
||||
from freqtrade.exchange import Exchange, get_exchange, timeframe_to_minutes, timeframe_to_next_date
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.rpc import RPCManager, RPCMessageType
|
||||
from freqtrade.resolvers import ExchangeResolver, StrategyResolver, PairListResolver
|
||||
from freqtrade.resolvers import StrategyResolver, PairListResolver
|
||||
from freqtrade.state import State, RunMode
|
||||
from freqtrade.strategy.interface import SellType, IStrategy
|
||||
from freqtrade.wallets import Wallets
|
||||
@ -57,10 +57,10 @@ class FreqtradeBot(object):
|
||||
|
||||
self.rpc: RPCManager = RPCManager(self)
|
||||
|
||||
self.exchange = ExchangeResolver(self.config['exchange']['name'], self.config).exchange
|
||||
self.exchange: Exchange = get_exchange(self.config)
|
||||
|
||||
self.wallets = Wallets(self.config, self.exchange)
|
||||
self.dataprovider = DataProvider(self.config, self.exchange)
|
||||
self.wallets = Wallets(self.config)
|
||||
self.dataprovider = DataProvider(self.config)
|
||||
|
||||
# Attach Dataprovider to Strategy baseclass
|
||||
IStrategy.dp = self.dataprovider
|
||||
|
@ -15,10 +15,10 @@ from freqtrade import OperationalException
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.data import history
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.exchange import timeframe_to_minutes
|
||||
from freqtrade.exchange import Exchange, get_exchange, timeframe_to_minutes
|
||||
from freqtrade.misc import file_dump_json
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
|
||||
from freqtrade.resolvers import StrategyResolver
|
||||
from freqtrade.state import RunMode
|
||||
from freqtrade.strategy.interface import IStrategy, SellType
|
||||
from tabulate import tabulate
|
||||
@ -64,11 +64,11 @@ class Backtesting(object):
|
||||
self.config['dry_run'] = True
|
||||
self.strategylist: List[IStrategy] = []
|
||||
|
||||
self.exchange = ExchangeResolver(self.config['exchange']['name'], self.config).exchange
|
||||
self.fee = self.exchange.get_fee()
|
||||
exchange: Exchange = get_exchange(self.config)
|
||||
self.fee = exchange.get_fee()
|
||||
|
||||
if self.config.get('runmode') != RunMode.HYPEROPT:
|
||||
self.dataprovider = DataProvider(self.config, self.exchange)
|
||||
self.dataprovider = DataProvider(self.config)
|
||||
IStrategy.dp = self.dataprovider
|
||||
|
||||
if self.config.get('strategy_list', None):
|
||||
@ -406,12 +406,13 @@ class Backtesting(object):
|
||||
|
||||
timerange = TimeRange.parse_timerange(None if self.config.get(
|
||||
'timerange') is None else str(self.config.get('timerange')))
|
||||
exchange: Exchange = get_exchange(self.config)
|
||||
data = history.load_data(
|
||||
datadir=Path(self.config['datadir']) if self.config.get('datadir') else None,
|
||||
pairs=pairs,
|
||||
ticker_interval=self.ticker_interval,
|
||||
refresh_pairs=self.config.get('refresh_pairs', False),
|
||||
exchange=self.exchange,
|
||||
exchange=exchange,
|
||||
timerange=timerange,
|
||||
)
|
||||
|
||||
|
@ -10,7 +10,7 @@ from freqtrade import constants
|
||||
from freqtrade.edge import Edge
|
||||
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.exchange import Exchange
|
||||
from freqtrade.exchange import Exchange, get_exchange
|
||||
from freqtrade.resolvers import StrategyResolver
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -35,7 +35,7 @@ class EdgeCli(object):
|
||||
self.config['exchange']['uid'] = ''
|
||||
self.config['stake_amount'] = constants.UNLIMITED_STAKE_AMOUNT
|
||||
self.config['dry_run'] = True
|
||||
self.exchange = Exchange(self.config)
|
||||
self.exchange: Exchange = get_exchange(self.config)
|
||||
self.strategy = StrategyResolver(self.config).strategy
|
||||
|
||||
self.edge = Edge(config, self.exchange, self.strategy)
|
||||
|
@ -24,6 +24,7 @@ from skopt.space import Dimension
|
||||
|
||||
from freqtrade.configuration import TimeRange
|
||||
from freqtrade.data.history import load_data, get_timeframe
|
||||
from freqtrade.exchange import Exchange, get_exchange
|
||||
from freqtrade.optimize.backtesting import Backtesting
|
||||
# Import IHyperOptLoss to allow users import from this file
|
||||
from freqtrade.optimize.hyperopt_loss_interface import IHyperOptLoss # noqa: F4
|
||||
@ -342,12 +343,13 @@ class Hyperopt(Backtesting):
|
||||
def start(self) -> None:
|
||||
timerange = TimeRange.parse_timerange(None if self.config.get(
|
||||
'timerange') is None else str(self.config.get('timerange')))
|
||||
exchange: Exchange = get_exchange(self.config)
|
||||
data = load_data(
|
||||
datadir=Path(self.config['datadir']) if self.config.get('datadir') else None,
|
||||
pairs=self.config['exchange']['pair_whitelist'],
|
||||
ticker_interval=self.ticker_interval,
|
||||
refresh_pairs=self.config.get('refresh_pairs', False),
|
||||
exchange=self.exchange,
|
||||
exchange=exchange,
|
||||
timerange=timerange
|
||||
)
|
||||
|
||||
@ -371,9 +373,6 @@ class Hyperopt(Backtesting):
|
||||
|
||||
dump(preprocessed, self.tickerdata_pickle)
|
||||
|
||||
# We don't need exchange instance anymore while running hyperopt
|
||||
self.exchange = None # type: ignore
|
||||
|
||||
self.load_previous_results()
|
||||
|
||||
cpus = cpu_count()
|
||||
|
@ -1,6 +1,6 @@
|
||||
import logging
|
||||
from pathlib import Path
|
||||
from typing import Dict, List, Optional
|
||||
from typing import Dict, List
|
||||
|
||||
import pandas as pd
|
||||
|
||||
@ -8,8 +8,9 @@ from freqtrade.configuration import TimeRange
|
||||
from freqtrade.data import history
|
||||
from freqtrade.data.btanalysis import (combine_tickers_with_mean,
|
||||
create_cum_profit, load_trades)
|
||||
from freqtrade.exchange import Exchange
|
||||
from freqtrade.resolvers import ExchangeResolver, StrategyResolver
|
||||
from freqtrade.exchange import Exchange, get_exchange
|
||||
from freqtrade.resolvers import StrategyResolver
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@ -19,7 +20,7 @@ try:
|
||||
from plotly.offline import plot
|
||||
import plotly.graph_objects as go
|
||||
except ImportError:
|
||||
logger.exception("Module plotly not found \n Please install using `pip install plotly`")
|
||||
logger.exception("Module plotly not found.\nPlease install using `pip install plotly`.")
|
||||
exit(1)
|
||||
|
||||
|
||||
@ -28,12 +29,11 @@ def init_plotscript(config):
|
||||
Initialize objects needed for plotting
|
||||
:return: Dict with tickers, trades, pairs and strategy
|
||||
"""
|
||||
exchange: Optional[Exchange] = None
|
||||
exchange: Exchange = None
|
||||
|
||||
# Exchange is only needed when downloading data!
|
||||
if config.get("refresh_pairs", False):
|
||||
exchange = ExchangeResolver(config.get('exchange', {}).get('name'),
|
||||
config).exchange
|
||||
exchange = get_exchange(config)
|
||||
|
||||
strategy = StrategyResolver(config).strategy
|
||||
if "pairs" in config:
|
||||
|
@ -3,7 +3,6 @@ This module loads custom exchanges
|
||||
"""
|
||||
import logging
|
||||
|
||||
from freqtrade.exchange import Exchange
|
||||
import freqtrade.exchange as exchanges
|
||||
from freqtrade.resolvers import IResolver
|
||||
|
||||
@ -29,10 +28,10 @@ class ExchangeResolver(IResolver):
|
||||
logger.info(
|
||||
f"No {exchange_name} specific subclass found. Using the generic class instead.")
|
||||
if not hasattr(self, "exchange"):
|
||||
self.exchange = Exchange(config)
|
||||
self.exchange = exchanges.Exchange(config)
|
||||
|
||||
def _load_exchange(
|
||||
self, exchange_name: str, kwargs: dict) -> Exchange:
|
||||
self, exchange_name: str, kwargs: dict) -> exchanges.Exchange:
|
||||
"""
|
||||
Loads the specified exchange.
|
||||
Only checks for exchanges exported in freqtrade.exchanges
|
||||
|
@ -14,7 +14,7 @@ def test_ohlcv(mocker, default_conf, ticker_history):
|
||||
exchange._klines[("XRP/BTC", ticker_interval)] = ticker_history
|
||||
exchange._klines[("UNITTEST/BTC", ticker_interval)] = ticker_history
|
||||
|
||||
dp = DataProvider(default_conf, exchange)
|
||||
dp = DataProvider(default_conf)
|
||||
assert dp.runmode == RunMode.DRY_RUN
|
||||
assert ticker_history.equals(dp.ohlcv("UNITTEST/BTC", ticker_interval))
|
||||
assert isinstance(dp.ohlcv("UNITTEST/BTC", ticker_interval), DataFrame)
|
||||
@ -27,12 +27,12 @@ def test_ohlcv(mocker, default_conf, ticker_history):
|
||||
assert dp.ohlcv("UNITTEST/BTC", ticker_interval).equals(dp.ohlcv("UNITTEST/BTC"))
|
||||
|
||||
default_conf["runmode"] = RunMode.LIVE
|
||||
dp = DataProvider(default_conf, exchange)
|
||||
dp = DataProvider(default_conf)
|
||||
assert dp.runmode == RunMode.LIVE
|
||||
assert isinstance(dp.ohlcv("UNITTEST/BTC", ticker_interval), DataFrame)
|
||||
|
||||
default_conf["runmode"] = RunMode.BACKTEST
|
||||
dp = DataProvider(default_conf, exchange)
|
||||
dp = DataProvider(default_conf)
|
||||
assert dp.runmode == RunMode.BACKTEST
|
||||
assert dp.ohlcv("UNITTEST/BTC", ticker_interval).empty
|
||||
|
||||
@ -41,7 +41,7 @@ def test_historic_ohlcv(mocker, default_conf, ticker_history):
|
||||
historymock = MagicMock(return_value=ticker_history)
|
||||
mocker.patch("freqtrade.data.dataprovider.load_pair_history", historymock)
|
||||
|
||||
dp = DataProvider(default_conf, None)
|
||||
dp = DataProvider(default_conf)
|
||||
data = dp.historic_ohlcv("UNITTEST/BTC", "5m")
|
||||
assert isinstance(data, DataFrame)
|
||||
assert historymock.call_count == 1
|
||||
@ -57,7 +57,7 @@ def test_get_pair_dataframe(mocker, default_conf, ticker_history):
|
||||
exchange._klines[("XRP/BTC", ticker_interval)] = ticker_history
|
||||
exchange._klines[("UNITTEST/BTC", ticker_interval)] = ticker_history
|
||||
|
||||
dp = DataProvider(default_conf, exchange)
|
||||
dp = DataProvider(default_conf)
|
||||
assert dp.runmode == RunMode.DRY_RUN
|
||||
assert ticker_history.equals(dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval))
|
||||
assert isinstance(dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval), DataFrame)
|
||||
@ -70,7 +70,7 @@ def test_get_pair_dataframe(mocker, default_conf, ticker_history):
|
||||
ticker_interval).equals(dp.get_pair_dataframe("UNITTEST/BTC"))
|
||||
|
||||
default_conf["runmode"] = RunMode.LIVE
|
||||
dp = DataProvider(default_conf, exchange)
|
||||
dp = DataProvider(default_conf)
|
||||
assert dp.runmode == RunMode.LIVE
|
||||
assert isinstance(dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval), DataFrame)
|
||||
assert dp.get_pair_dataframe("NONESENSE/AAA", ticker_interval).empty
|
||||
@ -78,7 +78,7 @@ def test_get_pair_dataframe(mocker, default_conf, ticker_history):
|
||||
historymock = MagicMock(return_value=ticker_history)
|
||||
mocker.patch("freqtrade.data.dataprovider.load_pair_history", historymock)
|
||||
default_conf["runmode"] = RunMode.BACKTEST
|
||||
dp = DataProvider(default_conf, exchange)
|
||||
dp = DataProvider(default_conf)
|
||||
assert dp.runmode == RunMode.BACKTEST
|
||||
assert isinstance(dp.get_pair_dataframe("UNITTEST/BTC", ticker_interval), DataFrame)
|
||||
# assert dp.get_pair_dataframe("NONESENSE/AAA", ticker_interval).empty
|
||||
@ -90,7 +90,7 @@ def test_available_pairs(mocker, default_conf, ticker_history):
|
||||
exchange._klines[("XRP/BTC", ticker_interval)] = ticker_history
|
||||
exchange._klines[("UNITTEST/BTC", ticker_interval)] = ticker_history
|
||||
|
||||
dp = DataProvider(default_conf, exchange)
|
||||
dp = DataProvider(default_conf)
|
||||
assert len(dp.available_pairs) == 2
|
||||
assert dp.available_pairs == [
|
||||
("XRP/BTC", ticker_interval),
|
||||
@ -108,7 +108,7 @@ def test_refresh(mocker, default_conf, ticker_history):
|
||||
|
||||
pairs_non_trad = [("ETH/USDT", ticker_interval), ("BTC/TUSD", "1h")]
|
||||
|
||||
dp = DataProvider(default_conf, exchange)
|
||||
dp = DataProvider(default_conf)
|
||||
dp.refresh(pairs)
|
||||
|
||||
assert refresh_mock.call_count == 1
|
||||
|
@ -9,8 +9,7 @@ import arrow
|
||||
from freqtrade.configuration import Configuration, TimeRange
|
||||
from freqtrade.configuration.directory_operations import create_userdata_dir
|
||||
from freqtrade.data.history import download_pair_history
|
||||
from freqtrade.exchange import available_exchanges
|
||||
from freqtrade.resolvers import ExchangeResolver
|
||||
from freqtrade.exchange import Exchange, get_exchange, available_exchanges
|
||||
from freqtrade.state import RunMode
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -79,7 +78,7 @@ def start_download_data(args: Namespace) -> None:
|
||||
|
||||
try:
|
||||
# Init exchange
|
||||
exchange = ExchangeResolver(config['exchange']['name'], config).exchange
|
||||
exchange: Exchange = get_exchange(config)
|
||||
|
||||
for pair in config["pairs"]:
|
||||
if pair not in exchange.markets:
|
||||
|
@ -2,8 +2,8 @@
|
||||
""" Wallet """
|
||||
|
||||
import logging
|
||||
from typing import Dict, NamedTuple
|
||||
from freqtrade.exchange import Exchange
|
||||
from typing import Dict, NamedTuple, Optional
|
||||
from freqtrade.exchange import Exchange, get_exchange
|
||||
from freqtrade import constants
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@ -19,9 +19,9 @@ class Wallet(NamedTuple):
|
||||
|
||||
class Wallets(object):
|
||||
|
||||
def __init__(self, config: dict, exchange: Exchange) -> None:
|
||||
def __init__(self, config: dict, exchange_name: Optional[str] = None) -> None:
|
||||
self._config = config
|
||||
self._exchange = exchange
|
||||
self._exchange_name = exchange_name
|
||||
self._wallets: Dict[str, Wallet] = {}
|
||||
|
||||
self.update()
|
||||
@ -61,7 +61,8 @@ class Wallets(object):
|
||||
|
||||
def update(self) -> None:
|
||||
|
||||
balances = self._exchange.get_balances()
|
||||
exchange: Exchange = get_exchange(self._config, self._exchange_name)
|
||||
balances = exchange.get_balances()
|
||||
|
||||
for currency in balances:
|
||||
self._wallets[currency] = Wallet(
|
||||
|
Loading…
Reference in New Issue
Block a user