Repository: freqtrade/freqtrade

Issue:
Exchange.__del__() fails when called on a semi-initialized object
Ccxt exchange object constructor called in `Exchange._init_ccxt()` may raise an exception. Thus, the `Exchange.__del__()` method is called on the semi-initialized object and fails.

Example:
Set exchange name to 'theocean' in the config and run the bot:
```
$ ./freqtrade/main.py -c config_theocean.json 
2019-07-03 04:15:14,787 - freqtrade.configuration - INFO - Verbosity set to 0
2019-07-03 04:15:14,787 - freqtrade.configuration - INFO - Dry run is enabled
2019-07-03 04:15:14,787 - freqtrade.configuration - INFO - Using DB: "sqlite://"
2019-07-03 04:15:14,787 - freqtrade.configuration - INFO - Checking exchange...
2019-07-03 04:15:14,787 - freqtrade.configuration - WARNING - Exchange "theocean" is supported by ccxt and therefore available for the bot but not officially supported by the Freqtrade development team. It may work flawlessly (please report back) or have serious issues. Use it at your own discretion.
2019-07-03 04:15:14,787 - freqtrade.configuration - INFO - Using max_open_trades: 3 ...
2019-07-03 04:15:14,787 - freqtrade.configuration - INFO - Using data folder: user_data/data/theocean ...
2019-07-03 04:15:14,788 - freqtrade.freqtradebot - INFO - Starting freqtrade 2019.6-dev
2019-07-03 04:15:14,792 - freqtrade.resolvers.strategy_resolver - INFO - Using resolved strategy DefaultStrategy from '/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/freqtrade/strategy'
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Override strategy 'ticker_interval' with value in config file: 5m.
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Override strategy 'trailing_stop' with value in config file: False.
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Override strategy 'stake_currency' with value in config file: BTC.
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Override strategy 'stake_amount' with value in config file: 0.05.
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Override strategy 'use_sell_signal' with value in config file: False.
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Override strategy 'sell_profit_only' with value in config file: False.
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Override strategy 'ignore_roi_if_buy_signal' with value in config file: False.
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using minimal_roi: {'40': 0.0, '30': 0.01, '20': 0.02, '0': 0.04}
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using ticker_interval: 5m
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using stoploss: -0.1
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using trailing_stop: False
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using trailing_stop_positive_offset: 0.0
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using trailing_only_offset_is_reached: False
2019-07-03 04:15:14,794 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using process_only_new_candles: False
2019-07-03 04:15:14,794 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using order_types: {'buy': 'limit', 'sell': 'limit', 'stoploss': 'limit', 'stoploss_on_exchange': False}
2019-07-03 04:15:14,794 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using order_time_in_force: {'buy': 'gtc', 'sell': 'gtc'}
2019-07-03 04:15:14,794 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using stake_currency: BTC
2019-07-03 04:15:14,794 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using stake_amount: 0.05
2019-07-03 04:15:14,794 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using use_sell_signal: False
2019-07-03 04:15:14,794 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using sell_profit_only: False
2019-07-03 04:15:14,794 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using ignore_roi_if_buy_signal: False
2019-07-03 04:15:14,794 - freqtrade.resolvers.exchange_resolver - INFO - No Theocean specific subclass found. Using the generic class instead.
2019-07-03 04:15:14,794 - freqtrade.exchange.exchange - INFO - Instance is running with dry_run enabled
2019-07-03 04:15:14,794 - freqtrade.exchange.exchange - INFO - Applying additional ccxt config: {'enableRateLimit': True}
2019-07-03 04:15:14,794 - freqtrade - ERROR - Fatal exception!
Traceback (most recent call last):
  File "./freqtrade/main.py", line 51, in main
    worker = Worker(args)
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/freqtrade/worker.py", line 35, in __init__
    self._init(False)
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/freqtrade/worker.py", line 51, in _init
    self.freqtrade = FreqtradeBot(self._config)
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/freqtrade/freqtradebot.py", line 56, in __init__
    self.exchange = ExchangeResolver(self.config['exchange']['name'], self.config).exchange
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/freqtrade/resolvers/exchange_resolver.py", line 32, in __init__
    self.exchange = Exchange(config)
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/freqtrade/exchange/exchange.py", line 121, in __init__
    exchange_config, ccxt_kwargs=exchange_config.get('ccxt_config'))
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/freqtrade/exchange/exchange.py", line 173, in _init_ccxt
    api = getattr(ccxt_module, name.lower())(ex_config)
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/.env/lib/python3.6/site-packages/ccxt/base/exchange.py", line 330, in __init__
    settings = self.deep_extend(self.describe(), config)
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/.env/lib/python3.6/site-packages/ccxt/theocean.py", line 25, in describe
    self.check_required_dependencies()
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/.env/lib/python3.6/site-packages/ccxt/base/exchange.py", line 1638, in check_required_dependencies
    raise NotSupported("Web3 functionality requires Python3 and web3 package installed: https://github.com/ethereum/web3.py")
ccxt.base.errors.NotSupported: Web3 functionality requires Python3 and web3 package installed: https://github.com/ethereum/web3.py
Exception ignored in: <bound method Exchange.__del__ of <freqtrade.exchange.exchange.Exchange object at 0x7f0021ea0080>>
Traceback (most recent call last):
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/freqtrade/exchange/exchange.py", line 147, in __del__
    if self._api_async and inspect.iscoroutinefunction(self._api_async.close):
AttributeError: 'Exchange' object has no attribute '_api_async'
```

