Merge branch 'develop' into cyber-forcesell-tg
This commit is contained in:
@@ -137,7 +137,7 @@ def test_fiat_too_many_requests_response(mocker, caplog):
|
||||
assert len(fiat_convert._coinlistings) == 0
|
||||
assert fiat_convert._backoff > datetime.datetime.now().timestamp()
|
||||
assert log_has(
|
||||
'Too many requests for Coingecko API, backing off and trying again later.',
|
||||
'Too many requests for CoinGecko API, backing off and trying again later.',
|
||||
caplog
|
||||
)
|
||||
|
||||
@@ -156,7 +156,7 @@ def test_fiat_multiple_coins(mocker, caplog):
|
||||
assert fiat_convert._get_gekko_id('hnt') is None
|
||||
assert fiat_convert._get_gekko_id('eth') == 'ethereum'
|
||||
|
||||
assert log_has('Found multiple mappings in goingekko for hnt.', caplog)
|
||||
assert log_has('Found multiple mappings in CoinGecko for hnt.', caplog)
|
||||
|
||||
|
||||
def test_fiat_invalid_response(mocker, caplog):
|
||||
|
@@ -8,7 +8,7 @@ import pytest
|
||||
from numpy import isnan
|
||||
|
||||
from freqtrade.edge import PairInfo
|
||||
from freqtrade.enums import State
|
||||
from freqtrade.enums import SignalDirection, State, TradingMode
|
||||
from freqtrade.exceptions import ExchangeError, InvalidOrderException, TemporaryError
|
||||
from freqtrade.persistence import Trade
|
||||
from freqtrade.persistence.models import Order
|
||||
@@ -71,6 +71,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
'max_rate': ANY,
|
||||
'strategy': ANY,
|
||||
'buy_tag': ANY,
|
||||
'enter_tag': ANY,
|
||||
'timeframe': 5,
|
||||
'open_order_id': ANY,
|
||||
'close_date': None,
|
||||
@@ -109,13 +110,20 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
'stoploss_entry_dist_ratio': -0.10448878,
|
||||
'open_order': None,
|
||||
'exchange': 'binance',
|
||||
'leverage': 1.0,
|
||||
'interest_rate': 0.0,
|
||||
'liquidation_price': None,
|
||||
'is_short': False,
|
||||
'funding_fees': 0.0,
|
||||
'trading_mode': TradingMode.SPOT,
|
||||
'orders': [{
|
||||
'amount': 91.07468123, 'average': 1.098e-05, 'safe_price': 1.098e-05,
|
||||
'cost': 0.0009999999999054, 'filled': 91.07468123, 'ft_order_side': 'buy',
|
||||
'order_date': ANY, 'order_timestamp': ANY, 'order_filled_date': ANY,
|
||||
'order_filled_timestamp': ANY, 'order_type': 'limit', 'price': 1.098e-05,
|
||||
'is_open': False, 'pair': 'ETH/BTC', 'order_id': ANY,
|
||||
'remaining': ANY, 'status': ANY}],
|
||||
'remaining': ANY, 'status': ANY, 'ft_is_entry': True,
|
||||
}],
|
||||
}
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_rate',
|
||||
@@ -145,6 +153,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
'max_rate': ANY,
|
||||
'strategy': ANY,
|
||||
'buy_tag': ANY,
|
||||
'enter_tag': ANY,
|
||||
'timeframe': ANY,
|
||||
'open_order_id': ANY,
|
||||
'close_date': None,
|
||||
@@ -183,13 +192,20 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
|
||||
'stoploss_entry_dist_ratio': -0.10448878,
|
||||
'open_order': None,
|
||||
'exchange': 'binance',
|
||||
'leverage': 1.0,
|
||||
'interest_rate': 0.0,
|
||||
'liquidation_price': None,
|
||||
'is_short': False,
|
||||
'funding_fees': 0.0,
|
||||
'trading_mode': TradingMode.SPOT,
|
||||
'orders': [{
|
||||
'amount': 91.07468123, 'average': 1.098e-05, 'safe_price': 1.098e-05,
|
||||
'cost': 0.0009999999999054, 'filled': 91.07468123, 'ft_order_side': 'buy',
|
||||
'order_date': ANY, 'order_timestamp': ANY, 'order_filled_date': ANY,
|
||||
'order_filled_timestamp': ANY, 'order_type': 'limit', 'price': 1.098e-05,
|
||||
'is_open': False, 'pair': 'ETH/BTC', 'order_id': ANY,
|
||||
'remaining': ANY, 'status': ANY}],
|
||||
'remaining': ANY, 'status': ANY, 'ft_is_entry': True,
|
||||
}],
|
||||
}
|
||||
|
||||
|
||||
@@ -304,7 +320,8 @@ def test_rpc_daily_profit(default_conf, update, ticker, fee,
|
||||
rpc._rpc_daily_profit(0, stake_currency, fiat_display_currency)
|
||||
|
||||
|
||||
def test_rpc_trade_history(mocker, default_conf, markets, fee):
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
def test_rpc_trade_history(mocker, default_conf, markets, fee, is_short):
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
@@ -312,7 +329,7 @@ def test_rpc_trade_history(mocker, default_conf, markets, fee):
|
||||
)
|
||||
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
create_mock_trades(fee)
|
||||
create_mock_trades(fee, is_short)
|
||||
rpc = RPC(freqtradebot)
|
||||
rpc._fiat_converter = CryptoToFiatConverter()
|
||||
trades = rpc._rpc_trade_history(2)
|
||||
@@ -329,7 +346,8 @@ def test_rpc_trade_history(mocker, default_conf, markets, fee):
|
||||
assert trades['trades'][0]['pair'] == 'XRP/BTC'
|
||||
|
||||
|
||||
def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog):
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog, is_short):
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
stoploss_mock = MagicMock()
|
||||
cancel_mock = MagicMock()
|
||||
@@ -342,7 +360,7 @@ def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog):
|
||||
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
freqtradebot.strategy.order_types['stoploss_on_exchange'] = True
|
||||
create_mock_trades(fee)
|
||||
create_mock_trades(fee, is_short)
|
||||
rpc = RPC(freqtradebot)
|
||||
with pytest.raises(RPCException, match='invalid argument'):
|
||||
rpc._rpc_delete('200')
|
||||
@@ -584,6 +602,30 @@ def test_rpc_balance_handle(default_conf, mocker, tickers):
|
||||
'used': 5.0,
|
||||
}
|
||||
}
|
||||
mock_pos = [
|
||||
{
|
||||
"symbol": "ETH/USDT:USDT",
|
||||
"timestamp": None,
|
||||
"datetime": None,
|
||||
"initialMargin": 0.0,
|
||||
"initialMarginPercentage": None,
|
||||
"maintenanceMargin": 0.0,
|
||||
"maintenanceMarginPercentage": 0.005,
|
||||
"entryPrice": 0.0,
|
||||
"notional": 100.0,
|
||||
"leverage": 5.0,
|
||||
"unrealizedPnl": 0.0,
|
||||
"contracts": 100.0,
|
||||
"contractSize": 1,
|
||||
"marginRatio": None,
|
||||
"liquidationPrice": 0.0,
|
||||
"markPrice": 2896.41,
|
||||
"collateral": 20,
|
||||
"marginType": "isolated",
|
||||
"side": 'short',
|
||||
"percentage": None
|
||||
}
|
||||
]
|
||||
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
|
||||
@@ -593,47 +635,77 @@ def test_rpc_balance_handle(default_conf, mocker, tickers):
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
validate_trading_mode_and_margin_mode=MagicMock(),
|
||||
get_balances=MagicMock(return_value=mock_balance),
|
||||
fetch_positions=MagicMock(return_value=mock_pos),
|
||||
get_tickers=tickers,
|
||||
get_valid_pair_combination=MagicMock(
|
||||
side_effect=lambda a, b: f"{b}/{a}" if a == "USDT" else f"{a}/{b}")
|
||||
)
|
||||
default_conf['dry_run'] = False
|
||||
default_conf['trading_mode'] = 'futures'
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
patch_get_signal(freqtradebot)
|
||||
rpc = RPC(freqtradebot)
|
||||
rpc._fiat_converter = CryptoToFiatConverter()
|
||||
|
||||
result = rpc._rpc_balance(default_conf['stake_currency'], default_conf['fiat_display_currency'])
|
||||
assert prec_satoshi(result['total'], 12.30909624)
|
||||
assert prec_satoshi(result['value'], 184636.443606915)
|
||||
assert prec_satoshi(result['total'], 30.30909624)
|
||||
assert prec_satoshi(result['value'], 454636.44360691)
|
||||
assert tickers.call_count == 1
|
||||
assert tickers.call_args_list[0][1]['cached'] is True
|
||||
assert 'USD' == result['symbol']
|
||||
assert result['currencies'] == [
|
||||
{'currency': 'BTC',
|
||||
'free': 10.0,
|
||||
'balance': 12.0,
|
||||
'used': 2.0,
|
||||
'est_stake': 12.0,
|
||||
'stake': 'BTC',
|
||||
},
|
||||
{'free': 1.0,
|
||||
'balance': 5.0,
|
||||
'currency': 'ETH',
|
||||
'est_stake': 0.30794,
|
||||
'used': 4.0,
|
||||
'stake': 'BTC',
|
||||
},
|
||||
{'free': 5.0,
|
||||
'balance': 10.0,
|
||||
'currency': 'USDT',
|
||||
'est_stake': 0.0011562404610161968,
|
||||
'used': 5.0,
|
||||
'stake': 'BTC',
|
||||
}
|
||||
{
|
||||
'currency': 'BTC',
|
||||
'free': 10.0,
|
||||
'balance': 12.0,
|
||||
'used': 2.0,
|
||||
'est_stake': 10.0, # In futures mode, "free" is used here.
|
||||
'stake': 'BTC',
|
||||
'is_position': False,
|
||||
'leverage': 1.0,
|
||||
'position': 0.0,
|
||||
'side': 'long',
|
||||
},
|
||||
{
|
||||
'free': 1.0,
|
||||
'balance': 5.0,
|
||||
'currency': 'ETH',
|
||||
'est_stake': 0.30794,
|
||||
'used': 4.0,
|
||||
'stake': 'BTC',
|
||||
'is_position': False,
|
||||
'leverage': 1.0,
|
||||
'position': 0.0,
|
||||
'side': 'long',
|
||||
|
||||
},
|
||||
{
|
||||
'free': 5.0,
|
||||
'balance': 10.0,
|
||||
'currency': 'USDT',
|
||||
'est_stake': 0.0011562404610161968,
|
||||
'used': 5.0,
|
||||
'stake': 'BTC',
|
||||
'is_position': False,
|
||||
'leverage': 1.0,
|
||||
'position': 0.0,
|
||||
'side': 'long',
|
||||
},
|
||||
{
|
||||
'free': 0.0,
|
||||
'balance': 0.0,
|
||||
'currency': 'ETH/USDT:USDT',
|
||||
'est_stake': 20,
|
||||
'used': 0,
|
||||
'stake': 'BTC',
|
||||
'is_position': True,
|
||||
'leverage': 5.0,
|
||||
'position': 1000.0,
|
||||
'side': 'short',
|
||||
}
|
||||
]
|
||||
assert result['total'] == 12.309096240461017
|
||||
|
||||
|
||||
def test_rpc_start(mocker, default_conf) -> None:
|
||||
@@ -697,7 +769,7 @@ def test_rpc_stopbuy(mocker, default_conf) -> None:
|
||||
assert freqtradebot.config['max_open_trades'] == 0
|
||||
|
||||
|
||||
def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
||||
def test_rpc_forceexit(default_conf, ticker, fee, mocker) -> None:
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
|
||||
cancel_order_mock = MagicMock()
|
||||
@@ -724,29 +796,29 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
||||
|
||||
freqtradebot.state = State.STOPPED
|
||||
with pytest.raises(RPCException, match=r'.*trader is not running*'):
|
||||
rpc._rpc_forcesell(None)
|
||||
rpc._rpc_forceexit(None)
|
||||
|
||||
freqtradebot.state = State.RUNNING
|
||||
with pytest.raises(RPCException, match=r'.*invalid argument*'):
|
||||
rpc._rpc_forcesell(None)
|
||||
rpc._rpc_forceexit(None)
|
||||
|
||||
msg = rpc._rpc_forcesell('all')
|
||||
msg = rpc._rpc_forceexit('all')
|
||||
assert msg == {'result': 'Created sell orders for all open trades.'}
|
||||
|
||||
freqtradebot.enter_positions()
|
||||
msg = rpc._rpc_forcesell('all')
|
||||
msg = rpc._rpc_forceexit('all')
|
||||
assert msg == {'result': 'Created sell orders for all open trades.'}
|
||||
|
||||
freqtradebot.enter_positions()
|
||||
msg = rpc._rpc_forcesell('2')
|
||||
msg = rpc._rpc_forceexit('2')
|
||||
assert msg == {'result': 'Created sell order for trade 2.'}
|
||||
|
||||
freqtradebot.state = State.STOPPED
|
||||
with pytest.raises(RPCException, match=r'.*trader is not running*'):
|
||||
rpc._rpc_forcesell(None)
|
||||
rpc._rpc_forceexit(None)
|
||||
|
||||
with pytest.raises(RPCException, match=r'.*trader is not running*'):
|
||||
rpc._rpc_forcesell('all')
|
||||
rpc._rpc_forceexit('all')
|
||||
|
||||
freqtradebot.state = State.RUNNING
|
||||
assert cancel_order_mock.call_count == 0
|
||||
@@ -775,7 +847,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
||||
)
|
||||
# check that the trade is called, which is done by ensuring exchange.cancel_order is called
|
||||
# and trade amount is updated
|
||||
rpc._rpc_forcesell('3')
|
||||
rpc._rpc_forceexit('3')
|
||||
assert cancel_order_mock.call_count == 1
|
||||
assert trade.amount == filled_amount
|
||||
|
||||
@@ -803,7 +875,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
||||
}
|
||||
)
|
||||
# check that the trade is called, which is done by ensuring exchange.cancel_order is called
|
||||
msg = rpc._rpc_forcesell('4')
|
||||
msg = rpc._rpc_forceexit('4')
|
||||
assert msg == {'result': 'Created sell order for trade 4.'}
|
||||
assert cancel_order_mock.call_count == 2
|
||||
assert trade.amount == amount
|
||||
@@ -820,7 +892,7 @@ def test_rpc_forcesell(default_conf, ticker, fee, mocker) -> None:
|
||||
'filled': 0.0
|
||||
}
|
||||
)
|
||||
msg = rpc._rpc_forcesell('3')
|
||||
msg = rpc._rpc_forceexit('3')
|
||||
assert msg == {'result': 'Created sell order for trade 3.'}
|
||||
# status quo, no exchange calls
|
||||
assert cancel_order_mock.call_count == 3
|
||||
@@ -862,8 +934,8 @@ def test_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
||||
assert prec_satoshi(res[0]['profit_pct'], 6.2)
|
||||
|
||||
|
||||
def test_buy_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
||||
limit_sell_order, mocker) -> None:
|
||||
def test_enter_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
||||
limit_sell_order, mocker) -> None:
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
@@ -891,23 +963,23 @@ def test_buy_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
||||
|
||||
trade.close_date = datetime.utcnow()
|
||||
trade.is_open = False
|
||||
res = rpc._rpc_buy_tag_performance(None)
|
||||
res = rpc._rpc_enter_tag_performance(None)
|
||||
|
||||
assert len(res) == 1
|
||||
assert res[0]['buy_tag'] == 'Other'
|
||||
assert res[0]['enter_tag'] == 'Other'
|
||||
assert res[0]['count'] == 1
|
||||
assert prec_satoshi(res[0]['profit_pct'], 6.2)
|
||||
|
||||
trade.buy_tag = "TEST_TAG"
|
||||
res = rpc._rpc_buy_tag_performance(None)
|
||||
trade.enter_tag = "TEST_TAG"
|
||||
res = rpc._rpc_enter_tag_performance(None)
|
||||
|
||||
assert len(res) == 1
|
||||
assert res[0]['buy_tag'] == 'TEST_TAG'
|
||||
assert res[0]['enter_tag'] == 'TEST_TAG'
|
||||
assert res[0]['count'] == 1
|
||||
assert prec_satoshi(res[0]['profit_pct'], 6.2)
|
||||
|
||||
|
||||
def test_buy_tag_performance_handle_2(mocker, default_conf, markets, fee):
|
||||
def test_enter_tag_performance_handle_2(mocker, default_conf, markets, fee):
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
@@ -918,21 +990,21 @@ def test_buy_tag_performance_handle_2(mocker, default_conf, markets, fee):
|
||||
create_mock_trades(fee)
|
||||
rpc = RPC(freqtradebot)
|
||||
|
||||
res = rpc._rpc_buy_tag_performance(None)
|
||||
res = rpc._rpc_enter_tag_performance(None)
|
||||
|
||||
assert len(res) == 2
|
||||
assert res[0]['buy_tag'] == 'TEST1'
|
||||
assert res[0]['enter_tag'] == 'TEST1'
|
||||
assert res[0]['count'] == 1
|
||||
assert prec_satoshi(res[0]['profit_pct'], 0.5)
|
||||
assert res[1]['buy_tag'] == 'Other'
|
||||
assert res[1]['enter_tag'] == 'Other'
|
||||
assert res[1]['count'] == 1
|
||||
assert prec_satoshi(res[1]['profit_pct'], 1.0)
|
||||
|
||||
# Test for a specific pair
|
||||
res = rpc._rpc_buy_tag_performance('ETC/BTC')
|
||||
res = rpc._rpc_enter_tag_performance('ETC/BTC')
|
||||
assert len(res) == 1
|
||||
assert res[0]['count'] == 1
|
||||
assert res[0]['buy_tag'] == 'TEST1'
|
||||
assert res[0]['enter_tag'] == 'TEST1'
|
||||
assert prec_satoshi(res[0]['profit_pct'], 0.5)
|
||||
|
||||
|
||||
@@ -1046,7 +1118,7 @@ def test_mix_tag_performance_handle(default_conf, ticker, limit_buy_order, fee,
|
||||
assert res[0]['count'] == 1
|
||||
assert prec_satoshi(res[0]['profit_pct'], 6.2)
|
||||
|
||||
trade.buy_tag = "TESTBUY"
|
||||
trade.enter_tag = "TESTBUY"
|
||||
trade.sell_reason = "TESTSELL"
|
||||
res = rpc._rpc_mix_tag_performance(None)
|
||||
|
||||
@@ -1108,7 +1180,7 @@ def test_rpc_count(mocker, default_conf, ticker, fee) -> None:
|
||||
assert counts["current"] == 1
|
||||
|
||||
|
||||
def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) -> None:
|
||||
def test_rpc_forceentry(mocker, default_conf, ticker, fee, limit_buy_order_open) -> None:
|
||||
default_conf['forcebuy_enable'] = True
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
buy_mm = MagicMock(return_value=limit_buy_order_open)
|
||||
@@ -1124,16 +1196,16 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) ->
|
||||
patch_get_signal(freqtradebot)
|
||||
rpc = RPC(freqtradebot)
|
||||
pair = 'ETH/BTC'
|
||||
trade = rpc._rpc_forcebuy(pair, None)
|
||||
trade = rpc._rpc_force_entry(pair, None)
|
||||
assert isinstance(trade, Trade)
|
||||
assert trade.pair == pair
|
||||
assert trade.open_rate == ticker()['bid']
|
||||
|
||||
# Test buy duplicate
|
||||
with pytest.raises(RPCException, match=r'position for ETH/BTC already open - id: 1'):
|
||||
rpc._rpc_forcebuy(pair, 0.0001)
|
||||
rpc._rpc_force_entry(pair, 0.0001)
|
||||
pair = 'XRP/BTC'
|
||||
trade = rpc._rpc_forcebuy(pair, 0.0001, order_type='limit')
|
||||
trade = rpc._rpc_force_entry(pair, 0.0001, order_type='limit')
|
||||
assert isinstance(trade, Trade)
|
||||
assert trade.pair == pair
|
||||
assert trade.open_rate == 0.0001
|
||||
@@ -1141,11 +1213,11 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) ->
|
||||
# Test buy pair not with stakes
|
||||
with pytest.raises(RPCException,
|
||||
match=r'Wrong pair selected. Only pairs with stake-currency.*'):
|
||||
rpc._rpc_forcebuy('LTC/ETH', 0.0001)
|
||||
rpc._rpc_force_entry('LTC/ETH', 0.0001)
|
||||
|
||||
# Test with defined stake_amount
|
||||
pair = 'LTC/BTC'
|
||||
trade = rpc._rpc_forcebuy(pair, 0.0001, order_type='limit', stake_amount=0.05)
|
||||
trade = rpc._rpc_force_entry(pair, 0.0001, order_type='limit', stake_amount=0.05)
|
||||
assert trade.stake_amount == 0.05
|
||||
assert trade.buy_tag == 'forceentry'
|
||||
|
||||
@@ -1156,11 +1228,11 @@ def test_rpcforcebuy(mocker, default_conf, ticker, fee, limit_buy_order_open) ->
|
||||
patch_get_signal(freqtradebot)
|
||||
rpc = RPC(freqtradebot)
|
||||
pair = 'TKN/BTC'
|
||||
trade = rpc._rpc_forcebuy(pair, None)
|
||||
trade = rpc._rpc_force_entry(pair, None)
|
||||
assert trade is None
|
||||
|
||||
|
||||
def test_rpcforcebuy_stopped(mocker, default_conf) -> None:
|
||||
def test_rpc_forceentry_stopped(mocker, default_conf) -> None:
|
||||
default_conf['forcebuy_enable'] = True
|
||||
default_conf['initial_state'] = 'stopped'
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
@@ -1170,18 +1242,30 @@ def test_rpcforcebuy_stopped(mocker, default_conf) -> None:
|
||||
rpc = RPC(freqtradebot)
|
||||
pair = 'ETH/BTC'
|
||||
with pytest.raises(RPCException, match=r'trader is not running'):
|
||||
rpc._rpc_forcebuy(pair, None)
|
||||
rpc._rpc_force_entry(pair, None)
|
||||
|
||||
|
||||
def test_rpcforcebuy_disabled(mocker, default_conf) -> None:
|
||||
def test_rpc_forceentry_disabled(mocker, default_conf) -> None:
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
patch_get_signal(freqtradebot)
|
||||
rpc = RPC(freqtradebot)
|
||||
pair = 'ETH/BTC'
|
||||
with pytest.raises(RPCException, match=r'Forcebuy not enabled.'):
|
||||
rpc._rpc_forcebuy(pair, None)
|
||||
with pytest.raises(RPCException, match=r'Forceentry not enabled.'):
|
||||
rpc._rpc_force_entry(pair, None)
|
||||
|
||||
|
||||
def test_rpc_forceentry_wrong_mode(mocker, default_conf) -> None:
|
||||
default_conf['forcebuy_enable'] = True
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
|
||||
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
|
||||
patch_get_signal(freqtradebot)
|
||||
rpc = RPC(freqtradebot)
|
||||
pair = 'ETH/BTC'
|
||||
with pytest.raises(RPCException, match="Can't go short on Spot markets."):
|
||||
rpc._rpc_force_entry(pair, None, order_side=SignalDirection.SHORT)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
|
@@ -17,7 +17,7 @@ from numpy import isnan
|
||||
from requests.auth import _basic_auth_str
|
||||
|
||||
from freqtrade.__init__ import __version__
|
||||
from freqtrade.enums import RunMode, State
|
||||
from freqtrade.enums import CandleType, RunMode, State, TradingMode
|
||||
from freqtrade.exceptions import DependencyException, ExchangeError, OperationalException
|
||||
from freqtrade.loggers import setup_logging, setup_logging_pre
|
||||
from freqtrade.persistence import PairLocks, Trade
|
||||
@@ -25,8 +25,8 @@ from freqtrade.rpc import RPC
|
||||
from freqtrade.rpc.api_server import ApiServer
|
||||
from freqtrade.rpc.api_server.api_auth import create_token, get_user_from_token
|
||||
from freqtrade.rpc.api_server.uvicorn_threaded import UvicornServer
|
||||
from tests.conftest import (create_mock_trades, get_mock_coro, get_patched_freqtradebot, log_has,
|
||||
log_has_re, patch_get_signal)
|
||||
from tests.conftest import (CURRENT_TEST_STRATEGY, create_mock_trades, get_mock_coro,
|
||||
get_patched_freqtradebot, log_has, log_has_re, patch_get_signal)
|
||||
|
||||
|
||||
BASE_URI = "/api/v1"
|
||||
@@ -452,6 +452,10 @@ def test_api_balance(botclient, mocker, rpc_balance, tickers):
|
||||
'used': 0.0,
|
||||
'est_stake': 12.0,
|
||||
'stake': 'BTC',
|
||||
'is_position': False,
|
||||
'leverage': 1.0,
|
||||
'position': 0.0,
|
||||
'side': 'long',
|
||||
}
|
||||
assert 'starting_capital' in response
|
||||
assert 'starting_capital_fiat' in response
|
||||
@@ -459,7 +463,8 @@ def test_api_balance(botclient, mocker, rpc_balance, tickers):
|
||||
assert 'starting_capital_ratio' in response
|
||||
|
||||
|
||||
def test_api_count(botclient, mocker, ticker, fee, markets):
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
def test_api_count(botclient, mocker, ticker, fee, markets, is_short):
|
||||
ftbot, client = botclient
|
||||
patch_get_signal(ftbot)
|
||||
mocker.patch.multiple(
|
||||
@@ -476,7 +481,7 @@ def test_api_count(botclient, mocker, ticker, fee, markets):
|
||||
assert rc.json()["max"] == 1
|
||||
|
||||
# Create some test data
|
||||
create_mock_trades(fee)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
rc = client_get(client, f"{BASE_URI}/count")
|
||||
assert_response(rc)
|
||||
assert rc.json()["current"] == 4
|
||||
@@ -527,21 +532,23 @@ def test_api_show_config(botclient):
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/show_config")
|
||||
assert_response(rc)
|
||||
assert 'dry_run' in rc.json()
|
||||
assert rc.json()['exchange'] == 'binance'
|
||||
assert rc.json()['timeframe'] == '5m'
|
||||
assert rc.json()['timeframe_ms'] == 300000
|
||||
assert rc.json()['timeframe_min'] == 5
|
||||
assert rc.json()['state'] == 'running'
|
||||
assert rc.json()['bot_name'] == 'freqtrade'
|
||||
assert rc.json()['strategy_version'] is None
|
||||
assert not rc.json()['trailing_stop']
|
||||
assert 'bid_strategy' in rc.json()
|
||||
assert 'ask_strategy' in rc.json()
|
||||
assert 'unfilledtimeout' in rc.json()
|
||||
assert 'version' in rc.json()
|
||||
assert 'api_version' in rc.json()
|
||||
assert 1.1 <= rc.json()['api_version'] <= 1.2
|
||||
response = rc.json()
|
||||
assert 'dry_run' in response
|
||||
assert response['exchange'] == 'binance'
|
||||
assert response['timeframe'] == '5m'
|
||||
assert response['timeframe_ms'] == 300000
|
||||
assert response['timeframe_min'] == 5
|
||||
assert response['state'] == 'running'
|
||||
assert response['bot_name'] == 'freqtrade'
|
||||
assert response['trading_mode'] == 'spot'
|
||||
assert response['strategy_version'] is None
|
||||
assert not response['trailing_stop']
|
||||
assert 'entry_pricing' in response
|
||||
assert 'exit_pricing' in response
|
||||
assert 'unfilledtimeout' in response
|
||||
assert 'version' in response
|
||||
assert 'api_version' in response
|
||||
assert 2.1 <= response['api_version'] <= 2.2
|
||||
|
||||
|
||||
def test_api_daily(botclient, mocker, ticker, fee, markets):
|
||||
@@ -562,7 +569,8 @@ def test_api_daily(botclient, mocker, ticker, fee, markets):
|
||||
assert rc.json()['data'][0]['date'] == str(datetime.utcnow().date())
|
||||
|
||||
|
||||
def test_api_trades(botclient, mocker, fee, markets):
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
def test_api_trades(botclient, mocker, fee, markets, is_short):
|
||||
ftbot, client = botclient
|
||||
patch_get_signal(ftbot)
|
||||
mocker.patch.multiple(
|
||||
@@ -575,13 +583,15 @@ def test_api_trades(botclient, mocker, fee, markets):
|
||||
assert rc.json()['trades_count'] == 0
|
||||
assert rc.json()['total_trades'] == 0
|
||||
|
||||
create_mock_trades(fee)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
Trade.query.session.flush()
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/trades")
|
||||
assert_response(rc)
|
||||
assert len(rc.json()['trades']) == 2
|
||||
assert rc.json()['trades_count'] == 2
|
||||
assert rc.json()['total_trades'] == 2
|
||||
assert rc.json()['trades'][0]['is_short'] == is_short
|
||||
rc = client_get(client, f"{BASE_URI}/trades?limit=1")
|
||||
assert_response(rc)
|
||||
assert len(rc.json()['trades']) == 1
|
||||
@@ -589,9 +599,10 @@ def test_api_trades(botclient, mocker, fee, markets):
|
||||
assert rc.json()['total_trades'] == 2
|
||||
|
||||
|
||||
def test_api_trade_single(botclient, mocker, fee, ticker, markets):
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
def test_api_trade_single(botclient, mocker, fee, ticker, markets, is_short):
|
||||
ftbot, client = botclient
|
||||
patch_get_signal(ftbot)
|
||||
patch_get_signal(ftbot, enter_long=not is_short, enter_short=is_short)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
markets=PropertyMock(return_value=markets),
|
||||
@@ -601,16 +612,19 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets):
|
||||
assert_response(rc, 404)
|
||||
assert rc.json()['detail'] == 'Trade not found.'
|
||||
|
||||
create_mock_trades(fee)
|
||||
Trade.query.session.rollback()
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/trade/3")
|
||||
assert_response(rc)
|
||||
assert rc.json()['trade_id'] == 3
|
||||
assert rc.json()['is_short'] == is_short
|
||||
|
||||
|
||||
def test_api_delete_trade(botclient, mocker, fee, markets):
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
def test_api_delete_trade(botclient, mocker, fee, markets, is_short):
|
||||
ftbot, client = botclient
|
||||
patch_get_signal(ftbot)
|
||||
patch_get_signal(ftbot, enter_long=not is_short, enter_short=is_short)
|
||||
stoploss_mock = MagicMock()
|
||||
cancel_mock = MagicMock()
|
||||
mocker.patch.multiple(
|
||||
@@ -619,11 +633,8 @@ def test_api_delete_trade(botclient, mocker, fee, markets):
|
||||
cancel_order=cancel_mock,
|
||||
cancel_stoploss_order=stoploss_mock,
|
||||
)
|
||||
rc = client_delete(client, f"{BASE_URI}/trades/1")
|
||||
# Error - trade won't exist yet.
|
||||
assert_response(rc, 502)
|
||||
|
||||
create_mock_trades(fee)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
|
||||
ftbot.strategy.order_types['stoploss_on_exchange'] = True
|
||||
trades = Trade.query.all()
|
||||
@@ -650,6 +661,10 @@ def test_api_delete_trade(botclient, mocker, fee, markets):
|
||||
assert len(trades) - 2 == len(Trade.query.all())
|
||||
assert stoploss_mock.call_count == 1
|
||||
|
||||
rc = client_delete(client, f"{BASE_URI}/trades/502")
|
||||
# Error - trade won't exist.
|
||||
assert_response(rc, 502)
|
||||
|
||||
|
||||
def test_api_logs(botclient):
|
||||
ftbot, client = botclient
|
||||
@@ -698,7 +713,48 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets):
|
||||
assert rc.json() == {"error": "Error querying /api/v1/edge: Edge is not enabled."}
|
||||
|
||||
|
||||
def test_api_profit(botclient, mocker, ticker, fee, markets):
|
||||
@pytest.mark.parametrize('is_short,expected', [
|
||||
(
|
||||
True,
|
||||
{'best_pair': 'ETC/BTC', 'best_rate': -0.5, 'best_pair_profit_ratio': -0.005,
|
||||
'profit_all_coin': 43.61269123,
|
||||
'profit_all_fiat': 538398.67323435, 'profit_all_percent_mean': 66.41,
|
||||
'profit_all_ratio_mean': 0.664109545, 'profit_all_percent_sum': 398.47,
|
||||
'profit_all_ratio_sum': 3.98465727, 'profit_all_percent': 4.36,
|
||||
'profit_all_ratio': 0.043612222872799825, 'profit_closed_coin': -0.00673913,
|
||||
'profit_closed_fiat': -83.19455985, 'profit_closed_ratio_mean': -0.0075,
|
||||
'profit_closed_percent_mean': -0.75, 'profit_closed_ratio_sum': -0.015,
|
||||
'profit_closed_percent_sum': -1.5, 'profit_closed_ratio': -6.739057628404269e-06,
|
||||
'profit_closed_percent': -0.0, 'winning_trades': 0, 'losing_trades': 2}
|
||||
),
|
||||
(
|
||||
False,
|
||||
{'best_pair': 'XRP/BTC', 'best_rate': 1.0, 'best_pair_profit_ratio': 0.01,
|
||||
'profit_all_coin': -44.0631579,
|
||||
'profit_all_fiat': -543959.6842755, 'profit_all_percent_mean': -66.41,
|
||||
'profit_all_ratio_mean': -0.6641100666666667, 'profit_all_percent_sum': -398.47,
|
||||
'profit_all_ratio_sum': -3.9846604, 'profit_all_percent': -4.41,
|
||||
'profit_all_ratio': -0.044063014216106644, 'profit_closed_coin': 0.00073913,
|
||||
'profit_closed_fiat': 9.124559849999999, 'profit_closed_ratio_mean': 0.0075,
|
||||
'profit_closed_percent_mean': 0.75, 'profit_closed_ratio_sum': 0.015,
|
||||
'profit_closed_percent_sum': 1.5, 'profit_closed_ratio': 7.391275897987988e-07,
|
||||
'profit_closed_percent': 0.0, 'winning_trades': 2, 'losing_trades': 0}
|
||||
),
|
||||
(
|
||||
None,
|
||||
{'best_pair': 'XRP/BTC', 'best_rate': 1.0, 'best_pair_profit_ratio': 0.01,
|
||||
'profit_all_coin': -14.43790415,
|
||||
'profit_all_fiat': -178235.92673175, 'profit_all_percent_mean': 0.08,
|
||||
'profit_all_ratio_mean': 0.000835751666666662, 'profit_all_percent_sum': 0.5,
|
||||
'profit_all_ratio_sum': 0.005014509999999972, 'profit_all_percent': -1.44,
|
||||
'profit_all_ratio': -0.014437768014451796, 'profit_closed_coin': -0.00542913,
|
||||
'profit_closed_fiat': -67.02260985, 'profit_closed_ratio_mean': 0.0025,
|
||||
'profit_closed_percent_mean': 0.25, 'profit_closed_ratio_sum': 0.005,
|
||||
'profit_closed_percent_sum': 0.5, 'profit_closed_ratio': -5.429078808526421e-06,
|
||||
'profit_closed_percent': -0.0, 'winning_trades': 1, 'losing_trades': 1}
|
||||
)
|
||||
])
|
||||
def test_api_profit(botclient, mocker, ticker, fee, markets, is_short, expected):
|
||||
ftbot, client = botclient
|
||||
patch_get_signal(ftbot)
|
||||
mocker.patch.multiple(
|
||||
@@ -713,45 +769,48 @@ def test_api_profit(botclient, mocker, ticker, fee, markets):
|
||||
assert_response(rc, 200)
|
||||
assert rc.json()['trade_count'] == 0
|
||||
|
||||
create_mock_trades(fee)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
# Simulate fulfilled LIMIT_BUY order for trade
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/profit")
|
||||
assert_response(rc)
|
||||
assert rc.json() == {'avg_duration': ANY,
|
||||
'best_pair': 'XRP/BTC',
|
||||
'best_rate': 1.0,
|
||||
'best_pair_profit_ratio': 0.01,
|
||||
'first_trade_date': ANY,
|
||||
'first_trade_timestamp': ANY,
|
||||
'latest_trade_date': '5 minutes ago',
|
||||
'latest_trade_timestamp': ANY,
|
||||
'profit_all_coin': -44.0631579,
|
||||
'profit_all_fiat': -543959.6842755,
|
||||
'profit_all_percent_mean': -66.41,
|
||||
'profit_all_ratio_mean': -0.6641100666666667,
|
||||
'profit_all_percent_sum': -398.47,
|
||||
'profit_all_ratio_sum': -3.9846604,
|
||||
'profit_all_percent': -4.41,
|
||||
'profit_all_ratio': -0.044063014216106644,
|
||||
'profit_closed_coin': 0.00073913,
|
||||
'profit_closed_fiat': 9.124559849999999,
|
||||
'profit_closed_ratio_mean': 0.0075,
|
||||
'profit_closed_percent_mean': 0.75,
|
||||
'profit_closed_ratio_sum': 0.015,
|
||||
'profit_closed_percent_sum': 1.5,
|
||||
'profit_closed_ratio': 7.391275897987988e-07,
|
||||
'profit_closed_percent': 0.0,
|
||||
'trade_count': 6,
|
||||
'closed_trade_count': 2,
|
||||
'winning_trades': 2,
|
||||
'losing_trades': 0,
|
||||
}
|
||||
# raise ValueError(rc.json())
|
||||
assert rc.json() == {
|
||||
'avg_duration': ANY,
|
||||
'best_pair': expected['best_pair'],
|
||||
'best_pair_profit_ratio': expected['best_pair_profit_ratio'],
|
||||
'best_rate': expected['best_rate'],
|
||||
'first_trade_date': ANY,
|
||||
'first_trade_timestamp': ANY,
|
||||
'latest_trade_date': '5 minutes ago',
|
||||
'latest_trade_timestamp': ANY,
|
||||
'profit_all_coin': expected['profit_all_coin'],
|
||||
'profit_all_fiat': expected['profit_all_fiat'],
|
||||
'profit_all_percent_mean': expected['profit_all_percent_mean'],
|
||||
'profit_all_ratio_mean': expected['profit_all_ratio_mean'],
|
||||
'profit_all_percent_sum': expected['profit_all_percent_sum'],
|
||||
'profit_all_ratio_sum': expected['profit_all_ratio_sum'],
|
||||
'profit_all_percent': expected['profit_all_percent'],
|
||||
'profit_all_ratio': expected['profit_all_ratio'],
|
||||
'profit_closed_coin': expected['profit_closed_coin'],
|
||||
'profit_closed_fiat': expected['profit_closed_fiat'],
|
||||
'profit_closed_ratio_mean': expected['profit_closed_ratio_mean'],
|
||||
'profit_closed_percent_mean': expected['profit_closed_percent_mean'],
|
||||
'profit_closed_ratio_sum': expected['profit_closed_ratio_sum'],
|
||||
'profit_closed_percent_sum': expected['profit_closed_percent_sum'],
|
||||
'profit_closed_ratio': expected['profit_closed_ratio'],
|
||||
'profit_closed_percent': expected['profit_closed_percent'],
|
||||
'trade_count': 6,
|
||||
'closed_trade_count': 2,
|
||||
'winning_trades': expected['winning_trades'],
|
||||
'losing_trades': expected['losing_trades'],
|
||||
}
|
||||
|
||||
|
||||
def test_api_stats(botclient, mocker, ticker, fee, markets,):
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
def test_api_stats(botclient, mocker, ticker, fee, markets, is_short):
|
||||
ftbot, client = botclient
|
||||
patch_get_signal(ftbot)
|
||||
patch_get_signal(ftbot, enter_long=not is_short, enter_short=is_short)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
get_balances=MagicMock(return_value=ticker),
|
||||
@@ -765,7 +824,7 @@ def test_api_stats(botclient, mocker, ticker, fee, markets,):
|
||||
assert 'durations' in rc.json()
|
||||
assert 'sell_reasons' in rc.json()
|
||||
|
||||
create_mock_trades(fee)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/stats")
|
||||
assert_response(rc, 200)
|
||||
@@ -825,7 +884,12 @@ def test_api_performance(botclient, fee):
|
||||
'profit_ratio': -0.05570419, 'profit_abs': -0.1150375}]
|
||||
|
||||
|
||||
def test_api_status(botclient, mocker, ticker, fee, markets):
|
||||
@pytest.mark.parametrize(
|
||||
'is_short,current_rate,open_order_id,open_trade_value',
|
||||
[(True, 1.098e-05, 'dry_run_buy_short_12345', 15.0911775),
|
||||
(False, 1.099e-05, 'dry_run_buy_long_12345', 15.1668225)])
|
||||
def test_api_status(botclient, mocker, ticker, fee, markets, is_short,
|
||||
current_rate, open_order_id, open_trade_value):
|
||||
ftbot, client = botclient
|
||||
patch_get_signal(ftbot)
|
||||
mocker.patch.multiple(
|
||||
@@ -840,7 +904,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
|
||||
rc = client_get(client, f"{BASE_URI}/status")
|
||||
assert_response(rc, 200)
|
||||
assert rc.json() == []
|
||||
create_mock_trades(fee)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/status")
|
||||
assert_response(rc)
|
||||
@@ -861,7 +925,7 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
|
||||
'profit_pct': ANY,
|
||||
'profit_abs': ANY,
|
||||
'profit_fiat': ANY,
|
||||
'current_rate': 1.099e-05,
|
||||
'current_rate': current_rate,
|
||||
'open_date': ANY,
|
||||
'open_timestamp': ANY,
|
||||
'open_order': None,
|
||||
@@ -891,19 +955,24 @@ def test_api_status(botclient, mocker, ticker, fee, markets):
|
||||
'fee_open_cost': None,
|
||||
'fee_open_currency': None,
|
||||
'is_open': True,
|
||||
"is_short": is_short,
|
||||
'max_rate': ANY,
|
||||
'min_rate': ANY,
|
||||
'open_order_id': 'dry_run_buy_12345',
|
||||
'open_order_id': open_order_id,
|
||||
'open_rate_requested': ANY,
|
||||
'open_trade_value': 15.1668225,
|
||||
'open_trade_value': open_trade_value,
|
||||
'sell_reason': None,
|
||||
'sell_order_status': None,
|
||||
'strategy': 'StrategyTestV2',
|
||||
'strategy': CURRENT_TEST_STRATEGY,
|
||||
'buy_tag': None,
|
||||
'enter_tag': None,
|
||||
'timeframe': 5,
|
||||
'exchange': 'binance',
|
||||
'leverage': 1.0,
|
||||
'interest_rate': 0.0,
|
||||
'funding_fees': None,
|
||||
'trading_mode': ANY,
|
||||
'orders': [ANY],
|
||||
|
||||
}
|
||||
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_rate',
|
||||
@@ -977,8 +1046,8 @@ def test_api_blacklist(botclient, mocker):
|
||||
"NOTHING/BTC": {
|
||||
"error_msg": "Pair NOTHING/BTC is not in the current blacklist."
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
}
|
||||
rc = client_delete(
|
||||
client,
|
||||
f"{BASE_URI}/blacklist?pairs_to_delete=HOT/BTC&pairs_to_delete=ETH/BTC")
|
||||
@@ -1003,23 +1072,27 @@ def test_api_whitelist(botclient):
|
||||
}
|
||||
|
||||
|
||||
def test_api_forcebuy(botclient, mocker, fee):
|
||||
@pytest.mark.parametrize('endpoint', [
|
||||
'forcebuy',
|
||||
'forceenter',
|
||||
])
|
||||
def test_api_forceentry(botclient, mocker, fee, endpoint):
|
||||
ftbot, client = botclient
|
||||
|
||||
rc = client_post(client, f"{BASE_URI}/forcebuy",
|
||||
rc = client_post(client, f"{BASE_URI}/{endpoint}",
|
||||
data='{"pair": "ETH/BTC"}')
|
||||
assert_response(rc, 502)
|
||||
assert rc.json() == {"error": "Error querying /api/v1/forcebuy: Forcebuy not enabled."}
|
||||
assert rc.json() == {"error": f"Error querying /api/v1/{endpoint}: Forceentry not enabled."}
|
||||
|
||||
# enable forcebuy
|
||||
ftbot.config['forcebuy_enable'] = True
|
||||
|
||||
fbuy_mock = MagicMock(return_value=None)
|
||||
mocker.patch("freqtrade.rpc.RPC._rpc_forcebuy", fbuy_mock)
|
||||
rc = client_post(client, f"{BASE_URI}/forcebuy",
|
||||
mocker.patch("freqtrade.rpc.RPC._rpc_force_entry", fbuy_mock)
|
||||
rc = client_post(client, f"{BASE_URI}/{endpoint}",
|
||||
data='{"pair": "ETH/BTC"}')
|
||||
assert_response(rc)
|
||||
assert rc.json() == {"status": "Error buying pair ETH/BTC."}
|
||||
assert rc.json() == {"status": "Error entering long trade for pair ETH/BTC."}
|
||||
|
||||
# Test creating trade
|
||||
fbuy_mock = MagicMock(return_value=Trade(
|
||||
@@ -1032,21 +1105,23 @@ def test_api_forcebuy(botclient, mocker, fee):
|
||||
open_order_id="123456",
|
||||
open_date=datetime.utcnow(),
|
||||
is_open=False,
|
||||
is_short=False,
|
||||
fee_close=fee.return_value,
|
||||
fee_open=fee.return_value,
|
||||
close_rate=0.265441,
|
||||
id=22,
|
||||
timeframe=5,
|
||||
strategy="StrategyTestV2"
|
||||
strategy=CURRENT_TEST_STRATEGY,
|
||||
trading_mode=TradingMode.SPOT
|
||||
))
|
||||
mocker.patch("freqtrade.rpc.RPC._rpc_forcebuy", fbuy_mock)
|
||||
mocker.patch("freqtrade.rpc.RPC._rpc_force_entry", fbuy_mock)
|
||||
|
||||
rc = client_post(client, f"{BASE_URI}/forcebuy",
|
||||
rc = client_post(client, f"{BASE_URI}/{endpoint}",
|
||||
data='{"pair": "ETH/BTC"}')
|
||||
assert_response(rc)
|
||||
assert rc.json() == {
|
||||
'amount': 1,
|
||||
'amount_requested': 1,
|
||||
'amount': 1.0,
|
||||
'amount_requested': 1.0,
|
||||
'trade_id': 22,
|
||||
'close_date': None,
|
||||
'close_timestamp': None,
|
||||
@@ -1080,6 +1155,7 @@ def test_api_forcebuy(botclient, mocker, fee):
|
||||
'fee_open_cost': None,
|
||||
'fee_open_currency': None,
|
||||
'is_open': False,
|
||||
'is_short': False,
|
||||
'max_rate': None,
|
||||
'min_rate': None,
|
||||
'open_order_id': '123456',
|
||||
@@ -1087,10 +1163,15 @@ def test_api_forcebuy(botclient, mocker, fee):
|
||||
'open_trade_value': 0.24605460,
|
||||
'sell_reason': None,
|
||||
'sell_order_status': None,
|
||||
'strategy': 'StrategyTestV2',
|
||||
'strategy': CURRENT_TEST_STRATEGY,
|
||||
'buy_tag': None,
|
||||
'enter_tag': None,
|
||||
'timeframe': 5,
|
||||
'exchange': 'binance',
|
||||
'leverage': None,
|
||||
'interest_rate': None,
|
||||
'funding_fees': None,
|
||||
'trading_mode': 'spot',
|
||||
'orders': [],
|
||||
}
|
||||
|
||||
@@ -1146,17 +1227,19 @@ def test_api_pair_candles(botclient, ohlcv_history):
|
||||
assert 'data_stop_ts' in rc.json()
|
||||
assert len(rc.json()['data']) == 0
|
||||
ohlcv_history['sma'] = ohlcv_history['close'].rolling(2).mean()
|
||||
ohlcv_history['buy'] = 0
|
||||
ohlcv_history.loc[1, 'buy'] = 1
|
||||
ohlcv_history['sell'] = 0
|
||||
ohlcv_history['enter_long'] = 0
|
||||
ohlcv_history.loc[1, 'enter_long'] = 1
|
||||
ohlcv_history['exit_long'] = 0
|
||||
ohlcv_history['enter_short'] = 0
|
||||
ohlcv_history['exit_short'] = 0
|
||||
|
||||
ftbot.dataprovider._set_cached_df("XRP/BTC", timeframe, ohlcv_history)
|
||||
ftbot.dataprovider._set_cached_df("XRP/BTC", timeframe, ohlcv_history, CandleType.SPOT)
|
||||
|
||||
rc = client_get(client,
|
||||
f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}")
|
||||
assert_response(rc)
|
||||
assert 'strategy' in rc.json()
|
||||
assert rc.json()['strategy'] == 'StrategyTestV2'
|
||||
assert rc.json()['strategy'] == CURRENT_TEST_STRATEGY
|
||||
assert 'columns' in rc.json()
|
||||
assert 'data_start_ts' in rc.json()
|
||||
assert 'data_start' in rc.json()
|
||||
@@ -1167,9 +1250,12 @@ def test_api_pair_candles(botclient, ohlcv_history):
|
||||
assert rc.json()['data_stop'] == '2017-11-26 09:00:00+00:00'
|
||||
assert rc.json()['data_stop_ts'] == 1511686800000
|
||||
assert isinstance(rc.json()['columns'], list)
|
||||
assert rc.json()['columns'] == ['date', 'open', 'high',
|
||||
'low', 'close', 'volume', 'sma', 'buy', 'sell',
|
||||
'__date_ts', '_buy_signal_close', '_sell_signal_close']
|
||||
assert set(rc.json()['columns']) == {
|
||||
'date', 'open', 'high', 'low', 'close', 'volume',
|
||||
'sma', 'enter_long', 'exit_long', 'enter_short', 'exit_short', '__date_ts',
|
||||
'_enter_long_signal_close', '_exit_long_signal_close',
|
||||
'_enter_short_signal_close', '_exit_short_signal_close'
|
||||
}
|
||||
assert 'pair' in rc.json()
|
||||
assert rc.json()['pair'] == 'XRP/BTC'
|
||||
|
||||
@@ -1178,31 +1264,32 @@ def test_api_pair_candles(botclient, ohlcv_history):
|
||||
|
||||
assert (rc.json()['data'] ==
|
||||
[['2017-11-26 08:50:00', 8.794e-05, 8.948e-05, 8.794e-05, 8.88e-05, 0.0877869,
|
||||
None, 0, 0, 1511686200000, None, None],
|
||||
None, 0, 0, 0, 0, 1511686200000, None, None, None, None],
|
||||
['2017-11-26 08:55:00', 8.88e-05, 8.942e-05, 8.88e-05,
|
||||
8.893e-05, 0.05874751, 8.886500000000001e-05, 1, 0, 1511686500000, 8.893e-05,
|
||||
None],
|
||||
8.893e-05, 0.05874751, 8.886500000000001e-05, 1, 0, 0, 0, 1511686500000, 8.893e-05,
|
||||
None, None, None],
|
||||
['2017-11-26 09:00:00', 8.891e-05, 8.893e-05, 8.875e-05, 8.877e-05,
|
||||
0.7039405, 8.885e-05, 0, 0, 1511686800000, None, None]
|
||||
0.7039405, 8.885e-05, 0, 0, 0, 0, 1511686800000, None, None, None, None]
|
||||
|
||||
])
|
||||
ohlcv_history['sell'] = ohlcv_history['sell'].astype('float64')
|
||||
ohlcv_history.at[0, 'sell'] = float('inf')
|
||||
ohlcv_history['exit_long'] = ohlcv_history['exit_long'].astype('float64')
|
||||
ohlcv_history.at[0, 'exit_long'] = float('inf')
|
||||
ohlcv_history['date1'] = ohlcv_history['date']
|
||||
ohlcv_history.at[0, 'date1'] = pd.NaT
|
||||
|
||||
ftbot.dataprovider._set_cached_df("XRP/BTC", timeframe, ohlcv_history)
|
||||
ftbot.dataprovider._set_cached_df("XRP/BTC", timeframe, ohlcv_history, CandleType.SPOT)
|
||||
rc = client_get(client,
|
||||
f"{BASE_URI}/pair_candles?limit={amount}&pair=XRP%2FBTC&timeframe={timeframe}")
|
||||
assert_response(rc)
|
||||
assert (rc.json()['data'] ==
|
||||
[['2017-11-26 08:50:00', 8.794e-05, 8.948e-05, 8.794e-05, 8.88e-05, 0.0877869,
|
||||
None, 0, None, None, 1511686200000, None, None],
|
||||
None, 0, None, 0, 0, None, 1511686200000, None, None, None, None],
|
||||
['2017-11-26 08:55:00', 8.88e-05, 8.942e-05, 8.88e-05,
|
||||
8.893e-05, 0.05874751, 8.886500000000001e-05, 1, 0.0, '2017-11-26 08:55:00',
|
||||
1511686500000, 8.893e-05, None],
|
||||
8.893e-05, 0.05874751, 8.886500000000001e-05, 1, 0.0, 0, 0, '2017-11-26 08:55:00',
|
||||
1511686500000, 8.893e-05, None, None, None],
|
||||
['2017-11-26 09:00:00', 8.891e-05, 8.893e-05, 8.875e-05, 8.877e-05,
|
||||
0.7039405, 8.885e-05, 0, 0.0, '2017-11-26 09:00:00', 1511686800000, None, None]
|
||||
0.7039405, 8.885e-05, 0, 0.0, 0, 0, '2017-11-26 09:00:00', 1511686800000,
|
||||
None, None, None, None]
|
||||
])
|
||||
|
||||
|
||||
@@ -1213,19 +1300,19 @@ def test_api_pair_history(botclient, ohlcv_history):
|
||||
# No pair
|
||||
rc = client_get(client,
|
||||
f"{BASE_URI}/pair_history?timeframe={timeframe}"
|
||||
"&timerange=20180111-20180112&strategy=StrategyTestV2")
|
||||
f"&timerange=20180111-20180112&strategy={CURRENT_TEST_STRATEGY}")
|
||||
assert_response(rc, 422)
|
||||
|
||||
# No Timeframe
|
||||
rc = client_get(client,
|
||||
f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC"
|
||||
"&timerange=20180111-20180112&strategy=StrategyTestV2")
|
||||
f"&timerange=20180111-20180112&strategy={CURRENT_TEST_STRATEGY}")
|
||||
assert_response(rc, 422)
|
||||
|
||||
# No timerange
|
||||
rc = client_get(client,
|
||||
f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
|
||||
"&strategy=StrategyTestV2")
|
||||
f"&strategy={CURRENT_TEST_STRATEGY}")
|
||||
assert_response(rc, 422)
|
||||
|
||||
# No strategy
|
||||
@@ -1237,14 +1324,14 @@ def test_api_pair_history(botclient, ohlcv_history):
|
||||
# Working
|
||||
rc = client_get(client,
|
||||
f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
|
||||
"&timerange=20180111-20180112&strategy=StrategyTestV2")
|
||||
f"&timerange=20180111-20180112&strategy={CURRENT_TEST_STRATEGY}")
|
||||
assert_response(rc, 200)
|
||||
assert rc.json()['length'] == 289
|
||||
assert len(rc.json()['data']) == rc.json()['length']
|
||||
assert 'columns' in rc.json()
|
||||
assert 'data' in rc.json()
|
||||
assert rc.json()['pair'] == 'UNITTEST/BTC'
|
||||
assert rc.json()['strategy'] == 'StrategyTestV2'
|
||||
assert rc.json()['strategy'] == CURRENT_TEST_STRATEGY
|
||||
assert rc.json()['data_start'] == '2018-01-11 00:00:00+00:00'
|
||||
assert rc.json()['data_start_ts'] == 1515628800000
|
||||
assert rc.json()['data_stop'] == '2018-01-12 00:00:00+00:00'
|
||||
@@ -1253,7 +1340,7 @@ def test_api_pair_history(botclient, ohlcv_history):
|
||||
# No data found
|
||||
rc = client_get(client,
|
||||
f"{BASE_URI}/pair_history?pair=UNITTEST%2FBTC&timeframe={timeframe}"
|
||||
"&timerange=20200111-20200112&strategy=StrategyTestV2")
|
||||
f"&timerange=20200111-20200112&strategy={CURRENT_TEST_STRATEGY}")
|
||||
assert_response(rc, 502)
|
||||
assert rc.json()['error'] == ("Error querying /api/v1/pair_history: "
|
||||
"No data for UNITTEST/BTC, 5m in 20200111-20200112 found.")
|
||||
@@ -1294,19 +1381,21 @@ def test_api_strategies(botclient):
|
||||
'HyperoptableStrategy',
|
||||
'InformativeDecoratorTest',
|
||||
'StrategyTestV2',
|
||||
'TestStrategyLegacyV1'
|
||||
'StrategyTestV3',
|
||||
'StrategyTestV3Futures',
|
||||
'TestStrategyLegacyV1',
|
||||
]}
|
||||
|
||||
|
||||
def test_api_strategy(botclient):
|
||||
ftbot, client = botclient
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/strategy/StrategyTestV2")
|
||||
rc = client_get(client, f"{BASE_URI}/strategy/{CURRENT_TEST_STRATEGY}")
|
||||
|
||||
assert_response(rc)
|
||||
assert rc.json()['strategy'] == 'StrategyTestV2'
|
||||
assert rc.json()['strategy'] == CURRENT_TEST_STRATEGY
|
||||
|
||||
data = (Path(__file__).parents[1] / "strategy/strats/strategy_test_v2.py").read_text()
|
||||
data = (Path(__file__).parents[1] / "strategy/strats/strategy_test_v3.py").read_text()
|
||||
assert rc.json()['code'] == data
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/strategy/NoStrat")
|
||||
@@ -1338,6 +1427,20 @@ def test_list_available_pairs(botclient):
|
||||
assert rc.json()['pairs'] == ['XRP/ETH']
|
||||
assert len(rc.json()['pair_interval']) == 1
|
||||
|
||||
ftbot.config['trading_mode'] = 'futures'
|
||||
rc = client_get(
|
||||
client, f"{BASE_URI}/available_pairs?timeframe=1h")
|
||||
assert_response(rc)
|
||||
assert rc.json()['length'] == 1
|
||||
assert rc.json()['pairs'] == ['XRP/USDT']
|
||||
|
||||
rc = client_get(
|
||||
client, f"{BASE_URI}/available_pairs?timeframe=1h&candletype=mark")
|
||||
assert_response(rc)
|
||||
assert rc.json()['length'] == 2
|
||||
assert rc.json()['pairs'] == ['UNITTEST/USDT', 'XRP/USDT']
|
||||
assert len(rc.json()['pair_interval']) == 2
|
||||
|
||||
|
||||
def test_sysinfo(botclient):
|
||||
ftbot, client = botclient
|
||||
@@ -1383,7 +1486,7 @@ def test_api_backtesting(botclient, mocker, fee, caplog, tmpdir):
|
||||
|
||||
# start backtesting
|
||||
data = {
|
||||
"strategy": "StrategyTestV2",
|
||||
"strategy": CURRENT_TEST_STRATEGY,
|
||||
"timeframe": "5m",
|
||||
"timerange": "20180110-20180111",
|
||||
"max_open_trades": 3,
|
||||
|
@@ -18,7 +18,7 @@ from telegram.error import BadRequest, NetworkError, TelegramError
|
||||
from freqtrade import __version__
|
||||
from freqtrade.constants import CANCEL_REASON
|
||||
from freqtrade.edge import PairInfo
|
||||
from freqtrade.enums import RPCMessageType, RunMode, SellType, State
|
||||
from freqtrade.enums import ExitType, RPCMessageType, RunMode, SignalDirection, State
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.freqtradebot import FreqtradeBot
|
||||
from freqtrade.loggers import setup_logging
|
||||
@@ -27,8 +27,8 @@ from freqtrade.persistence.models import Order
|
||||
from freqtrade.rpc import RPC
|
||||
from freqtrade.rpc.rpc import RPCException
|
||||
from freqtrade.rpc.telegram import Telegram, authorized_only
|
||||
from tests.conftest import (create_mock_trades, get_patched_freqtradebot, log_has, log_has_re,
|
||||
patch_exchange, patch_get_signal, patch_whitelist)
|
||||
from tests.conftest import (CURRENT_TEST_STRATEGY, create_mock_trades, get_patched_freqtradebot,
|
||||
log_has, log_has_re, patch_exchange, patch_get_signal, patch_whitelist)
|
||||
|
||||
|
||||
class DummyCls(Telegram):
|
||||
@@ -94,8 +94,10 @@ def test_telegram_init(default_conf, mocker, caplog) -> None:
|
||||
assert start_polling.start_polling.call_count == 1
|
||||
|
||||
message_str = ("rpc.telegram is listening for following commands: [['status'], ['profit'], "
|
||||
"['balance'], ['start'], ['stop'], ['forcesell'], ['forcebuy'], ['trades'], "
|
||||
"['delete'], ['performance'], ['buys'], ['sells'], ['mix_tags'], "
|
||||
"['balance'], ['start'], ['stop'], "
|
||||
"['forcesell', 'forceexit'], ['forcebuy', 'forcelong'], ['forceshort'], "
|
||||
"['trades'], ['delete'], ['performance'], "
|
||||
"['buys', 'entries'], ['sells'], ['mix_tags'], "
|
||||
"['stats'], ['daily'], ['weekly'], ['monthly'], "
|
||||
"['count'], ['locks'], ['unlock', 'delete_locks'], "
|
||||
"['reload_config', 'reload_conf'], ['show_config', 'show_conf'], "
|
||||
@@ -191,6 +193,7 @@ def test_telegram_status(default_conf, update, mocker) -> None:
|
||||
'amount': 90.99181074,
|
||||
'stake_amount': 90.99181074,
|
||||
'buy_tag': None,
|
||||
'enter_tag': None,
|
||||
'close_profit_ratio': None,
|
||||
'profit': -0.0059,
|
||||
'profit_ratio': -0.0059,
|
||||
@@ -203,6 +206,8 @@ def test_telegram_status(default_conf, update, mocker) -> None:
|
||||
'stop_loss_ratio': -0.0001,
|
||||
'open_order': '(limit buy rem=0.00000000)',
|
||||
'is_open': True,
|
||||
'is_short': False,
|
||||
'filled_entry_orders': [],
|
||||
'orders': []
|
||||
}]),
|
||||
)
|
||||
@@ -393,7 +398,8 @@ def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> None:
|
||||
fields = re.sub('[ ]+', ' ', line[2].strip()).split(' ')
|
||||
|
||||
assert int(fields[0]) == 1
|
||||
assert 'ETH/BTC' in fields[1]
|
||||
assert 'L' in fields[1]
|
||||
assert 'ETH/BTC' in fields[2]
|
||||
assert msg_mock.call_count == 1
|
||||
|
||||
|
||||
@@ -623,7 +629,7 @@ def test_weekly_wrong_input(default_conf, update, ticker, mocker) -> None:
|
||||
context.args = ["this week"]
|
||||
telegram._weekly(update=update, context=context)
|
||||
assert str('Weekly Profit over the last 8 weeks (starting from Monday)</b>:') \
|
||||
in msg_mock.call_args_list[0][0][0]
|
||||
in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_monthly_handle(default_conf, update, ticker, limit_buy_order, fee,
|
||||
@@ -809,8 +815,9 @@ def test_profit_handle(default_conf, update, ticker, ticker_sell_up, fee,
|
||||
assert '*Best Performing:* `ETH/BTC: 6.20%`' in msg_mock.call_args_list[-1][0][0]
|
||||
|
||||
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
def test_telegram_stats(default_conf, update, ticker, ticker_sell_up, fee,
|
||||
limit_buy_order, limit_sell_order, mocker) -> None:
|
||||
limit_buy_order, limit_sell_order, mocker, is_short) -> None:
|
||||
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
@@ -826,7 +833,7 @@ def test_telegram_stats(default_conf, update, ticker, ticker_sell_up, fee,
|
||||
msg_mock.reset_mock()
|
||||
|
||||
# Create some test data
|
||||
create_mock_trades(fee)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
|
||||
telegram._stats(update=update, context=MagicMock())
|
||||
assert msg_mock.call_count == 1
|
||||
@@ -900,6 +907,10 @@ def test_balance_handle_too_large_response(default_conf, update, mocker) -> None
|
||||
'balance': i,
|
||||
'est_stake': 1,
|
||||
'stake': 'BTC',
|
||||
'is_position': False,
|
||||
'leverage': 1.0,
|
||||
'position': 0.0,
|
||||
'side': 'long',
|
||||
})
|
||||
mocker.patch('freqtrade.rpc.rpc.RPC._rpc_balance', return_value={
|
||||
'currencies': balances,
|
||||
@@ -1024,7 +1035,7 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee,
|
||||
# /forcesell 1
|
||||
context = MagicMock()
|
||||
context.args = ["1"]
|
||||
telegram._forcesell(update=update, context=context)
|
||||
telegram._forceexit(update=update, context=context)
|
||||
|
||||
assert msg_mock.call_count == 4
|
||||
last_msg = msg_mock.call_args_list[-2][0][0]
|
||||
@@ -1034,18 +1045,21 @@ def test_telegram_forcesell_handle(default_conf, update, ticker, fee,
|
||||
'exchange': 'Binance',
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'profit',
|
||||
'leverage': 1.0,
|
||||
'limit': 1.173e-05,
|
||||
'amount': 91.07468123,
|
||||
'order_type': 'limit',
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.173e-05,
|
||||
'direction': 'Long',
|
||||
'profit_amount': 6.314e-05,
|
||||
'profit_ratio': 0.0629778,
|
||||
'stake_currency': 'BTC',
|
||||
'base_currency': 'ETH',
|
||||
'fiat_currency': 'USD',
|
||||
'buy_tag': ANY,
|
||||
'sell_reason': SellType.FORCE_SELL.value,
|
||||
'enter_tag': ANY,
|
||||
'sell_reason': ExitType.FORCE_SELL.value,
|
||||
'open_date': ANY,
|
||||
'close_date': ANY,
|
||||
'close_rate': ANY,
|
||||
@@ -1088,7 +1102,7 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee,
|
||||
# /forcesell 1
|
||||
context = MagicMock()
|
||||
context.args = ["1"]
|
||||
telegram._forcesell(update=update, context=context)
|
||||
telegram._forceexit(update=update, context=context)
|
||||
|
||||
assert msg_mock.call_count == 4
|
||||
|
||||
@@ -1099,18 +1113,21 @@ def test_telegram_forcesell_down_handle(default_conf, update, ticker, fee,
|
||||
'exchange': 'Binance',
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'loss',
|
||||
'leverage': 1.0,
|
||||
'limit': 1.043e-05,
|
||||
'amount': 91.07468123,
|
||||
'order_type': 'limit',
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.043e-05,
|
||||
'direction': 'Long',
|
||||
'profit_amount': -5.497e-05,
|
||||
'profit_ratio': -0.05482878,
|
||||
'stake_currency': 'BTC',
|
||||
'base_currency': 'ETH',
|
||||
'fiat_currency': 'USD',
|
||||
'buy_tag': ANY,
|
||||
'sell_reason': SellType.FORCE_SELL.value,
|
||||
'enter_tag': ANY,
|
||||
'sell_reason': ExitType.FORCE_SELL.value,
|
||||
'open_date': ANY,
|
||||
'close_date': ANY,
|
||||
'close_rate': ANY,
|
||||
@@ -1143,7 +1160,7 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
|
||||
# /forcesell all
|
||||
context = MagicMock()
|
||||
context.args = ["all"]
|
||||
telegram._forcesell(update=update, context=context)
|
||||
telegram._forceexit(update=update, context=context)
|
||||
|
||||
# Called for each trade 2 times
|
||||
assert msg_mock.call_count == 8
|
||||
@@ -1154,18 +1171,21 @@ def test_forcesell_all_handle(default_conf, update, ticker, fee, mocker) -> None
|
||||
'exchange': 'Binance',
|
||||
'pair': 'ETH/BTC',
|
||||
'gain': 'loss',
|
||||
'leverage': 1.0,
|
||||
'limit': 1.099e-05,
|
||||
'amount': 91.07468123,
|
||||
'order_type': 'limit',
|
||||
'open_rate': 1.098e-05,
|
||||
'current_rate': 1.099e-05,
|
||||
'direction': 'Long',
|
||||
'profit_amount': -4.09e-06,
|
||||
'profit_ratio': -0.00408133,
|
||||
'stake_currency': 'BTC',
|
||||
'base_currency': 'ETH',
|
||||
'fiat_currency': 'USD',
|
||||
'buy_tag': ANY,
|
||||
'sell_reason': SellType.FORCE_SELL.value,
|
||||
'enter_tag': ANY,
|
||||
'sell_reason': ExitType.FORCE_SELL.value,
|
||||
'open_date': ANY,
|
||||
'close_date': ANY,
|
||||
'close_rate': ANY,
|
||||
@@ -1184,7 +1204,7 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:
|
||||
# /forcesell 1
|
||||
context = MagicMock()
|
||||
context.args = ["1"]
|
||||
telegram._forcesell(update=update, context=context)
|
||||
telegram._forceexit(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'not running' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
@@ -1194,36 +1214,37 @@ def test_forcesell_handle_invalid(default_conf, update, mocker) -> None:
|
||||
# /forcesell 123456
|
||||
context = MagicMock()
|
||||
context.args = ["123456"]
|
||||
telegram._forcesell(update=update, context=context)
|
||||
telegram._forceexit(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'invalid argument' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_forcebuy_handle(default_conf, update, mocker) -> None:
|
||||
def test_forceenter_handle(default_conf, update, mocker) -> None:
|
||||
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
|
||||
fbuy_mock = MagicMock(return_value=None)
|
||||
mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock)
|
||||
mocker.patch('freqtrade.rpc.RPC._rpc_force_entry', fbuy_mock)
|
||||
|
||||
telegram, freqtradebot, _ = get_telegram_testobject(mocker, default_conf)
|
||||
patch_get_signal(freqtradebot)
|
||||
|
||||
# /forcebuy ETH/BTC
|
||||
# /forcelong ETH/BTC
|
||||
context = MagicMock()
|
||||
context.args = ["ETH/BTC"]
|
||||
telegram._forcebuy(update=update, context=context)
|
||||
telegram._forceenter(update=update, context=context, order_side=SignalDirection.LONG)
|
||||
|
||||
assert fbuy_mock.call_count == 1
|
||||
assert fbuy_mock.call_args_list[0][0][0] == 'ETH/BTC'
|
||||
assert fbuy_mock.call_args_list[0][0][1] is None
|
||||
assert fbuy_mock.call_args_list[0][1]['order_side'] == SignalDirection.LONG
|
||||
|
||||
# Reset and retry with specified price
|
||||
fbuy_mock = MagicMock(return_value=None)
|
||||
mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock)
|
||||
# /forcebuy ETH/BTC 0.055
|
||||
mocker.patch('freqtrade.rpc.RPC._rpc_force_entry', fbuy_mock)
|
||||
# /forcelong ETH/BTC 0.055
|
||||
context = MagicMock()
|
||||
context.args = ["ETH/BTC", "0.055"]
|
||||
telegram._forcebuy(update=update, context=context)
|
||||
telegram._forceenter(update=update, context=context, order_side=SignalDirection.LONG)
|
||||
|
||||
assert fbuy_mock.call_count == 1
|
||||
assert fbuy_mock.call_args_list[0][0][0] == 'ETH/BTC'
|
||||
@@ -1231,24 +1252,24 @@ def test_forcebuy_handle(default_conf, update, mocker) -> None:
|
||||
assert fbuy_mock.call_args_list[0][0][1] == 0.055
|
||||
|
||||
|
||||
def test_forcebuy_handle_exception(default_conf, update, mocker) -> None:
|
||||
def test_forceenter_handle_exception(default_conf, update, mocker) -> None:
|
||||
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
|
||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
patch_get_signal(freqtradebot)
|
||||
|
||||
update.message.text = '/forcebuy ETH/Nonepair'
|
||||
telegram._forcebuy(update=update, context=MagicMock())
|
||||
telegram._forceenter(update=update, context=MagicMock(), order_side=SignalDirection.LONG)
|
||||
|
||||
assert msg_mock.call_count == 1
|
||||
assert msg_mock.call_args_list[0][0][0] == 'Forcebuy not enabled.'
|
||||
assert msg_mock.call_args_list[0][0][0] == 'Forceentry not enabled.'
|
||||
|
||||
|
||||
def test_forcebuy_no_pair(default_conf, update, mocker) -> None:
|
||||
def test_forceenter_no_pair(default_conf, update, mocker) -> None:
|
||||
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
|
||||
|
||||
fbuy_mock = MagicMock(return_value=None)
|
||||
mocker.patch('freqtrade.rpc.RPC._rpc_forcebuy', fbuy_mock)
|
||||
mocker.patch('freqtrade.rpc.RPC._rpc_force_entry', fbuy_mock)
|
||||
|
||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
|
||||
@@ -1256,7 +1277,7 @@ def test_forcebuy_no_pair(default_conf, update, mocker) -> None:
|
||||
|
||||
context = MagicMock()
|
||||
context.args = []
|
||||
telegram._forcebuy(update=update, context=context)
|
||||
telegram._forceenter(update=update, context=context, order_side=SignalDirection.LONG)
|
||||
|
||||
assert fbuy_mock.call_count == 0
|
||||
assert msg_mock.call_count == 1
|
||||
@@ -1267,8 +1288,8 @@ def test_forcebuy_no_pair(default_conf, update, mocker) -> None:
|
||||
assert reduce(lambda acc, x: acc + len(x), keyboard, 0) == 5
|
||||
update = MagicMock()
|
||||
update.callback_query = MagicMock()
|
||||
update.callback_query.data = 'XRP/USDT'
|
||||
telegram._forcebuy_inline(update, None)
|
||||
update.callback_query.data = 'XRP/USDT_||_long'
|
||||
telegram._forceenter_inline(update, None)
|
||||
assert fbuy_mock.call_count == 1
|
||||
|
||||
|
||||
@@ -1318,12 +1339,12 @@ def test_telegram_buy_tag_performance_handle(default_conf, update, ticker, fee,
|
||||
freqtradebot.enter_positions()
|
||||
trade = Trade.query.first()
|
||||
assert trade
|
||||
trade.buy_tag = "TESTBUY"
|
||||
|
||||
# Simulate fulfilled LIMIT_BUY order for trade
|
||||
oobj = Order.parse_from_ccxt_object(limit_buy_order, limit_buy_order['symbol'], 'buy')
|
||||
trade.update_trade(oobj)
|
||||
|
||||
trade.enter_tag = "TESTBUY"
|
||||
# Simulate fulfilled LIMIT_SELL order for trade
|
||||
oobj = Order.parse_from_ccxt_object(limit_sell_order, limit_sell_order['symbol'], 'sell')
|
||||
trade.update_trade(oobj)
|
||||
@@ -1331,19 +1352,19 @@ def test_telegram_buy_tag_performance_handle(default_conf, update, ticker, fee,
|
||||
trade.close_date = datetime.utcnow()
|
||||
trade.is_open = False
|
||||
context = MagicMock()
|
||||
telegram._buy_tag_performance(update=update, context=context)
|
||||
telegram._enter_tag_performance(update=update, context=context)
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'Buy Tag Performance' in msg_mock.call_args_list[0][0][0]
|
||||
assert '<code>TESTBUY\t0.00006217 BTC (6.20%) (1)</code>' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
context.args = [trade.pair]
|
||||
telegram._buy_tag_performance(update=update, context=context)
|
||||
telegram._enter_tag_performance(update=update, context=context)
|
||||
assert msg_mock.call_count == 2
|
||||
|
||||
msg_mock.reset_mock()
|
||||
mocker.patch('freqtrade.rpc.rpc.RPC._rpc_buy_tag_performance',
|
||||
mocker.patch('freqtrade.rpc.rpc.RPC._rpc_enter_tag_performance',
|
||||
side_effect=RPCException('Error'))
|
||||
telegram._buy_tag_performance(update=update, context=MagicMock())
|
||||
telegram._enter_tag_performance(update=update, context=MagicMock())
|
||||
|
||||
assert msg_mock.call_count == 1
|
||||
assert "Error" in msg_mock.call_args_list[0][0][0]
|
||||
@@ -1407,7 +1428,8 @@ def test_telegram_mix_tag_performance_handle(default_conf, update, ticker, fee,
|
||||
freqtradebot.enter_positions()
|
||||
trade = Trade.query.first()
|
||||
assert trade
|
||||
trade.buy_tag = "TESTBUY"
|
||||
|
||||
trade.enter_tag = "TESTBUY"
|
||||
trade.sell_reason = "TESTSELL"
|
||||
|
||||
# Simulate fulfilled LIMIT_BUY order for trade
|
||||
@@ -1633,7 +1655,10 @@ def test_edge_enabled(edge_conf, update, mocker) -> None:
|
||||
assert 'Winrate' not in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_telegram_trades(mocker, update, default_conf, fee):
|
||||
@pytest.mark.parametrize('is_short,regex_pattern',
|
||||
[(True, r"just now[ ]*XRP\/BTC \(#3\) -1.00% \("),
|
||||
(False, r"just now[ ]*XRP\/BTC \(#3\) 1.00% \(")])
|
||||
def test_telegram_trades(mocker, update, default_conf, fee, is_short, regex_pattern):
|
||||
|
||||
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
|
||||
@@ -1651,7 +1676,7 @@ def test_telegram_trades(mocker, update, default_conf, fee):
|
||||
assert "<pre>" not in msg_mock.call_args_list[0][0][0]
|
||||
msg_mock.reset_mock()
|
||||
|
||||
create_mock_trades(fee)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
|
||||
context = MagicMock()
|
||||
context.args = [5]
|
||||
@@ -1661,11 +1686,11 @@ def test_telegram_trades(mocker, update, default_conf, fee):
|
||||
assert "Profit (" in msg_mock.call_args_list[0][0][0]
|
||||
assert "Close Date" in msg_mock.call_args_list[0][0][0]
|
||||
assert "<pre>" in msg_mock.call_args_list[0][0][0]
|
||||
assert bool(re.search(r"just now[ ]*XRP\/BTC \(#3\) 1.00% \(",
|
||||
msg_mock.call_args_list[0][0][0]))
|
||||
assert bool(re.search(regex_pattern, msg_mock.call_args_list[0][0][0]))
|
||||
|
||||
|
||||
def test_telegram_delete_trade(mocker, update, default_conf, fee):
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
def test_telegram_delete_trade(mocker, update, default_conf, fee, is_short):
|
||||
|
||||
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
context = MagicMock()
|
||||
@@ -1675,7 +1700,7 @@ def test_telegram_delete_trade(mocker, update, default_conf, fee):
|
||||
assert "Trade-id not set." in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
msg_mock.reset_mock()
|
||||
create_mock_trades(fee)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
|
||||
context = MagicMock()
|
||||
context.args = [1]
|
||||
@@ -1720,7 +1745,7 @@ def test_show_config_handle(default_conf, update, mocker) -> None:
|
||||
assert msg_mock.call_count == 1
|
||||
assert '*Mode:* `{}`'.format('Dry-run') in msg_mock.call_args_list[0][0][0]
|
||||
assert '*Exchange:* `binance`' in msg_mock.call_args_list[0][0][0]
|
||||
assert '*Strategy:* `StrategyTestV2`' in msg_mock.call_args_list[0][0][0]
|
||||
assert f'*Strategy:* `{CURRENT_TEST_STRATEGY}`' in msg_mock.call_args_list[0][0][0]
|
||||
assert '*Stoploss:* `-0.1`' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
msg_mock.reset_mock()
|
||||
@@ -1729,18 +1754,25 @@ def test_show_config_handle(default_conf, update, mocker) -> None:
|
||||
assert msg_mock.call_count == 1
|
||||
assert '*Mode:* `{}`'.format('Dry-run') in msg_mock.call_args_list[0][0][0]
|
||||
assert '*Exchange:* `binance`' in msg_mock.call_args_list[0][0][0]
|
||||
assert '*Strategy:* `StrategyTestV2`' in msg_mock.call_args_list[0][0][0]
|
||||
assert f'*Strategy:* `{CURRENT_TEST_STRATEGY}`' in msg_mock.call_args_list[0][0][0]
|
||||
assert '*Initial Stoploss:* `-0.1`' in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None:
|
||||
@pytest.mark.parametrize('message_type,enter,enter_signal,leverage', [
|
||||
(RPCMessageType.BUY, 'Long', 'long_signal_01', None),
|
||||
(RPCMessageType.BUY, 'Long', 'long_signal_01', 1.0),
|
||||
(RPCMessageType.BUY, 'Long', 'long_signal_01', 5.0),
|
||||
(RPCMessageType.SHORT, 'Short', 'short_signal_01', 2.0)])
|
||||
def test_send_msg_buy_notification(default_conf, mocker, caplog, message_type,
|
||||
enter, enter_signal, leverage) -> None:
|
||||
|
||||
msg = {
|
||||
'type': RPCMessageType.BUY,
|
||||
'type': message_type,
|
||||
'trade_id': 1,
|
||||
'buy_tag': 'buy_signal_01',
|
||||
'enter_tag': enter_signal,
|
||||
'exchange': 'Binance',
|
||||
'pair': 'ETH/BTC',
|
||||
'leverage': leverage,
|
||||
'limit': 1.099e-05,
|
||||
'order_type': 'limit',
|
||||
'stake_amount': 0.01465333,
|
||||
@@ -1754,13 +1786,17 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None:
|
||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
|
||||
telegram.send_msg(msg)
|
||||
assert msg_mock.call_args[0][0] \
|
||||
== '\N{LARGE BLUE CIRCLE} *Binance:* Buying ETH/BTC (#1)\n' \
|
||||
'*Buy Tag:* `buy_signal_01`\n' \
|
||||
'*Amount:* `1333.33333333`\n' \
|
||||
'*Open Rate:* `0.00001099`\n' \
|
||||
'*Current Rate:* `0.00001099`\n' \
|
||||
'*Total:* `(0.01465333 BTC, 180.895 USD)`'
|
||||
leverage_text = f'*Leverage:* `{leverage}`\n' if leverage and leverage != 1.0 else ''
|
||||
|
||||
assert msg_mock.call_args[0][0] == (
|
||||
f'\N{LARGE BLUE CIRCLE} *Binance:* {enter} ETH/BTC (#1)\n'
|
||||
f'*Enter Tag:* `{enter_signal}`\n'
|
||||
'*Amount:* `1333.33333333`\n'
|
||||
f'{leverage_text}'
|
||||
'*Open Rate:* `0.00001099`\n'
|
||||
'*Current Rate:* `0.00001099`\n'
|
||||
'*Total:* `(0.01465333 BTC, 180.895 USD)`'
|
||||
)
|
||||
|
||||
freqtradebot.config['telegram']['notification_settings'] = {'buy': 'off'}
|
||||
caplog.clear()
|
||||
@@ -1778,20 +1814,23 @@ def test_send_msg_buy_notification(default_conf, mocker, caplog) -> None:
|
||||
msg_mock.call_args_list[0][1]['disable_notification'] is True
|
||||
|
||||
|
||||
def test_send_msg_buy_cancel_notification(default_conf, mocker) -> None:
|
||||
@pytest.mark.parametrize('message_type,enter_signal', [
|
||||
(RPCMessageType.BUY_CANCEL, 'long_signal_01'),
|
||||
(RPCMessageType.SHORT_CANCEL, 'short_signal_01')])
|
||||
def test_send_msg_buy_cancel_notification(default_conf, mocker, message_type, enter_signal) -> None:
|
||||
|
||||
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
|
||||
telegram.send_msg({
|
||||
'type': RPCMessageType.BUY_CANCEL,
|
||||
'buy_tag': 'buy_signal_01',
|
||||
'type': message_type,
|
||||
'enter_tag': enter_signal,
|
||||
'trade_id': 1,
|
||||
'exchange': 'Binance',
|
||||
'pair': 'ETH/BTC',
|
||||
'reason': CANCEL_REASON['TIMEOUT']
|
||||
})
|
||||
assert (msg_mock.call_args[0][0] == '\N{WARNING SIGN} *Binance:* '
|
||||
'Cancelling open buy Order for ETH/BTC (#1). '
|
||||
'Cancelling enter Order for ETH/BTC (#1). '
|
||||
'Reason: cancelled due to timeout.')
|
||||
|
||||
|
||||
@@ -1823,17 +1862,24 @@ def test_send_msg_protection_notification(default_conf, mocker, time_machine) ->
|
||||
"*All pairs* will be locked until `2021-09-01 06:45:00`.")
|
||||
|
||||
|
||||
def test_send_msg_buy_fill_notification(default_conf, mocker) -> None:
|
||||
@pytest.mark.parametrize('message_type,entered,enter_signal,leverage', [
|
||||
(RPCMessageType.BUY_FILL, 'Longed', 'long_signal_01', 1.0),
|
||||
(RPCMessageType.BUY_FILL, 'Longed', 'long_signal_02', 2.0),
|
||||
(RPCMessageType.SHORT_FILL, 'Shorted', 'short_signal_01', 2.0),
|
||||
])
|
||||
def test_send_msg_buy_fill_notification(default_conf, mocker, message_type, entered,
|
||||
enter_signal, leverage) -> None:
|
||||
|
||||
default_conf['telegram']['notification_settings']['buy_fill'] = 'on'
|
||||
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
|
||||
telegram.send_msg({
|
||||
'type': RPCMessageType.BUY_FILL,
|
||||
'type': message_type,
|
||||
'trade_id': 1,
|
||||
'buy_tag': 'buy_signal_01',
|
||||
'enter_tag': enter_signal,
|
||||
'exchange': 'Binance',
|
||||
'pair': 'ETH/BTC',
|
||||
'leverage': leverage,
|
||||
'stake_amount': 0.01465333,
|
||||
# 'stake_amount_fiat': 0.0,
|
||||
'stake_currency': 'BTC',
|
||||
@@ -1842,13 +1888,15 @@ def test_send_msg_buy_fill_notification(default_conf, mocker) -> None:
|
||||
'amount': 1333.3333333333335,
|
||||
'open_date': arrow.utcnow().shift(hours=-1)
|
||||
})
|
||||
|
||||
assert msg_mock.call_args[0][0] \
|
||||
== '\N{CHECK MARK} *Binance:* Bought ETH/BTC (#1)\n' \
|
||||
'*Buy Tag:* `buy_signal_01`\n' \
|
||||
'*Amount:* `1333.33333333`\n' \
|
||||
'*Open Rate:* `0.00001099`\n' \
|
||||
'*Total:* `(0.01465333 BTC, 180.895 USD)`'
|
||||
leverage_text = f'*Leverage:* `{leverage}`\n' if leverage != 1.0 else ''
|
||||
assert msg_mock.call_args[0][0] == (
|
||||
f'\N{CHECK MARK} *Binance:* {entered} ETH/BTC (#1)\n'
|
||||
f'*Enter Tag:* `{enter_signal}`\n'
|
||||
'*Amount:* `1333.33333333`\n'
|
||||
f"{leverage_text}"
|
||||
'*Open Rate:* `0.00001099`\n'
|
||||
'*Total:* `(0.01465333 BTC, 180.895 USD)`'
|
||||
)
|
||||
|
||||
|
||||
def test_send_msg_sell_notification(default_conf, mocker) -> None:
|
||||
@@ -1862,6 +1910,8 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
|
||||
'trade_id': 1,
|
||||
'exchange': 'Binance',
|
||||
'pair': 'KEY/ETH',
|
||||
'leverage': 1.0,
|
||||
'direction': 'Long',
|
||||
'gain': 'loss',
|
||||
'limit': 3.201e-05,
|
||||
'amount': 1333.3333333333335,
|
||||
@@ -1872,22 +1922,23 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
|
||||
'profit_ratio': -0.57405275,
|
||||
'stake_currency': 'ETH',
|
||||
'fiat_currency': 'USD',
|
||||
'buy_tag': 'buy_signal1',
|
||||
'sell_reason': SellType.STOP_LOSS.value,
|
||||
'enter_tag': 'buy_signal1',
|
||||
'sell_reason': ExitType.STOP_LOSS.value,
|
||||
'open_date': arrow.utcnow().shift(hours=-1),
|
||||
'close_date': arrow.utcnow(),
|
||||
})
|
||||
assert msg_mock.call_args[0][0] \
|
||||
== ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n'
|
||||
'*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n'
|
||||
'*Buy Tag:* `buy_signal1`\n'
|
||||
'*Sell Reason:* `stop_loss`\n'
|
||||
'*Duration:* `1:00:00 (60.0 min)`\n'
|
||||
'*Amount:* `1333.33333333`\n'
|
||||
'*Open Rate:* `0.00007500`\n'
|
||||
'*Current Rate:* `0.00003201`\n'
|
||||
'*Close Rate:* `0.00003201`'
|
||||
)
|
||||
assert msg_mock.call_args[0][0] == (
|
||||
'\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n'
|
||||
'*Unrealized Profit:* `-57.41% (loss: -0.05746268 ETH / -24.812 USD)`\n'
|
||||
'*Enter Tag:* `buy_signal1`\n'
|
||||
'*Exit Reason:* `stop_loss`\n'
|
||||
'*Duration:* `1:00:00 (60.0 min)`\n'
|
||||
'*Direction:* `Long`\n'
|
||||
'*Amount:* `1333.33333333`\n'
|
||||
'*Open Rate:* `0.00007500`\n'
|
||||
'*Current Rate:* `0.00003201`\n'
|
||||
'*Close Rate:* `0.00003201`'
|
||||
)
|
||||
|
||||
msg_mock.reset_mock()
|
||||
telegram.send_msg({
|
||||
@@ -1895,6 +1946,7 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
|
||||
'trade_id': 1,
|
||||
'exchange': 'Binance',
|
||||
'pair': 'KEY/ETH',
|
||||
'direction': 'Long',
|
||||
'gain': 'loss',
|
||||
'limit': 3.201e-05,
|
||||
'amount': 1333.3333333333335,
|
||||
@@ -1904,22 +1956,23 @@ def test_send_msg_sell_notification(default_conf, mocker) -> None:
|
||||
'profit_amount': -0.05746268,
|
||||
'profit_ratio': -0.57405275,
|
||||
'stake_currency': 'ETH',
|
||||
'buy_tag': 'buy_signal1',
|
||||
'sell_reason': SellType.STOP_LOSS.value,
|
||||
'enter_tag': 'buy_signal1',
|
||||
'sell_reason': ExitType.STOP_LOSS.value,
|
||||
'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30),
|
||||
'close_date': arrow.utcnow(),
|
||||
})
|
||||
assert msg_mock.call_args[0][0] \
|
||||
== ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n'
|
||||
'*Unrealized Profit:* `-57.41%`\n'
|
||||
'*Buy Tag:* `buy_signal1`\n'
|
||||
'*Sell Reason:* `stop_loss`\n'
|
||||
'*Duration:* `1 day, 2:30:00 (1590.0 min)`\n'
|
||||
'*Amount:* `1333.33333333`\n'
|
||||
'*Open Rate:* `0.00007500`\n'
|
||||
'*Current Rate:* `0.00003201`\n'
|
||||
'*Close Rate:* `0.00003201`'
|
||||
)
|
||||
assert msg_mock.call_args[0][0] == (
|
||||
'\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n'
|
||||
'*Unrealized Profit:* `-57.41%`\n'
|
||||
'*Enter Tag:* `buy_signal1`\n'
|
||||
'*Exit Reason:* `stop_loss`\n'
|
||||
'*Duration:* `1 day, 2:30:00 (1590.0 min)`\n'
|
||||
'*Direction:* `Long`\n'
|
||||
'*Amount:* `1333.33333333`\n'
|
||||
'*Open Rate:* `0.00007500`\n'
|
||||
'*Current Rate:* `0.00003201`\n'
|
||||
'*Close Rate:* `0.00003201`'
|
||||
)
|
||||
# Reset singleton function to avoid random breaks
|
||||
telegram._rpc._fiat_converter.convert_amount = old_convamount
|
||||
|
||||
@@ -1937,9 +1990,9 @@ def test_send_msg_sell_cancel_notification(default_conf, mocker) -> None:
|
||||
'pair': 'KEY/ETH',
|
||||
'reason': 'Cancelled on exchange'
|
||||
})
|
||||
assert msg_mock.call_args[0][0] \
|
||||
== ('\N{WARNING SIGN} *Binance:* Cancelling open sell Order for KEY/ETH (#1).'
|
||||
' Reason: Cancelled on exchange.')
|
||||
assert msg_mock.call_args[0][0] == (
|
||||
'\N{WARNING SIGN} *Binance:* Cancelling exit Order for KEY/ETH (#1).'
|
||||
' Reason: Cancelled on exchange.')
|
||||
|
||||
msg_mock.reset_mock()
|
||||
telegram.send_msg({
|
||||
@@ -1949,14 +2002,19 @@ def test_send_msg_sell_cancel_notification(default_conf, mocker) -> None:
|
||||
'pair': 'KEY/ETH',
|
||||
'reason': 'timeout'
|
||||
})
|
||||
assert msg_mock.call_args[0][0] \
|
||||
== ('\N{WARNING SIGN} *Binance:* Cancelling open sell Order for KEY/ETH (#1).'
|
||||
' Reason: timeout.')
|
||||
assert msg_mock.call_args[0][0] == (
|
||||
'\N{WARNING SIGN} *Binance:* Cancelling exit Order for KEY/ETH (#1). Reason: timeout.')
|
||||
# Reset singleton function to avoid random breaks
|
||||
telegram._rpc._fiat_converter.convert_amount = old_convamount
|
||||
|
||||
|
||||
def test_send_msg_sell_fill_notification(default_conf, mocker) -> None:
|
||||
@pytest.mark.parametrize('direction,enter_signal,leverage', [
|
||||
('Long', 'long_signal_01', None),
|
||||
('Long', 'long_signal_01', 1.0),
|
||||
('Long', 'long_signal_01', 5.0),
|
||||
('Short', 'short_signal_01', 2.0)])
|
||||
def test_send_msg_sell_fill_notification(default_conf, mocker, direction,
|
||||
enter_signal, leverage) -> None:
|
||||
|
||||
default_conf['telegram']['notification_settings']['sell_fill'] = 'on'
|
||||
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
@@ -1966,6 +2024,8 @@ def test_send_msg_sell_fill_notification(default_conf, mocker) -> None:
|
||||
'trade_id': 1,
|
||||
'exchange': 'Binance',
|
||||
'pair': 'KEY/ETH',
|
||||
'leverage': leverage,
|
||||
'direction': direction,
|
||||
'gain': 'loss',
|
||||
'limit': 3.201e-05,
|
||||
'amount': 1333.3333333333335,
|
||||
@@ -1975,21 +2035,25 @@ def test_send_msg_sell_fill_notification(default_conf, mocker) -> None:
|
||||
'profit_amount': -0.05746268,
|
||||
'profit_ratio': -0.57405275,
|
||||
'stake_currency': 'ETH',
|
||||
'buy_tag': 'buy_signal1',
|
||||
'sell_reason': SellType.STOP_LOSS.value,
|
||||
'enter_tag': enter_signal,
|
||||
'sell_reason': ExitType.STOP_LOSS.value,
|
||||
'open_date': arrow.utcnow().shift(days=-1, hours=-2, minutes=-30),
|
||||
'close_date': arrow.utcnow(),
|
||||
})
|
||||
assert msg_mock.call_args[0][0] \
|
||||
== ('\N{WARNING SIGN} *Binance:* Sold KEY/ETH (#1)\n'
|
||||
'*Profit:* `-57.41%`\n'
|
||||
'*Buy Tag:* `buy_signal1`\n'
|
||||
'*Sell Reason:* `stop_loss`\n'
|
||||
'*Duration:* `1 day, 2:30:00 (1590.0 min)`\n'
|
||||
'*Amount:* `1333.33333333`\n'
|
||||
'*Open Rate:* `0.00007500`\n'
|
||||
'*Close Rate:* `0.00003201`'
|
||||
)
|
||||
|
||||
leverage_text = f'*Leverage:* `{leverage}`\n' if leverage and leverage != 1.0 else ''
|
||||
assert msg_mock.call_args[0][0] == (
|
||||
'\N{WARNING SIGN} *Binance:* Exited KEY/ETH (#1)\n'
|
||||
'*Profit:* `-57.41%`\n'
|
||||
f'*Enter Tag:* `{enter_signal}`\n'
|
||||
'*Exit Reason:* `stop_loss`\n'
|
||||
'*Duration:* `1 day, 2:30:00 (1590.0 min)`\n'
|
||||
f"*Direction:* `{direction}`\n"
|
||||
f"{leverage_text}"
|
||||
'*Amount:* `1333.33333333`\n'
|
||||
'*Open Rate:* `0.00007500`\n'
|
||||
'*Close Rate:* `0.00003201`'
|
||||
)
|
||||
|
||||
|
||||
def test_send_msg_status_notification(default_conf, mocker) -> None:
|
||||
@@ -2028,16 +2092,22 @@ def test_send_msg_unknown_type(default_conf, mocker) -> None:
|
||||
})
|
||||
|
||||
|
||||
def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None:
|
||||
@pytest.mark.parametrize('message_type,enter,enter_signal,leverage', [
|
||||
(RPCMessageType.BUY, 'Long', 'long_signal_01', None),
|
||||
(RPCMessageType.BUY, 'Long', 'long_signal_01', 2.0),
|
||||
(RPCMessageType.SHORT, 'Short', 'short_signal_01', 2.0)])
|
||||
def test_send_msg_buy_notification_no_fiat(
|
||||
default_conf, mocker, message_type, enter, enter_signal, leverage) -> None:
|
||||
del default_conf['fiat_display_currency']
|
||||
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
|
||||
telegram.send_msg({
|
||||
'type': RPCMessageType.BUY,
|
||||
'buy_tag': 'buy_signal_01',
|
||||
'type': message_type,
|
||||
'enter_tag': enter_signal,
|
||||
'trade_id': 1,
|
||||
'exchange': 'Binance',
|
||||
'pair': 'ETH/BTC',
|
||||
'leverage': leverage,
|
||||
'limit': 1.099e-05,
|
||||
'order_type': 'limit',
|
||||
'stake_amount': 0.01465333,
|
||||
@@ -2048,15 +2118,27 @@ def test_send_msg_buy_notification_no_fiat(default_conf, mocker) -> None:
|
||||
'amount': 1333.3333333333335,
|
||||
'open_date': arrow.utcnow().shift(hours=-1)
|
||||
})
|
||||
assert msg_mock.call_args[0][0] == ('\N{LARGE BLUE CIRCLE} *Binance:* Buying ETH/BTC (#1)\n'
|
||||
'*Buy Tag:* `buy_signal_01`\n'
|
||||
'*Amount:* `1333.33333333`\n'
|
||||
'*Open Rate:* `0.00001099`\n'
|
||||
'*Current Rate:* `0.00001099`\n'
|
||||
'*Total:* `(0.01465333 BTC)`')
|
||||
|
||||
leverage_text = f'*Leverage:* `{leverage}`\n' if leverage and leverage != 1.0 else ''
|
||||
assert msg_mock.call_args[0][0] == (
|
||||
f'\N{LARGE BLUE CIRCLE} *Binance:* {enter} ETH/BTC (#1)\n'
|
||||
f'*Enter Tag:* `{enter_signal}`\n'
|
||||
'*Amount:* `1333.33333333`\n'
|
||||
f'{leverage_text}'
|
||||
'*Open Rate:* `0.00001099`\n'
|
||||
'*Current Rate:* `0.00001099`\n'
|
||||
'*Total:* `(0.01465333 BTC)`'
|
||||
)
|
||||
|
||||
|
||||
def test_send_msg_sell_notification_no_fiat(default_conf, mocker) -> None:
|
||||
@pytest.mark.parametrize('direction,enter_signal,leverage', [
|
||||
('Long', 'long_signal_01', None),
|
||||
('Long', 'long_signal_01', 1.0),
|
||||
('Long', 'long_signal_01', 5.0),
|
||||
('Short', 'short_signal_01', 2.0),
|
||||
])
|
||||
def test_send_msg_sell_notification_no_fiat(
|
||||
default_conf, mocker, direction, enter_signal, leverage) -> None:
|
||||
del default_conf['fiat_display_currency']
|
||||
telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
|
||||
@@ -2066,6 +2148,8 @@ def test_send_msg_sell_notification_no_fiat(default_conf, mocker) -> None:
|
||||
'exchange': 'Binance',
|
||||
'pair': 'KEY/ETH',
|
||||
'gain': 'loss',
|
||||
'leverage': leverage,
|
||||
'direction': direction,
|
||||
'limit': 3.201e-05,
|
||||
'amount': 1333.3333333333335,
|
||||
'order_type': 'limit',
|
||||
@@ -2075,21 +2159,26 @@ def test_send_msg_sell_notification_no_fiat(default_conf, mocker) -> None:
|
||||
'profit_ratio': -0.57405275,
|
||||
'stake_currency': 'ETH',
|
||||
'fiat_currency': 'USD',
|
||||
'buy_tag': 'buy_signal1',
|
||||
'sell_reason': SellType.STOP_LOSS.value,
|
||||
'enter_tag': enter_signal,
|
||||
'sell_reason': ExitType.STOP_LOSS.value,
|
||||
'open_date': arrow.utcnow().shift(hours=-2, minutes=-35, seconds=-3),
|
||||
'close_date': arrow.utcnow(),
|
||||
})
|
||||
assert msg_mock.call_args[0][0] == ('\N{WARNING SIGN} *Binance:* Selling KEY/ETH (#1)\n'
|
||||
'*Unrealized Profit:* `-57.41%`\n'
|
||||
'*Buy Tag:* `buy_signal1`\n'
|
||||
'*Sell Reason:* `stop_loss`\n'
|
||||
'*Duration:* `2:35:03 (155.1 min)`\n'
|
||||
'*Amount:* `1333.33333333`\n'
|
||||
'*Open Rate:* `0.00007500`\n'
|
||||
'*Current Rate:* `0.00003201`\n'
|
||||
'*Close Rate:* `0.00003201`'
|
||||
)
|
||||
|
||||
leverage_text = f'*Leverage:* `{leverage}`\n' if leverage and leverage != 1.0 else ''
|
||||
assert msg_mock.call_args[0][0] == (
|
||||
'\N{WARNING SIGN} *Binance:* Exiting KEY/ETH (#1)\n'
|
||||
'*Unrealized Profit:* `-57.41%`\n'
|
||||
f'*Enter Tag:* `{enter_signal}`\n'
|
||||
'*Exit Reason:* `stop_loss`\n'
|
||||
'*Duration:* `2:35:03 (155.1 min)`\n'
|
||||
f'*Direction:* `{direction}`\n'
|
||||
f'{leverage_text}'
|
||||
'*Amount:* `1333.33333333`\n'
|
||||
'*Open Rate:* `0.00007500`\n'
|
||||
'*Current Rate:* `0.00003201`\n'
|
||||
'*Close Rate:* `0.00003201`'
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('msg,expected', [
|
||||
|
@@ -5,7 +5,7 @@ from unittest.mock import MagicMock
|
||||
import pytest
|
||||
from requests import RequestException
|
||||
|
||||
from freqtrade.enums import RPCMessageType, SellType
|
||||
from freqtrade.enums import ExitType, RPCMessageType
|
||||
from freqtrade.rpc import RPC
|
||||
from freqtrade.rpc.webhook import Webhook
|
||||
from tests.conftest import get_patched_freqtradebot, log_has
|
||||
@@ -18,17 +18,23 @@ def get_webhook_dict() -> dict:
|
||||
"webhookbuy": {
|
||||
"value1": "Buying {pair}",
|
||||
"value2": "limit {limit:8f}",
|
||||
"value3": "{stake_amount:8f} {stake_currency}"
|
||||
"value3": "{stake_amount:8f} {stake_currency}",
|
||||
"value4": "leverage {leverage:.1f}",
|
||||
"value5": "direction {direction}"
|
||||
},
|
||||
"webhookbuycancel": {
|
||||
"value1": "Cancelling Open Buy Order for {pair}",
|
||||
"value2": "limit {limit:8f}",
|
||||
"value3": "{stake_amount:8f} {stake_currency}"
|
||||
"value3": "{stake_amount:8f} {stake_currency}",
|
||||
"value4": "leverage {leverage:.1f}",
|
||||
"value5": "direction {direction}"
|
||||
},
|
||||
"webhookbuyfill": {
|
||||
"value1": "Buy Order for {pair} filled",
|
||||
"value2": "at {open_rate:8f}",
|
||||
"value3": "{stake_amount:8f} {stake_currency}"
|
||||
"value3": "{stake_amount:8f} {stake_currency}",
|
||||
"value4": "leverage {leverage:.1f}",
|
||||
"value5": "direction {direction}"
|
||||
},
|
||||
"webhooksell": {
|
||||
"value1": "Selling {pair}",
|
||||
@@ -71,6 +77,8 @@ def test_send_msg_webhook(default_conf, mocker):
|
||||
'type': RPCMessageType.BUY,
|
||||
'exchange': 'Binance',
|
||||
'pair': 'ETH/BTC',
|
||||
'leverage': 1.0,
|
||||
'direction': 'Long',
|
||||
'limit': 0.005,
|
||||
'stake_amount': 0.8,
|
||||
'stake_amount_fiat': 500,
|
||||
@@ -85,6 +93,37 @@ def test_send_msg_webhook(default_conf, mocker):
|
||||
default_conf["webhook"]["webhookbuy"]["value2"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value3"] ==
|
||||
default_conf["webhook"]["webhookbuy"]["value3"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value4"] ==
|
||||
default_conf["webhook"]["webhookbuy"]["value4"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value5"] ==
|
||||
default_conf["webhook"]["webhookbuy"]["value5"].format(**msg))
|
||||
# Test short
|
||||
msg_mock.reset_mock()
|
||||
|
||||
msg = {
|
||||
'type': RPCMessageType.SHORT,
|
||||
'exchange': 'Binance',
|
||||
'pair': 'ETH/BTC',
|
||||
'leverage': 2.0,
|
||||
'direction': 'Short',
|
||||
'limit': 0.005,
|
||||
'stake_amount': 0.8,
|
||||
'stake_amount_fiat': 500,
|
||||
'stake_currency': 'BTC',
|
||||
'fiat_currency': 'EUR'
|
||||
}
|
||||
webhook.send_msg(msg=msg)
|
||||
assert msg_mock.call_count == 1
|
||||
assert (msg_mock.call_args[0][0]["value1"] ==
|
||||
default_conf["webhook"]["webhookbuy"]["value1"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value2"] ==
|
||||
default_conf["webhook"]["webhookbuy"]["value2"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value3"] ==
|
||||
default_conf["webhook"]["webhookbuy"]["value3"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value4"] ==
|
||||
default_conf["webhook"]["webhookbuy"]["value4"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value5"] ==
|
||||
default_conf["webhook"]["webhookbuy"]["value5"].format(**msg))
|
||||
# Test buy cancel
|
||||
msg_mock.reset_mock()
|
||||
|
||||
@@ -92,6 +131,8 @@ def test_send_msg_webhook(default_conf, mocker):
|
||||
'type': RPCMessageType.BUY_CANCEL,
|
||||
'exchange': 'Binance',
|
||||
'pair': 'ETH/BTC',
|
||||
'leverage': 1.0,
|
||||
'direction': 'Long',
|
||||
'limit': 0.005,
|
||||
'stake_amount': 0.8,
|
||||
'stake_amount_fiat': 500,
|
||||
@@ -106,6 +147,33 @@ def test_send_msg_webhook(default_conf, mocker):
|
||||
default_conf["webhook"]["webhookbuycancel"]["value2"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value3"] ==
|
||||
default_conf["webhook"]["webhookbuycancel"]["value3"].format(**msg))
|
||||
# Test short cancel
|
||||
msg_mock.reset_mock()
|
||||
|
||||
msg = {
|
||||
'type': RPCMessageType.SHORT_CANCEL,
|
||||
'exchange': 'Binance',
|
||||
'pair': 'ETH/BTC',
|
||||
'leverage': 2.0,
|
||||
'direction': 'Short',
|
||||
'limit': 0.005,
|
||||
'stake_amount': 0.8,
|
||||
'stake_amount_fiat': 500,
|
||||
'stake_currency': 'BTC',
|
||||
'fiat_currency': 'EUR'
|
||||
}
|
||||
webhook.send_msg(msg=msg)
|
||||
assert msg_mock.call_count == 1
|
||||
assert (msg_mock.call_args[0][0]["value1"] ==
|
||||
default_conf["webhook"]["webhookbuycancel"]["value1"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value2"] ==
|
||||
default_conf["webhook"]["webhookbuycancel"]["value2"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value3"] ==
|
||||
default_conf["webhook"]["webhookbuycancel"]["value3"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value4"] ==
|
||||
default_conf["webhook"]["webhookbuycancel"]["value4"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value5"] ==
|
||||
default_conf["webhook"]["webhookbuycancel"]["value5"].format(**msg))
|
||||
# Test buy fill
|
||||
msg_mock.reset_mock()
|
||||
|
||||
@@ -113,6 +181,8 @@ def test_send_msg_webhook(default_conf, mocker):
|
||||
'type': RPCMessageType.BUY_FILL,
|
||||
'exchange': 'Binance',
|
||||
'pair': 'ETH/BTC',
|
||||
'leverage': 1.0,
|
||||
'direction': 'Long',
|
||||
'open_rate': 0.005,
|
||||
'stake_amount': 0.8,
|
||||
'stake_amount_fiat': 500,
|
||||
@@ -127,8 +197,40 @@ def test_send_msg_webhook(default_conf, mocker):
|
||||
default_conf["webhook"]["webhookbuyfill"]["value2"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value3"] ==
|
||||
default_conf["webhook"]["webhookbuyfill"]["value3"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value4"] ==
|
||||
default_conf["webhook"]["webhookbuycancel"]["value4"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value5"] ==
|
||||
default_conf["webhook"]["webhookbuycancel"]["value5"].format(**msg))
|
||||
# Test short fill
|
||||
msg_mock.reset_mock()
|
||||
|
||||
msg = {
|
||||
'type': RPCMessageType.SHORT_FILL,
|
||||
'exchange': 'Binance',
|
||||
'pair': 'ETH/BTC',
|
||||
'leverage': 2.0,
|
||||
'direction': 'Short',
|
||||
'open_rate': 0.005,
|
||||
'stake_amount': 0.8,
|
||||
'stake_amount_fiat': 500,
|
||||
'stake_currency': 'BTC',
|
||||
'fiat_currency': 'EUR'
|
||||
}
|
||||
webhook.send_msg(msg=msg)
|
||||
assert msg_mock.call_count == 1
|
||||
assert (msg_mock.call_args[0][0]["value1"] ==
|
||||
default_conf["webhook"]["webhookbuyfill"]["value1"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value2"] ==
|
||||
default_conf["webhook"]["webhookbuyfill"]["value2"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value3"] ==
|
||||
default_conf["webhook"]["webhookbuyfill"]["value3"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value4"] ==
|
||||
default_conf["webhook"]["webhookbuycancel"]["value4"].format(**msg))
|
||||
assert (msg_mock.call_args[0][0]["value5"] ==
|
||||
default_conf["webhook"]["webhookbuycancel"]["value5"].format(**msg))
|
||||
# Test sell
|
||||
msg_mock.reset_mock()
|
||||
|
||||
msg = {
|
||||
'type': RPCMessageType.SELL,
|
||||
'exchange': 'Binance',
|
||||
@@ -142,7 +244,7 @@ def test_send_msg_webhook(default_conf, mocker):
|
||||
'profit_amount': 0.001,
|
||||
'profit_ratio': 0.20,
|
||||
'stake_currency': 'BTC',
|
||||
'sell_reason': SellType.STOP_LOSS.value
|
||||
'sell_reason': ExitType.STOP_LOSS.value
|
||||
}
|
||||
webhook.send_msg(msg=msg)
|
||||
assert msg_mock.call_count == 1
|
||||
@@ -167,7 +269,7 @@ def test_send_msg_webhook(default_conf, mocker):
|
||||
'profit_amount': 0.001,
|
||||
'profit_ratio': 0.20,
|
||||
'stake_currency': 'BTC',
|
||||
'sell_reason': SellType.STOP_LOSS.value
|
||||
'sell_reason': ExitType.STOP_LOSS.value
|
||||
}
|
||||
webhook.send_msg(msg=msg)
|
||||
assert msg_mock.call_count == 1
|
||||
@@ -192,7 +294,7 @@ def test_send_msg_webhook(default_conf, mocker):
|
||||
'profit_amount': 0.001,
|
||||
'profit_ratio': 0.20,
|
||||
'stake_currency': 'BTC',
|
||||
'sell_reason': SellType.STOP_LOSS.value
|
||||
'sell_reason': ExitType.STOP_LOSS.value
|
||||
}
|
||||
webhook.send_msg(msg=msg)
|
||||
assert msg_mock.call_count == 1
|
||||
|
Reference in New Issue
Block a user