diff --git a/config.json.example b/config.json.example index 2c1ef4fd0..4ab4f2c72 100644 --- a/config.json.example +++ b/config.json.example @@ -28,7 +28,10 @@ "name": "bittrex", "key": "your_exchange_key", "secret": "your_exchange_secret", - "ccxt_rate_limit": true, + "ccxt_config": {"enableRateLimit": true}, + "ccxt_async_config": { + "enableRateLimit": false + }, "pair_whitelist": [ "ETH/BTC", "LTC/BTC", diff --git a/config_full.json.example b/config_full.json.example index 8541d984b..e8a3417df 100644 --- a/config_full.json.example +++ b/config_full.json.example @@ -37,7 +37,11 @@ "name": "bittrex", "key": "your_exchange_key", "secret": "your_exchange_secret", - "ccxt_rate_limit": true, + "ccxt_config": {"enableRateLimit": true}, + "ccxt_async_config": { + "enableRateLimit": false, + "aiohttp_trust_env": false + }, "pair_whitelist": [ "ETH/BTC", "LTC/BTC", diff --git a/docs/configuration.md b/docs/configuration.md index 010e693d4..3512f891b 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -44,7 +44,9 @@ The table below will list all configuration parameters. | `exchange.secret` | secret | No | API secret to use for the exchange. Only required when you are in production mode. | `exchange.pair_whitelist` | [] | No | List of currency to use by the bot. Can be overrided with `--dynamic-whitelist` param. | `exchange.pair_blacklist` | [] | No | List of currency the bot must avoid. Useful when using `--dynamic-whitelist` param. -| `exchange.ccxt_rate_limit` | True | No | Have CCXT handle Exchange rate limits. Depending on the exchange, having this to false can lead to temporary bans from the exchange. +| `exchange.ccxt_rate_limit` | True | No | DEPRECATED!! Have CCXT handle Exchange rate limits. Depending on the exchange, having this to false can lead to temporary bans from the exchange. +| `exchange.ccxt_config` | None | No | Additional CCXT parameters passed to the regular ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) +| `exchange.ccxt_async_config` | None | No | Additional CCXT parameters passed to the async ccxt instance. Parameters may differ from exchange to exchange and are documented in the [ccxt documentation](https://ccxt.readthedocs.io/en/latest/manual.html#instantiation) | `experimental.use_sell_signal` | false | No | Use your sell strategy in addition of the `minimal_roi`. | `experimental.sell_profit_only` | false | No | waits until you have made a positive profit before taking a sell decision. | `experimental.ignore_roi_if_buy_signal` | false | No | Does not sell if the buy-signal is still active. Takes preference over `minimal_roi` and `use_sell_signal` @@ -204,8 +206,29 @@ you run it in production mode. } ``` + If you have not your Bittrex API key yet, [see our tutorial](https://github.com/freqtrade/freqtrade/blob/develop/docs/pre-requisite.md). +### Using proxy with FreqTrade + +To use a proxy with freqtrade, add the kwarg `"aiohttp_trust_env"=true` to the `"ccxt_async_kwargs"` dict in the exchange section of the configuration. + +An example for this can be found in `config_full.json.example` + +``` json +"ccxt_async_config": { + "aiohttp_trust_env": true +} +``` + +Then, export your proxy settings using the variables `"HTTP_PROXY"` and `"HTTPS_PROXY"` set to the appropriate values + +``` bash +export HTTP_PROXY="http://addr:port" +export HTTPS_PROXY="http://addr:port" +freqtrade +``` + ### Embedding Strategies @@ -213,7 +236,7 @@ FreqTrade provides you with with an easy way to embed the strategy into your con This is done by utilizing BASE64 encoding and providing this string at the strategy configuration field, in your chosen config file. -##### Encoding a string as BASE64 +#### Encoding a string as BASE64 This is a quick example, how to generate the BASE64 string in python diff --git a/docs/hyperopt.md b/docs/hyperopt.md index 3f568d82e..d1f363733 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -20,8 +20,8 @@ We recommend you start by taking a look at `hyperopt.py` file located in [freqtr ### Configure your Guards and Triggers There are two places you need to change to add a new buy strategy for testing: -- Inside [populate_buy_trend()](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/optimize/hyperopt.py#L278-L294). -- Inside [hyperopt_space()](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/optimize/hyperopt.py#L218-L229) +- Inside [populate_buy_trend()](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/optimize/hyperopt.py#L231-L264). +- Inside [hyperopt_space()](https://github.com/freqtrade/freqtrade/blob/develop/freqtrade/optimize/hyperopt.py#L213-L224) and the associated methods `indicator_space`, `roi_space`, `stoploss_space`. There you have two different type of indicators: 1. `guards` and 2. `triggers`. @@ -131,12 +131,12 @@ you have on-disk, use the `--datadir PATH` option. Default hyperopt will use data from directory `user_data/data`. ### Running Hyperopt with Smaller Testset -Use the `--timeperiod` argument to change how much of the testset +Use the `--timerange` argument to change how much of the testset you want to use. The last N ticks/timeframes will be used. Example: ```bash -python3 ./freqtrade/main.py hyperopt --timeperiod -200 +python3 ./freqtrade/main.py hyperopt --timerange -200 ``` ### Running Hyperopt with Smaller Search Space diff --git a/docs/installation.md b/docs/installation.md index 1ceda6b1c..1000600e6 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -245,8 +245,8 @@ conda install python=3.6 conda create -n freqtrade python=3.6 conda install scipy pandas -pip install -r requirements.txt -pip install -e . +python3 -m pip install -r requirements.txt +python3 -m pip install -e . ``` ### MacOS diff --git a/docs/stoploss.md b/docs/stoploss.md index 9740672cd..f34050a85 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -48,4 +48,4 @@ Both values can be configured in the main configuration file and requires `"trai The 0.01 would translate to a 1% stop loss, once you hit 1.1% profit. -You should also make sure to have this value higher than your minimal ROI, otherwise minimal ROI will apply first and sell your trade. +You should also make sure to have this value (`trailing_stop_positive_offset`) lower than your minimal ROI, otherwise minimal ROI will apply first and sell your trade. diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index ca764c9b4..945e31f9c 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -129,9 +129,3 @@ Day Profit BTC Profit USD ## /version > **Version:** `0.14.3` -### using proxy with telegram -``` -$ export HTTP_PROXY="http://addr:port" -$ export HTTPS_PROXY="http://addr:port" -$ freqtrade -``` diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 46825f548..89145d9e2 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,5 +1,5 @@ """ FreqTrade bot """ -__version__ = '0.17.2' +__version__ = '0.17.3' class DependencyException(BaseException): diff --git a/freqtrade/configuration.py b/freqtrade/configuration.py index 4e1137f33..d61ff4dd7 100644 --- a/freqtrade/configuration.py +++ b/freqtrade/configuration.py @@ -271,6 +271,11 @@ class Configuration(object): raise OperationalException( exception_msg ) + # Depreciation warning + if 'ccxt_rate_limit' in config.get('exchange', {}): + logger.warning("`ccxt_rate_limit` has been deprecated in favor of " + "`ccxt_config` and `ccxt_async_config` and will be removed " + "in a future version.") logger.debug('Exchange "%s" supported', exchange) return True diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 5a03f81cd..217855ecf 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -165,7 +165,9 @@ CONF_SCHEMA = { }, 'uniqueItems': True }, - 'outdated_offset': {'type': 'integer', 'minimum': 1} + 'outdated_offset': {'type': 'integer', 'minimum': 1}, + 'ccxt_config': {'type': 'object'}, + 'ccxt_async_config': {'type': 'object'} }, 'required': ['name', 'key', 'secret', 'pair_whitelist'] }, diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index d3c60c256..7fd0e5f43 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -93,8 +93,9 @@ class Exchange(object): logger.info('Instance is running with dry_run enabled') exchange_config = config['exchange'] - self._api = self._init_ccxt(exchange_config) - self._api_async = self._init_ccxt(exchange_config, ccxt_async) + self._api = self._init_ccxt(exchange_config, ccxt_kwargs=exchange_config.get('ccxt_config')) + 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) @@ -114,7 +115,8 @@ class Exchange(object): if self._api_async and inspect.iscoroutinefunction(self._api_async.close): asyncio.get_event_loop().run_until_complete(self._api_async.close()) - def _init_ccxt(self, exchange_config: dict, ccxt_module=ccxt) -> ccxt.Exchange: + 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. @@ -124,14 +126,20 @@ class Exchange(object): if name not in ccxt_module.exchanges: raise OperationalException(f'Exchange {name} is not supported') - try: - api = getattr(ccxt_module, name.lower())({ + + ex_config = { 'apiKey': exchange_config.get('key'), 'secret': exchange_config.get('secret'), 'password': exchange_config.get('password'), 'uid': exchange_config.get('uid', ''), 'enableRateLimit': exchange_config.get('ccxt_rate_limit', True) - }) + } + 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): raise OperationalException(f'Exchange {name} is not supported') diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 0983ec677..cd822023f 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -75,8 +75,6 @@ class Backtesting(object): else: # only one strategy - strat = StrategyResolver(self.config).strategy - self.strategylist.append(StrategyResolver(self.config).strategy) # Load one strategy self._set_strategy(self.strategylist[0]) diff --git a/freqtrade/optimize/hyperopt.py b/freqtrade/optimize/hyperopt.py index 086cad5aa..4a239ab28 100644 --- a/freqtrade/optimize/hyperopt.py +++ b/freqtrade/optimize/hyperopt.py @@ -152,7 +152,7 @@ class Hyperopt(Backtesting): @staticmethod def generate_roi_table(params: Dict) -> Dict[int, float]: """ - Generate the ROI table thqt will be used by Hyperopt + Generate the ROI table that will be used by Hyperopt """ roi_table = {} roi_table[0] = params['roi_p1'] + params['roi_p2'] + params['roi_p3'] @@ -402,6 +402,13 @@ def start(args: Namespace) -> None: config['exchange']['key'] = '' config['exchange']['secret'] = '' + if config.get('strategy') and config.get('strategy') != 'DefaultStrategy': + logger.error("Please don't use --strategy for hyperopt.") + logger.error( + "Read the documentation at " + "https://github.com/freqtrade/freqtrade/blob/develop/docs/hyperopt.md " + "to understand how to configure hyperopt.") + raise ValueError("--strategy configured but not supported for hyperopt") # Initialize backtesting object hyperopt = Hyperopt(config) hyperopt.start() diff --git a/freqtrade/persistence.py b/freqtrade/persistence.py index c26d74015..02267ac21 100644 --- a/freqtrade/persistence.py +++ b/freqtrade/persistence.py @@ -4,7 +4,7 @@ This module contains the class to persist trades into SQLite import logging from datetime import datetime -from decimal import Decimal, getcontext +from decimal import Decimal from typing import Any, Dict, Optional import arrow @@ -241,7 +241,6 @@ class Trade(_DECL_BASE): logger.info('Updating trade (id=%d) ...', self.id) - getcontext().prec = 8 # Bittrex do not go above 8 decimal if order_type == 'limit' and order['side'] == 'buy': # Update open rate and actual amount self.open_rate = Decimal(order['price']) @@ -278,7 +277,6 @@ class Trade(_DECL_BASE): If rate is not set self.fee will be used :return: Price in BTC of the open trade """ - getcontext().prec = 8 buy_trade = (Decimal(self.amount) * Decimal(self.open_rate)) fees = buy_trade * Decimal(fee or self.fee_open) @@ -296,7 +294,6 @@ class Trade(_DECL_BASE): If rate is not set self.close_rate will be used :return: Price in BTC of the open trade """ - getcontext().prec = 8 if rate is None and not self.close_rate: return 0.0 @@ -336,7 +333,6 @@ class Trade(_DECL_BASE): :param fee: fee to use on the close rate (optional). :return: profit in percentage as float """ - getcontext().prec = 8 open_trade_price = self.calc_open_trade_price() close_trade_price = self.calc_close_trade_price( diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index dc3d9bd65..d653ea176 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -84,9 +84,7 @@ class RPC(object): """ # Fetch open trade trades = Trade.query.filter(Trade.is_open.is_(True)).all() - if self._freqtrade.state != State.RUNNING: - raise RPCException('trader is not running') - elif not trades: + if not trades: raise RPCException('no active trade') else: results = [] @@ -118,9 +116,7 @@ class RPC(object): def _rpc_status_table(self) -> DataFrame: trades = Trade.query.filter(Trade.is_open.is_(True)).all() - if self._freqtrade.state != State.RUNNING: - raise RPCException('trader is not running') - elif not trades: + if not trades: raise RPCException('no active order') else: trades_list = [] @@ -363,6 +359,7 @@ class RPC(object): # Execute sell for all open orders for trade in Trade.query.filter(Trade.is_open.is_(True)).all(): _exec_forcesell(trade) + Trade.session.flush() return # Query for trade @@ -384,8 +381,6 @@ class RPC(object): Handler for performance. Shows a performance statistic from finished trades """ - if self._freqtrade.state != State.RUNNING: - raise RPCException('trader is not running') pair_rates = Trade.session.query(Trade.pair, sql.func.sum(Trade.close_profit).label('profit_sum'), diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 64708ef74..040f053f1 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -307,11 +307,14 @@ class Telegram(RPC): result = self._rpc_balance(self._config.get('fiat_display_currency', '')) output = '' for currency in result['currencies']: - output += "*{currency}:*\n" \ - "\t`Available: {available: .8f}`\n" \ - "\t`Balance: {balance: .8f}`\n" \ - "\t`Pending: {pending: .8f}`\n" \ - "\t`Est. BTC: {est_btc: .8f}`\n".format(**currency) + if currency['est_btc'] > 0.0001: + output += "*{currency}:*\n" \ + "\t`Available: {available: .8f}`\n" \ + "\t`Balance: {balance: .8f}`\n" \ + "\t`Pending: {pending: .8f}`\n" \ + "\t`Est. BTC: {est_btc: .8f}`\n".format(**currency) + else: + output += "*{currency}:* not showing <1$ amount \n".format(**currency) output += "\n*Estimated Value*:\n" \ "\t`BTC: {total: .8f}`\n" \ @@ -437,6 +440,7 @@ class Telegram(RPC): "*/count:* `Show number of trades running compared to allowed number of trades`" \ "\n" \ "*/balance:* `Show account balance per currency`\n" \ + "*/reload_conf:* `Reload configuration file` \n" \ "*/help:* `This help message`\n" \ "*/version:* `Show version`" diff --git a/freqtrade/strategy/__init__.py b/freqtrade/strategy/__init__.py index 38a110bd7..b29e26ef9 100644 --- a/freqtrade/strategy/__init__.py +++ b/freqtrade/strategy/__init__.py @@ -3,7 +3,8 @@ import sys from copy import deepcopy 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__) diff --git a/freqtrade/tests/exchange/__init__.py b/freqtrade/tests/exchange/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/freqtrade/tests/exchange/test_exchange.py b/freqtrade/tests/exchange/test_exchange.py index d9d68c3b8..93cd1e546 100644 --- a/freqtrade/tests/exchange/test_exchange.py +++ b/freqtrade/tests/exchange/test_exchange.py @@ -1,5 +1,6 @@ # pragma pylint: disable=missing-docstring, C0103, bad-continuation, global-statement # pragma pylint: disable=protected-access +import copy import logging from datetime import datetime from random import randint @@ -56,6 +57,32 @@ def test_init(default_conf, mocker, caplog): assert log_has('Instance is running with dry_run enabled', caplog.record_tuples) +def test_init_ccxt_kwargs(default_conf, mocker, caplog): + mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={})) + caplog.set_level(logging.INFO) + conf = copy.deepcopy(default_conf) + conf['exchange']['ccxt_async_config'] = {'aiohttp_trust_env': True} + ex = Exchange(conf) + assert log_has("Applying additional ccxt config: {'aiohttp_trust_env': True}", + caplog.record_tuples) + assert ex._api_async.aiohttp_trust_env + assert not ex._api.aiohttp_trust_env + + # Reset logging and config + caplog.clear() + conf = copy.deepcopy(default_conf) + conf['exchange']['ccxt_config'] = {'TestKWARG': 11} + ex = Exchange(conf) + assert not log_has("Applying additional ccxt config: {'aiohttp_trust_env': True}", + caplog.record_tuples) + assert not ex._api_async.aiohttp_trust_env + assert hasattr(ex._api, 'TestKWARG') + assert ex._api.TestKWARG == 11 + assert not hasattr(ex._api_async, 'TestKWARG') + assert log_has("Applying additional ccxt config: {'TestKWARG': 11}", + caplog.record_tuples) + + def test_destroy(default_conf, mocker, caplog): caplog.set_level(logging.DEBUG) get_patched_exchange(mocker, default_conf) diff --git a/freqtrade/tests/optimize/__init__.py b/freqtrade/tests/optimize/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/freqtrade/tests/optimize/test_backtesting.py b/freqtrade/tests/optimize/test_backtesting.py index a17867b3a..3ea0f240c 100644 --- a/freqtrade/tests/optimize/test_backtesting.py +++ b/freqtrade/tests/optimize/test_backtesting.py @@ -534,7 +534,7 @@ def test_backtest(default_conf, fee, mocker) -> None: expected = pd.DataFrame( {'pair': [pair, pair], - 'profit_percent': [0.00029975, 0.00056708], + 'profit_percent': [0.00029977, 0.00056716], 'profit_abs': [1.49e-06, 7.6e-07], 'open_time': [Arrow(2018, 1, 29, 18, 40, 0).datetime, Arrow(2018, 1, 30, 3, 30, 0).datetime], diff --git a/freqtrade/tests/optimize/test_hyperopt.py b/freqtrade/tests/optimize/test_hyperopt.py index 65a3c2fdb..2035e23df 100644 --- a/freqtrade/tests/optimize/test_hyperopt.py +++ b/freqtrade/tests/optimize/test_hyperopt.py @@ -65,6 +65,31 @@ def test_start(mocker, default_conf, caplog) -> None: assert start_mock.call_count == 1 +def test_start_failure(mocker, default_conf, caplog) -> None: + start_mock = MagicMock() + mocker.patch( + 'freqtrade.configuration.Configuration._load_config_file', + lambda *args, **kwargs: default_conf + ) + mocker.patch('freqtrade.optimize.hyperopt.Hyperopt.start', start_mock) + patch_exchange(mocker) + + args = [ + '--config', 'config.json', + '--strategy', 'TestStrategy', + 'hyperopt', + '--epochs', '5' + ] + args = get_args(args) + StrategyResolver({'strategy': 'DefaultStrategy'}) + with pytest.raises(ValueError): + start(args) + assert log_has( + "Please don't use --strategy for hyperopt.", + caplog.record_tuples + ) + + def test_loss_calculation_prefer_correct_trade_count(hyperopt) -> None: StrategyResolver({'strategy': 'DefaultStrategy'}) diff --git a/freqtrade/tests/rpc/__init__.py b/freqtrade/tests/rpc/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/freqtrade/tests/rpc/test_rpc.py b/freqtrade/tests/rpc/test_rpc.py index efc136777..88bf5e9ad 100644 --- a/freqtrade/tests/rpc/test_rpc.py +++ b/freqtrade/tests/rpc/test_rpc.py @@ -40,10 +40,6 @@ def test_rpc_trade_status(default_conf, ticker, fee, markets, mocker) -> None: patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) - freqtradebot.state = State.STOPPED - with pytest.raises(RPCException, match=r'.*trader is not running*'): - rpc._rpc_trade_status() - freqtradebot.state = State.RUNNING with pytest.raises(RPCException, match=r'.*no active trade*'): rpc._rpc_trade_status() @@ -81,10 +77,6 @@ def test_rpc_status_table(default_conf, ticker, fee, markets, mocker) -> None: patch_get_signal(freqtradebot, (True, False)) rpc = RPC(freqtradebot) - freqtradebot.state = State.STOPPED - with pytest.raises(RPCException, match=r'.*trader is not running*'): - rpc._rpc_status_table() - freqtradebot.state = State.RUNNING with pytest.raises(RPCException, match=r'.*no active order*'): rpc._rpc_status_table() diff --git a/freqtrade/tests/rpc/test_rpc_telegram.py b/freqtrade/tests/rpc/test_rpc_telegram.py index 182c1d2e7..45e01aa57 100644 --- a/freqtrade/tests/rpc/test_rpc_telegram.py +++ b/freqtrade/tests/rpc/test_rpc_telegram.py @@ -250,9 +250,10 @@ def test_status_handle(default_conf, update, ticker, fee, markets, mocker) -> No telegram = Telegram(freqtradebot) freqtradebot.state = State.STOPPED + # Status is also enabled when stopped telegram._status(bot=MagicMock(), update=update) assert msg_mock.call_count == 1 - assert 'trader is not running' in msg_mock.call_args_list[0][0][0] + assert 'no active trade' in msg_mock.call_args_list[0][0][0] msg_mock.reset_mock() freqtradebot.state = State.RUNNING @@ -295,9 +296,10 @@ def test_status_table_handle(default_conf, update, ticker, fee, markets, mocker) telegram = Telegram(freqtradebot) freqtradebot.state = State.STOPPED + # Status table is also enabled when stopped telegram._status_table(bot=MagicMock(), update=update) assert msg_mock.call_count == 1 - assert 'trader is not running' in msg_mock.call_args_list[0][0][0] + assert 'no active order' in msg_mock.call_args_list[0][0][0] msg_mock.reset_mock() freqtradebot.state = State.RUNNING @@ -507,7 +509,12 @@ def test_telegram_balance_handle(default_conf, update, mocker) -> None: 'total': 10.0, 'free': 10.0, 'used': 0.0 - } + }, + 'XRP': { + 'total': 1.0, + 'free': 1.0, + 'used': 0.0 + } } def mock_ticker(symbol, refresh): @@ -517,7 +524,12 @@ def test_telegram_balance_handle(default_conf, update, mocker) -> None: 'ask': 10000.00, 'last': 10000.00, } - + elif symbol == 'XRP/BTC': + return { + 'bid': 0.00001, + 'ask': 0.00001, + 'last': 0.00001, + } return { 'bid': 0.1, 'ask': 0.1, @@ -548,7 +560,8 @@ def test_telegram_balance_handle(default_conf, update, mocker) -> None: assert '*USDT:*' in result assert 'Balance:' in result assert 'Est. BTC:' in result - assert 'BTC: 14.00000000' in result + assert 'BTC: 12.00000000' in result + assert '*XRP:* not showing <1$ amount' in result def test_balance_handle_empty_response(default_conf, update, mocker) -> None: @@ -712,7 +725,7 @@ def test_forcesell_handle(default_conf, update, ticker, fee, 'open_rate': 1.099e-05, 'current_rate': 1.172e-05, 'profit_amount': 6.126e-05, - 'profit_percent': 0.06110514, + 'profit_percent': 0.0611052, 'stake_currency': 'BTC', 'fiat_currency': 'USD', } == last_msg @@ -765,7 +778,7 @@ def test_forcesell_down_handle(default_conf, update, ticker, fee, 'open_rate': 1.099e-05, 'current_rate': 1.044e-05, 'profit_amount': -5.492e-05, - 'profit_percent': -0.05478343, + 'profit_percent': -0.05478342, 'stake_currency': 'BTC', 'fiat_currency': 'USD', } == last_msg @@ -810,7 +823,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, markets, mocker 'open_rate': 1.099e-05, 'current_rate': 1.098e-05, 'profit_amount': -5.91e-06, - 'profit_percent': -0.00589292, + 'profit_percent': -0.00589291, 'stake_currency': 'BTC', 'fiat_currency': 'USD', } == msg @@ -895,26 +908,6 @@ def test_performance_handle(default_conf, update, ticker, fee, assert 'ETH/BTC\t6.20% (1)' in msg_mock.call_args_list[0][0][0] -def test_performance_handle_invalid(default_conf, update, mocker) -> None: - patch_coinmarketcap(mocker) - patch_exchange(mocker) - msg_mock = MagicMock() - mocker.patch.multiple( - 'freqtrade.rpc.telegram.Telegram', - _init=MagicMock(), - _send_msg=msg_mock - ) - freqtradebot = FreqtradeBot(default_conf) - patch_get_signal(freqtradebot, (True, False)) - telegram = Telegram(freqtradebot) - - # Trader is not running - freqtradebot.state = State.STOPPED - telegram._performance(bot=MagicMock(), update=update) - assert msg_mock.call_count == 1 - assert 'not running' in msg_mock.call_args_list[0][0][0] - - def test_count_handle(default_conf, update, ticker, fee, markets, mocker) -> None: patch_coinmarketcap(mocker) patch_exchange(mocker) diff --git a/freqtrade/tests/strategy/__init__.py b/freqtrade/tests/strategy/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/freqtrade/tests/test_configuration.py b/freqtrade/tests/test_configuration.py index bf41aab83..547c38de9 100644 --- a/freqtrade/tests/test_configuration.py +++ b/freqtrade/tests/test_configuration.py @@ -371,7 +371,7 @@ def test_hyperopt_with_arguments(mocker, default_conf, caplog) -> None: assert log_has('Parameter -s/--spaces detected: [\'all\']', caplog.record_tuples) -def test_check_exchange(default_conf) -> None: +def test_check_exchange(default_conf, caplog) -> None: configuration = Configuration(Namespace()) # Test a valid exchange @@ -392,6 +392,15 @@ def test_check_exchange(default_conf) -> None: ): configuration.check_exchange(default_conf) + # Test ccxt_rate_limit depreciation + default_conf.get('exchange').update({'name': 'binance'}) + default_conf['exchange']['ccxt_rate_limit'] = True + configuration.check_exchange(default_conf) + assert log_has("`ccxt_rate_limit` has been deprecated in favor of " + "`ccxt_config` and `ccxt_async_config` and will be removed " + "in a future version.", + caplog.record_tuples) + def test_cli_verbose_with_params(default_conf, mocker, caplog) -> None: mocker.patch('freqtrade.configuration.open', mocker.mock_open( diff --git a/freqtrade/tests/test_freqtradebot.py b/freqtrade/tests/test_freqtradebot.py index f38ecd78a..a0cbc6c10 100644 --- a/freqtrade/tests/test_freqtradebot.py +++ b/freqtrade/tests/test_freqtradebot.py @@ -167,11 +167,6 @@ def test_gen_pair_whitelist_not_supported(mocker, default_conf, tickers) -> None freqtrade._gen_pair_whitelist(base_currency='BTC') -@pytest.mark.skip(reason="Test not implemented") -def test_refresh_whitelist() -> None: - pass - - def test_get_trade_stake_amount(default_conf, ticker, limit_buy_order, fee, mocker) -> None: patch_RPCManager(mocker) patch_exchange(mocker) @@ -905,7 +900,7 @@ def test_handle_trade(default_conf, limit_buy_order, limit_sell_order, trade.update(limit_sell_order) assert trade.close_rate == 0.00001173 - assert trade.close_profit == 0.06201057 + assert trade.close_profit == 0.06201058 assert trade.calc_profit() == 0.00006217 assert trade.close_date is not None @@ -1098,6 +1093,7 @@ def test_check_handle_timedout_buy_exception(default_conf, ticker, limit_buy_ord fee, mocker) -> None: rpc_mock = patch_RPCManager(mocker) cancel_order_mock = MagicMock() + patch_exchange(mocker) mocker.patch.multiple( 'freqtrade.exchange.Exchange', validate_pairs=MagicMock(), @@ -1331,7 +1327,7 @@ def test_execute_sell_up(default_conf, ticker, fee, ticker_sell_up, markets, moc 'open_rate': 1.099e-05, 'current_rate': 1.172e-05, 'profit_amount': 6.126e-05, - 'profit_percent': 0.06110514, + 'profit_percent': 0.0611052, 'stake_currency': 'BTC', 'fiat_currency': 'USD', } == last_msg @@ -1377,7 +1373,7 @@ def test_execute_sell_down(default_conf, ticker, fee, ticker_sell_down, markets, 'open_rate': 1.099e-05, 'current_rate': 1.044e-05, 'profit_amount': -5.492e-05, - 'profit_percent': -0.05478343, + 'profit_percent': -0.05478342, 'stake_currency': 'BTC', 'fiat_currency': 'USD', } == last_msg @@ -1424,7 +1420,7 @@ def test_execute_sell_without_conf_sell_up(default_conf, ticker, fee, 'open_rate': 1.099e-05, 'current_rate': 1.172e-05, 'profit_amount': 6.126e-05, - 'profit_percent': 0.06110514, + 'profit_percent': 0.0611052, } == last_msg @@ -1470,7 +1466,7 @@ def test_execute_sell_without_conf_sell_down(default_conf, ticker, fee, 'open_rate': 1.099e-05, 'current_rate': 1.044e-05, 'profit_amount': -5.492e-05, - 'profit_percent': -0.05478343, + 'profit_percent': -0.05478342, } == last_msg diff --git a/freqtrade/tests/test_persistence.py b/freqtrade/tests/test_persistence.py index 7584537e2..5e0647dff 100644 --- a/freqtrade/tests/test_persistence.py +++ b/freqtrade/tests/test_persistence.py @@ -113,7 +113,7 @@ def test_update_with_bittrex(limit_buy_order, limit_sell_order, fee): trade.update(limit_sell_order) assert trade.open_order_id is None assert trade.close_rate == 0.00001173 - assert trade.close_profit == 0.06201057 + assert trade.close_profit == 0.06201058 assert trade.close_date is not None @@ -129,16 +129,16 @@ def test_calc_open_close_trade_price(limit_buy_order, limit_sell_order, fee): trade.open_order_id = 'something' trade.update(limit_buy_order) - assert trade.calc_open_trade_price() == 0.001002500 + assert trade.calc_open_trade_price() == 0.0010024999999225068 trade.update(limit_sell_order) - assert trade.calc_close_trade_price() == 0.0010646656 + assert trade.calc_close_trade_price() == 0.0010646656050132426 # Profit in BTC assert trade.calc_profit() == 0.00006217 # Profit in percent - assert trade.calc_profit_percent() == 0.06201057 + assert trade.calc_profit_percent() == 0.06201058 @pytest.mark.usefixtures("init_persistence") @@ -207,10 +207,10 @@ def test_calc_open_trade_price(limit_buy_order, fee): trade.update(limit_buy_order) # Buy @ 0.00001099 # Get the open rate price with the standard fee rate - assert trade.calc_open_trade_price() == 0.001002500 + assert trade.calc_open_trade_price() == 0.0010024999999225068 # Get the open rate price with a custom fee rate - assert trade.calc_open_trade_price(fee=0.003) == 0.001003000 + assert trade.calc_open_trade_price(fee=0.003) == 0.001002999999922468 @pytest.mark.usefixtures("init_persistence") @@ -226,14 +226,14 @@ def test_calc_close_trade_price(limit_buy_order, limit_sell_order, fee): trade.update(limit_buy_order) # Buy @ 0.00001099 # Get the close rate price with a custom close rate and a regular fee rate - assert trade.calc_close_trade_price(rate=0.00001234) == 0.0011200318 + assert trade.calc_close_trade_price(rate=0.00001234) == 0.0011200318470471794 # Get the close rate price with a custom close rate and a custom fee rate - assert trade.calc_close_trade_price(rate=0.00001234, fee=0.003) == 0.0011194704 + assert trade.calc_close_trade_price(rate=0.00001234, fee=0.003) == 0.0011194704275749754 # Test when we apply a Sell order, and ask price with a custom fee rate trade.update(limit_sell_order) - assert trade.calc_close_trade_price(fee=0.005) == 0.0010619972 + assert trade.calc_close_trade_price(fee=0.005) == 0.0010619972701635854 @pytest.mark.usefixtures("init_persistence") @@ -281,17 +281,17 @@ def test_calc_profit_percent(limit_buy_order, limit_sell_order, fee): trade.update(limit_buy_order) # Buy @ 0.00001099 # Get percent of profit with a custom rate (Higher than open rate) - assert trade.calc_profit_percent(rate=0.00001234) == 0.1172387 + assert trade.calc_profit_percent(rate=0.00001234) == 0.11723875 # Get percent of profit with a custom rate (Lower than open rate) - assert trade.calc_profit_percent(rate=0.00000123) == -0.88863827 + assert trade.calc_profit_percent(rate=0.00000123) == -0.88863828 # Test when we apply a Sell order. Sell higher than open rate @ 0.00001173 trade.update(limit_sell_order) - assert trade.calc_profit_percent() == 0.06201057 + assert trade.calc_profit_percent() == 0.06201058 # Test with a custom fee rate on the close trade - assert trade.calc_profit_percent(fee=0.003) == 0.0614782 + assert trade.calc_profit_percent(fee=0.003) == 0.06147824 def test_clean_dry_run_db(default_conf, fee): diff --git a/requirements.txt b/requirements.txt index d09167155..4856c588e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,18 +1,18 @@ -ccxt==1.17.351 +ccxt==1.17.402 SQLAlchemy==1.2.12 python-telegram-bot==11.1.0 arrow==0.12.1 cachetools==2.1.0 -requests==2.19.1 -urllib3==1.22 +requests==2.20.0 +urllib3==1.24 wrapt==1.10.11 pandas==0.23.4 -scikit-learn==0.19.2 +scikit-learn==0.20.0 scipy==1.1.0 jsonschema==2.6.0 -numpy==1.15.2 +numpy==1.15.3 TA-Lib==0.4.17 -pytest==3.8.1 +pytest==3.9.2 pytest-mock==1.10.0 pytest-asyncio==0.9.0 pytest-cov==2.6.0 diff --git a/scripts/download_backtest_data.py b/scripts/download_backtest_data.py index 27c4c1e1c..b83eb2c4b 100755 --- a/scripts/download_backtest_data.py +++ b/scripts/download_backtest_data.py @@ -10,7 +10,14 @@ from freqtrade import arguments from freqtrade.arguments import TimeRange from freqtrade.exchange import Exchange from freqtrade.optimize import download_backtesting_testdata +from freqtrade.configuration import set_loggers +import logging +logging.basicConfig( + level=logging.INFO, + format='%(asctime)s - %(name)s - %(levelname)s - %(message)s', +) +set_loggers(0) DEFAULT_DL_PATH = 'user_data/data' @@ -54,7 +61,9 @@ exchange = Exchange({'key': '', 'exchange': { 'name': args.exchange, 'pair_whitelist': [], - 'ccxt_rate_limit': False + 'ccxt_async_config': { + "enableRateLimit": False + } } }) pairs_not_available = []