Exchange.__del__() fails when called on a semi-initialized object
Ccxt exchange object constructor called in `Exchange._init_ccxt()` may raise an exception. Thus, the `Exchange.__del__()` method is called on the semi-initialized object and fails.

Example:
Set exchange name to 'theocean' in the config and run the bot:
```
$ ./freqtrade/main.py -c config_theocean.json 
2019-07-03 04:15:14,787 - freqtrade.configuration - INFO - Verbosity set to 0
2019-07-03 04:15:14,787 - freqtrade.configuration - INFO - Dry run is enabled
2019-07-03 04:15:14,787 - freqtrade.configuration - INFO - Using DB: "sqlite://"
2019-07-03 04:15:14,787 - freqtrade.configuration - INFO - Checking exchange...
2019-07-03 04:15:14,787 - freqtrade.configuration - WARNING - Exchange "theocean" is supported by ccxt and therefore available for the bot but not officially supported by the Freqtrade development team. It may work flawlessly (please report back) or have serious issues. Use it at your own discretion.
2019-07-03 04:15:14,787 - freqtrade.configuration - INFO - Using max_open_trades: 3 ...
2019-07-03 04:15:14,787 - freqtrade.configuration - INFO - Using data folder: user_data/data/theocean ...
2019-07-03 04:15:14,788 - freqtrade.freqtradebot - INFO - Starting freqtrade 2019.6-dev
2019-07-03 04:15:14,792 - freqtrade.resolvers.strategy_resolver - INFO - Using resolved strategy DefaultStrategy from '/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/freqtrade/strategy'
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Override strategy 'ticker_interval' with value in config file: 5m.
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Override strategy 'trailing_stop' with value in config file: False.
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Override strategy 'stake_currency' with value in config file: BTC.
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Override strategy 'stake_amount' with value in config file: 0.05.
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Override strategy 'use_sell_signal' with value in config file: False.
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Override strategy 'sell_profit_only' with value in config file: False.
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Override strategy 'ignore_roi_if_buy_signal' with value in config file: False.
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using minimal_roi: {'40': 0.0, '30': 0.01, '20': 0.02, '0': 0.04}
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using ticker_interval: 5m
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using stoploss: -0.1
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using trailing_stop: False
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using trailing_stop_positive_offset: 0.0
2019-07-03 04:15:14,793 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using trailing_only_offset_is_reached: False
2019-07-03 04:15:14,794 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using process_only_new_candles: False
2019-07-03 04:15:14,794 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using order_types: {'buy': 'limit', 'sell': 'limit', 'stoploss': 'limit', 'stoploss_on_exchange': False}
2019-07-03 04:15:14,794 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using order_time_in_force: {'buy': 'gtc', 'sell': 'gtc'}
2019-07-03 04:15:14,794 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using stake_currency: BTC
2019-07-03 04:15:14,794 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using stake_amount: 0.05
2019-07-03 04:15:14,794 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using use_sell_signal: False
2019-07-03 04:15:14,794 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using sell_profit_only: False
2019-07-03 04:15:14,794 - freqtrade.resolvers.strategy_resolver - INFO - Strategy using ignore_roi_if_buy_signal: False
2019-07-03 04:15:14,794 - freqtrade.resolvers.exchange_resolver - INFO - No Theocean specific subclass found. Using the generic class instead.
2019-07-03 04:15:14,794 - freqtrade.exchange.exchange - INFO - Instance is running with dry_run enabled
2019-07-03 04:15:14,794 - freqtrade.exchange.exchange - INFO - Applying additional ccxt config: {'enableRateLimit': True}
2019-07-03 04:15:14,794 - freqtrade - ERROR - Fatal exception!
Traceback (most recent call last):
  File "./freqtrade/main.py", line 51, in main
    worker = Worker(args)
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/freqtrade/worker.py", line 35, in __init__
    self._init(False)
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/freqtrade/worker.py", line 51, in _init
    self.freqtrade = FreqtradeBot(self._config)
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/freqtrade/freqtradebot.py", line 56, in __init__
    self.exchange = ExchangeResolver(self.config['exchange']['name'], self.config).exchange
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/freqtrade/resolvers/exchange_resolver.py", line 32, in __init__
    self.exchange = Exchange(config)
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/freqtrade/exchange/exchange.py", line 121, in __init__
    exchange_config, ccxt_kwargs=exchange_config.get('ccxt_config'))
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/freqtrade/exchange/exchange.py", line 173, in _init_ccxt
    api = getattr(ccxt_module, name.lower())(ex_config)
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/.env/lib/python3.6/site-packages/ccxt/base/exchange.py", line 330, in __init__
    settings = self.deep_extend(self.describe(), config)
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/.env/lib/python3.6/site-packages/ccxt/theocean.py", line 25, in describe
    self.check_required_dependencies()
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/.env/lib/python3.6/site-packages/ccxt/base/exchange.py", line 1638, in check_required_dependencies
    raise NotSupported("Web3 functionality requires Python3 and web3 package installed: https://github.com/ethereum/web3.py")
ccxt.base.errors.NotSupported: Web3 functionality requires Python3 and web3 package installed: https://github.com/ethereum/web3.py
Exception ignored in: <bound method Exchange.__del__ of <freqtrade.exchange.exchange.Exchange object at 0x7f0021ea0080>>
Traceback (most recent call last):
  File "/home/user/freqtrade-wrk/github-hroff-1902/freqtrade/freqtrade/exchange/exchange.py", line 147, in __del__
    if self._api_async and inspect.iscoroutinefunction(self._api_async.close):
AttributeError: 'Exchange' object has no attribute '_api_async'
```



