diff --git a/build_helpers/install_windows.ps1 b/build_helpers/install_windows.ps1 index 30427c3cc..138fba208 100644 --- a/build_helpers/install_windows.ps1 +++ b/build_helpers/install_windows.ps1 @@ -2,6 +2,7 @@ # Downloaded from https://www.lfd.uci.edu/~gohlke/pythonlibs/#ta-lib # Invoke-WebRequest -Uri "https://download.lfd.uci.edu/pythonlibs/xxxxxxx/TA_Lib-0.4.17-cp37-cp37m-win_amd64.whl" -OutFile "TA_Lib-0.4.17-cp37-cp37m-win_amd64.whl" +python -m pip install --upgrade pip pip install build_helpers\TA_Lib-0.4.17-cp37-cp37m-win_amd64.whl pip install -r requirements-dev.txt diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index fa9a8424a..afe23d5a5 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -63,8 +63,7 @@ class FreqtradeBot: self.exchange = ExchangeResolver.load_exchange(self.config['exchange']['name'], self.config) - persistence.init(self.config.get('db_url', None), - clean_open_orders=self.config.get('dry_run', False)) + persistence.init(self.config.get('db_url', None), clean_open_orders=self.config['dry_run']) self.wallets = Wallets(self.config, self.exchange) @@ -219,7 +218,7 @@ class FreqtradeBot: return trades_created - def get_target_bid(self, pair: str, tick: Dict = None) -> float: + def get_buy_rate(self, pair: str, tick: Dict = None) -> float: """ Calculates bid target between current ask price and last price :return: float: Price @@ -436,7 +435,7 @@ class FreqtradeBot: buy_limit_requested = price else: # Calculate price - buy_limit_requested = self.get_target_bid(pair) + buy_limit_requested = self.get_buy_rate(pair) min_stake_amount = self._get_min_pair_stake_amount(pair, buy_limit_requested) if min_stake_amount is not None and min_stake_amount > stake_amount: @@ -926,7 +925,7 @@ class FreqtradeBot: # if stoploss is on exchange and we are on dry_run mode, # we consider the sell price stop price - if self.config.get('dry_run', False) and sell_type == 'stoploss' \ + if self.config['dry_run'] and sell_type == 'stoploss' \ and self.strategy.order_types['stoploss_on_exchange']: limit = trade.stop_loss diff --git a/freqtrade/optimize/optimize_reports.py b/freqtrade/optimize/optimize_reports.py index 3801546b1..67056eaa9 100644 --- a/freqtrade/optimize/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports.py @@ -70,7 +70,7 @@ def generate_text_table_sell_reason(data: Dict[str, Dict], results: DataFrame) - for reason, count in results['sell_reason'].value_counts().iteritems(): result = results.loc[results['sell_reason'] == reason] profit = len(result[result['profit_abs'] >= 0]) - loss = len(result[results['profit_abs'] < 0]) + loss = len(result[result['profit_abs'] < 0]) profit_mean = round(result['profit_percent'].mean() * 100.0, 2) tabular_data.append([reason.value, count, profit, loss, profit_mean]) return tabulate(tabular_data, headers=headers, tablefmt="pipe") diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 0469d418d..d58b99f39 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -88,7 +88,7 @@ class RPC: """ config = self._freqtrade.config val = { - 'dry_run': config.get('dry_run', False), + 'dry_run': config['dry_run'], 'stake_currency': config['stake_currency'], 'stake_amount': config['stake_amount'], 'minimal_roi': config['minimal_roi'].copy(), @@ -337,7 +337,7 @@ class RPC: 'stake': stake_currency, }) if total == 0.0: - if self._freqtrade.config.get('dry_run', False): + if self._freqtrade.config['dry_run']: raise RPCException('Running in Dry Run, balances are not available.') else: raise RPCException('All balances are zero.') @@ -351,7 +351,7 @@ class RPC: 'symbol': symbol, 'value': value, 'stake': stake_currency, - 'note': 'Simulated balances' if self._freqtrade.config.get('dry_run', False) else '' + 'note': 'Simulated balances' if self._freqtrade.config['dry_run'] else '' } def _rpc_start(self) -> Dict[str, str]: diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py index cb9e697e9..f687fe4d1 100644 --- a/freqtrade/rpc/rpc_manager.py +++ b/freqtrade/rpc/rpc_manager.py @@ -62,7 +62,7 @@ class RPCManager: logger.error(f"Message type {msg['type']} not implemented by handler {mod.name}.") def startup_messages(self, config, pairlist) -> None: - if config.get('dry_run', False): + if config['dry_run']: self.send_msg({ 'type': RPCMessageType.WARNING_NOTIFICATION, 'status': 'Dry run is enabled. All trades are simulated.' diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 7bd6a9ac5..27bc8280e 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -389,9 +389,11 @@ class IStrategy(ABC): trade.adjust_stop_loss(high or current_rate, stop_loss_value) # evaluate if the stoploss was hit if stoploss is not on exchange + # in Dry-Run, this handles stoploss logic as well, as the logic will not be different to + # regular stoploss handling. if ((self.stoploss is not None) and (trade.stop_loss >= current_rate) and - (not self.order_types.get('stoploss_on_exchange'))): + (not self.order_types.get('stoploss_on_exchange') or self.config['dry_run'])): sell_type = SellType.STOP_LOSS diff --git a/freqtrade/templates/sample_hyperopt_loss.py b/freqtrade/templates/sample_hyperopt_loss.py index 5a2fb72b6..4173d97f5 100644 --- a/freqtrade/templates/sample_hyperopt_loss.py +++ b/freqtrade/templates/sample_hyperopt_loss.py @@ -27,7 +27,8 @@ class SampleHyperOptLoss(IHyperOptLoss): Defines the default loss function for hyperopt This is intended to give you some inspiration for your own loss function. - The Function needs to return a number (float) - which becomes for better backtest results. + The Function needs to return a number (float) - which becomes smaller for better backtest + results. """ @staticmethod diff --git a/requirements-common.txt b/requirements-common.txt index 15a9d687f..daf4984c0 100644 --- a/requirements-common.txt +++ b/requirements-common.txt @@ -1,6 +1,6 @@ # requirements without requirements installable via conda # mainly used for Raspberry pi installs -ccxt==1.21.56 +ccxt==1.21.76 SQLAlchemy==1.3.12 python-telegram-bot==12.3.0 arrow==0.15.5 diff --git a/requirements-dev.txt b/requirements-dev.txt index 16f9baf95..e602bf184 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -8,7 +8,7 @@ flake8==3.7.9 flake8-type-annotations==0.1.0 flake8-tidy-imports==4.0.0 mypy==0.761 -pytest==5.3.2 +pytest==5.3.3 pytest-asyncio==0.10.0 pytest-cov==2.8.1 pytest-mock==2.0.0 diff --git a/tests/test_configuration.py b/tests/test_configuration.py index a4d4c4abc..cbcd6416a 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -323,7 +323,7 @@ def test_load_dry_run(default_conf, mocker, config_value, expected, arglist) -> configuration = Configuration(Arguments(arglist).get_parsed_arg()) validated_conf = configuration.load_config() - assert validated_conf.get('dry_run') is expected + assert validated_conf['dry_run'] is expected def test_load_custom_strategy(default_conf, mocker) -> None: diff --git a/tests/test_freqtradebot.py b/tests/test_freqtradebot.py index 48bd2deb5..3df15fc5f 100644 --- a/tests/test_freqtradebot.py +++ b/tests/test_freqtradebot.py @@ -912,7 +912,7 @@ def test_balance_fully_ask_side(mocker, default_conf) -> None: mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={'ask': 20, 'last': 10})) - assert freqtrade.get_target_bid('ETH/BTC') == 20 + assert freqtrade.get_buy_rate('ETH/BTC') == 20 def test_balance_fully_last_side(mocker, default_conf) -> None: @@ -921,7 +921,7 @@ def test_balance_fully_last_side(mocker, default_conf) -> None: mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={'ask': 20, 'last': 10})) - assert freqtrade.get_target_bid('ETH/BTC') == 10 + assert freqtrade.get_buy_rate('ETH/BTC') == 10 def test_balance_bigger_last_ask(mocker, default_conf) -> None: @@ -929,7 +929,7 @@ def test_balance_bigger_last_ask(mocker, default_conf) -> None: freqtrade = get_patched_freqtradebot(mocker, default_conf) mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={'ask': 5, 'last': 10})) - assert freqtrade.get_target_bid('ETH/BTC') == 5 + assert freqtrade.get_buy_rate('ETH/BTC') == 5 def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None: @@ -938,10 +938,10 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None: freqtrade = FreqtradeBot(default_conf) stake_amount = 2 bid = 0.11 - get_bid = MagicMock(return_value=bid) + buy_rate_mock = MagicMock(return_value=bid) mocker.patch.multiple( 'freqtrade.freqtradebot.FreqtradeBot', - get_target_bid=get_bid, + get_buy_rate=buy_rate_mock, _get_min_pair_stake_amount=MagicMock(return_value=1) ) buy_mm = MagicMock(return_value={'id': limit_buy_order['id']}) @@ -958,7 +958,7 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None: pair = 'ETH/BTC' assert freqtrade.execute_buy(pair, stake_amount) - assert get_bid.call_count == 1 + assert buy_rate_mock.call_count == 1 assert buy_mm.call_count == 1 call_args = buy_mm.call_args_list[0][1] assert call_args['pair'] == pair @@ -975,8 +975,8 @@ def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None: # Test calling with price fix_price = 0.06 assert freqtrade.execute_buy(pair, stake_amount, fix_price) - # Make sure get_target_bid wasn't called again - assert get_bid.call_count == 1 + # Make sure get_buy_rate wasn't called again + assert buy_rate_mock.call_count == 1 assert buy_mm.call_count == 2 call_args = buy_mm.call_args_list[1][1] @@ -1319,6 +1319,14 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog, order_types=freqtrade.strategy.order_types, stop_price=0.00002344 * 0.95) + # price fell below stoploss, so dry-run sells trade. + mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={ + 'bid': 0.00002144, + 'ask': 0.00002146, + 'last': 0.00002144 + })) + assert freqtrade.handle_trade(trade) is True + def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, caplog, limit_buy_order, limit_sell_order) -> None: @@ -3495,7 +3503,7 @@ def test_order_book_depth_of_market_high_delta(default_conf, ticker, limit_buy_o def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None: """ - test if function get_target_bid will return the order book price + test if function get_buy_rate will return the order book price instead of the ask rate """ patch_exchange(mocker) @@ -3513,13 +3521,13 @@ def test_order_book_bid_strategy1(mocker, default_conf, order_book_l2) -> None: default_conf['telegram']['enabled'] = False freqtrade = FreqtradeBot(default_conf) - assert freqtrade.get_target_bid('ETH/BTC') == 0.043935 + assert freqtrade.get_buy_rate('ETH/BTC') == 0.043935 assert ticker_mock.call_count == 0 def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2) -> None: """ - test if function get_target_bid will return the ask rate (since its value is lower) + test if function get_buy_rate will return the ask rate (since its value is lower) instead of the order book rate (even if enabled) """ patch_exchange(mocker) @@ -3538,7 +3546,7 @@ def test_order_book_bid_strategy2(mocker, default_conf, order_book_l2) -> None: freqtrade = FreqtradeBot(default_conf) # orderbook shall be used even if tickers would be lower. - assert freqtrade.get_target_bid('ETH/BTC') != 0.042 + assert freqtrade.get_buy_rate('ETH/BTC') != 0.042 assert ticker_mock.call_count == 0