Merge pull request #5890 from aezomz/todo-rpc
Todo-lev RPC tests and model
This commit is contained in:
commit
4f0a73010a
@ -154,6 +154,7 @@ class TradeSchema(BaseModel):
|
||||
trade_id: int
|
||||
pair: str
|
||||
is_open: bool
|
||||
is_short: bool
|
||||
exchange: str
|
||||
amount: float
|
||||
amount_requested: float
|
||||
|
@ -156,8 +156,9 @@ class RPC:
|
||||
# calculate profit and send message to user
|
||||
if trade.is_open:
|
||||
try:
|
||||
closing_side = "buy" if trade.is_short else "sell"
|
||||
current_rate = self._freqtrade.exchange.get_rate(
|
||||
trade.pair, refresh=False, side="sell")
|
||||
trade.pair, refresh=False, side=closing_side)
|
||||
except (ExchangeError, PricingError):
|
||||
current_rate = NAN
|
||||
else:
|
||||
@ -216,8 +217,9 @@ class RPC:
|
||||
for trade in trades:
|
||||
# calculate profit and send message to user
|
||||
try:
|
||||
closing_side = "buy" if trade.is_short else "sell"
|
||||
current_rate = self._freqtrade.exchange.get_rate(
|
||||
trade.pair, refresh=False, side="sell")
|
||||
trade.pair, refresh=False, side=closing_side)
|
||||
except (PricingError, ExchangeError):
|
||||
current_rate = NAN
|
||||
trade_percent = (100 * trade.calc_profit_ratio(current_rate))
|
||||
@ -376,8 +378,9 @@ class RPC:
|
||||
else:
|
||||
# Get current rate
|
||||
try:
|
||||
closing_side = "buy" if trade.is_short else "sell"
|
||||
current_rate = self._freqtrade.exchange.get_rate(
|
||||
trade.pair, refresh=False, side="sell")
|
||||
trade.pair, refresh=False, side=closing_side)
|
||||
except (PricingError, ExchangeError):
|
||||
current_rate = NAN
|
||||
profit_ratio = trade.calc_profit_ratio(rate=current_rate)
|
||||
@ -572,8 +575,9 @@ class RPC:
|
||||
|
||||
if not fully_canceled:
|
||||
# Get current rate and execute sell
|
||||
closing_side = "buy" if trade.is_short else "sell"
|
||||
current_rate = self._freqtrade.exchange.get_rate(
|
||||
trade.pair, refresh=False, side="sell")
|
||||
trade.pair, refresh=False, side=closing_side)
|
||||
sell_reason = SellCheckTuple(sell_type=SellType.FORCE_SELL)
|
||||
self._freqtrade.execute_trade_exit(trade, current_rate, sell_reason)
|
||||
# ---- EOF def _exec_forcesell ----
|
||||
|
@ -248,33 +248,35 @@ def patch_get_signal(
|
||||
freqtrade.exchange.refresh_latest_ohlcv = lambda p: None
|
||||
|
||||
|
||||
def create_mock_trades(fee, is_short: bool = False, use_db: bool = True):
|
||||
def create_mock_trades(fee, is_short: Optional[bool] = False, use_db: bool = True):
|
||||
"""
|
||||
Create some fake trades ...
|
||||
:param is_short: Optional bool, None creates a mix of long and short trades.
|
||||
"""
|
||||
def add_trade(trade):
|
||||
if use_db:
|
||||
Trade.query.session.add(trade)
|
||||
else:
|
||||
LocalTrade.add_bt_trade(trade)
|
||||
|
||||
is_short1 = is_short if is_short is not None else True
|
||||
is_short2 = is_short if is_short is not None else False
|
||||
# Simulate dry_run entries
|
||||
trade = mock_trade_1(fee, is_short)
|
||||
trade = mock_trade_1(fee, is_short1)
|
||||
add_trade(trade)
|
||||
|
||||
trade = mock_trade_2(fee, is_short)
|
||||
trade = mock_trade_2(fee, is_short1)
|
||||
add_trade(trade)
|
||||
|
||||
trade = mock_trade_3(fee, is_short)
|
||||
trade = mock_trade_3(fee, is_short2)
|
||||
add_trade(trade)
|
||||
|
||||
trade = mock_trade_4(fee, is_short)
|
||||
trade = mock_trade_4(fee, is_short2)
|
||||
add_trade(trade)
|
||||
|
||||
trade = mock_trade_5(fee, is_short)
|
||||
trade = mock_trade_5(fee, is_short2)
|
||||
add_trade(trade)
|
||||
|
||||
trade = mock_trade_6(fee, is_short)
|
||||
trade = mock_trade_6(fee, is_short1)
|
||||
add_trade(trade)
|
||||
|
||||
if use_db:
|
||||
|
@ -317,6 +317,7 @@ def mock_trade_6(fee, is_short: bool):
|
||||
buy_tag='TEST2',
|
||||
open_order_id=f"prod_sell_{direc(is_short)}_6",
|
||||
timeframe=5,
|
||||
is_short=is_short
|
||||
)
|
||||
o = Order.parse_from_ccxt_object(mock_order_6(is_short), 'LTC/BTC', enter_side(is_short))
|
||||
trade.orders.append(o)
|
||||
|
@ -476,7 +476,7 @@ def test_api_count(botclient, mocker, ticker, fee, markets, is_short):
|
||||
assert rc.json()["max"] == 1
|
||||
|
||||
# Create some test data
|
||||
create_mock_trades(fee, is_short)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
rc = client_get(client, f"{BASE_URI}/count")
|
||||
assert_response(rc)
|
||||
assert rc.json()["current"] == 4
|
||||
@ -571,7 +571,7 @@ def test_api_trades(botclient, mocker, fee, markets, is_short):
|
||||
assert rc.json()['trades_count'] == 0
|
||||
assert rc.json()['total_trades'] == 0
|
||||
|
||||
create_mock_trades(fee, is_short)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
Trade.query.session.flush()
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/trades")
|
||||
@ -579,6 +579,7 @@ def test_api_trades(botclient, mocker, fee, markets, is_short):
|
||||
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
|
||||
@ -605,6 +606,7 @@ def test_api_trade_single(botclient, mocker, fee, ticker, markets, 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
|
||||
|
||||
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
@ -623,7 +625,7 @@ def test_api_delete_trade(botclient, mocker, fee, markets, is_short):
|
||||
# Error - trade won't exist yet.
|
||||
assert_response(rc, 502)
|
||||
|
||||
create_mock_trades(fee, False)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
|
||||
ftbot.strategy.order_types['stoploss_on_exchange'] = True
|
||||
trades = Trade.query.all()
|
||||
@ -698,8 +700,46 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets):
|
||||
assert rc.json() == {"error": "Error querying /api/v1/edge: Edge is not enabled."}
|
||||
|
||||
|
||||
# TODO-lev: @pytest.mark.parametrize('is_short', [True, False])
|
||||
def test_api_profit(botclient, mocker, ticker, fee, markets):
|
||||
@pytest.mark.parametrize(
|
||||
'is_short,expected',
|
||||
[(
|
||||
True,
|
||||
{'best_pair': 'ETC/BTC', 'best_rate': -0.5, '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, '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, '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(
|
||||
@ -714,39 +754,41 @@ def test_api_profit(botclient, mocker, ticker, fee, markets):
|
||||
assert_response(rc, 200)
|
||||
assert rc.json()['trade_count'] == 0
|
||||
|
||||
create_mock_trades(fee, False)
|
||||
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,
|
||||
'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_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'],
|
||||
}
|
||||
|
||||
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
@ -826,11 +868,12 @@ def test_api_performance(botclient, fee):
|
||||
'profit_ratio': -0.05570419, 'profit_abs': -0.1150375}]
|
||||
|
||||
|
||||
# TODO-lev: @pytest.mark.parametrize('is_short,side', [
|
||||
# (True, "short"),
|
||||
# (False, "long")
|
||||
# ])
|
||||
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(
|
||||
@ -845,7 +888,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, False)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
|
||||
rc = client_get(client, f"{BASE_URI}/status")
|
||||
assert_response(rc)
|
||||
@ -866,7 +909,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,
|
||||
@ -896,11 +939,12 @@ 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_long_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': CURRENT_TEST_STRATEGY,
|
||||
@ -974,6 +1018,7 @@ def test_api_whitelist(botclient):
|
||||
}
|
||||
|
||||
|
||||
# TODO -lev: add test for forcebuy (short) when feature is supported
|
||||
def test_api_forcebuy(botclient, mocker, fee):
|
||||
ftbot, client = botclient
|
||||
|
||||
@ -1003,6 +1048,7 @@ 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,
|
||||
@ -1051,6 +1097,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',
|
||||
|
@ -499,7 +499,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, is_short)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
|
||||
telegram._stats(update=update, context=MagicMock())
|
||||
assert msg_mock.call_count == 1
|
||||
@ -1261,8 +1261,10 @@ def test_edge_enabled(edge_conf, update, mocker) -> None:
|
||||
assert 'Winrate' not in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
# TODO-lev: @pytest.mark.parametrize('is_short', [True, False])
|
||||
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)
|
||||
|
||||
@ -1280,7 +1282,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, False)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
|
||||
context = MagicMock()
|
||||
context.args = [5]
|
||||
@ -1290,8 +1292,7 @@ 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]))
|
||||
|
||||
|
||||
@pytest.mark.parametrize('is_short', [True, False])
|
||||
@ -1305,7 +1306,7 @@ def test_telegram_delete_trade(mocker, update, default_conf, fee, is_short):
|
||||
assert "Trade-id not set." in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
msg_mock.reset_mock()
|
||||
create_mock_trades(fee, is_short)
|
||||
create_mock_trades(fee, is_short=is_short)
|
||||
|
||||
context = MagicMock()
|
||||
context.args = [1]
|
||||
|
@ -4317,7 +4317,7 @@ def test_sync_wallet_dry_run(mocker, default_conf_usdt, ticker_usdt, fee, limit_
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
@pytest.mark.parametrize("is_short,buy_calls,sell_calls", [
|
||||
(False, 1, 2),
|
||||
(True, 2, 1),
|
||||
(True, 1, 2),
|
||||
])
|
||||
def test_cancel_all_open_orders(mocker, default_conf_usdt, fee, limit_order, limit_order_open,
|
||||
is_short, buy_calls, sell_calls):
|
||||
|
Loading…
Reference in New Issue
Block a user