freqtrade_origin/tests/rpc/test_rpc.py

1307 lines
45 KiB
Python
Raw Normal View History

from copy import deepcopy
2021-03-01 06:51:33 +00:00
from datetime import datetime, timedelta, timezone
from unittest.mock import ANY, MagicMock, PropertyMock
2018-02-13 03:45:59 +00:00
2018-06-08 02:52:50 +00:00
import pytest
2018-10-10 20:03:54 +00:00
from numpy import isnan
2023-03-16 06:25:04 +00:00
from sqlalchemy import select
2018-06-08 02:52:50 +00:00
from freqtrade.edge import PairInfo
2022-03-03 06:07:33 +00:00
from freqtrade.enums import SignalDirection, State, TradingMode
from freqtrade.exceptions import ExchangeError, InvalidOrderException, TemporaryError
2023-12-23 08:50:42 +00:00
from freqtrade.persistence import Order, Trade
from freqtrade.persistence.key_value_store import set_startup_time
2021-03-01 19:08:49 +00:00
from freqtrade.persistence.pairlock_middleware import PairLocks
from freqtrade.rpc import RPC, RPCException
from freqtrade.rpc.fiat_convert import CryptoToFiatConverter
from tests.conftest import (EXMS, create_mock_trades, create_mock_trades_usdt,
get_patched_freqtradebot, patch_get_signal)
2019-10-26 08:08:23 +00:00
def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None:
gen_response = {
'trade_id': 1,
'pair': 'ETH/BTC',
2022-04-09 14:42:18 +00:00
'base_currency': 'ETH',
'quote_currency': 'BTC',
2019-05-06 04:56:07 +00:00
'open_date': ANY,
2020-05-24 06:47:10 +00:00
'open_timestamp': ANY,
2024-02-15 19:39:06 +00:00
'open_fill_date': ANY,
'open_fill_timestamp': ANY,
2020-04-06 09:32:00 +00:00
'is_open': ANY,
'fee_open': ANY,
2020-04-30 04:58:43 +00:00
'fee_open_cost': ANY,
'fee_open_currency': ANY,
2020-06-04 05:20:50 +00:00
'fee_close': fee.return_value,
2020-04-30 04:58:43 +00:00
'fee_close_cost': ANY,
'fee_close_currency': ANY,
2020-04-06 09:32:00 +00:00
'open_rate_requested': ANY,
'open_trade_value': 0.0010025,
2020-04-06 09:32:00 +00:00
'close_rate_requested': ANY,
2022-03-24 19:33:47 +00:00
'exit_reason': ANY,
2022-04-03 09:17:01 +00:00
'exit_order_status': ANY,
2020-04-06 09:32:00 +00:00
'min_rate': ANY,
'max_rate': ANY,
'strategy': ANY,
2021-11-21 08:51:16 +00:00
'enter_tag': ANY,
'timeframe': 5,
2019-05-06 04:56:07 +00:00
'close_date': None,
2020-05-24 06:47:10 +00:00
'close_timestamp': None,
'open_rate': 1.098e-05,
'close_rate': None,
'current_rate': 1.099e-05,
'amount': 91.07468123,
'amount_requested': 91.07468124,
2019-04-04 10:06:45 +00:00
'stake_amount': 0.001,
2023-03-05 18:45:04 +00:00
'max_stake_amount': None,
2021-01-23 11:43:27 +00:00
'trade_duration': None,
'trade_duration_s': None,
'close_profit': None,
2020-05-26 17:25:03 +00:00
'close_profit_pct': None,
'close_profit_abs': None,
'profit_ratio': -0.00408133,
'profit_pct': -0.41,
'profit_abs': -4.09e-06,
2021-04-02 12:35:19 +00:00
'profit_fiat': ANY,
'stop_loss_abs': 9.89e-06,
2020-06-04 05:20:50 +00:00
'stop_loss_pct': -10.0,
'stop_loss_ratio': -0.1,
'stoploss_last_update': ANY,
'stoploss_last_update_timestamp': ANY,
'initial_stop_loss_abs': 9.89e-06,
2020-06-04 05:20:50 +00:00
'initial_stop_loss_pct': -10.0,
'initial_stop_loss_ratio': -0.1,
'stoploss_current_dist': pytest.approx(-1.0999999e-06),
'stoploss_current_dist_ratio': -0.10009099,
'stoploss_current_dist_pct': -10.01,
'stoploss_entry_dist': -0.00010402,
'stoploss_entry_dist_ratio': -0.10376381,
2023-06-21 01:52:06 +00:00
'open_orders': '',
'realized_profit': 0.0,
2023-03-04 17:20:31 +00:00
'realized_profit_ratio': None,
2023-02-28 19:31:02 +00:00
'total_profit_abs': -4.09e-06,
'total_profit_fiat': ANY,
2023-03-05 18:45:04 +00:00
'total_profit_ratio': None,
2021-04-20 10:54:22 +00:00
'exchange': 'binance',
2021-07-05 05:12:07 +00:00
'leverage': 1.0,
'interest_rate': 0.0,
2022-03-06 18:05:20 +00:00
'liquidation_price': None,
'is_short': False,
'funding_fees': 0.0,
2022-02-11 16:02:04 +00:00
'trading_mode': TradingMode.SPOT,
'amount_precision': 8.0,
'price_precision': 8.0,
'precision_mode': 2,
'contract_size': 1,
'has_open_orders': False,
'orders': [{
'amount': 91.07468123, 'average': 1.098e-05, 'safe_price': 1.098e-05,
2022-02-03 18:11:35 +00:00
'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,
2022-02-26 15:10:54 +00:00
'is_open': False, 'pair': 'ETH/BTC', 'order_id': ANY,
2023-07-10 17:47:37 +00:00
'remaining': ANY, 'status': ANY, 'ft_is_entry': True, 'ft_fee_base': None,
2024-01-28 21:48:26 +00:00
'funding_fee': ANY, 'ft_order_tag': None,
2022-03-03 19:51:52 +00:00
}],
2020-06-04 05:20:50 +00:00
}
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
fetch_ticker=ticker,
get_fee=fee,
2023-02-16 18:47:12 +00:00
_dry_is_price_crossed=MagicMock(side_effect=[False, True]),
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
rpc = RPC(freqtradebot)
freqtradebot.state = State.RUNNING
with pytest.raises(RPCException, match=r'.*no active trade*'):
rpc._rpc_trade_status()
freqtradebot.enter_positions()
# Open order...
results = rpc._rpc_trade_status()
response_unfilled = deepcopy(gen_response)
# Different from "filled" response:
response_unfilled.update({
'amount': 91.07468124,
'profit_ratio': 0.0,
'profit_pct': 0.0,
'profit_abs': 0.0,
2023-02-28 19:31:02 +00:00
'total_profit_abs': 0.0,
2023-06-21 01:52:06 +00:00
'open_orders': '(limit buy rem=91.07468123)',
'has_open_orders': True,
})
response_unfilled['orders'][0].update({
'is_open': True,
'filled': 0.0,
'remaining': 91.07468123
})
assert results[0] == response_unfilled
# Open order without remaining
trade = Trade.get_open_trades()[0]
# kucoin case (no remaining set).
trade.orders[0].remaining = None
Trade.commit()
results = rpc._rpc_trade_status()
# Reuse above object, only remaining changed.
response_unfilled['orders'][0].update({
'remaining': None,
})
assert results[0] == response_unfilled
trade = Trade.get_open_trades()[0]
trade.orders[0].remaining = trade.amount
Trade.commit()
# Fill open order ...
freqtradebot.manage_open_orders()
trades = Trade.get_open_trades()
freqtradebot.exit_positions(trades)
results = rpc._rpc_trade_status()
response = deepcopy(gen_response)
2023-03-05 18:45:04 +00:00
response.update({
'max_stake_amount': 0.001,
2023-08-29 19:29:36 +00:00
'total_profit_ratio': pytest.approx(-0.00409153),
'has_open_orders': False,
2023-03-05 18:45:04 +00:00
})
assert results[0] == response
mocker.patch(f'{EXMS}.get_rate',
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
results = rpc._rpc_trade_status()
assert isnan(results[0]['profit_ratio'])
assert isnan(results[0]['current_rate'])
response_norate = deepcopy(gen_response)
# Update elements that are NaN when no rate is available.
response_norate.update({
2020-06-04 05:20:50 +00:00
'stoploss_current_dist': ANY,
'stoploss_current_dist_ratio': ANY,
'stoploss_current_dist_pct': ANY,
2023-03-05 18:45:04 +00:00
'max_stake_amount': 0.001,
'profit_ratio': ANY,
'profit_pct': ANY,
'profit_abs': ANY,
2023-02-28 19:31:02 +00:00
'total_profit_abs': ANY,
'total_profit_ratio': ANY,
'current_rate': ANY,
})
assert results[0] == response_norate
2019-10-26 08:08:23 +00:00
def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None:
2019-11-12 13:58:41 +00:00
mocker.patch.multiple(
2020-03-07 12:01:26 +00:00
'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
get_price=MagicMock(return_value={'bitcoin': {'usd': 15000.0}}),
2019-11-12 13:58:41 +00:00
)
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
2018-02-13 03:45:59 +00:00
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
2019-12-18 15:34:30 +00:00
fetch_ticker=ticker,
2018-06-16 23:23:12 +00:00
get_fee=fee,
2018-02-13 03:45:59 +00:00
)
2020-12-24 07:36:35 +00:00
del default_conf['fiat_display_currency']
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
2018-02-13 03:45:59 +00:00
rpc = RPC(freqtradebot)
freqtradebot.state = State.RUNNING
2019-12-29 18:51:47 +00:00
with pytest.raises(RPCException, match=r'.*no active trade*'):
2019-11-12 13:58:41 +00:00
rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
mocker.patch(f'{EXMS}._dry_is_price_crossed', return_value=False)
freqtradebot.enter_positions()
2019-11-12 13:58:41 +00:00
2022-05-08 14:04:06 +00:00
result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
assert "Since" in headers
assert "Pair" in headers
assert 'instantly' == result[0][2]
assert 'ETH/BTC' in result[0][1]
assert '0.00 (0.00)' == result[0][3]
assert '0.00' == f'{fiat_profit_sum:.2f}'
2022-05-08 14:04:06 +00:00
mocker.patch(f'{EXMS}._dry_is_price_crossed', return_value=True)
2022-05-08 14:04:06 +00:00
freqtradebot.process()
result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
2019-11-12 13:58:41 +00:00
assert "Since" in headers
assert "Pair" in headers
assert 'instantly' == result[0][2]
2020-02-08 20:02:52 +00:00
assert 'ETH/BTC' in result[0][1]
assert '-0.41% (-0.00)' == result[0][3]
assert '-0.00' == f'{fiat_profit_sum:.2f}'
2019-11-12 13:58:41 +00:00
# Test with fiat convert
2019-11-12 13:58:41 +00:00
rpc._fiat_converter = CryptoToFiatConverter()
result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
2019-11-12 12:54:26 +00:00
assert "Since" in headers
assert "Pair" in headers
2022-01-21 06:04:25 +00:00
assert len(result[0]) == 4
2019-11-12 13:58:41 +00:00
assert 'instantly' == result[0][2]
2020-02-08 20:02:52 +00:00
assert 'ETH/BTC' in result[0][1]
assert '-0.41% (-0.06)' == result[0][3]
assert '-0.06' == f'{fiat_profit_sum:.2f}'
2022-01-21 06:04:25 +00:00
rpc._config['position_adjustment_enable'] = True
rpc._config['max_entry_position_adjustment'] = 3
2022-01-21 06:04:25 +00:00
result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
2022-02-07 07:31:35 +00:00
assert "# Entries" in headers
2022-01-21 06:04:25 +00:00
assert len(result[0]) == 5
# 4th column should be 1/4 - as 1 order filled (a total of 4 is possible)
# 3 on top of the initial one.
assert result[0][4] == '1/4'
2022-01-21 06:04:25 +00:00
mocker.patch(f'{EXMS}.get_rate',
MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")))
result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf['stake_currency'], 'USD')
2019-11-12 13:58:41 +00:00
assert 'instantly' == result[0][2]
2020-02-08 20:02:52 +00:00
assert 'ETH/BTC' in result[0][1]
2019-11-12 13:58:41 +00:00
assert 'nan%' == result[0][3]
assert isnan(fiat_profit_sum)
def test__rpc_timeunit_profit(
default_conf_usdt, ticker, fee, markets, mocker, time_machine) -> None:
time_machine.move_to("2023-09-05 10:00:00 +00:00", tick=False)
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
2018-02-13 03:45:59 +00:00
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
2019-12-18 15:34:30 +00:00
fetch_ticker=ticker,
2018-06-16 23:23:12 +00:00
get_fee=fee,
2019-03-05 18:46:03 +00:00
markets=PropertyMock(return_value=markets)
2018-02-13 03:45:59 +00:00
)
2022-06-11 06:58:36 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf_usdt)
create_mock_trades_usdt(fee)
stake_currency = default_conf_usdt['stake_currency']
fiat_display_currency = default_conf_usdt['fiat_display_currency']
2018-02-13 03:45:59 +00:00
rpc = RPC(freqtradebot)
2018-07-21 18:44:38 +00:00
rpc._fiat_converter = CryptoToFiatConverter()
# Try valid data
2022-06-09 04:45:22 +00:00
days = rpc._rpc_timeunit_profit(7, stake_currency, fiat_display_currency)
2020-05-17 18:12:01 +00:00
assert len(days['data']) == 7
2022-06-11 06:58:36 +00:00
assert days['stake_currency'] == default_conf_usdt['stake_currency']
assert days['fiat_display_currency'] == default_conf_usdt['fiat_display_currency']
2020-05-17 18:12:01 +00:00
for day in days['data']:
2022-06-11 06:58:36 +00:00
# {'date': datetime.date(2022, 6, 11), 'abs_profit': 13.8299999,
# 'starting_balance': 1055.37, 'rel_profit': 0.0131044,
2022-06-11 06:58:36 +00:00
# 'fiat_value': 0.0, 'trade_count': 2}
assert day['abs_profit'] in (0.0, pytest.approx(6.83), pytest.approx(-4.09))
assert day['rel_profit'] in (0.0, pytest.approx(0.00642902), pytest.approx(-0.00383512))
2022-06-11 06:58:36 +00:00
assert day['trade_count'] in (0, 1, 2)
assert day['starting_balance'] in (pytest.approx(1062.37), pytest.approx(1066.46))
2022-06-11 06:58:36 +00:00
assert day['fiat_value'] in (0.0, )
# ensure first day is current date
2023-05-14 06:42:30 +00:00
assert str(days['data'][0]['date']) == str(datetime.now(timezone.utc).date())
# Try invalid data
2018-06-08 02:52:50 +00:00
with pytest.raises(RPCException, match=r'.*must be an integer greater than 0*'):
2022-06-09 04:45:22 +00:00
rpc._rpc_timeunit_profit(0, stake_currency, fiat_display_currency)
2021-09-20 02:24:22 +00:00
@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(
2023-03-01 19:14:15 +00:00
EXMS,
markets=PropertyMock(return_value=markets)
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
2021-09-20 02:24:22 +00:00
create_mock_trades(fee, is_short)
rpc = RPC(freqtradebot)
rpc._fiat_converter = CryptoToFiatConverter()
trades = rpc._rpc_trade_history(2)
assert len(trades['trades']) == 2
assert trades['trades_count'] == 2
assert isinstance(trades['trades'][0], dict)
assert isinstance(trades['trades'][1], dict)
trades = rpc._rpc_trade_history(0)
assert len(trades['trades']) == 2
assert trades['trades_count'] == 2
# The first closed trade is for ETC ... sorting is descending
assert trades['trades'][-1]['pair'] == 'ETC/BTC'
assert trades['trades'][0]['pair'] == 'XRP/BTC'
2021-09-20 02:24:22 +00:00
@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()
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
markets=PropertyMock(return_value=markets),
cancel_order=cancel_mock,
cancel_stoploss_order=stoploss_mock,
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
freqtradebot.strategy.order_types['stoploss_on_exchange'] = True
2021-09-20 02:24:22 +00:00
create_mock_trades(fee, is_short)
rpc = RPC(freqtradebot)
with pytest.raises(RPCException, match='invalid argument'):
rpc._rpc_delete('200')
2023-03-16 06:25:04 +00:00
trades = Trade.session.scalars(select(Trade)).all()
2023-12-23 08:50:42 +00:00
trades[2].orders.append(
Order(
ft_order_side='stoploss',
ft_pair=trades[2].pair,
ft_is_open=True,
ft_amount=trades[2].amount,
ft_price=trades[2].stop_loss,
order_id='102',
status='open',
)
)
assert len(trades) > 2
res = rpc._rpc_delete('1')
assert isinstance(res, dict)
assert res['result'] == 'success'
assert res['trade_id'] == '1'
assert res['cancel_order_count'] == 1
assert cancel_mock.call_count == 1
assert stoploss_mock.call_count == 0
cancel_mock.reset_mock()
stoploss_mock.reset_mock()
2023-12-23 08:50:42 +00:00
res = rpc._rpc_delete('5')
assert isinstance(res, dict)
assert stoploss_mock.call_count == 1
2023-08-24 15:38:56 +00:00
assert res['cancel_order_count'] == 1
stoploss_mock = mocker.patch(f'{EXMS}.cancel_stoploss_order', side_effect=InvalidOrderException)
res = rpc._rpc_delete('3')
assert stoploss_mock.call_count == 1
stoploss_mock.reset_mock()
cancel_mock = mocker.patch(f'{EXMS}.cancel_order', side_effect=InvalidOrderException)
res = rpc._rpc_delete('4')
assert cancel_mock.call_count == 1
assert stoploss_mock.call_count == 0
def test_rpc_trade_statistics(default_conf_usdt, ticker, fee, mocker) -> None:
2022-06-13 04:46:34 +00:00
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=1.1)
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
2018-02-13 03:45:59 +00:00
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
2019-12-18 15:34:30 +00:00
fetch_ticker=ticker,
2018-06-16 23:23:12 +00:00
get_fee=fee,
2018-02-13 03:45:59 +00:00
)
2022-06-13 04:46:34 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf_usdt)
stake_currency = default_conf_usdt['stake_currency']
fiat_display_currency = default_conf_usdt['fiat_display_currency']
2018-02-13 03:45:59 +00:00
rpc = RPC(freqtradebot)
2018-07-21 18:44:38 +00:00
rpc._fiat_converter = CryptoToFiatConverter()
2018-02-13 03:45:59 +00:00
res = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
assert res['trade_count'] == 0
assert res['first_trade_date'] == ''
assert res['first_trade_timestamp'] == 0
assert res['latest_trade_date'] == ''
assert res['latest_trade_timestamp'] == 0
2023-07-15 15:09:13 +00:00
assert res['expectancy'] == 0
2023-07-23 06:00:08 +00:00
assert res['expectancy_ratio'] == 100
# Create some test data
2022-06-13 04:46:34 +00:00
create_mock_trades_usdt(fee)
2018-06-08 02:52:50 +00:00
stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
assert pytest.approx(stats['profit_closed_coin']) == 2.74
2022-06-13 04:46:34 +00:00
assert pytest.approx(stats['profit_closed_percent_mean']) == -1.67
assert pytest.approx(stats['profit_closed_fiat']) == 3.014
2022-06-13 04:46:34 +00:00
assert pytest.approx(stats['profit_all_coin']) == -77.45964918
assert pytest.approx(stats['profit_all_percent_mean']) == -57.86
assert pytest.approx(stats['profit_all_fiat']) == -85.205614098
assert pytest.approx(stats['winrate']) == 0.666666667
assert pytest.approx(stats['expectancy']) == 0.913333333
assert pytest.approx(stats['expectancy_ratio']) == 0.223308883
2022-06-13 04:46:34 +00:00
assert stats['trade_count'] == 7
assert stats['first_trade_humanized'] == '2 days ago'
assert stats['latest_trade_humanized'] == '17 minutes ago'
2022-06-13 04:46:34 +00:00
assert stats['avg_duration'] in ('0:17:40')
assert stats['best_pair'] == 'XRP/USDT'
assert stats['best_rate'] == 10.0
# Test non-available pair
mocker.patch(f'{EXMS}.get_rate',
2022-06-13 04:46:34 +00:00
MagicMock(side_effect=ExchangeError("Pair 'XRP/USDT' not available")))
stats = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency)
2022-06-13 04:46:34 +00:00
assert stats['trade_count'] == 7
assert stats['first_trade_humanized'] == '2 days ago'
assert stats['latest_trade_humanized'] == '17 minutes ago'
2022-06-13 04:46:34 +00:00
assert stats['avg_duration'] in ('0:17:40')
assert stats['best_pair'] == 'XRP/USDT'
assert stats['best_rate'] == 10.0
assert isnan(stats['profit_all_coin'])
2018-10-10 20:03:54 +00:00
2019-07-03 18:07:26 +00:00
def test_rpc_balance_handle_error(default_conf, mocker):
2018-05-14 21:31:56 +00:00
mock_balance = {
'BTC': {
'free': 10.0,
'total': 12.0,
'used': 2.0,
2018-02-13 03:45:59 +00:00
},
2018-05-14 21:31:56 +00:00
'ETH': {
2018-08-08 19:55:48 +00:00
'free': 1.0,
'total': 5.0,
'used': 4.0,
2018-02-13 03:45:59 +00:00
}
2018-05-14 21:31:56 +00:00
}
2018-08-08 19:55:48 +00:00
# ETH will be skipped due to mocked Error below
2018-02-13 03:45:59 +00:00
mocker.patch.multiple(
2020-03-07 12:01:26 +00:00
'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
get_price=MagicMock(return_value={'bitcoin': {'usd': 15000.0}}),
2018-02-13 03:45:59 +00:00
)
2018-07-21 18:44:38 +00:00
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
2018-02-13 03:45:59 +00:00
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
2018-08-08 19:55:48 +00:00
get_balances=MagicMock(return_value=mock_balance),
2019-11-14 19:12:41 +00:00
get_tickers=MagicMock(side_effect=TemporaryError('Could not load ticker due to xxx'))
2018-02-13 03:45:59 +00:00
)
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
2018-02-13 03:45:59 +00:00
rpc = RPC(freqtradebot)
2018-07-21 18:44:38 +00:00
rpc._fiat_converter = CryptoToFiatConverter()
2019-11-14 19:12:41 +00:00
with pytest.raises(RPCException, match="Error getting current tickers."):
2019-11-15 05:33:07 +00:00
rpc._rpc_balance(default_conf['stake_currency'], default_conf['fiat_display_currency'])
2018-08-08 19:54:52 +00:00
2019-11-14 19:12:41 +00:00
def test_rpc_balance_handle(default_conf, mocker, tickers):
2019-07-03 18:07:26 +00:00
mock_balance = {
'BTC': {
'free': 10.0,
'total': 12.0,
'used': 2.0,
},
'ETH': {
'free': 1.0,
'total': 5.0,
'used': 4.0,
},
2019-11-14 19:12:41 +00:00
'USDT': {
2019-07-03 18:07:26 +00:00
'free': 5.0,
'total': 10.0,
'used': 5.0,
}
}
2022-02-23 06:40:15 +00:00
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
}
]
2019-07-03 18:07:26 +00:00
mocker.patch.multiple(
2020-03-07 12:01:26 +00:00
'freqtrade.rpc.fiat_convert.CoinGeckoAPI',
get_price=MagicMock(return_value={'bitcoin': {'usd': 15000.0}}),
2019-07-03 18:07:26 +00:00
)
mocker.patch('freqtrade.rpc.rpc.CryptoToFiatConverter._find_price', return_value=15000.0)
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
2022-02-23 06:40:15 +00:00
validate_trading_mode_and_margin_mode=MagicMock(),
2019-07-03 18:07:26 +00:00
get_balances=MagicMock(return_value=mock_balance),
fetch_positions=MagicMock(return_value=mock_pos),
2019-11-14 19:12:41 +00:00
get_tickers=tickers,
2019-07-03 18:07:26 +00:00
get_valid_pair_combination=MagicMock(
2019-11-14 19:12:41 +00:00
side_effect=lambda a, b: f"{b}/{a}" if a == "USDT" else f"{a}/{b}")
2019-07-03 18:07:26 +00:00
)
default_conf['dry_run'] = False
2022-02-23 06:40:15 +00:00
default_conf['trading_mode'] = 'futures'
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
2019-07-03 18:07:26 +00:00
rpc = RPC(freqtradebot)
rpc._fiat_converter = CryptoToFiatConverter()
2019-11-15 05:33:07 +00:00
result = rpc._rpc_balance(default_conf['stake_currency'], default_conf['fiat_display_currency'])
2023-02-28 17:22:17 +00:00
assert pytest.approx(result['total']) == 30.30909624
assert pytest.approx(result['value']) == 454636.44360691
assert tickers.call_count == 1
2021-04-13 20:17:42 +00:00
assert tickers.call_args_list[0][1]['cached'] is True
2019-07-03 18:07:26 +00:00
assert 'USD' == result['symbol']
assert result['currencies'] == [
2022-02-23 06:40:15 +00:00
{
'currency': 'BTC',
'free': 10.0,
'balance': 12.0,
'used': 2.0,
2023-04-22 15:42:09 +00:00
'bot_owned': 9.9, # available stake - reducing by reserved amount
'est_stake': 10.0, # In futures mode, "free" is used here.
2023-04-24 10:12:45 +00:00
'est_stake_bot': 9.9,
2022-02-23 06:40:15 +00:00
'stake': 'BTC',
'is_position': False,
'leverage': 1.0,
'position': 0.0,
'side': 'long',
2023-04-22 14:13:27 +00:00
'is_bot_managed': True,
2022-02-23 06:40:15 +00:00
},
{
'free': 1.0,
'balance': 5.0,
'currency': 'ETH',
2023-04-22 15:21:03 +00:00
'bot_owned': 0,
2022-02-23 06:40:15 +00:00
'est_stake': 0.30794,
2023-04-22 15:21:03 +00:00
'est_stake_bot': 0,
2022-02-23 06:40:15 +00:00
'used': 4.0,
'stake': 'BTC',
'is_position': False,
'leverage': 1.0,
'position': 0.0,
'side': 'long',
2023-04-22 14:13:27 +00:00
'is_bot_managed': False,
2022-02-23 06:40:15 +00:00
},
{
'free': 5.0,
'balance': 10.0,
'currency': 'USDT',
2023-04-22 15:21:03 +00:00
'bot_owned': 0,
2022-03-18 07:18:17 +00:00
'est_stake': 0.0011562404610161968,
2023-04-22 15:21:03 +00:00
'est_stake_bot': 0,
2022-02-23 06:40:15 +00:00
'used': 5.0,
'stake': 'BTC',
'is_position': False,
'leverage': 1.0,
'position': 0.0,
'side': 'long',
2023-04-22 14:13:27 +00:00
'is_bot_managed': False,
2022-02-23 06:40:15 +00:00
},
{
'free': 0.0,
'balance': 0.0,
'currency': 'ETH/USDT:USDT',
'est_stake': 20,
2023-04-24 10:12:45 +00:00
'est_stake_bot': 20,
2022-02-23 06:40:15 +00:00
'used': 0,
'stake': 'BTC',
'is_position': True,
'leverage': 5.0,
'position': 1000.0,
'side': 'short',
2023-04-22 14:13:27 +00:00
'is_bot_managed': True,
2022-02-23 06:40:15 +00:00
}
2019-07-03 18:07:26 +00:00
]
2023-04-24 10:12:45 +00:00
assert pytest.approx(result['total_bot']) == 29.9
2023-04-22 14:13:27 +00:00
assert pytest.approx(result['total']) == 30.309096
2023-04-22 09:27:04 +00:00
assert result['starting_capital'] == 10
2023-04-24 10:12:45 +00:00
# Very high starting capital ratio, because the futures position really has the wrong unit.
# TODO: improve this test (see comment above)
assert result['starting_capital_ratio'] == pytest.approx(1.98999999)
2019-07-03 18:07:26 +00:00
2018-02-13 03:45:59 +00:00
def test_rpc_start(mocker, default_conf) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
2018-02-13 03:45:59 +00:00
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
2019-12-18 15:34:30 +00:00
fetch_ticker=MagicMock()
2018-02-13 03:45:59 +00:00
)
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
2018-02-13 03:45:59 +00:00
rpc = RPC(freqtradebot)
freqtradebot.state = State.STOPPED
2018-02-13 03:45:59 +00:00
2018-06-08 02:52:50 +00:00
result = rpc._rpc_start()
assert {'status': 'starting trader ...'} == result
assert freqtradebot.state == State.RUNNING
2018-02-13 03:45:59 +00:00
2018-06-08 02:52:50 +00:00
result = rpc._rpc_start()
assert {'status': 'already running'} == result
assert freqtradebot.state == State.RUNNING
2018-02-13 03:45:59 +00:00
def test_rpc_stop(mocker, default_conf) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
2018-02-13 03:45:59 +00:00
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
2019-12-18 15:34:30 +00:00
fetch_ticker=MagicMock()
2018-02-13 03:45:59 +00:00
)
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
2018-02-13 03:45:59 +00:00
rpc = RPC(freqtradebot)
freqtradebot.state = State.RUNNING
2018-02-13 03:45:59 +00:00
2018-06-08 02:52:50 +00:00
result = rpc._rpc_stop()
assert {'status': 'stopping trader ...'} == result
assert freqtradebot.state == State.STOPPED
2018-02-13 03:45:59 +00:00
2018-06-08 02:52:50 +00:00
result = rpc._rpc_stop()
assert {'status': 'already stopped'} == result
assert freqtradebot.state == State.STOPPED
2018-02-13 03:45:59 +00:00
2022-08-28 09:32:53 +00:00
def test_rpc_stopentry(mocker, default_conf) -> None:
2019-03-17 18:36:02 +00:00
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
2019-12-18 15:34:30 +00:00
fetch_ticker=MagicMock()
2019-03-17 18:36:02 +00:00
)
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
2019-03-17 18:36:02 +00:00
rpc = RPC(freqtradebot)
freqtradebot.state = State.RUNNING
assert freqtradebot.config['max_open_trades'] != 0
2022-08-28 09:32:53 +00:00
result = rpc._rpc_stopentry()
assert {'status': 'No more entries will occur from now. Run /reload_config to reset.'} == result
2019-03-17 18:36:02 +00:00
assert freqtradebot.config['max_open_trades'] == 0
2022-04-05 11:28:30 +00:00
def test_rpc_force_exit(default_conf, ticker, fee, mocker) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
2018-02-13 03:45:59 +00:00
cancel_order_mock = MagicMock()
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
2019-12-18 15:34:30 +00:00
fetch_ticker=ticker,
2018-02-13 03:45:59 +00:00
cancel_order=cancel_order_mock,
fetch_order=MagicMock(
2018-02-13 03:45:59 +00:00
return_value={
'status': 'closed',
'type': 'limit',
2020-08-26 19:37:52 +00:00
'side': 'buy',
'filled': 0.0,
2018-02-13 03:45:59 +00:00
}
2018-04-21 17:39:18 +00:00
),
2023-02-16 18:47:12 +00:00
_dry_is_price_crossed=MagicMock(return_value=True),
2018-04-21 17:39:18 +00:00
get_fee=fee,
2018-02-13 03:45:59 +00:00
)
mocker.patch('freqtrade.wallets.Wallets.get_free', return_value=1000)
2018-02-13 03:45:59 +00:00
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
2018-02-13 03:45:59 +00:00
rpc = RPC(freqtradebot)
freqtradebot.state = State.STOPPED
with pytest.raises(RPCException, match=r'.*trader is not running*'):
2022-04-05 11:28:30 +00:00
rpc._rpc_force_exit(None)
2018-02-13 03:45:59 +00:00
freqtradebot.state = State.RUNNING
with pytest.raises(RPCException, match=r'.*invalid argument*'):
2022-04-05 11:28:30 +00:00
rpc._rpc_force_exit(None)
2018-02-13 03:45:59 +00:00
2022-04-05 11:28:30 +00:00
msg = rpc._rpc_force_exit('all')
2023-05-31 12:31:45 +00:00
assert msg == {'result': 'Created exit orders for all open trades.'}
2018-02-13 03:45:59 +00:00
freqtradebot.enter_positions()
2022-04-05 11:28:30 +00:00
msg = rpc._rpc_force_exit('all')
2023-05-31 12:31:45 +00:00
assert msg == {'result': 'Created exit orders for all open trades.'}
2018-02-13 03:45:59 +00:00
2020-08-26 19:37:52 +00:00
freqtradebot.enter_positions()
2022-04-05 11:28:30 +00:00
msg = rpc._rpc_force_exit('2')
2023-05-31 12:31:45 +00:00
assert msg == {'result': 'Created exit order for trade 2.'}
2018-02-13 03:45:59 +00:00
freqtradebot.state = State.STOPPED
with pytest.raises(RPCException, match=r'.*trader is not running*'):
2022-04-05 11:28:30 +00:00
rpc._rpc_force_exit(None)
2018-02-13 03:45:59 +00:00
with pytest.raises(RPCException, match=r'.*trader is not running*'):
2022-04-05 11:28:30 +00:00
rpc._rpc_force_exit('all')
2018-02-13 03:45:59 +00:00
freqtradebot.state = State.RUNNING
2018-02-13 03:45:59 +00:00
assert cancel_order_mock.call_count == 0
mocker.patch(f'{EXMS}._dry_is_price_crossed', MagicMock(return_value=False))
2020-08-26 19:37:52 +00:00
freqtradebot.enter_positions()
2018-02-13 03:45:59 +00:00
# make an limit-buy open trade
2023-03-16 06:25:04 +00:00
trade = Trade.session.scalars(select(Trade).filter(Trade.id == '3')).first()
filled_amount = trade.amount / 2
2020-08-26 19:37:52 +00:00
# Fetch order - it's open first, and closed after cancel_order is called.
2018-02-13 03:45:59 +00:00
mocker.patch(
f'{EXMS}.fetch_order',
2020-08-26 19:37:52 +00:00
side_effect=[{
'id': trade.orders[0].order_id,
'status': 'open',
'type': 'limit',
'side': 'buy',
'filled': filled_amount
2020-08-26 19:37:52 +00:00
}, {
'id': trade.orders[0].order_id,
2020-08-26 19:37:52 +00:00
'status': 'closed',
'type': 'limit',
'side': 'buy',
'filled': filled_amount
}]
2018-02-13 03:45:59 +00:00
)
# check that the trade is called, which is done by ensuring exchange.cancel_order is called
# and trade amount is updated
2022-04-05 11:28:30 +00:00
rpc._rpc_force_exit('3')
2018-02-13 03:45:59 +00:00
assert cancel_order_mock.call_count == 1
2022-08-15 18:29:05 +00:00
assert pytest.approx(trade.amount) == filled_amount
2020-08-26 19:37:52 +00:00
mocker.patch(
f'{EXMS}.fetch_order',
2020-08-26 19:37:52 +00:00
return_value={
'status': 'open',
'type': 'limit',
'side': 'buy',
'filled': filled_amount
})
freqtradebot.config['max_open_trades'] = 3
freqtradebot.enter_positions()
2023-05-31 12:59:41 +00:00
cancel_order_mock.reset_mock()
trade = Trade.session.scalars(select(Trade).filter(Trade.id == '3')).first()
amount = trade.amount
# make an limit-sell open order trade
mocker.patch(
f'{EXMS}.fetch_order',
return_value={
'status': 'open',
'type': 'limit',
'side': 'sell',
'amount': amount,
'remaining': amount,
'filled': 0.0,
'id': trade.orders[-1].order_id,
}
)
cancel_order_3 = mocker.patch(
f'{EXMS}.cancel_order_with_result',
return_value={
'status': 'canceled',
'type': 'limit',
'side': 'sell',
'amount': amount,
'remaining': amount,
'filled': 0.0,
'id': trade.orders[-1].order_id,
}
)
msg = rpc._rpc_force_exit('3')
assert msg == {'result': 'Created exit order for trade 3.'}
# status quo, no exchange calls
assert cancel_order_3.call_count == 1
assert cancel_order_mock.call_count == 0
2018-02-13 03:45:59 +00:00
trade = Trade.session.scalars(select(Trade).filter(Trade.id == '4')).first()
2023-05-31 12:59:41 +00:00
amount = trade.amount
# make an limit-buy open trade, if there is no 'filled', don't sell it
2018-02-13 03:45:59 +00:00
mocker.patch(
f'{EXMS}.fetch_order',
2018-02-13 03:45:59 +00:00
return_value={
'status': 'open',
'type': 'limit',
'side': 'buy',
'filled': None
2018-02-13 03:45:59 +00:00
}
)
cancel_order_4 = mocker.patch(
f'{EXMS}.cancel_order_with_result',
return_value={
'status': 'canceled',
'type': 'limit',
'side': 'sell',
'amount': amount,
'remaining': 0.0,
'filled': amount,
'id': trade.orders[0].order_id,
}
)
# check that the trade is called, which is done by ensuring exchange.cancel_order is called
msg = rpc._rpc_force_exit('4')
assert msg == {'result': 'Created exit order for trade 4.'}
assert cancel_order_4.call_count == 1
assert cancel_order_mock.call_count == 0
assert pytest.approx(trade.amount) == amount
2018-02-13 03:45:59 +00:00
def test_performance_handle(default_conf_usdt, ticker, fee, mocker) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
2018-02-13 03:45:59 +00:00
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
2018-02-13 03:45:59 +00:00
get_balances=MagicMock(return_value=ticker),
2019-12-18 15:34:30 +00:00
fetch_ticker=ticker,
2018-06-16 23:23:12 +00:00
get_fee=fee,
2018-02-13 03:45:59 +00:00
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf_usdt)
patch_get_signal(freqtradebot)
2018-02-13 03:45:59 +00:00
rpc = RPC(freqtradebot)
# Create some test data
create_mock_trades_usdt(fee)
2018-06-08 02:52:50 +00:00
res = rpc._rpc_performance()
assert len(res) == 3
2022-08-16 08:59:43 +00:00
assert res[0]['pair'] == 'NEO/USDT'
assert res[0]['count'] == 1
assert res[0]['profit_pct'] == 5.0
def test_enter_tag_performance_handle(default_conf, ticker, fee, mocker) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
get_balances=MagicMock(return_value=ticker),
fetch_ticker=ticker,
get_fee=fee,
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
rpc = RPC(freqtradebot)
# Create some test data
create_mock_trades_usdt(fee)
freqtradebot.enter_positions()
2021-11-21 08:51:16 +00:00
res = rpc._rpc_enter_tag_performance(None)
assert len(res) == 3
assert res[0]['enter_tag'] == 'TEST1'
assert res[0]['count'] == 1
assert res[0]['profit_pct'] == 5.0
2021-11-21 08:51:16 +00:00
res = rpc._rpc_enter_tag_performance(None)
assert len(res) == 3
assert res[0]['enter_tag'] == 'TEST1'
assert res[0]['count'] == 1
assert res[0]['profit_pct'] == 5.0
2021-11-21 08:51:16 +00:00
def test_enter_tag_performance_handle_2(mocker, default_conf, markets, fee):
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
markets=PropertyMock(return_value=markets)
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
create_mock_trades(fee)
rpc = RPC(freqtradebot)
2021-11-21 08:51:16 +00:00
res = rpc._rpc_enter_tag_performance(None)
assert len(res) == 2
2021-11-21 08:51:16 +00:00
assert res[0]['enter_tag'] == 'TEST1'
assert res[0]['count'] == 1
assert pytest.approx(res[0]['profit_pct']) == 0.5
2021-11-21 08:51:16 +00:00
assert res[1]['enter_tag'] == 'Other'
assert res[1]['count'] == 1
assert pytest.approx(res[1]['profit_pct']) == 1.0
# Test for a specific pair
2021-11-21 08:51:16 +00:00
res = rpc._rpc_enter_tag_performance('ETC/BTC')
assert len(res) == 1
assert res[0]['count'] == 1
2021-11-21 08:51:16 +00:00
assert res[0]['enter_tag'] == 'TEST1'
assert pytest.approx(res[0]['profit_pct']) == 0.5
def test_exit_reason_performance_handle(default_conf_usdt, ticker, fee, mocker) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
get_balances=MagicMock(return_value=ticker),
fetch_ticker=ticker,
get_fee=fee,
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf_usdt)
patch_get_signal(freqtradebot)
rpc = RPC(freqtradebot)
# Create some test data
create_mock_trades_usdt(fee)
2022-04-03 08:39:35 +00:00
res = rpc._rpc_exit_reason_performance(None)
assert len(res) == 3
assert res[0]['exit_reason'] == 'exit_signal'
assert res[0]['count'] == 1
assert res[0]['profit_pct'] == 5.0
assert res[1]['exit_reason'] == 'roi'
assert res[2]['exit_reason'] == 'Other'
2022-04-03 08:39:35 +00:00
def test_exit_reason_performance_handle_2(mocker, default_conf, markets, fee):
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
markets=PropertyMock(return_value=markets)
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
create_mock_trades(fee)
rpc = RPC(freqtradebot)
2022-04-03 08:39:35 +00:00
res = rpc._rpc_exit_reason_performance(None)
assert len(res) == 2
2022-04-03 08:39:35 +00:00
assert res[0]['exit_reason'] == 'sell_signal'
assert res[0]['count'] == 1
assert pytest.approx(res[0]['profit_pct']) == 0.5
2022-04-03 08:39:35 +00:00
assert res[1]['exit_reason'] == 'roi'
assert res[1]['count'] == 1
assert pytest.approx(res[1]['profit_pct']) == 1.0
# Test for a specific pair
2022-04-03 08:39:35 +00:00
res = rpc._rpc_exit_reason_performance('ETC/BTC')
assert len(res) == 1
assert res[0]['count'] == 1
2022-04-03 08:39:35 +00:00
assert res[0]['exit_reason'] == 'sell_signal'
assert pytest.approx(res[0]['profit_pct']) == 0.5
def test_mix_tag_performance_handle(default_conf, ticker, fee, mocker) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
get_balances=MagicMock(return_value=ticker),
fetch_ticker=ticker,
get_fee=fee,
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
rpc = RPC(freqtradebot)
# Create some test data
create_mock_trades_usdt(fee)
res = rpc._rpc_mix_tag_performance(None)
assert len(res) == 3
assert res[0]['mix_tag'] == 'TEST1 exit_signal'
assert res[0]['count'] == 1
assert res[0]['profit_pct'] == 5.0
2018-02-13 03:45:59 +00:00
def test_mix_tag_performance_handle_2(mocker, default_conf, markets, fee):
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
markets=PropertyMock(return_value=markets)
)
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
create_mock_trades(fee)
rpc = RPC(freqtradebot)
res = rpc._rpc_mix_tag_performance(None)
assert len(res) == 2
assert res[0]['mix_tag'] == 'TEST1 sell_signal'
assert res[0]['count'] == 1
assert pytest.approx(res[0]['profit_pct']) == 0.5
assert res[1]['mix_tag'] == 'Other roi'
assert res[1]['count'] == 1
assert pytest.approx(res[1]['profit_pct']) == 1.0
# Test for a specific pair
res = rpc._rpc_mix_tag_performance('ETC/BTC')
assert len(res) == 1
assert res[0]['count'] == 1
assert res[0]['mix_tag'] == 'TEST1 sell_signal'
assert pytest.approx(res[0]['profit_pct']) == 0.5
2018-02-13 03:45:59 +00:00
2019-10-26 08:08:23 +00:00
def test_rpc_count(mocker, default_conf, ticker, fee) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
2018-02-13 03:45:59 +00:00
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
2018-02-13 03:45:59 +00:00
get_balances=MagicMock(return_value=ticker),
2019-12-18 15:34:30 +00:00
fetch_ticker=ticker,
2018-04-21 17:39:18 +00:00
get_fee=fee,
2018-02-13 03:45:59 +00:00
)
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
2018-02-13 03:45:59 +00:00
rpc = RPC(freqtradebot)
2019-04-06 18:01:29 +00:00
counts = rpc._rpc_count()
assert counts["current"] == 0
2018-02-13 03:45:59 +00:00
# Create some test data
freqtradebot.enter_positions()
2019-04-06 18:01:29 +00:00
counts = rpc._rpc_count()
assert counts["current"] == 1
2018-10-09 18:04:53 +00:00
2022-04-05 11:28:30 +00:00
def test_rpc_force_entry(mocker, default_conf, ticker, fee, limit_buy_order_open) -> None:
2022-04-08 11:39:41 +00:00
default_conf['force_entry_enable'] = True
default_conf['max_open_trades'] = 0
2018-10-09 18:04:53 +00:00
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
buy_mm = MagicMock(return_value=limit_buy_order_open)
2018-10-09 18:04:53 +00:00
mocker.patch.multiple(
2023-03-01 19:14:15 +00:00
EXMS,
2018-10-09 18:04:53 +00:00
get_balances=MagicMock(return_value=ticker),
2019-12-18 15:34:30 +00:00
fetch_ticker=ticker,
2018-10-09 18:04:53 +00:00
get_fee=fee,
create_order=buy_mm
2018-10-09 18:04:53 +00:00
)
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
2018-10-09 18:04:53 +00:00
rpc = RPC(freqtradebot)
pair = 'ETH/BTC'
with pytest.raises(RPCException, match='Maximum number of trades is reached.'):
rpc._rpc_force_entry(pair, None)
freqtradebot.config['max_open_trades'] = 5
2022-01-26 18:08:37 +00:00
trade = rpc._rpc_force_entry(pair, None)
2018-10-09 18:04:53 +00:00
assert isinstance(trade, Trade)
assert trade.pair == pair
assert trade.open_rate == ticker()['bid']
2018-10-09 18:04:53 +00:00
# Test buy duplicate
with pytest.raises(RPCException, match=r'position for ETH/BTC already open - id: 1'):
2022-01-26 18:08:37 +00:00
rpc._rpc_force_entry(pair, 0.0001)
2018-10-09 18:04:53 +00:00
pair = 'XRP/BTC'
2022-01-26 18:08:37 +00:00
trade = rpc._rpc_force_entry(pair, 0.0001, order_type='limit')
2018-10-09 18:04:53 +00:00
assert isinstance(trade, Trade)
assert trade.pair == pair
assert trade.open_rate == 0.0001
2022-11-29 17:27:08 +00:00
with pytest.raises(RPCException,
match=r'Symbol does not exist or market is not active.'):
rpc._rpc_force_entry('LTC/NOTHING', 0.0001)
2018-10-09 18:04:53 +00:00
# Test buy pair not with stakes
with pytest.raises(RPCException,
match=r'Wrong pair selected. Only pairs with stake-currency.*'):
2022-01-26 18:08:37 +00:00
rpc._rpc_force_entry('LTC/ETH', 0.0001)
# Test with defined stake_amount
pair = 'LTC/BTC'
2022-01-26 18:08:37 +00:00
trade = rpc._rpc_force_entry(pair, 0.0001, order_type='limit', stake_amount=0.05)
assert trade.stake_amount == 0.05
2022-04-05 11:28:30 +00:00
assert trade.buy_tag == 'force_entry'
2023-08-25 05:09:14 +00:00
assert trade.open_orders_ids[-1] == 'mocked_limit_buy'
freqtradebot.strategy.position_adjustment_enable = True
with pytest.raises(RPCException, match=r'position for LTC/BTC already open.*open order.*'):
rpc._rpc_force_entry(pair, 0.0001, order_type='limit', stake_amount=0.05)
2018-10-09 18:04:53 +00:00
# Test not buying
pair = 'XRP/BTC'
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
freqtradebot.config['stake_amount'] = 0
patch_get_signal(freqtradebot)
2018-10-09 18:04:53 +00:00
rpc = RPC(freqtradebot)
pair = 'TKN/BTC'
with pytest.raises(RPCException, match=r"Failed to enter position for TKN/BTC."):
trade = rpc._rpc_force_entry(pair, None)
2018-10-09 18:04:53 +00:00
2022-04-05 11:28:30 +00:00
def test_rpc_force_entry_stopped(mocker, default_conf) -> None:
2022-04-08 11:39:41 +00:00
default_conf['force_entry_enable'] = True
2018-10-09 18:04:53 +00:00
default_conf['initial_state'] = 'stopped'
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
2018-10-09 18:04:53 +00:00
rpc = RPC(freqtradebot)
pair = 'ETH/BTC'
with pytest.raises(RPCException, match=r'trader is not running'):
2022-01-26 18:08:37 +00:00
rpc._rpc_force_entry(pair, None)
2018-10-09 18:04:53 +00:00
2022-04-05 11:28:30 +00:00
def test_rpc_force_entry_disabled(mocker, default_conf) -> None:
2018-10-09 18:04:53 +00:00
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
patch_get_signal(freqtradebot)
2018-10-09 18:04:53 +00:00
rpc = RPC(freqtradebot)
pair = 'ETH/BTC'
2022-04-05 11:28:30 +00:00
with pytest.raises(RPCException, match=r'Force_entry not enabled.'):
2022-01-26 18:08:37 +00:00
rpc._rpc_force_entry(pair, None)
2018-11-10 19:07:09 +00:00
2022-04-05 11:28:30 +00:00
def test_rpc_force_entry_wrong_mode(mocker, default_conf) -> None:
2022-04-08 11:39:41 +00:00
default_conf['force_entry_enable'] = True
2022-01-27 05:40:41 +00:00
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)
2021-03-01 06:51:33 +00:00
@pytest.mark.usefixtures("init_persistence")
def test_rpc_delete_lock(mocker, default_conf):
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc = RPC(freqtradebot)
pair = 'ETH/BTC'
PairLocks.lock_pair(pair, datetime.now(timezone.utc) + timedelta(minutes=4))
PairLocks.lock_pair(pair, datetime.now(timezone.utc) + timedelta(minutes=5))
PairLocks.lock_pair(pair, datetime.now(timezone.utc) + timedelta(minutes=10))
locks = rpc._rpc_locks()
assert locks['lock_count'] == 3
locks1 = rpc._rpc_delete_lock(lockid=locks['locks'][0]['id'])
assert locks1['lock_count'] == 2
locks2 = rpc._rpc_delete_lock(pair=pair)
assert locks2['lock_count'] == 0
2018-11-10 19:07:09 +00:00
def test_rpc_whitelist(mocker, default_conf) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
2018-11-10 19:07:09 +00:00
rpc = RPC(freqtradebot)
ret = rpc._rpc_whitelist()
2019-11-09 13:00:32 +00:00
assert len(ret['method']) == 1
assert 'StaticPairList' in ret['method']
2018-11-10 19:07:09 +00:00
assert ret['whitelist'] == default_conf['exchange']['pair_whitelist']
def test_rpc_whitelist_dynamic(mocker, default_conf) -> None:
2019-11-09 13:00:32 +00:00
default_conf['pairlists'] = [{'method': 'VolumePairList',
2019-11-19 05:34:54 +00:00
'number_assets': 4,
2019-11-09 13:00:32 +00:00
}]
mocker.patch(f'{EXMS}.exchange_has', MagicMock(return_value=True))
2018-11-10 19:07:09 +00:00
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
2018-11-10 19:07:09 +00:00
rpc = RPC(freqtradebot)
ret = rpc._rpc_whitelist()
2019-11-09 13:00:32 +00:00
assert len(ret['method']) == 1
assert 'VolumePairList' in ret['method']
2018-12-03 19:31:25 +00:00
assert ret['length'] == 4
2018-11-10 19:07:09 +00:00
assert ret['whitelist'] == default_conf['exchange']['pair_whitelist']
2019-03-24 15:09:20 +00:00
def test_rpc_blacklist(mocker, default_conf) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
2019-03-24 15:09:20 +00:00
rpc = RPC(freqtradebot)
ret = rpc._rpc_blacklist(None)
2019-11-09 13:00:32 +00:00
assert len(ret['method']) == 1
assert 'StaticPairList' in ret['method']
assert len(ret['blacklist']) == 2
2019-03-24 15:09:20 +00:00
assert ret['blacklist'] == default_conf['exchange']['pair_blacklist']
assert ret['blacklist'] == ['DOGE/BTC', 'HOT/BTC']
ret = rpc._rpc_blacklist(["ETH/BTC"])
2019-11-09 13:00:32 +00:00
assert 'StaticPairList' in ret['method']
assert len(ret['blacklist']) == 3
assert ret['blacklist'] == default_conf['exchange']['pair_blacklist']
assert ret['blacklist'] == ['DOGE/BTC', 'HOT/BTC', 'ETH/BTC']
ret = rpc._rpc_blacklist(["ETH/BTC"])
assert 'errors' in ret
assert isinstance(ret['errors'], dict)
assert ret['errors']['ETH/BTC']['error_msg'] == 'Pair ETH/BTC already in pairlist.'
ret = rpc._rpc_blacklist(["*/BTC"])
assert 'StaticPairList' in ret['method']
assert len(ret['blacklist']) == 3
assert ret['blacklist'] == default_conf['exchange']['pair_blacklist']
assert ret['blacklist'] == ['DOGE/BTC', 'HOT/BTC', 'ETH/BTC']
assert ret['blacklist_expanded'] == ['ETH/BTC']
assert 'errors' in ret
assert isinstance(ret['errors'], dict)
assert ret['errors'] == {'*/BTC': {'error_msg': 'Pair */BTC is not a valid wildcard.'}}
ret = rpc._rpc_blacklist(["XRP/.*"])
assert 'StaticPairList' in ret['method']
assert len(ret['blacklist']) == 4
assert ret['blacklist'] == default_conf['exchange']['pair_blacklist']
assert ret['blacklist'] == ['DOGE/BTC', 'HOT/BTC', 'ETH/BTC', 'XRP/.*']
2021-10-01 09:10:43 +00:00
assert ret['blacklist_expanded'] == ['ETH/BTC', 'XRP/BTC', 'XRP/USDT']
2021-12-11 15:09:20 +00:00
assert 'errors' in ret
assert isinstance(ret['errors'], dict)
ret = rpc._rpc_blacklist_delete(["DOGE/BTC", 'HOT/BTC'])
assert 'StaticPairList' in ret['method']
assert len(ret['blacklist']) == 2
assert ret['blacklist'] == default_conf['exchange']['pair_blacklist']
assert ret['blacklist'] == ['ETH/BTC', 'XRP/.*']
assert ret['blacklist_expanded'] == ['ETH/BTC', 'XRP/BTC', 'XRP/USDT']
assert 'errors' in ret
assert isinstance(ret['errors'], dict)
2019-03-27 13:04:33 +00:00
def test_rpc_edge_disabled(mocker, default_conf) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
rpc = RPC(freqtradebot)
with pytest.raises(RPCException, match=r'Edge is not enabled.'):
2019-03-27 13:04:33 +00:00
rpc._rpc_edge()
def test_rpc_edge_enabled(mocker, edge_conf) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
mocker.patch('freqtrade.edge.Edge._cached_pairs', mocker.PropertyMock(
return_value={
'E/F': PairInfo(-0.02, 0.66, 3.71, 0.50, 1.71, 10, 60),
}
))
2019-10-26 08:08:23 +00:00
freqtradebot = get_patched_freqtradebot(mocker, edge_conf)
rpc = RPC(freqtradebot)
ret = rpc._rpc_edge()
assert len(ret) == 1
assert ret[0]['Pair'] == 'E/F'
assert ret[0]['Winrate'] == 0.66
assert ret[0]['Expectancy'] == 1.71
2019-03-27 13:04:33 +00:00
assert ret[0]['Stoploss'] == -0.02
def test_rpc_health(mocker, default_conf) -> None:
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
freqtradebot = get_patched_freqtradebot(mocker, default_conf)
set_startup_time()
rpc = RPC(freqtradebot)
result = rpc.health()
assert result['last_process'] is None
assert result['last_process_ts'] is None