Complete rename of stoploss_limit to stoploss
This commit is contained in:
parent
256fc2e78c
commit
16b34e11ca
@ -530,7 +530,7 @@ class Exchange:
|
|||||||
Note: Changes to this interface need to be applied to all sub-classes too.
|
Note: Changes to this interface need to be applied to all sub-classes too.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
raise OperationalException(f"stoploss_limit is not implemented for {self.name}.")
|
raise OperationalException(f"stoploss is not implemented for {self.name}.")
|
||||||
|
|
||||||
@retrier
|
@retrier
|
||||||
def get_balance(self, currency: str) -> float:
|
def get_balance(self, currency: str) -> float:
|
||||||
|
@ -1758,9 +1758,9 @@ def test_get_fee(default_conf, mocker, exchange_name):
|
|||||||
'get_fee', 'calculate_fee', symbol="ETH/BTC")
|
'get_fee', 'calculate_fee', symbol="ETH/BTC")
|
||||||
|
|
||||||
|
|
||||||
def test_stoploss_limit_order_unsupported_exchange(default_conf, mocker):
|
def test_stoploss_order_unsupported_exchange(default_conf, mocker):
|
||||||
exchange = get_patched_exchange(mocker, default_conf, 'bittrex')
|
exchange = get_patched_exchange(mocker, default_conf, 'bittrex')
|
||||||
with pytest.raises(OperationalException, match=r"stoploss_limit is not implemented .*"):
|
with pytest.raises(OperationalException, match=r"stoploss is not implemented .*"):
|
||||||
exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={})
|
exchange.stoploss(pair='ETH/BTC', amount=1, stop_price=220, order_types={})
|
||||||
|
|
||||||
|
|
||||||
|
@ -1031,8 +1031,8 @@ def test_add_stoploss_on_exchange(mocker, default_conf, limit_buy_order) -> None
|
|||||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
|
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
|
||||||
return_value=limit_buy_order['amount'])
|
return_value=limit_buy_order['amount'])
|
||||||
|
|
||||||
stoploss_limit = MagicMock(return_value={'id': 13434334})
|
stoploss = MagicMock(return_value={'id': 13434334})
|
||||||
mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_limit)
|
mocker.patch('freqtrade.exchange.Exchange.stoploss', stoploss)
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
||||||
@ -1045,13 +1045,13 @@ def test_add_stoploss_on_exchange(mocker, default_conf, limit_buy_order) -> None
|
|||||||
|
|
||||||
freqtrade.exit_positions(trades)
|
freqtrade.exit_positions(trades)
|
||||||
assert trade.stoploss_order_id == '13434334'
|
assert trade.stoploss_order_id == '13434334'
|
||||||
assert stoploss_limit.call_count == 1
|
assert stoploss.call_count == 1
|
||||||
assert trade.is_open is True
|
assert trade.is_open is True
|
||||||
|
|
||||||
|
|
||||||
def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
|
def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
|
||||||
limit_buy_order, limit_sell_order) -> None:
|
limit_buy_order, limit_sell_order) -> None:
|
||||||
stoploss_limit = MagicMock(return_value={'id': 13434334})
|
stoploss = MagicMock(return_value={'id': 13434334})
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -1064,7 +1064,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
|
|||||||
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
|
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
stoploss_limit=stoploss_limit
|
stoploss=stoploss
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
@ -1078,7 +1078,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
|
|||||||
trade.stoploss_order_id = None
|
trade.stoploss_order_id = None
|
||||||
|
|
||||||
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||||
assert stoploss_limit.call_count == 1
|
assert stoploss.call_count == 1
|
||||||
assert trade.stoploss_order_id == "13434334"
|
assert trade.stoploss_order_id == "13434334"
|
||||||
|
|
||||||
# Second case: when stoploss is set but it is not yet hit
|
# Second case: when stoploss is set but it is not yet hit
|
||||||
@ -1102,10 +1102,10 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
|
|||||||
|
|
||||||
canceled_stoploss_order = MagicMock(return_value={'status': 'canceled'})
|
canceled_stoploss_order = MagicMock(return_value={'status': 'canceled'})
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_order', canceled_stoploss_order)
|
mocker.patch('freqtrade.exchange.Exchange.get_order', canceled_stoploss_order)
|
||||||
stoploss_limit.reset_mock()
|
stoploss.reset_mock()
|
||||||
|
|
||||||
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
assert freqtrade.handle_stoploss_on_exchange(trade) is False
|
||||||
assert stoploss_limit.call_count == 1
|
assert stoploss.call_count == 1
|
||||||
assert trade.stoploss_order_id == "13434334"
|
assert trade.stoploss_order_id == "13434334"
|
||||||
|
|
||||||
# Fourth case: when stoploss is set and it is hit
|
# Fourth case: when stoploss is set and it is hit
|
||||||
@ -1132,7 +1132,7 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
|
|||||||
assert trade.is_open is False
|
assert trade.is_open is False
|
||||||
|
|
||||||
mocker.patch(
|
mocker.patch(
|
||||||
'freqtrade.exchange.Exchange.stoploss_limit',
|
'freqtrade.exchange.Exchange.stoploss',
|
||||||
side_effect=DependencyException()
|
side_effect=DependencyException()
|
||||||
)
|
)
|
||||||
freqtrade.handle_stoploss_on_exchange(trade)
|
freqtrade.handle_stoploss_on_exchange(trade)
|
||||||
@ -1142,11 +1142,11 @@ def test_handle_stoploss_on_exchange(mocker, default_conf, fee, caplog,
|
|||||||
# Fifth case: get_order returns InvalidOrder
|
# Fifth case: get_order returns InvalidOrder
|
||||||
# It should try to add stoploss order
|
# It should try to add stoploss order
|
||||||
trade.stoploss_order_id = 100
|
trade.stoploss_order_id = 100
|
||||||
stoploss_limit.reset_mock()
|
stoploss.reset_mock()
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_order', side_effect=InvalidOrderException())
|
mocker.patch('freqtrade.exchange.Exchange.get_order', side_effect=InvalidOrderException())
|
||||||
mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_limit)
|
mocker.patch('freqtrade.exchange.Exchange.stoploss', stoploss)
|
||||||
freqtrade.handle_stoploss_on_exchange(trade)
|
freqtrade.handle_stoploss_on_exchange(trade)
|
||||||
assert stoploss_limit.call_count == 1
|
assert stoploss.call_count == 1
|
||||||
|
|
||||||
|
|
||||||
def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog,
|
def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog,
|
||||||
@ -1165,7 +1165,7 @@ def test_handle_sle_cancel_cant_recreate(mocker, default_conf, fee, caplog,
|
|||||||
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
|
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
get_order=MagicMock(return_value={'status': 'canceled'}),
|
get_order=MagicMock(return_value={'status': 'canceled'}),
|
||||||
stoploss_limit=MagicMock(side_effect=DependencyException()),
|
stoploss=MagicMock(side_effect=DependencyException()),
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
@ -1199,7 +1199,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee,
|
|||||||
sell=sell_mock,
|
sell=sell_mock,
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
get_order=MagicMock(return_value={'status': 'canceled'}),
|
get_order=MagicMock(return_value={'status': 'canceled'}),
|
||||||
stoploss_limit=MagicMock(side_effect=InvalidOrderException()),
|
stoploss=MagicMock(side_effect=InvalidOrderException()),
|
||||||
)
|
)
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
patch_get_signal(freqtrade)
|
patch_get_signal(freqtrade)
|
||||||
@ -1229,7 +1229,7 @@ def test_create_stoploss_order_invalid_order(mocker, default_conf, caplog, fee,
|
|||||||
def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
|
def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
|
||||||
limit_buy_order, limit_sell_order) -> None:
|
limit_buy_order, limit_sell_order) -> None:
|
||||||
# When trailing stoploss is set
|
# When trailing stoploss is set
|
||||||
stoploss_limit = MagicMock(return_value={'id': 13434334})
|
stoploss = MagicMock(return_value={'id': 13434334})
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
@ -1241,7 +1241,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
|
|||||||
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
|
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
stoploss_limit=stoploss_limit
|
stoploss=stoploss
|
||||||
)
|
)
|
||||||
|
|
||||||
# enabling TSL
|
# enabling TSL
|
||||||
@ -1296,7 +1296,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
|
|||||||
cancel_order_mock = MagicMock()
|
cancel_order_mock = MagicMock()
|
||||||
stoploss_order_mock = MagicMock()
|
stoploss_order_mock = MagicMock()
|
||||||
mocker.patch('freqtrade.exchange.Exchange.cancel_order', cancel_order_mock)
|
mocker.patch('freqtrade.exchange.Exchange.cancel_order', cancel_order_mock)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_order_mock)
|
mocker.patch('freqtrade.exchange.Exchange.stoploss', stoploss_order_mock)
|
||||||
|
|
||||||
# stoploss should not be updated as the interval is 60 seconds
|
# stoploss should not be updated as the interval is 60 seconds
|
||||||
assert freqtrade.handle_trade(trade) is False
|
assert freqtrade.handle_trade(trade) is False
|
||||||
@ -1322,7 +1322,7 @@ def test_handle_stoploss_on_exchange_trailing(mocker, default_conf, fee, caplog,
|
|||||||
def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, caplog,
|
def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, caplog,
|
||||||
limit_buy_order, limit_sell_order) -> None:
|
limit_buy_order, limit_sell_order) -> None:
|
||||||
# When trailing stoploss is set
|
# When trailing stoploss is set
|
||||||
stoploss_limit = MagicMock(return_value={'id': 13434334})
|
stoploss = MagicMock(return_value={'id': 13434334})
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
|
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
@ -1335,7 +1335,7 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c
|
|||||||
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
|
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
stoploss_limit=stoploss_limit
|
stoploss=stoploss
|
||||||
)
|
)
|
||||||
|
|
||||||
# enabling TSL
|
# enabling TSL
|
||||||
@ -1375,12 +1375,12 @@ def test_handle_stoploss_on_exchange_trailing_error(mocker, default_conf, fee, c
|
|||||||
assert log_has_re(r"Could not cancel stoploss order abcd for pair ETH/BTC.*", caplog)
|
assert log_has_re(r"Could not cancel stoploss order abcd for pair ETH/BTC.*", caplog)
|
||||||
|
|
||||||
# Still try to create order
|
# Still try to create order
|
||||||
assert stoploss_limit.call_count == 1
|
assert stoploss.call_count == 1
|
||||||
|
|
||||||
# Fail creating stoploss order
|
# Fail creating stoploss order
|
||||||
caplog.clear()
|
caplog.clear()
|
||||||
cancel_mock = mocker.patch("freqtrade.exchange.Exchange.cancel_order", MagicMock())
|
cancel_mock = mocker.patch("freqtrade.exchange.Exchange.cancel_order", MagicMock())
|
||||||
mocker.patch("freqtrade.exchange.Exchange.stoploss_limit", side_effect=DependencyException())
|
mocker.patch("freqtrade.exchange.Exchange.stoploss", side_effect=DependencyException())
|
||||||
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
|
freqtrade.handle_trailing_stoploss_on_exchange(trade, stoploss_order_hanging)
|
||||||
assert cancel_mock.call_count == 1
|
assert cancel_mock.call_count == 1
|
||||||
assert log_has_re(r"Could not create trailing stoploss order for pair ETH/BTC\..*", caplog)
|
assert log_has_re(r"Could not create trailing stoploss order for pair ETH/BTC\..*", caplog)
|
||||||
@ -1390,7 +1390,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog,
|
|||||||
limit_buy_order, limit_sell_order) -> None:
|
limit_buy_order, limit_sell_order) -> None:
|
||||||
|
|
||||||
# When trailing stoploss is set
|
# When trailing stoploss is set
|
||||||
stoploss_limit = MagicMock(return_value={'id': 13434334})
|
stoploss = MagicMock(return_value={'id': 13434334})
|
||||||
patch_RPCManager(mocker)
|
patch_RPCManager(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
patch_edge(mocker)
|
patch_edge(mocker)
|
||||||
@ -1406,7 +1406,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog,
|
|||||||
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
buy=MagicMock(return_value={'id': limit_buy_order['id']}),
|
||||||
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
|
sell=MagicMock(return_value={'id': limit_sell_order['id']}),
|
||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
stoploss_limit=stoploss_limit
|
stoploss=stoploss
|
||||||
)
|
)
|
||||||
|
|
||||||
# enabling TSL
|
# enabling TSL
|
||||||
@ -1459,7 +1459,7 @@ def test_tsl_on_exchange_compatible_with_edge(mocker, edge_conf, fee, caplog,
|
|||||||
cancel_order_mock = MagicMock()
|
cancel_order_mock = MagicMock()
|
||||||
stoploss_order_mock = MagicMock()
|
stoploss_order_mock = MagicMock()
|
||||||
mocker.patch('freqtrade.exchange.Exchange.cancel_order', cancel_order_mock)
|
mocker.patch('freqtrade.exchange.Exchange.cancel_order', cancel_order_mock)
|
||||||
mocker.patch('freqtrade.exchange.Exchange.stoploss_limit', stoploss_order_mock)
|
mocker.patch('freqtrade.exchange.Exchange.stoploss', stoploss_order_mock)
|
||||||
|
|
||||||
# price goes down 5%
|
# price goes down 5%
|
||||||
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={
|
mocker.patch('freqtrade.exchange.Exchange.fetch_ticker', MagicMock(return_value={
|
||||||
@ -2423,7 +2423,7 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticke
|
|||||||
default_conf['exchange']['name'] = 'binance'
|
default_conf['exchange']['name'] = 'binance'
|
||||||
rpc_mock = patch_RPCManager(mocker)
|
rpc_mock = patch_RPCManager(mocker)
|
||||||
patch_exchange(mocker)
|
patch_exchange(mocker)
|
||||||
stoploss_limit = MagicMock(return_value={
|
stoploss = MagicMock(return_value={
|
||||||
'id': 123,
|
'id': 123,
|
||||||
'info': {
|
'info': {
|
||||||
'foo': 'bar'
|
'foo': 'bar'
|
||||||
@ -2437,7 +2437,7 @@ def test_execute_sell_with_stoploss_on_exchange(default_conf, ticker, fee, ticke
|
|||||||
get_fee=fee,
|
get_fee=fee,
|
||||||
amount_to_precision=lambda s, x, y: y,
|
amount_to_precision=lambda s, x, y: y,
|
||||||
price_to_precision=lambda s, x, y: y,
|
price_to_precision=lambda s, x, y: y,
|
||||||
stoploss_limit=stoploss_limit,
|
stoploss=stoploss,
|
||||||
cancel_order=cancel_order,
|
cancel_order=cancel_order,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -2482,14 +2482,14 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, ticker, f
|
|||||||
price_to_precision=lambda s, x, y: y,
|
price_to_precision=lambda s, x, y: y,
|
||||||
)
|
)
|
||||||
|
|
||||||
stoploss_limit = MagicMock(return_value={
|
stoploss = MagicMock(return_value={
|
||||||
'id': 123,
|
'id': 123,
|
||||||
'info': {
|
'info': {
|
||||||
'foo': 'bar'
|
'foo': 'bar'
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
mocker.patch('freqtrade.exchange.Binance.stoploss_limit', stoploss_limit)
|
mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss)
|
||||||
|
|
||||||
freqtrade = FreqtradeBot(default_conf)
|
freqtrade = FreqtradeBot(default_conf)
|
||||||
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
freqtrade.strategy.order_types['stoploss_on_exchange'] = True
|
||||||
@ -2507,7 +2507,7 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, ticker, f
|
|||||||
# Assuming stoploss on exchnage is hit
|
# Assuming stoploss on exchnage is hit
|
||||||
# stoploss_order_id should become None
|
# stoploss_order_id should become None
|
||||||
# and trade should be sold at the price of stoploss
|
# and trade should be sold at the price of stoploss
|
||||||
stoploss_limit_executed = MagicMock(return_value={
|
stoploss_executed = MagicMock(return_value={
|
||||||
"id": "123",
|
"id": "123",
|
||||||
"timestamp": 1542707426845,
|
"timestamp": 1542707426845,
|
||||||
"datetime": "2018-11-20T09:50:26.845Z",
|
"datetime": "2018-11-20T09:50:26.845Z",
|
||||||
@ -2525,7 +2525,7 @@ def test_may_execute_sell_after_stoploss_on_exchange_hit(default_conf, ticker, f
|
|||||||
"fee": None,
|
"fee": None,
|
||||||
"trades": None
|
"trades": None
|
||||||
})
|
})
|
||||||
mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_limit_executed)
|
mocker.patch('freqtrade.exchange.Exchange.get_order', stoploss_executed)
|
||||||
|
|
||||||
freqtrade.exit_positions(trades)
|
freqtrade.exit_positions(trades)
|
||||||
assert trade.stoploss_order_id is None
|
assert trade.stoploss_order_id is None
|
||||||
|
@ -20,7 +20,7 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee,
|
|||||||
default_conf['max_open_trades'] = 3
|
default_conf['max_open_trades'] = 3
|
||||||
default_conf['exchange']['name'] = 'binance'
|
default_conf['exchange']['name'] = 'binance'
|
||||||
|
|
||||||
stoploss_limit = {
|
stoploss = {
|
||||||
'id': 123,
|
'id': 123,
|
||||||
'info': {}
|
'info': {}
|
||||||
}
|
}
|
||||||
@ -53,7 +53,7 @@ def test_may_execute_sell_stoploss_on_exchange_multi(default_conf, ticker, fee,
|
|||||||
SellCheckTuple(sell_flag=True, sell_type=SellType.SELL_SIGNAL)]
|
SellCheckTuple(sell_flag=True, sell_type=SellType.SELL_SIGNAL)]
|
||||||
)
|
)
|
||||||
cancel_order_mock = MagicMock()
|
cancel_order_mock = MagicMock()
|
||||||
mocker.patch('freqtrade.exchange.Binance.stoploss_limit', stoploss_limit)
|
mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss)
|
||||||
mocker.patch.multiple(
|
mocker.patch.multiple(
|
||||||
'freqtrade.exchange.Exchange',
|
'freqtrade.exchange.Exchange',
|
||||||
fetch_ticker=ticker,
|
fetch_ticker=ticker,
|
||||||
|
Loading…
Reference in New Issue
Block a user