Merge branch 'develop' into stoploss_market
This commit is contained in:
		| @@ -7,6 +7,7 @@ import traceback | ||||
| from datetime import datetime | ||||
| from math import isclose | ||||
| from os import getpid | ||||
| from threading import Lock | ||||
| from typing import Any, Dict, List, Optional, Tuple | ||||
|  | ||||
| import arrow | ||||
| @@ -27,7 +28,6 @@ from freqtrade.state import State | ||||
| from freqtrade.strategy.interface import IStrategy, SellType | ||||
| from freqtrade.wallets import Wallets | ||||
|  | ||||
|  | ||||
| logger = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| @@ -92,6 +92,8 @@ class FreqtradeBot: | ||||
|         # the initial state of the bot. | ||||
|         # Keep this at the end of this initialization method. | ||||
|         self.rpc: RPCManager = RPCManager(self) | ||||
|         # Protect sell-logic from forcesell and viceversa | ||||
|         self._sell_lock = Lock() | ||||
|  | ||||
|     def cleanup(self) -> None: | ||||
|         """ | ||||
| @@ -132,8 +134,12 @@ class FreqtradeBot: | ||||
|         self.dataprovider.refresh(self._create_pair_whitelist(self.active_pair_whitelist), | ||||
|                                   self.strategy.informative_pairs()) | ||||
|  | ||||
|         # First process current opened trades (positions) | ||||
|         self.exit_positions(trades) | ||||
|         # Protect from collisions with forcesell. | ||||
|         # Without this, freqtrade my try to recreate stoploss_on_exchange orders | ||||
|         # while selling is in process, since telegram messages arrive in an different thread. | ||||
|         with self._sell_lock: | ||||
|             # First process current opened trades (positions) | ||||
|             self.exit_positions(trades) | ||||
|  | ||||
|         # Then looking for buy opportunities | ||||
|         if self.get_free_open_trades(): | ||||
| @@ -744,8 +750,8 @@ class FreqtradeBot: | ||||
|         Check and execute sell | ||||
|         """ | ||||
|         should_sell = self.strategy.should_sell( | ||||
|                 trade, sell_rate, datetime.utcnow(), buy, sell, | ||||
|                 force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0 | ||||
|             trade, sell_rate, datetime.utcnow(), buy, sell, | ||||
|             force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0 | ||||
|         ) | ||||
|  | ||||
|         if should_sell.sell_flag: | ||||
|   | ||||
| @@ -420,24 +420,27 @@ class RPC: | ||||
|         if self._freqtrade.state != State.RUNNING: | ||||
|             raise RPCException('trader is not running') | ||||
|  | ||||
|         if trade_id == 'all': | ||||
|             # Execute sell for all open orders | ||||
|             for trade in Trade.get_open_trades(): | ||||
|                 _exec_forcesell(trade) | ||||
|         with self._freqtrade._sell_lock: | ||||
|             if trade_id == 'all': | ||||
|                 # Execute sell for all open orders | ||||
|                 for trade in Trade.get_open_trades(): | ||||
|                     _exec_forcesell(trade) | ||||
|                 Trade.session.flush() | ||||
|                 self._freqtrade.wallets.update() | ||||
|                 return {'result': 'Created sell orders for all open trades.'} | ||||
|  | ||||
|             # Query for trade | ||||
|             trade = Trade.get_trades( | ||||
|                 trade_filter=[Trade.id == trade_id, Trade.is_open.is_(True), ] | ||||
|             ).first() | ||||
|             if not trade: | ||||
|                 logger.warning('forcesell: Invalid argument received') | ||||
|                 raise RPCException('invalid argument') | ||||
|  | ||||
|             _exec_forcesell(trade) | ||||
|             Trade.session.flush() | ||||
|             return {'result': 'Created sell orders for all open trades.'} | ||||
|  | ||||
|         # Query for trade | ||||
|         trade = Trade.get_trades( | ||||
|             trade_filter=[Trade.id == trade_id, Trade.is_open.is_(True), ] | ||||
|         ).first() | ||||
|         if not trade: | ||||
|             logger.warning('forcesell: Invalid argument received') | ||||
|             raise RPCException('invalid argument') | ||||
|  | ||||
|         _exec_forcesell(trade) | ||||
|         Trade.session.flush() | ||||
|         return {'result': f'Created sell order for trade {trade_id}.'} | ||||
|             self._freqtrade.wallets.update() | ||||
|             return {'result': f'Created sell order for trade {trade_id}.'} | ||||
|  | ||||
|     def _rpc_forcebuy(self, pair: str, price: Optional[float]) -> Optional[Trade]: | ||||
|         """ | ||||
|   | ||||
| @@ -906,30 +906,22 @@ def test_process_informative_pairs_added(default_conf, ticker, mocker) -> None: | ||||
|     assert ("ETH/BTC", default_conf["ticker_interval"]) in refresh_mock.call_args[0][0] | ||||
|  | ||||
|  | ||||
| def test_balance_fully_ask_side(mocker, default_conf) -> None: | ||||
|     default_conf['bid_strategy']['ask_last_balance'] = 0.0 | ||||
| @pytest.mark.parametrize("ask,last,last_ab,expected", [ | ||||
|     (20, 10, 0.0, 20),  # Full ask side | ||||
|     (20, 10, 1.0, 10),  # Full last side | ||||
|     (20, 10, 0.5, 15),  # Between ask and last | ||||
|     (20, 10, 0.7, 13),  # Between ask and last | ||||
|     (20, 10, 0.3, 17),  # Between ask and last | ||||
|     (5, 10, 1.0, 5),  # last bigger than ask | ||||
|     (5, 10, 0.5, 5),  # last bigger than ask | ||||
| ]) | ||||
| def test_get_buy_rate(mocker, default_conf, ask, last, last_ab, expected) -> None: | ||||
|     default_conf['bid_strategy']['ask_last_balance'] = last_ab | ||||
|     freqtrade = get_patched_freqtradebot(mocker, default_conf) | ||||
|     mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', | ||||
|                  MagicMock(return_value={'ask': 20, 'last': 10})) | ||||
|                  MagicMock(return_value={'ask': ask, 'last': last})) | ||||
|  | ||||
|     assert freqtrade.get_buy_rate('ETH/BTC') == 20 | ||||
|  | ||||
|  | ||||
| def test_balance_fully_last_side(mocker, default_conf) -> None: | ||||
|     default_conf['bid_strategy']['ask_last_balance'] = 1.0 | ||||
|     freqtrade = get_patched_freqtradebot(mocker, default_conf) | ||||
|     mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', | ||||
|                  MagicMock(return_value={'ask': 20, 'last': 10})) | ||||
|  | ||||
|     assert freqtrade.get_buy_rate('ETH/BTC') == 10 | ||||
|  | ||||
|  | ||||
| def test_balance_bigger_last_ask(mocker, default_conf) -> None: | ||||
|     default_conf['bid_strategy']['ask_last_balance'] = 1.0 | ||||
|     freqtrade = get_patched_freqtradebot(mocker, default_conf) | ||||
|     mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', | ||||
|                  MagicMock(return_value={'ask': 5, 'last': 10})) | ||||
|     assert freqtrade.get_buy_rate('ETH/BTC') == 5 | ||||
|     assert freqtrade.get_buy_rate('ETH/BTC') == expected | ||||
|  | ||||
|  | ||||
| def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None: | ||||
|   | ||||
		Reference in New Issue
	
	Block a user