Merge branch 'develop' into stoploss_market
This commit is contained in:
commit
1d141cd406
@ -7,6 +7,7 @@ import traceback
|
|||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
from math import isclose
|
from math import isclose
|
||||||
from os import getpid
|
from os import getpid
|
||||||
|
from threading import Lock
|
||||||
from typing import Any, Dict, List, Optional, Tuple
|
from typing import Any, Dict, List, Optional, Tuple
|
||||||
|
|
||||||
import arrow
|
import arrow
|
||||||
@ -27,7 +28,6 @@ from freqtrade.state import State
|
|||||||
from freqtrade.strategy.interface import IStrategy, SellType
|
from freqtrade.strategy.interface import IStrategy, SellType
|
||||||
from freqtrade.wallets import Wallets
|
from freqtrade.wallets import Wallets
|
||||||
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -92,6 +92,8 @@ class FreqtradeBot:
|
|||||||
# the initial state of the bot.
|
# the initial state of the bot.
|
||||||
# Keep this at the end of this initialization method.
|
# Keep this at the end of this initialization method.
|
||||||
self.rpc: RPCManager = RPCManager(self)
|
self.rpc: RPCManager = RPCManager(self)
|
||||||
|
# Protect sell-logic from forcesell and viceversa
|
||||||
|
self._sell_lock = Lock()
|
||||||
|
|
||||||
def cleanup(self) -> None:
|
def cleanup(self) -> None:
|
||||||
"""
|
"""
|
||||||
@ -132,8 +134,12 @@ class FreqtradeBot:
|
|||||||
self.dataprovider.refresh(self._create_pair_whitelist(self.active_pair_whitelist),
|
self.dataprovider.refresh(self._create_pair_whitelist(self.active_pair_whitelist),
|
||||||
self.strategy.informative_pairs())
|
self.strategy.informative_pairs())
|
||||||
|
|
||||||
# First process current opened trades (positions)
|
# Protect from collisions with forcesell.
|
||||||
self.exit_positions(trades)
|
# 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
|
# Then looking for buy opportunities
|
||||||
if self.get_free_open_trades():
|
if self.get_free_open_trades():
|
||||||
@ -744,8 +750,8 @@ class FreqtradeBot:
|
|||||||
Check and execute sell
|
Check and execute sell
|
||||||
"""
|
"""
|
||||||
should_sell = self.strategy.should_sell(
|
should_sell = self.strategy.should_sell(
|
||||||
trade, sell_rate, datetime.utcnow(), buy, sell,
|
trade, sell_rate, datetime.utcnow(), buy, sell,
|
||||||
force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0
|
force_stoploss=self.edge.stoploss(trade.pair) if self.edge else 0
|
||||||
)
|
)
|
||||||
|
|
||||||
if should_sell.sell_flag:
|
if should_sell.sell_flag:
|
||||||
|
@ -420,24 +420,27 @@ class RPC:
|
|||||||
if self._freqtrade.state != State.RUNNING:
|
if self._freqtrade.state != State.RUNNING:
|
||||||
raise RPCException('trader is not running')
|
raise RPCException('trader is not running')
|
||||||
|
|
||||||
if trade_id == 'all':
|
with self._freqtrade._sell_lock:
|
||||||
# Execute sell for all open orders
|
if trade_id == 'all':
|
||||||
for trade in Trade.get_open_trades():
|
# Execute sell for all open orders
|
||||||
_exec_forcesell(trade)
|
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()
|
Trade.session.flush()
|
||||||
return {'result': 'Created sell orders for all open trades.'}
|
self._freqtrade.wallets.update()
|
||||||
|
return {'result': f'Created sell order for trade {trade_id}.'}
|
||||||
# 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}.'}
|
|
||||||
|
|
||||||
def _rpc_forcebuy(self, pair: str, price: Optional[float]) -> Optional[Trade]:
|
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]
|
assert ("ETH/BTC", default_conf["ticker_interval"]) in refresh_mock.call_args[0][0]
|
||||||
|
|
||||||
|
|
||||||
def test_balance_fully_ask_side(mocker, default_conf) -> None:
|
@pytest.mark.parametrize("ask,last,last_ab,expected", [
|
||||||
default_conf['bid_strategy']['ask_last_balance'] = 0.0
|
(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)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker',
|
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
|
assert freqtrade.get_buy_rate('ETH/BTC') == expected
|
||||||
|
|
||||||
|
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None:
|
def test_execute_buy(mocker, default_conf, fee, limit_buy_order) -> None:
|
||||||
|
Loading…
Reference in New Issue
Block a user