mirror of
https://github.com/freqtrade/freqtrade.git
synced 2026-02-19 19:01:06 +00:00
Merge branch 'develop' into startup-time
This commit is contained in:
@@ -38,7 +38,7 @@ def mock_trade_1(fee, is_short: bool):
|
||||
trade = Trade(
|
||||
pair="ETH/BTC",
|
||||
stake_amount=0.001,
|
||||
amount=123.0,
|
||||
amount=50.0,
|
||||
amount_requested=123.0,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
@@ -201,7 +201,7 @@ def mock_trade_4(fee, is_short: bool):
|
||||
trade = Trade(
|
||||
pair="ETC/BTC",
|
||||
stake_amount=0.001,
|
||||
amount=123.0,
|
||||
amount=0.0,
|
||||
amount_requested=124.0,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
|
||||
@@ -224,7 +224,7 @@ def mock_trade_usdt_4(fee, is_short: bool):
|
||||
trade = Trade(
|
||||
pair="NEO/USDT",
|
||||
stake_amount=20.0,
|
||||
amount=10.0,
|
||||
amount=0.0,
|
||||
amount_requested=10.01,
|
||||
fee_open=fee.return_value,
|
||||
fee_close=fee.return_value,
|
||||
|
||||
@@ -7,6 +7,7 @@ import pytest
|
||||
|
||||
from freqtrade.enums import CandleType, MarginMode, TradingMode
|
||||
from freqtrade.exceptions import DependencyException, InvalidOrderException, OperationalException
|
||||
from freqtrade.persistence import Trade
|
||||
from tests.conftest import EXMS, get_mock_coro, get_patched_exchange, log_has_re
|
||||
from tests.exchange.test_exchange import ccxt_exceptionhandlers
|
||||
|
||||
@@ -171,59 +172,101 @@ def test_stoploss_adjust_binance(mocker, default_conf, sl1, sl2, sl3, side):
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"is_short, trading_mode, margin_mode, wallet_balance, "
|
||||
"mm_ex_1, upnl_ex_1, maintenance_amt, amount, open_rate, "
|
||||
"pair, is_short, trading_mode, margin_mode, wallet_balance, "
|
||||
"maintenance_amt, amount, open_rate, open_trades,"
|
||||
"mm_ratio, expected",
|
||||
[
|
||||
(
|
||||
"ETH/USDT:USDT",
|
||||
False,
|
||||
"futures",
|
||||
"isolated",
|
||||
1535443.01,
|
||||
0.0,
|
||||
0.0,
|
||||
135365.00,
|
||||
3683.979,
|
||||
1456.84,
|
||||
[],
|
||||
0.10,
|
||||
1114.78,
|
||||
),
|
||||
(
|
||||
"ETH/USDT:USDT",
|
||||
False,
|
||||
"futures",
|
||||
"isolated",
|
||||
1535443.01,
|
||||
0.0,
|
||||
0.0,
|
||||
16300.000,
|
||||
109.488,
|
||||
32481.980,
|
||||
[],
|
||||
0.025,
|
||||
18778.73,
|
||||
),
|
||||
(
|
||||
"ETH/USDT:USDT",
|
||||
False,
|
||||
"futures",
|
||||
"cross",
|
||||
1535443.01,
|
||||
71200.81144,
|
||||
-56354.57,
|
||||
135365.00,
|
||||
3683.979,
|
||||
1456.84,
|
||||
3683.979, # amount
|
||||
1456.84, # open_rate
|
||||
[
|
||||
{
|
||||
# From calc example
|
||||
"pair": "BTC/USDT:USDT",
|
||||
"open_rate": 32481.98,
|
||||
"amount": 109.488,
|
||||
"stake_amount": 3556387.02624, # open_rate * amount
|
||||
"mark_price": 31967.27,
|
||||
"mm_ratio": 0.025,
|
||||
"maintenance_amt": 16300.0,
|
||||
},
|
||||
{
|
||||
# From calc example
|
||||
"pair": "ETH/USDT:USDT",
|
||||
"open_rate": 1456.84,
|
||||
"amount": 3683.979,
|
||||
"stake_amount": 5366967.96,
|
||||
"mark_price": 1335.18,
|
||||
"mm_ratio": 0.10,
|
||||
"maintenance_amt": 135365.00,
|
||||
},
|
||||
],
|
||||
0.10,
|
||||
1153.26,
|
||||
),
|
||||
(
|
||||
"BTC/USDT:USDT",
|
||||
False,
|
||||
"futures",
|
||||
"cross",
|
||||
1535443.01,
|
||||
356512.508,
|
||||
-448192.89,
|
||||
16300.000,
|
||||
109.488,
|
||||
32481.980,
|
||||
16300.0,
|
||||
109.488, # amount
|
||||
32481.980, # open_rate
|
||||
[
|
||||
{
|
||||
# From calc example
|
||||
"pair": "BTC/USDT:USDT",
|
||||
"open_rate": 32481.98,
|
||||
"amount": 109.488,
|
||||
"stake_amount": 3556387.02624, # open_rate * amount
|
||||
"mark_price": 31967.27,
|
||||
"mm_ratio": 0.025,
|
||||
"maintenance_amt": 16300.0,
|
||||
},
|
||||
{
|
||||
# From calc example
|
||||
"pair": "ETH/USDT:USDT",
|
||||
"open_rate": 1456.84,
|
||||
"amount": 3683.979,
|
||||
"stake_amount": 5366967.96,
|
||||
"mark_price": 1335.18,
|
||||
"mm_ratio": 0.10,
|
||||
"maintenance_amt": 135365.00,
|
||||
},
|
||||
],
|
||||
0.025,
|
||||
26316.89,
|
||||
),
|
||||
@@ -232,15 +275,15 @@ def test_stoploss_adjust_binance(mocker, default_conf, sl1, sl2, sl3, side):
|
||||
def test_liquidation_price_binance(
|
||||
mocker,
|
||||
default_conf,
|
||||
open_rate,
|
||||
pair,
|
||||
is_short,
|
||||
trading_mode,
|
||||
margin_mode,
|
||||
wallet_balance,
|
||||
mm_ex_1,
|
||||
upnl_ex_1,
|
||||
maintenance_amt,
|
||||
amount,
|
||||
open_rate,
|
||||
open_trades,
|
||||
mm_ratio,
|
||||
expected,
|
||||
):
|
||||
@@ -248,20 +291,48 @@ def test_liquidation_price_binance(
|
||||
default_conf["margin_mode"] = margin_mode
|
||||
default_conf["liquidation_buffer"] = 0.0
|
||||
exchange = get_patched_exchange(mocker, default_conf, exchange="binance")
|
||||
exchange.get_maintenance_ratio_and_amt = MagicMock(return_value=(mm_ratio, maintenance_amt))
|
||||
|
||||
def get_maint_ratio(pair_, stake_amount):
|
||||
if pair_ != pair:
|
||||
oc = [c for c in open_trades if c["pair"] == pair_][0]
|
||||
return oc["mm_ratio"], oc["maintenance_amt"]
|
||||
return mm_ratio, maintenance_amt
|
||||
|
||||
def fetch_funding_rates(*args, **kwargs):
|
||||
return {
|
||||
t["pair"]: {
|
||||
"symbol": t["pair"],
|
||||
"markPrice": t["mark_price"],
|
||||
}
|
||||
for t in open_trades
|
||||
}
|
||||
|
||||
exchange.get_maintenance_ratio_and_amt = get_maint_ratio
|
||||
exchange.fetch_funding_rates = fetch_funding_rates
|
||||
|
||||
open_trade_objects = [
|
||||
Trade(
|
||||
pair=t["pair"],
|
||||
open_rate=t["open_rate"],
|
||||
amount=t["amount"],
|
||||
stake_amount=t["stake_amount"],
|
||||
fee_open=0,
|
||||
)
|
||||
for t in open_trades
|
||||
]
|
||||
|
||||
assert (
|
||||
pytest.approx(
|
||||
round(
|
||||
exchange.get_liquidation_price(
|
||||
pair="DOGE/USDT",
|
||||
pair=pair,
|
||||
open_rate=open_rate,
|
||||
is_short=is_short,
|
||||
wallet_balance=wallet_balance,
|
||||
mm_ex_1=mm_ex_1,
|
||||
upnl_ex_1=upnl_ex_1,
|
||||
amount=amount,
|
||||
stake_amount=open_rate * amount,
|
||||
leverage=5,
|
||||
open_trades=open_trade_objects,
|
||||
),
|
||||
2,
|
||||
)
|
||||
|
||||
@@ -5524,8 +5524,6 @@ def test_liquidation_price_is_none(
|
||||
stake_amount=open_rate * 71200.81144,
|
||||
leverage=5,
|
||||
wallet_balance=-56354.57,
|
||||
mm_ex_1=0.10,
|
||||
upnl_ex_1=0.0,
|
||||
)
|
||||
is None
|
||||
)
|
||||
@@ -6011,6 +6009,7 @@ def test_get_liquidation_price1(mocker, default_conf):
|
||||
stake_amount=18.884 * 0.8,
|
||||
leverage=leverage,
|
||||
wallet_balance=18.884 * 0.8,
|
||||
open_trades=[],
|
||||
)
|
||||
|
||||
|
||||
@@ -6141,6 +6140,7 @@ def test_get_liquidation_price(
|
||||
wallet_balance=amount * open_rate / leverage,
|
||||
leverage=leverage,
|
||||
is_short=is_short,
|
||||
open_trades=[],
|
||||
)
|
||||
if expected_liq is None:
|
||||
assert liq is None
|
||||
|
||||
@@ -457,6 +457,7 @@ class TestCCXTExchange:
|
||||
stake_amount=100,
|
||||
leverage=5,
|
||||
wallet_balance=100,
|
||||
open_trades=[],
|
||||
)
|
||||
assert isinstance(liquidation_price, float)
|
||||
assert liquidation_price >= 0.0
|
||||
@@ -469,6 +470,7 @@ class TestCCXTExchange:
|
||||
stake_amount=100,
|
||||
leverage=5,
|
||||
wallet_balance=100,
|
||||
open_trades=[],
|
||||
)
|
||||
assert isinstance(liquidation_price, float)
|
||||
assert liquidation_price >= 0.0
|
||||
|
||||
57
tests/leverage/test_update_liquidation_price.py
Normal file
57
tests/leverage/test_update_liquidation_price.py
Normal file
@@ -0,0 +1,57 @@
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
from freqtrade.enums.marginmode import MarginMode
|
||||
from freqtrade.leverage.liquidation_price import update_liquidation_prices
|
||||
|
||||
|
||||
@pytest.mark.parametrize("dry_run", [False, True])
|
||||
@pytest.mark.parametrize("margin_mode", [MarginMode.CROSS, MarginMode.ISOLATED])
|
||||
def test_update_liquidation_prices(mocker, margin_mode, dry_run):
|
||||
# Heavily mocked test - Only testing the logic of the function
|
||||
# update liquidation price for trade in isolated mode
|
||||
# update liquidation price for all trades in cross mode
|
||||
exchange = MagicMock()
|
||||
exchange.margin_mode = margin_mode
|
||||
wallets = MagicMock()
|
||||
trade_mock = MagicMock()
|
||||
|
||||
mocker.patch("freqtrade.persistence.Trade.get_open_trades", return_value=[trade_mock])
|
||||
|
||||
update_liquidation_prices(
|
||||
trade=trade_mock,
|
||||
exchange=exchange,
|
||||
wallets=wallets,
|
||||
stake_currency="USDT",
|
||||
dry_run=dry_run,
|
||||
)
|
||||
|
||||
assert trade_mock.set_liquidation_price.call_count == 1
|
||||
|
||||
assert wallets.get_total.call_count == (
|
||||
0 if margin_mode == MarginMode.ISOLATED or not dry_run else 1
|
||||
)
|
||||
|
||||
# Test with multiple trades
|
||||
trade_mock.reset_mock()
|
||||
trade_mock_2 = MagicMock()
|
||||
|
||||
mocker.patch(
|
||||
"freqtrade.persistence.Trade.get_open_trades", return_value=[trade_mock, trade_mock_2]
|
||||
)
|
||||
|
||||
update_liquidation_prices(
|
||||
trade=trade_mock,
|
||||
exchange=exchange,
|
||||
wallets=wallets,
|
||||
stake_currency="USDT",
|
||||
dry_run=dry_run,
|
||||
)
|
||||
# Trade2 is only updated in cross mode
|
||||
assert trade_mock_2.set_liquidation_price.call_count == (
|
||||
1 if margin_mode == MarginMode.CROSS else 0
|
||||
)
|
||||
assert trade_mock.set_liquidation_price.call_count == 1
|
||||
|
||||
assert wallets.call_count == 0 if not dry_run else 1
|
||||
@@ -1269,7 +1269,7 @@ def test_api_mix_tag(botclient, fee):
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"is_short,current_rate,open_trade_value",
|
||||
[(True, 1.098e-05, 15.0911775), (False, 1.099e-05, 15.1668225)],
|
||||
[(True, 1.098e-05, 6.134625), (False, 1.099e-05, 6.165375)],
|
||||
)
|
||||
def test_api_status(
|
||||
botclient, mocker, ticker, fee, markets, is_short, current_rate, open_trade_value
|
||||
@@ -1294,7 +1294,7 @@ def test_api_status(
|
||||
assert_response(rc)
|
||||
assert len(rc.json()) == 4
|
||||
assert rc.json()[0] == {
|
||||
"amount": 123.0,
|
||||
"amount": 50.0,
|
||||
"amount_requested": 123.0,
|
||||
"close_date": None,
|
||||
"close_timestamp": None,
|
||||
|
||||
@@ -362,7 +362,8 @@ def test_sync_wallet_dry(mocker, default_conf_usdt, fee):
|
||||
assert len(freqtrade.wallets._wallets) == 5
|
||||
assert len(freqtrade.wallets._positions) == 0
|
||||
bal = freqtrade.wallets.get_all_balances()
|
||||
assert bal["NEO"].total == 10
|
||||
# NEO trade is not filled yet.
|
||||
assert bal["NEO"].total == 0
|
||||
assert bal["XRP"].total == 10
|
||||
assert bal["LTC"].total == 2
|
||||
usdt_bal = bal["USDT"]
|
||||
@@ -410,11 +411,11 @@ def test_sync_wallet_futures_dry(mocker, default_conf, fee):
|
||||
def test_check_exit_amount(mocker, default_conf, fee):
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
update_mock = mocker.patch("freqtrade.wallets.Wallets.update")
|
||||
total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=123)
|
||||
total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=50.0)
|
||||
|
||||
create_mock_trades(fee, is_short=None)
|
||||
trade = Trade.session.scalars(select(Trade)).first()
|
||||
assert trade.amount == 123
|
||||
assert trade.amount == 50.0
|
||||
|
||||
assert freqtrade.wallets.check_exit_amount(trade) is True
|
||||
assert update_mock.call_count == 0
|
||||
@@ -423,7 +424,7 @@ def test_check_exit_amount(mocker, default_conf, fee):
|
||||
update_mock.reset_mock()
|
||||
# Reduce returned amount to below the trade amount - which should
|
||||
# trigger a wallet update and return False, triggering "order refinding"
|
||||
total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=100)
|
||||
total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=40)
|
||||
assert freqtrade.wallets.check_exit_amount(trade) is False
|
||||
assert update_mock.call_count == 1
|
||||
assert total_mock.call_count == 2
|
||||
@@ -433,12 +434,12 @@ def test_check_exit_amount_futures(mocker, default_conf, fee):
|
||||
default_conf["trading_mode"] = "futures"
|
||||
default_conf["margin_mode"] = "isolated"
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf)
|
||||
total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=123)
|
||||
total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=50)
|
||||
|
||||
create_mock_trades(fee, is_short=None)
|
||||
trade = Trade.session.scalars(select(Trade)).first()
|
||||
trade.trading_mode = "futures"
|
||||
assert trade.amount == 123
|
||||
assert trade.amount == 50
|
||||
|
||||
assert freqtrade.wallets.check_exit_amount(trade) is True
|
||||
assert total_mock.call_count == 0
|
||||
|
||||
Reference in New Issue
Block a user