Discussion:

Resolving patch:
diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py
--- a/freqtrade/exchange/exchange.py
+++ b/freqtrade/exchange/exchange.py
@@ -85,6 +85,9 @@ def __init__(self, config: dict) -> None:
         it does basic validation whether the specified exchange and pairs are valid.
         :return: None
         """
+        self._api: ccxt.Exchange = None
+        self._api_async: ccxt_async.Exchange = None
+
         self._config.update(config)
 
         self._cached_ticker: Dict[str, Any] = {}
@@ -117,9 +120,9 @@ def __init__(self, config: dict) -> None:
         self._ohlcv_partial_candle = self._ft_has['ohlcv_partial_candle']
 
         # Initialize ccxt objects
-        self._api: ccxt.Exchange = self._init_ccxt(
+        self._api = self._init_ccxt(
             exchange_config, ccxt_kwargs=exchange_config.get('ccxt_config'))
-        self._api_async: ccxt_async.Exchange = self._init_ccxt(
+        self._api_async = self._init_ccxt(
             exchange_config, ccxt_async, ccxt_kwargs=exchange_config.get('ccxt_async_config'))
 
         logger.info('Using Exchange "%s"', self.name)
@@ -173,6 +176,8 @@ def _init_ccxt(self, exchange_config: dict, ccxt_module=ccxt,
             api = getattr(ccxt_module, name.lower())(ex_config)
         except (KeyError, AttributeError):
             raise OperationalException(f'Exchange {name} is not supported')
+        except ccxt.BaseError as e:
+            raise OperationalException(f"Initialization of ccxt failed. Reason: {e}")
 
         self.set_sandbox(api, exchange_config, name)
 


Test patch: