stoploss on exchange added as a parameter to order_types
This commit is contained in:
parent
e4744c1ba4
commit
3e29fbb17a
@ -109,7 +109,8 @@ CONF_SCHEMA = {
|
|||||||
'properties': {
|
'properties': {
|
||||||
'buy': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES},
|
'buy': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES},
|
||||||
'sell': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES},
|
'sell': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES},
|
||||||
'stoploss': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES}
|
'stoploss': {'type': 'string', 'enum': ORDERTYPE_POSSIBILITIES},
|
||||||
|
'stoploss_on_exchange': {'type': 'boolean'}
|
||||||
},
|
},
|
||||||
'required': ['buy', 'sell', 'stoploss']
|
'required': ['buy', 'sell', 'stoploss']
|
||||||
},
|
},
|
||||||
|
@ -227,6 +227,12 @@ class Exchange(object):
|
|||||||
raise OperationalException(
|
raise OperationalException(
|
||||||
f'Exchange {self.name} does not support market orders.')
|
f'Exchange {self.name} does not support market orders.')
|
||||||
|
|
||||||
|
if order_types.get('stoploss_on_exchange', False):
|
||||||
|
if self.name is not 'Binance':
|
||||||
|
raise OperationalException(
|
||||||
|
'On exchange stoploss is not supported for %s.' % self.name
|
||||||
|
)
|
||||||
|
|
||||||
def exchange_has(self, endpoint: str) -> bool:
|
def exchange_has(self, endpoint: str) -> bool:
|
||||||
"""
|
"""
|
||||||
Checks if exchange implements a specific API endpoint.
|
Checks if exchange implements a specific API endpoint.
|
||||||
|
@ -54,7 +54,6 @@ class FreqtradeBot(object):
|
|||||||
# Init objects
|
# Init objects
|
||||||
self.config = config
|
self.config = config
|
||||||
self.strategy: IStrategy = StrategyResolver(self.config).strategy
|
self.strategy: IStrategy = StrategyResolver(self.config).strategy
|
||||||
self.check_strategy_config_consistency(config, self.strategy)
|
|
||||||
|
|
||||||
self.rpc: RPCManager = RPCManager(self)
|
self.rpc: RPCManager = RPCManager(self)
|
||||||
self.persistence = None
|
self.persistence = None
|
||||||
@ -68,16 +67,6 @@ class FreqtradeBot(object):
|
|||||||
self.active_pair_whitelist: List[str] = self.config['exchange']['pair_whitelist']
|
self.active_pair_whitelist: List[str] = self.config['exchange']['pair_whitelist']
|
||||||
self._init_modules()
|
self._init_modules()
|
||||||
|
|
||||||
def check_strategy_config_consistency(self, config, strategy: IStrategy) -> None:
|
|
||||||
"""
|
|
||||||
checks if config is compatible with the given strategy
|
|
||||||
"""
|
|
||||||
# Stoploss on exchange is only implemented for binance
|
|
||||||
if strategy.stoploss_on_exchange and config.get('exchange') is not 'binance':
|
|
||||||
raise OperationalException(
|
|
||||||
'On exchange stoploss is not supported for %s.' % config['exchange']['name']
|
|
||||||
)
|
|
||||||
|
|
||||||
def _init_modules(self) -> None:
|
def _init_modules(self) -> None:
|
||||||
"""
|
"""
|
||||||
Initializes all modules and updates the config
|
Initializes all modules and updates the config
|
||||||
@ -567,7 +556,7 @@ class FreqtradeBot(object):
|
|||||||
|
|
||||||
trade.update(order)
|
trade.update(order)
|
||||||
|
|
||||||
if self.strategy.stoploss_on_exchange:
|
if self.strategy.order_types.get('stoploss_on_exchange'):
|
||||||
result = self.handle_stoploss_on_exchange(trade)
|
result = self.handle_stoploss_on_exchange(trade)
|
||||||
if result:
|
if result:
|
||||||
self.wallets.update()
|
self.wallets.update()
|
||||||
@ -844,7 +833,7 @@ class FreqtradeBot(object):
|
|||||||
sell_type = 'stoploss'
|
sell_type = 'stoploss'
|
||||||
|
|
||||||
# First cancelling stoploss on exchange ...
|
# First cancelling stoploss on exchange ...
|
||||||
if self.strategy.stoploss_on_exchange and trade.stoploss_order_id:
|
if self.strategy.order_types.get('stoploss_on_exchange') and trade.stoploss_order_id:
|
||||||
self.exchange.cancel_order(trade.stoploss_order_id, trade.pair)
|
self.exchange.cancel_order(trade.stoploss_order_id, trade.pair)
|
||||||
|
|
||||||
# Execute sell and update trade record
|
# Execute sell and update trade record
|
||||||
|
@ -32,7 +32,8 @@ class DefaultStrategy(IStrategy):
|
|||||||
order_types = {
|
order_types = {
|
||||||
'buy': 'limit',
|
'buy': 'limit',
|
||||||
'sell': 'limit',
|
'sell': 'limit',
|
||||||
'stoploss': 'limit'
|
'stoploss': 'limit',
|
||||||
|
'stoploss_on_exchange': False
|
||||||
}
|
}
|
||||||
|
|
||||||
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||||
|
@ -68,11 +68,6 @@ class IStrategy(ABC):
|
|||||||
# associated stoploss
|
# associated stoploss
|
||||||
stoploss: float
|
stoploss: float
|
||||||
|
|
||||||
# if the stoploss should be on exchange.
|
|
||||||
# if this is True then a stoploss order will be placed
|
|
||||||
# immediately after a successful buy order.
|
|
||||||
stoploss_on_exchange: bool = False
|
|
||||||
|
|
||||||
# associated ticker interval
|
# associated ticker interval
|
||||||
ticker_interval: str
|
ticker_interval: str
|
||||||
|
|
||||||
@ -80,7 +75,8 @@ class IStrategy(ABC):
|
|||||||
order_types: Dict = {
|
order_types: Dict = {
|
||||||
'buy': 'limit',
|
'buy': 'limit',
|
||||||
'sell': 'limit',
|
'sell': 'limit',
|
||||||
'stoploss': 'limit'
|
'stoploss': 'limit',
|
||||||
|
'stoploss_on_exchange': False
|
||||||
}
|
}
|
||||||
|
|
||||||
# run "populate_indicators" only for new candle
|
# run "populate_indicators" only for new candle
|
||||||
@ -228,7 +224,7 @@ class IStrategy(ABC):
|
|||||||
current_rate = low or rate
|
current_rate = low or rate
|
||||||
current_profit = trade.calc_profit_percent(current_rate)
|
current_profit = trade.calc_profit_percent(current_rate)
|
||||||
|
|
||||||
if self.stoploss_on_exchange:
|
if self.order_types.get('stoploss_on_exchange'):
|
||||||
stoplossflag = SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
|
stoplossflag = SellCheckTuple(sell_flag=False, sell_type=SellType.NONE)
|
||||||
else:
|
else:
|
||||||
stoplossflag = self.stop_loss_reached(current_rate=current_rate, trade=trade,
|
stoplossflag = self.stop_loss_reached(current_rate=current_rate, trade=trade,
|
||||||
|
@ -362,7 +362,14 @@ def test_validate_order_types(default_conf, mocker):
|
|||||||
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
mocker.patch('freqtrade.exchange.Exchange._init_ccxt', MagicMock(return_value=api_mock))
|
||||||
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
mocker.patch('freqtrade.exchange.Exchange._load_markets', MagicMock(return_value={}))
|
||||||
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
mocker.patch('freqtrade.exchange.Exchange.validate_timeframes', MagicMock())
|
||||||
default_conf['order_types'] = {'buy': 'limit', 'sell': 'limit', 'stoploss': 'market'}
|
mocker.patch('freqtrade.exchange.Exchange.name', 'Bittrex')
|
||||||
|
default_conf['order_types'] = {
|
||||||
|
'buy': 'limit',
|
||||||
|
'sell': 'limit',
|
||||||
|
'stoploss': 'market',
|
||||||
|
'stoploss_on_exchange': False
|
||||||
|
}
|
||||||
|
|
||||||
Exchange(default_conf)
|
Exchange(default_conf)
|
||||||
|
|
||||||
type(api_mock).has = PropertyMock(return_value={'createMarketOrder': False})
|
type(api_mock).has = PropertyMock(return_value={'createMarketOrder': False})
|
||||||
@ -374,6 +381,17 @@ def test_validate_order_types(default_conf, mocker):
|
|||||||
match=r'Exchange .* does not support market orders.'):
|
match=r'Exchange .* does not support market orders.'):
|
||||||
Exchange(default_conf)
|
Exchange(default_conf)
|
||||||
|
|
||||||
|
default_conf['order_types'] = {
|
||||||
|
'buy': 'limit',
|
||||||
|
'sell': 'limit',
|
||||||
|
'stoploss': 'limit',
|
||||||
|
'stoploss_on_exchange': True
|
||||||
|
}
|
||||||
|
|
||||||
|
with pytest.raises(OperationalException,
|
||||||
|
match=r'On exchange stoploss is not supported for .*'):
|
||||||
|
Exchange(default_conf)
|
||||||
|
|
||||||
|
|
||||||
def test_validate_order_types_not_in_config(default_conf, mocker):
|
def test_validate_order_types_not_in_config(default_conf, mocker):
|
||||||
api_mock = MagicMock()
|
api_mock = MagicMock()
|
||||||
|
@ -893,7 +893,7 @@ def test_add_stoploss_on_exchange(mocker, default_conf, limit_buy_order) -> None
|
|||||||
mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_limit)
|
mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_limit)
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
freqtrade.strategy.stoploss_on_exchange = True
|
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
||||||
|
|
||||||
trade = MagicMock()
|
trade = MagicMock()
|
||||||
trade.open_order_id = None
|
trade.open_order_id = None
|
||||||
@ -1595,7 +1595,7 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf,
|
|||||||
mocker.patch('freqtrade.exchange.Exchange.cancel_order', cancel_order)
|
mocker.patch('freqtrade.exchange.Exchange.cancel_order', cancel_order)
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
freqtrade.strategy.stoploss_on_exchange = True
|
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
@ -1647,7 +1647,7 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf,
|
|||||||
mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_limit)
|
mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_limit)
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
freqtrade.strategy.stoploss_on_exchange = True
|
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
|
|
||||||
# Create some test data
|
# Create some test data
|
||||||
@ -2516,9 +2516,3 @@ def test_startup_messages(default_conf, mocker):
|
|||||||
default_conf['dynamic_whitelist'] = 20
|
default_conf['dynamic_whitelist'] = 20
|
||||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||||
assert freqtrade.state is State.RUNNING
|
assert freqtrade.state is State.RUNNING
|
||||||
|
|
||||||
|
|
||||||
def test_check_consistency(default_conf, mocker, caplog):
|
|
||||||
mocker.patch('freqtrade.freqtradebot.IStrategy.stoploss_on_exchange', True)
|
|
||||||
with pytest.raises(OperationalException):
|
|
||||||
FreqtradeBot(default_conf)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user