Merge branch 'develop' into move_datadownload
This commit is contained in:
@@ -1430,6 +1430,27 @@ def test_start_list_data(testdatadir, capsys):
|
||||
assert "\n| XRP/USDT | 1h | futures |\n" in captured.out
|
||||
assert "\n| XRP/USDT | 1h, 8h | mark |\n" in captured.out
|
||||
|
||||
args = [
|
||||
"list-data",
|
||||
"--data-format-ohlcv",
|
||||
"json",
|
||||
"--pairs", "XRP/ETH",
|
||||
"--datadir",
|
||||
str(testdatadir),
|
||||
"--show-timerange",
|
||||
]
|
||||
pargs = get_args(args)
|
||||
pargs['config'] = None
|
||||
start_list_data(pargs)
|
||||
captured = capsys.readouterr()
|
||||
assert "Found 2 pair / timeframe combinations." in captured.out
|
||||
assert ("\n| Pair | Timeframe | Type | From | To |\n"
|
||||
in captured.out)
|
||||
assert "UNITTEST/BTC" not in captured.out
|
||||
assert (
|
||||
"\n| XRP/ETH | 1m | spot | 2019-10-11 00:00:00 | 2019-10-13 11:19:00 |\n"
|
||||
in captured.out)
|
||||
|
||||
|
||||
@pytest.mark.usefixtures("init_persistence")
|
||||
def test_show_trades(mocker, fee, capsys, caplog):
|
||||
|
@@ -3085,416 +3085,416 @@ def leverage_tiers():
|
||||
return {
|
||||
"1000SHIB/USDT": [
|
||||
{
|
||||
'min': 0,
|
||||
'max': 50000,
|
||||
'mmr': 0.01,
|
||||
'lev': 50,
|
||||
'minNotional': 0,
|
||||
'maxNotional': 50000,
|
||||
'maintenanceMarginRate': 0.01,
|
||||
'maxLeverage': 50,
|
||||
'maintAmt': 0.0
|
||||
},
|
||||
{
|
||||
'min': 50000,
|
||||
'max': 150000,
|
||||
'mmr': 0.025,
|
||||
'lev': 20,
|
||||
'minNotional': 50000,
|
||||
'maxNotional': 150000,
|
||||
'maintenanceMarginRate': 0.025,
|
||||
'maxLeverage': 20,
|
||||
'maintAmt': 750.0
|
||||
},
|
||||
{
|
||||
'min': 150000,
|
||||
'max': 250000,
|
||||
'mmr': 0.05,
|
||||
'lev': 10,
|
||||
'minNotional': 150000,
|
||||
'maxNotional': 250000,
|
||||
'maintenanceMarginRate': 0.05,
|
||||
'maxLeverage': 10,
|
||||
'maintAmt': 4500.0
|
||||
},
|
||||
{
|
||||
'min': 250000,
|
||||
'max': 500000,
|
||||
'mmr': 0.1,
|
||||
'lev': 5,
|
||||
'minNotional': 250000,
|
||||
'maxNotional': 500000,
|
||||
'maintenanceMarginRate': 0.1,
|
||||
'maxLeverage': 5,
|
||||
'maintAmt': 17000.0
|
||||
},
|
||||
{
|
||||
'min': 500000,
|
||||
'max': 1000000,
|
||||
'mmr': 0.125,
|
||||
'lev': 4,
|
||||
'minNotional': 500000,
|
||||
'maxNotional': 1000000,
|
||||
'maintenanceMarginRate': 0.125,
|
||||
'maxLeverage': 4,
|
||||
'maintAmt': 29500.0
|
||||
},
|
||||
{
|
||||
'min': 1000000,
|
||||
'max': 2000000,
|
||||
'mmr': 0.25,
|
||||
'lev': 2,
|
||||
'minNotional': 1000000,
|
||||
'maxNotional': 2000000,
|
||||
'maintenanceMarginRate': 0.25,
|
||||
'maxLeverage': 2,
|
||||
'maintAmt': 154500.0
|
||||
},
|
||||
{
|
||||
'min': 2000000,
|
||||
'max': 30000000,
|
||||
'mmr': 0.5,
|
||||
'lev': 1,
|
||||
'minNotional': 2000000,
|
||||
'maxNotional': 30000000,
|
||||
'maintenanceMarginRate': 0.5,
|
||||
'maxLeverage': 1,
|
||||
'maintAmt': 654500.0
|
||||
},
|
||||
],
|
||||
"1INCH/USDT": [
|
||||
{
|
||||
'min': 0,
|
||||
'max': 5000,
|
||||
'mmr': 0.012,
|
||||
'lev': 50,
|
||||
'minNotional': 0,
|
||||
'maxNotional': 5000,
|
||||
'maintenanceMarginRate': 0.012,
|
||||
'maxLeverage': 50,
|
||||
'maintAmt': 0.0
|
||||
},
|
||||
{
|
||||
'min': 5000,
|
||||
'max': 25000,
|
||||
'mmr': 0.025,
|
||||
'lev': 20,
|
||||
'minNotional': 5000,
|
||||
'maxNotional': 25000,
|
||||
'maintenanceMarginRate': 0.025,
|
||||
'maxLeverage': 20,
|
||||
'maintAmt': 65.0
|
||||
},
|
||||
{
|
||||
'min': 25000,
|
||||
'max': 100000,
|
||||
'mmr': 0.05,
|
||||
'lev': 10,
|
||||
'minNotional': 25000,
|
||||
'maxNotional': 100000,
|
||||
'maintenanceMarginRate': 0.05,
|
||||
'maxLeverage': 10,
|
||||
'maintAmt': 690.0
|
||||
},
|
||||
{
|
||||
'min': 100000,
|
||||
'max': 250000,
|
||||
'mmr': 0.1,
|
||||
'lev': 5,
|
||||
'minNotional': 100000,
|
||||
'maxNotional': 250000,
|
||||
'maintenanceMarginRate': 0.1,
|
||||
'maxLeverage': 5,
|
||||
'maintAmt': 5690.0
|
||||
},
|
||||
{
|
||||
'min': 250000,
|
||||
'max': 1000000,
|
||||
'mmr': 0.125,
|
||||
'lev': 2,
|
||||
'minNotional': 250000,
|
||||
'maxNotional': 1000000,
|
||||
'maintenanceMarginRate': 0.125,
|
||||
'maxLeverage': 2,
|
||||
'maintAmt': 11940.0
|
||||
},
|
||||
{
|
||||
'min': 1000000,
|
||||
'max': 100000000,
|
||||
'mmr': 0.5,
|
||||
'lev': 1,
|
||||
'minNotional': 1000000,
|
||||
'maxNotional': 100000000,
|
||||
'maintenanceMarginRate': 0.5,
|
||||
'maxLeverage': 1,
|
||||
'maintAmt': 386940.0
|
||||
},
|
||||
],
|
||||
"AAVE/USDT": [
|
||||
{
|
||||
'min': 0,
|
||||
'max': 5000,
|
||||
'mmr': 0.01,
|
||||
'lev': 50,
|
||||
'minNotional': 0,
|
||||
'maxNotional': 5000,
|
||||
'maintenanceMarginRate': 0.01,
|
||||
'maxLeverage': 50,
|
||||
'maintAmt': 0.0
|
||||
},
|
||||
{
|
||||
'min': 5000,
|
||||
'max': 25000,
|
||||
'mmr': 0.02,
|
||||
'lev': 25,
|
||||
'minNotional': 5000,
|
||||
'maxNotional': 25000,
|
||||
'maintenanceMarginRate': 0.02,
|
||||
'maxLeverage': 25,
|
||||
'maintAmt': 75.0
|
||||
},
|
||||
{
|
||||
'min': 25000,
|
||||
'max': 100000,
|
||||
'mmr': 0.05,
|
||||
'lev': 10,
|
||||
'minNotional': 25000,
|
||||
'maxNotional': 100000,
|
||||
'maintenanceMarginRate': 0.05,
|
||||
'maxLeverage': 10,
|
||||
'maintAmt': 700.0
|
||||
},
|
||||
{
|
||||
'min': 100000,
|
||||
'max': 250000,
|
||||
'mmr': 0.1,
|
||||
'lev': 5,
|
||||
'minNotional': 100000,
|
||||
'maxNotional': 250000,
|
||||
'maintenanceMarginRate': 0.1,
|
||||
'maxLeverage': 5,
|
||||
'maintAmt': 5700.0
|
||||
},
|
||||
{
|
||||
'min': 250000,
|
||||
'max': 1000000,
|
||||
'mmr': 0.125,
|
||||
'lev': 2,
|
||||
'minNotional': 250000,
|
||||
'maxNotional': 1000000,
|
||||
'maintenanceMarginRate': 0.125,
|
||||
'maxLeverage': 2,
|
||||
'maintAmt': 11950.0
|
||||
},
|
||||
{
|
||||
'min': 10000000,
|
||||
'max': 50000000,
|
||||
'mmr': 0.5,
|
||||
'lev': 1,
|
||||
'minNotional': 10000000,
|
||||
'maxNotional': 50000000,
|
||||
'maintenanceMarginRate': 0.5,
|
||||
'maxLeverage': 1,
|
||||
'maintAmt': 386950.0
|
||||
},
|
||||
],
|
||||
"ADA/BUSD": [
|
||||
{
|
||||
"min": 0,
|
||||
"max": 100000,
|
||||
"mmr": 0.025,
|
||||
"lev": 20,
|
||||
"minNotional": 0,
|
||||
"maxNotional": 100000,
|
||||
"maintenanceMarginRate": 0.025,
|
||||
"maxLeverage": 20,
|
||||
"maintAmt": 0.0
|
||||
},
|
||||
{
|
||||
"min": 100000,
|
||||
"max": 500000,
|
||||
"mmr": 0.05,
|
||||
"lev": 10,
|
||||
"minNotional": 100000,
|
||||
"maxNotional": 500000,
|
||||
"maintenanceMarginRate": 0.05,
|
||||
"maxLeverage": 10,
|
||||
"maintAmt": 2500.0
|
||||
},
|
||||
{
|
||||
"min": 500000,
|
||||
"max": 1000000,
|
||||
"mmr": 0.1,
|
||||
"lev": 5,
|
||||
"minNotional": 500000,
|
||||
"maxNotional": 1000000,
|
||||
"maintenanceMarginRate": 0.1,
|
||||
"maxLeverage": 5,
|
||||
"maintAmt": 27500.0
|
||||
},
|
||||
{
|
||||
"min": 1000000,
|
||||
"max": 2000000,
|
||||
"mmr": 0.15,
|
||||
"lev": 3,
|
||||
"minNotional": 1000000,
|
||||
"maxNotional": 2000000,
|
||||
"maintenanceMarginRate": 0.15,
|
||||
"maxLeverage": 3,
|
||||
"maintAmt": 77500.0
|
||||
},
|
||||
{
|
||||
"min": 2000000,
|
||||
"max": 5000000,
|
||||
"mmr": 0.25,
|
||||
"lev": 2,
|
||||
"minNotional": 2000000,
|
||||
"maxNotional": 5000000,
|
||||
"maintenanceMarginRate": 0.25,
|
||||
"maxLeverage": 2,
|
||||
"maintAmt": 277500.0
|
||||
},
|
||||
{
|
||||
"min": 5000000,
|
||||
"max": 30000000,
|
||||
"mmr": 0.5,
|
||||
"lev": 1,
|
||||
"minNotional": 5000000,
|
||||
"maxNotional": 30000000,
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1,
|
||||
"maintAmt": 1527500.0
|
||||
},
|
||||
],
|
||||
'BNB/BUSD': [
|
||||
{
|
||||
"min": 0, # stake(before leverage) = 0
|
||||
"max": 100000, # max stake(before leverage) = 5000
|
||||
"mmr": 0.025,
|
||||
"lev": 20,
|
||||
"minNotional": 0, # stake(before leverage) = 0
|
||||
"maxNotional": 100000, # max stake(before leverage) = 5000
|
||||
"maintenanceMarginRate": 0.025,
|
||||
"maxLeverage": 20,
|
||||
"maintAmt": 0.0
|
||||
},
|
||||
{
|
||||
"min": 100000, # stake = 10000.0
|
||||
"max": 500000, # max_stake = 50000.0
|
||||
"mmr": 0.05,
|
||||
"lev": 10,
|
||||
"minNotional": 100000, # stake = 10000.0
|
||||
"maxNotional": 500000, # max_stake = 50000.0
|
||||
"maintenanceMarginRate": 0.05,
|
||||
"maxLeverage": 10,
|
||||
"maintAmt": 2500.0
|
||||
},
|
||||
{
|
||||
"min": 500000, # stake = 100000.0
|
||||
"max": 1000000, # max_stake = 200000.0
|
||||
"mmr": 0.1,
|
||||
"lev": 5,
|
||||
"minNotional": 500000, # stake = 100000.0
|
||||
"maxNotional": 1000000, # max_stake = 200000.0
|
||||
"maintenanceMarginRate": 0.1,
|
||||
"maxLeverage": 5,
|
||||
"maintAmt": 27500.0
|
||||
},
|
||||
{
|
||||
"min": 1000000, # stake = 333333.3333333333
|
||||
"max": 2000000, # max_stake = 666666.6666666666
|
||||
"mmr": 0.15,
|
||||
"lev": 3,
|
||||
"minNotional": 1000000, # stake = 333333.3333333333
|
||||
"maxNotional": 2000000, # max_stake = 666666.6666666666
|
||||
"maintenanceMarginRate": 0.15,
|
||||
"maxLeverage": 3,
|
||||
"maintAmt": 77500.0
|
||||
},
|
||||
{
|
||||
"min": 2000000, # stake = 1000000.0
|
||||
"max": 5000000, # max_stake = 2500000.0
|
||||
"mmr": 0.25,
|
||||
"lev": 2,
|
||||
"minNotional": 2000000, # stake = 1000000.0
|
||||
"maxNotional": 5000000, # max_stake = 2500000.0
|
||||
"maintenanceMarginRate": 0.25,
|
||||
"maxLeverage": 2,
|
||||
"maintAmt": 277500.0
|
||||
},
|
||||
{
|
||||
"min": 5000000, # stake = 5000000.0
|
||||
"max": 30000000, # max_stake = 30000000.0
|
||||
"mmr": 0.5,
|
||||
"lev": 1,
|
||||
"minNotional": 5000000, # stake = 5000000.0
|
||||
"maxNotional": 30000000, # max_stake = 30000000.0
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1,
|
||||
"maintAmt": 1527500.0
|
||||
}
|
||||
],
|
||||
'BNB/USDT': [
|
||||
{
|
||||
"min": 0, # stake = 0.0
|
||||
"max": 10000, # max_stake = 133.33333333333334
|
||||
"mmr": 0.0065,
|
||||
"lev": 75,
|
||||
"minNotional": 0, # stake = 0.0
|
||||
"maxNotional": 10000, # max_stake = 133.33333333333334
|
||||
"maintenanceMarginRate": 0.0065,
|
||||
"maxLeverage": 75,
|
||||
"maintAmt": 0.0
|
||||
},
|
||||
{
|
||||
"min": 10000, # stake = 200.0
|
||||
"max": 50000, # max_stake = 1000.0
|
||||
"mmr": 0.01,
|
||||
"lev": 50,
|
||||
"minNotional": 10000, # stake = 200.0
|
||||
"maxNotional": 50000, # max_stake = 1000.0
|
||||
"maintenanceMarginRate": 0.01,
|
||||
"maxLeverage": 50,
|
||||
"maintAmt": 35.0
|
||||
},
|
||||
{
|
||||
"min": 50000, # stake = 2000.0
|
||||
"max": 250000, # max_stake = 10000.0
|
||||
"mmr": 0.02,
|
||||
"lev": 25,
|
||||
"minNotional": 50000, # stake = 2000.0
|
||||
"maxNotional": 250000, # max_stake = 10000.0
|
||||
"maintenanceMarginRate": 0.02,
|
||||
"maxLeverage": 25,
|
||||
"maintAmt": 535.0
|
||||
},
|
||||
{
|
||||
"min": 250000, # stake = 25000.0
|
||||
"max": 1000000, # max_stake = 100000.0
|
||||
"mmr": 0.05,
|
||||
"lev": 10,
|
||||
"minNotional": 250000, # stake = 25000.0
|
||||
"maxNotional": 1000000, # max_stake = 100000.0
|
||||
"maintenanceMarginRate": 0.05,
|
||||
"maxLeverage": 10,
|
||||
"maintAmt": 8035.0
|
||||
},
|
||||
{
|
||||
"min": 1000000, # stake = 200000.0
|
||||
"max": 2000000, # max_stake = 400000.0
|
||||
"mmr": 0.1,
|
||||
"lev": 5,
|
||||
"minNotional": 1000000, # stake = 200000.0
|
||||
"maxNotional": 2000000, # max_stake = 400000.0
|
||||
"maintenanceMarginRate": 0.1,
|
||||
"maxLeverage": 5,
|
||||
"maintAmt": 58035.0
|
||||
},
|
||||
{
|
||||
"min": 2000000, # stake = 500000.0
|
||||
"max": 5000000, # max_stake = 1250000.0
|
||||
"mmr": 0.125,
|
||||
"lev": 4,
|
||||
"minNotional": 2000000, # stake = 500000.0
|
||||
"maxNotional": 5000000, # max_stake = 1250000.0
|
||||
"maintenanceMarginRate": 0.125,
|
||||
"maxLeverage": 4,
|
||||
"maintAmt": 108035.0
|
||||
},
|
||||
{
|
||||
"min": 5000000, # stake = 1666666.6666666667
|
||||
"max": 10000000, # max_stake = 3333333.3333333335
|
||||
"mmr": 0.15,
|
||||
"lev": 3,
|
||||
"minNotional": 5000000, # stake = 1666666.6666666667
|
||||
"maxNotional": 10000000, # max_stake = 3333333.3333333335
|
||||
"maintenanceMarginRate": 0.15,
|
||||
"maxLeverage": 3,
|
||||
"maintAmt": 233035.0
|
||||
},
|
||||
{
|
||||
"min": 10000000, # stake = 5000000.0
|
||||
"max": 20000000, # max_stake = 10000000.0
|
||||
"mmr": 0.25,
|
||||
"lev": 2,
|
||||
"minNotional": 10000000, # stake = 5000000.0
|
||||
"maxNotional": 20000000, # max_stake = 10000000.0
|
||||
"maintenanceMarginRate": 0.25,
|
||||
"maxLeverage": 2,
|
||||
"maintAmt": 1233035.0
|
||||
},
|
||||
{
|
||||
"min": 20000000, # stake = 20000000.0
|
||||
"max": 50000000, # max_stake = 50000000.0
|
||||
"mmr": 0.5,
|
||||
"lev": 1,
|
||||
"minNotional": 20000000, # stake = 20000000.0
|
||||
"maxNotional": 50000000, # max_stake = 50000000.0
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1,
|
||||
"maintAmt": 6233035.0
|
||||
},
|
||||
],
|
||||
'BTC/USDT': [
|
||||
{
|
||||
"min": 0, # stake = 0.0
|
||||
"max": 50000, # max_stake = 400.0
|
||||
"mmr": 0.004,
|
||||
"lev": 125,
|
||||
"minNotional": 0, # stake = 0.0
|
||||
"maxNotional": 50000, # max_stake = 400.0
|
||||
"maintenanceMarginRate": 0.004,
|
||||
"maxLeverage": 125,
|
||||
"maintAmt": 0.0
|
||||
},
|
||||
{
|
||||
"min": 50000, # stake = 500.0
|
||||
"max": 250000, # max_stake = 2500.0
|
||||
"mmr": 0.005,
|
||||
"lev": 100,
|
||||
"minNotional": 50000, # stake = 500.0
|
||||
"maxNotional": 250000, # max_stake = 2500.0
|
||||
"maintenanceMarginRate": 0.005,
|
||||
"maxLeverage": 100,
|
||||
"maintAmt": 50.0
|
||||
},
|
||||
{
|
||||
"min": 250000, # stake = 5000.0
|
||||
"max": 1000000, # max_stake = 20000.0
|
||||
"mmr": 0.01,
|
||||
"lev": 50,
|
||||
"minNotional": 250000, # stake = 5000.0
|
||||
"maxNotional": 1000000, # max_stake = 20000.0
|
||||
"maintenanceMarginRate": 0.01,
|
||||
"maxLeverage": 50,
|
||||
"maintAmt": 1300.0
|
||||
},
|
||||
{
|
||||
"min": 1000000, # stake = 50000.0
|
||||
"max": 7500000, # max_stake = 375000.0
|
||||
"mmr": 0.025,
|
||||
"lev": 20,
|
||||
"minNotional": 1000000, # stake = 50000.0
|
||||
"maxNotional": 7500000, # max_stake = 375000.0
|
||||
"maintenanceMarginRate": 0.025,
|
||||
"maxLeverage": 20,
|
||||
"maintAmt": 16300.0
|
||||
},
|
||||
{
|
||||
"min": 7500000, # stake = 750000.0
|
||||
"max": 40000000, # max_stake = 4000000.0
|
||||
"mmr": 0.05,
|
||||
"lev": 10,
|
||||
"minNotional": 7500000, # stake = 750000.0
|
||||
"maxNotional": 40000000, # max_stake = 4000000.0
|
||||
"maintenanceMarginRate": 0.05,
|
||||
"maxLeverage": 10,
|
||||
"maintAmt": 203800.0
|
||||
},
|
||||
{
|
||||
"min": 40000000, # stake = 8000000.0
|
||||
"max": 100000000, # max_stake = 20000000.0
|
||||
"mmr": 0.1,
|
||||
"lev": 5,
|
||||
"minNotional": 40000000, # stake = 8000000.0
|
||||
"maxNotional": 100000000, # max_stake = 20000000.0
|
||||
"maintenanceMarginRate": 0.1,
|
||||
"maxLeverage": 5,
|
||||
"maintAmt": 2203800.0
|
||||
},
|
||||
{
|
||||
"min": 100000000, # stake = 25000000.0
|
||||
"max": 200000000, # max_stake = 50000000.0
|
||||
"mmr": 0.125,
|
||||
"lev": 4,
|
||||
"minNotional": 100000000, # stake = 25000000.0
|
||||
"maxNotional": 200000000, # max_stake = 50000000.0
|
||||
"maintenanceMarginRate": 0.125,
|
||||
"maxLeverage": 4,
|
||||
"maintAmt": 4703800.0
|
||||
},
|
||||
{
|
||||
"min": 200000000, # stake = 66666666.666666664
|
||||
"max": 400000000, # max_stake = 133333333.33333333
|
||||
"mmr": 0.15,
|
||||
"lev": 3,
|
||||
"minNotional": 200000000, # stake = 66666666.666666664
|
||||
"maxNotional": 400000000, # max_stake = 133333333.33333333
|
||||
"maintenanceMarginRate": 0.15,
|
||||
"maxLeverage": 3,
|
||||
"maintAmt": 9703800.0
|
||||
},
|
||||
{
|
||||
"min": 400000000, # stake = 200000000.0
|
||||
"max": 600000000, # max_stake = 300000000.0
|
||||
"mmr": 0.25,
|
||||
"lev": 2,
|
||||
"minNotional": 400000000, # stake = 200000000.0
|
||||
"maxNotional": 600000000, # max_stake = 300000000.0
|
||||
"maintenanceMarginRate": 0.25,
|
||||
"maxLeverage": 2,
|
||||
"maintAmt": 4.97038E7
|
||||
},
|
||||
{
|
||||
"min": 600000000, # stake = 600000000.0
|
||||
"max": 1000000000, # max_stake = 1000000000.0
|
||||
"mmr": 0.5,
|
||||
"lev": 1,
|
||||
"minNotional": 600000000, # stake = 600000000.0
|
||||
"maxNotional": 1000000000, # max_stake = 1000000000.0
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1,
|
||||
"maintAmt": 1.997038E8
|
||||
},
|
||||
],
|
||||
"ZEC/USDT": [
|
||||
{
|
||||
'min': 0,
|
||||
'max': 50000,
|
||||
'mmr': 0.01,
|
||||
'lev': 50,
|
||||
'minNotional': 0,
|
||||
'maxNotional': 50000,
|
||||
'maintenanceMarginRate': 0.01,
|
||||
'maxLeverage': 50,
|
||||
'maintAmt': 0.0
|
||||
},
|
||||
{
|
||||
'min': 50000,
|
||||
'max': 150000,
|
||||
'mmr': 0.025,
|
||||
'lev': 20,
|
||||
'minNotional': 50000,
|
||||
'maxNotional': 150000,
|
||||
'maintenanceMarginRate': 0.025,
|
||||
'maxLeverage': 20,
|
||||
'maintAmt': 750.0
|
||||
},
|
||||
{
|
||||
'min': 150000,
|
||||
'max': 250000,
|
||||
'mmr': 0.05,
|
||||
'lev': 10,
|
||||
'minNotional': 150000,
|
||||
'maxNotional': 250000,
|
||||
'maintenanceMarginRate': 0.05,
|
||||
'maxLeverage': 10,
|
||||
'maintAmt': 4500.0
|
||||
},
|
||||
{
|
||||
'min': 250000,
|
||||
'max': 500000,
|
||||
'mmr': 0.1,
|
||||
'lev': 5,
|
||||
'minNotional': 250000,
|
||||
'maxNotional': 500000,
|
||||
'maintenanceMarginRate': 0.1,
|
||||
'maxLeverage': 5,
|
||||
'maintAmt': 17000.0
|
||||
},
|
||||
{
|
||||
'min': 500000,
|
||||
'max': 1000000,
|
||||
'mmr': 0.125,
|
||||
'lev': 4,
|
||||
'minNotional': 500000,
|
||||
'maxNotional': 1000000,
|
||||
'maintenanceMarginRate': 0.125,
|
||||
'maxLeverage': 4,
|
||||
'maintAmt': 29500.0
|
||||
},
|
||||
{
|
||||
'min': 1000000,
|
||||
'max': 2000000,
|
||||
'mmr': 0.25,
|
||||
'lev': 2,
|
||||
'minNotional': 1000000,
|
||||
'maxNotional': 2000000,
|
||||
'maintenanceMarginRate': 0.25,
|
||||
'maxLeverage': 2,
|
||||
'maintAmt': 154500.0
|
||||
},
|
||||
{
|
||||
'min': 2000000,
|
||||
'max': 30000000,
|
||||
'mmr': 0.5,
|
||||
'lev': 1,
|
||||
'minNotional': 2000000,
|
||||
'maxNotional': 30000000,
|
||||
'maintenanceMarginRate': 0.5,
|
||||
'maxLeverage': 1,
|
||||
'maintAmt': 654500.0
|
||||
},
|
||||
]
|
||||
|
@@ -1,4 +1,3 @@
|
||||
from math import isclose
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
@@ -269,7 +268,7 @@ def test_create_cum_profit(testdatadir):
|
||||
"cum_profits", timeframe="5m")
|
||||
assert "cum_profits" in cum_profits.columns
|
||||
assert cum_profits.iloc[0]['cum_profits'] == 0
|
||||
assert isclose(cum_profits.iloc[-1]['cum_profits'], 8.723007518796964e-06)
|
||||
assert pytest.approx(cum_profits.iloc[-1]['cum_profits']) == 8.723007518796964e-06
|
||||
|
||||
|
||||
def test_create_cum_profit1(testdatadir):
|
||||
@@ -287,7 +286,7 @@ def test_create_cum_profit1(testdatadir):
|
||||
"cum_profits", timeframe="5m")
|
||||
assert "cum_profits" in cum_profits.columns
|
||||
assert cum_profits.iloc[0]['cum_profits'] == 0
|
||||
assert isclose(cum_profits.iloc[-1]['cum_profits'], 8.723007518796964e-06)
|
||||
assert pytest.approx(cum_profits.iloc[-1]['cum_profits']) == 8.723007518796964e-06
|
||||
|
||||
with pytest.raises(ValueError, match='Trade dataframe empty.'):
|
||||
create_cum_profit(df.set_index('date'), bt_data[bt_data["pair"] == 'NOTAPAIR'],
|
||||
|
@@ -376,96 +376,96 @@ def test_fill_leverage_tiers_binance(default_conf, mocker):
|
||||
assert exchange._leverage_tiers == {
|
||||
'ADA/BUSD': [
|
||||
{
|
||||
"min": 0,
|
||||
"max": 100000,
|
||||
"mmr": 0.025,
|
||||
"lev": 20,
|
||||
"minNotional": 0,
|
||||
"maxNotional": 100000,
|
||||
"maintenanceMarginRate": 0.025,
|
||||
"maxLeverage": 20,
|
||||
"maintAmt": 0.0
|
||||
},
|
||||
{
|
||||
"min": 100000,
|
||||
"max": 500000,
|
||||
"mmr": 0.05,
|
||||
"lev": 10,
|
||||
"minNotional": 100000,
|
||||
"maxNotional": 500000,
|
||||
"maintenanceMarginRate": 0.05,
|
||||
"maxLeverage": 10,
|
||||
"maintAmt": 2500.0
|
||||
},
|
||||
{
|
||||
"min": 500000,
|
||||
"max": 1000000,
|
||||
"mmr": 0.1,
|
||||
"lev": 5,
|
||||
"minNotional": 500000,
|
||||
"maxNotional": 1000000,
|
||||
"maintenanceMarginRate": 0.1,
|
||||
"maxLeverage": 5,
|
||||
"maintAmt": 27500.0
|
||||
},
|
||||
{
|
||||
"min": 1000000,
|
||||
"max": 2000000,
|
||||
"mmr": 0.15,
|
||||
"lev": 3,
|
||||
"minNotional": 1000000,
|
||||
"maxNotional": 2000000,
|
||||
"maintenanceMarginRate": 0.15,
|
||||
"maxLeverage": 3,
|
||||
"maintAmt": 77500.0
|
||||
},
|
||||
{
|
||||
"min": 2000000,
|
||||
"max": 5000000,
|
||||
"mmr": 0.25,
|
||||
"lev": 2,
|
||||
"minNotional": 2000000,
|
||||
"maxNotional": 5000000,
|
||||
"maintenanceMarginRate": 0.25,
|
||||
"maxLeverage": 2,
|
||||
"maintAmt": 277500.0
|
||||
},
|
||||
{
|
||||
"min": 5000000,
|
||||
"max": 30000000,
|
||||
"mmr": 0.5,
|
||||
"lev": 1,
|
||||
"minNotional": 5000000,
|
||||
"maxNotional": 30000000,
|
||||
"maintenanceMarginRate": 0.5,
|
||||
"maxLeverage": 1,
|
||||
"maintAmt": 1527500.0
|
||||
}
|
||||
],
|
||||
"ZEC/USDT": [
|
||||
{
|
||||
'min': 0,
|
||||
'max': 50000,
|
||||
'mmr': 0.01,
|
||||
'lev': 50,
|
||||
'minNotional': 0,
|
||||
'maxNotional': 50000,
|
||||
'maintenanceMarginRate': 0.01,
|
||||
'maxLeverage': 50,
|
||||
'maintAmt': 0.0
|
||||
},
|
||||
{
|
||||
'min': 50000,
|
||||
'max': 150000,
|
||||
'mmr': 0.025,
|
||||
'lev': 20,
|
||||
'minNotional': 50000,
|
||||
'maxNotional': 150000,
|
||||
'maintenanceMarginRate': 0.025,
|
||||
'maxLeverage': 20,
|
||||
'maintAmt': 750.0
|
||||
},
|
||||
{
|
||||
'min': 150000,
|
||||
'max': 250000,
|
||||
'mmr': 0.05,
|
||||
'lev': 10,
|
||||
'minNotional': 150000,
|
||||
'maxNotional': 250000,
|
||||
'maintenanceMarginRate': 0.05,
|
||||
'maxLeverage': 10,
|
||||
'maintAmt': 4500.0
|
||||
},
|
||||
{
|
||||
'min': 250000,
|
||||
'max': 500000,
|
||||
'mmr': 0.1,
|
||||
'lev': 5,
|
||||
'minNotional': 250000,
|
||||
'maxNotional': 500000,
|
||||
'maintenanceMarginRate': 0.1,
|
||||
'maxLeverage': 5,
|
||||
'maintAmt': 17000.0
|
||||
},
|
||||
{
|
||||
'min': 500000,
|
||||
'max': 1000000,
|
||||
'mmr': 0.125,
|
||||
'lev': 4,
|
||||
'minNotional': 500000,
|
||||
'maxNotional': 1000000,
|
||||
'maintenanceMarginRate': 0.125,
|
||||
'maxLeverage': 4,
|
||||
'maintAmt': 29500.0
|
||||
},
|
||||
{
|
||||
'min': 1000000,
|
||||
'max': 2000000,
|
||||
'mmr': 0.25,
|
||||
'lev': 2,
|
||||
'minNotional': 1000000,
|
||||
'maxNotional': 2000000,
|
||||
'maintenanceMarginRate': 0.25,
|
||||
'maxLeverage': 2,
|
||||
'maintAmt': 154500.0
|
||||
},
|
||||
{
|
||||
'min': 2000000,
|
||||
'max': 30000000,
|
||||
'mmr': 0.5,
|
||||
'lev': 1,
|
||||
'minNotional': 2000000,
|
||||
'maxNotional': 30000000,
|
||||
'maintenanceMarginRate': 0.5,
|
||||
'maxLeverage': 1,
|
||||
'maintAmt': 654500.0
|
||||
},
|
||||
]
|
||||
|
@@ -137,6 +137,10 @@ def exchange_futures(request, exchange_conf, class_mocker):
|
||||
'freqtrade.exchange.binance.Binance.fill_leverage_tiers')
|
||||
class_mocker.patch('freqtrade.exchange.exchange.Exchange.fetch_trading_fees')
|
||||
class_mocker.patch('freqtrade.exchange.okx.Okx.additional_exchange_init')
|
||||
class_mocker.patch('freqtrade.exchange.exchange.Exchange.load_cached_leverage_tiers',
|
||||
return_value=None)
|
||||
class_mocker.patch('freqtrade.exchange.exchange.Exchange.cache_leverage_tiers')
|
||||
|
||||
exchange = ExchangeResolver.load_exchange(
|
||||
request.param, exchange_conf, validate=True, load_leverage_tiers=True)
|
||||
|
||||
@@ -405,14 +409,14 @@ class TestCCXTExchange():
|
||||
assert (isinstance(futures_leverage, float) or isinstance(futures_leverage, int))
|
||||
assert futures_leverage >= 1.0
|
||||
|
||||
def test_ccxt__get_contract_size(self, exchange_futures):
|
||||
def test_ccxt_get_contract_size(self, exchange_futures):
|
||||
futures, futures_name = exchange_futures
|
||||
if futures:
|
||||
futures_pair = EXCHANGES[futures_name].get(
|
||||
'futures_pair',
|
||||
EXCHANGES[futures_name]['pair']
|
||||
)
|
||||
contract_size = futures._get_contract_size(futures_pair)
|
||||
contract_size = futures.get_contract_size(futures_pair)
|
||||
assert (isinstance(contract_size, float) or isinstance(contract_size, int))
|
||||
assert contract_size >= 0.0
|
||||
|
||||
@@ -464,6 +468,7 @@ class TestCCXTExchange():
|
||||
False,
|
||||
100,
|
||||
100,
|
||||
100,
|
||||
)
|
||||
assert (isinstance(liquidation_price, float))
|
||||
assert liquidation_price >= 0.0
|
||||
@@ -474,6 +479,7 @@ class TestCCXTExchange():
|
||||
False,
|
||||
100,
|
||||
100,
|
||||
100,
|
||||
)
|
||||
assert (isinstance(liquidation_price, float))
|
||||
assert liquidation_price >= 0.0
|
||||
|
@@ -2,7 +2,6 @@ import copy
|
||||
import logging
|
||||
from copy import deepcopy
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from math import isclose
|
||||
from random import randint
|
||||
from unittest.mock import MagicMock, Mock, PropertyMock, patch
|
||||
|
||||
@@ -181,11 +180,11 @@ def test_init_ccxt_kwargs(default_conf, mocker, caplog):
|
||||
assert log_has("Applying additional ccxt config: {'TestKWARG': 11, 'TestKWARG44': 11}", caplog)
|
||||
assert log_has(asynclogmsg, caplog)
|
||||
# Test additional headers case
|
||||
Exchange._headers = {'hello': 'world'}
|
||||
Exchange._ccxt_params = {'hello': 'world'}
|
||||
ex = Exchange(conf)
|
||||
|
||||
assert log_has("Applying additional ccxt config: {'TestKWARG': 11, 'TestKWARG44': 11}", caplog)
|
||||
assert ex._api.headers == {'hello': 'world'}
|
||||
assert ex._api.hello == 'world'
|
||||
assert ex._ccxt_config == {}
|
||||
Exchange._headers = {}
|
||||
|
||||
@@ -275,7 +274,7 @@ def test_validate_order_time_in_force(default_conf, mocker, caplog):
|
||||
ex.validate_order_time_in_force(tif2)
|
||||
|
||||
# Patch to see if this will pass if the values are in the ft dict
|
||||
ex._ft_has.update({"order_time_in_force": ["gtc", "fok", "ioc"]})
|
||||
ex._ft_has.update({"order_time_in_force": ["GTC", "FOK", "IOC"]})
|
||||
ex.validate_order_time_in_force(tif2)
|
||||
|
||||
|
||||
@@ -407,10 +406,10 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None:
|
||||
# min
|
||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss)
|
||||
expected_result = 2 * (1 + 0.05) / (1 - abs(stoploss))
|
||||
assert isclose(result, expected_result)
|
||||
assert pytest.approx(result) == expected_result
|
||||
# With Leverage
|
||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 1, stoploss, 3.0)
|
||||
assert isclose(result, expected_result / 3)
|
||||
assert pytest.approx(result) == expected_result / 3
|
||||
# max
|
||||
result = exchange.get_max_pair_stake_amount('ETH/BTC', 2)
|
||||
assert result == 10000
|
||||
@@ -426,10 +425,10 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None:
|
||||
)
|
||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss)
|
||||
expected_result = 2 * 2 * (1 + 0.05) / (1 - abs(stoploss))
|
||||
assert isclose(result, expected_result)
|
||||
assert pytest.approx(result) == expected_result
|
||||
# With Leverage
|
||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 5.0)
|
||||
assert isclose(result, expected_result / 5)
|
||||
assert pytest.approx(result) == expected_result / 5
|
||||
# max
|
||||
result = exchange.get_max_pair_stake_amount('ETH/BTC', 2)
|
||||
assert result == 20000
|
||||
@@ -445,10 +444,10 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None:
|
||||
)
|
||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss)
|
||||
expected_result = max(2, 2 * 2) * (1 + 0.05) / (1 - abs(stoploss))
|
||||
assert isclose(result, expected_result)
|
||||
assert pytest.approx(result) == expected_result
|
||||
# With Leverage
|
||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 10)
|
||||
assert isclose(result, expected_result / 10)
|
||||
assert pytest.approx(result) == expected_result / 10
|
||||
|
||||
# min amount and cost are set (amount is minial)
|
||||
markets["ETH/BTC"]["limits"] = {
|
||||
@@ -461,20 +460,20 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None:
|
||||
)
|
||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss)
|
||||
expected_result = max(8, 2 * 2) * (1 + 0.05) / (1 - abs(stoploss))
|
||||
assert isclose(result, expected_result)
|
||||
assert pytest.approx(result) == expected_result
|
||||
# With Leverage
|
||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, stoploss, 7.0)
|
||||
assert isclose(result, expected_result / 7.0)
|
||||
assert pytest.approx(result) == expected_result / 7.0
|
||||
# Max
|
||||
result = exchange.get_max_pair_stake_amount('ETH/BTC', 2)
|
||||
assert result == 1000
|
||||
|
||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -0.4)
|
||||
expected_result = max(8, 2 * 2) * 1.5
|
||||
assert isclose(result, expected_result)
|
||||
assert pytest.approx(result) == expected_result
|
||||
# With Leverage
|
||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -0.4, 8.0)
|
||||
assert isclose(result, expected_result / 8.0)
|
||||
assert pytest.approx(result) == expected_result / 8.0
|
||||
# Max
|
||||
result = exchange.get_max_pair_stake_amount('ETH/BTC', 2)
|
||||
assert result == 1000
|
||||
@@ -482,10 +481,10 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None:
|
||||
# Really big stoploss
|
||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1)
|
||||
expected_result = max(8, 2 * 2) * 1.5
|
||||
assert isclose(result, expected_result)
|
||||
assert pytest.approx(result) == expected_result
|
||||
# With Leverage
|
||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1, 12.0)
|
||||
assert isclose(result, expected_result / 12)
|
||||
assert pytest.approx(result) == expected_result / 12
|
||||
# Max
|
||||
result = exchange.get_max_pair_stake_amount('ETH/BTC', 2)
|
||||
assert result == 1000
|
||||
@@ -501,7 +500,7 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None:
|
||||
|
||||
# Contract size 0.01
|
||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1)
|
||||
assert isclose(result, expected_result * 0.01)
|
||||
assert pytest.approx(result) == expected_result * 0.01
|
||||
# Max
|
||||
result = exchange.get_max_pair_stake_amount('ETH/BTC', 2)
|
||||
assert result == 10
|
||||
@@ -513,7 +512,7 @@ def test__get_stake_amount_limit(mocker, default_conf) -> None:
|
||||
)
|
||||
# With Leverage, Contract size 10
|
||||
result = exchange.get_min_pair_stake_amount('ETH/BTC', 2, -1, 12.0)
|
||||
assert isclose(result, (expected_result / 12) * 10.0)
|
||||
assert pytest.approx(result) == (expected_result / 12) * 10.0
|
||||
# Max
|
||||
result = exchange.get_max_pair_stake_amount('ETH/BTC', 2)
|
||||
assert result == 10000
|
||||
@@ -1503,7 +1502,7 @@ def test_buy_considers_time_in_force(default_conf, mocker, exchange_name):
|
||||
assert api_mock.create_order.call_args[0][3] == 1
|
||||
assert api_mock.create_order.call_args[0][4] == 200
|
||||
assert "timeInForce" in api_mock.create_order.call_args[0][5]
|
||||
assert api_mock.create_order.call_args[0][5]["timeInForce"] == time_in_force
|
||||
assert api_mock.create_order.call_args[0][5]["timeInForce"] == time_in_force.upper()
|
||||
|
||||
order_type = 'market'
|
||||
time_in_force = 'ioc'
|
||||
@@ -1642,10 +1641,10 @@ def test_sell_considers_time_in_force(default_conf, mocker, exchange_name):
|
||||
assert api_mock.create_order.call_args[0][3] == 1
|
||||
assert api_mock.create_order.call_args[0][4] == 200
|
||||
assert "timeInForce" in api_mock.create_order.call_args[0][5]
|
||||
assert api_mock.create_order.call_args[0][5]["timeInForce"] == time_in_force
|
||||
assert api_mock.create_order.call_args[0][5]["timeInForce"] == time_in_force.upper()
|
||||
|
||||
order_type = 'market'
|
||||
time_in_force = 'ioc'
|
||||
time_in_force = 'IOC'
|
||||
order = exchange.create_order(pair='ETH/BTC', ordertype=order_type, side="sell",
|
||||
amount=1, rate=200, leverage=1.0,
|
||||
time_in_force=time_in_force)
|
||||
@@ -2352,10 +2351,11 @@ def test_fetch_l2_order_book(default_conf, mocker, order_book_l2, exchange_name)
|
||||
order_book = exchange.fetch_l2_order_book(pair='ETH/BTC', limit=val)
|
||||
assert api_mock.fetch_l2_order_book.call_args_list[0][0][0] == 'ETH/BTC'
|
||||
# Not all exchanges support all limits for orderbook
|
||||
if not exchange._ft_has['l2_limit_range'] or val in exchange._ft_has['l2_limit_range']:
|
||||
if (not exchange.get_option('l2_limit_range')
|
||||
or val in exchange.get_option('l2_limit_range')):
|
||||
assert api_mock.fetch_l2_order_book.call_args_list[0][0][1] == val
|
||||
else:
|
||||
next_limit = exchange.get_next_limit_in_list(val, exchange._ft_has['l2_limit_range'])
|
||||
next_limit = exchange.get_next_limit_in_list(val, exchange.get_option('l2_limit_range'))
|
||||
assert api_mock.fetch_l2_order_book.call_args_list[0][0][1] == next_limit
|
||||
|
||||
|
||||
@@ -3238,7 +3238,7 @@ def test_get_trades_for_order(default_conf, mocker, exchange_name, trading_mode,
|
||||
orders = exchange.get_trades_for_order(order_id, 'ETH/USDT:USDT', since)
|
||||
assert len(orders) == 1
|
||||
assert orders[0]['price'] == 165
|
||||
assert isclose(orders[0]['amount'], amount)
|
||||
assert pytest.approx(orders[0]['amount']) == amount
|
||||
assert api_mock.fetch_my_trades.call_count == 1
|
||||
# since argument should be
|
||||
assert isinstance(api_mock.fetch_my_trades.call_args[0][1], int)
|
||||
@@ -3311,16 +3311,16 @@ def test_merge_ft_has_dict(default_conf, mocker):
|
||||
|
||||
ex = Kraken(default_conf)
|
||||
assert ex._ft_has != Exchange._ft_has_default
|
||||
assert ex._ft_has['trades_pagination'] == 'id'
|
||||
assert ex._ft_has['trades_pagination_arg'] == 'since'
|
||||
assert ex.get_option('trades_pagination') == 'id'
|
||||
assert ex.get_option('trades_pagination_arg') == 'since'
|
||||
|
||||
# Binance defines different values
|
||||
ex = Binance(default_conf)
|
||||
assert ex._ft_has != Exchange._ft_has_default
|
||||
assert ex._ft_has['stoploss_on_exchange']
|
||||
assert ex._ft_has['order_time_in_force'] == ['gtc', 'fok', 'ioc']
|
||||
assert ex._ft_has['trades_pagination'] == 'id'
|
||||
assert ex._ft_has['trades_pagination_arg'] == 'fromId'
|
||||
assert ex.get_option('stoploss_on_exchange')
|
||||
assert ex.get_option('order_time_in_force') == ['GTC', 'FOK', 'IOC']
|
||||
assert ex.get_option('trades_pagination') == 'id'
|
||||
assert ex.get_option('trades_pagination_arg') == 'fromId'
|
||||
|
||||
conf = copy.deepcopy(default_conf)
|
||||
conf['exchange']['_ft_has_params'] = {"DeadBeef": 20,
|
||||
@@ -3775,8 +3775,8 @@ def test__get_funding_fees_from_exchange(default_conf, mocker, exchange_name):
|
||||
since=unix_time
|
||||
)
|
||||
|
||||
assert (isclose(expected_fees, fees_from_datetime))
|
||||
assert (isclose(expected_fees, fees_from_unix_time))
|
||||
assert pytest.approx(expected_fees) == fees_from_datetime
|
||||
assert pytest.approx(expected_fees) == fees_from_unix_time
|
||||
|
||||
ccxt_exceptionhandlers(
|
||||
mocker,
|
||||
@@ -4088,66 +4088,6 @@ def test_combine_funding_and_mark(
|
||||
assert len(df) == 0
|
||||
|
||||
|
||||
def test_get_or_calculate_liquidation_price(mocker, default_conf):
|
||||
|
||||
api_mock = MagicMock()
|
||||
positions = [
|
||||
{
|
||||
'info': {},
|
||||
'symbol': 'NEAR/USDT:USDT',
|
||||
'timestamp': 1642164737148,
|
||||
'datetime': '2022-01-14T12:52:17.148Z',
|
||||
'initialMargin': 1.51072,
|
||||
'initialMarginPercentage': 0.1,
|
||||
'maintenanceMargin': 0.38916147,
|
||||
'maintenanceMarginPercentage': 0.025,
|
||||
'entryPrice': 18.884,
|
||||
'notional': 15.1072,
|
||||
'leverage': 9.97,
|
||||
'unrealizedPnl': 0.0048,
|
||||
'contracts': 8,
|
||||
'contractSize': 0.1,
|
||||
'marginRatio': None,
|
||||
'liquidationPrice': 17.47,
|
||||
'markPrice': 18.89,
|
||||
'margin_mode': 1.52549075,
|
||||
'marginType': 'isolated',
|
||||
'side': 'buy',
|
||||
'percentage': 0.003177292946409658
|
||||
}
|
||||
]
|
||||
api_mock.fetch_positions = MagicMock(return_value=positions)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
exchange_has=MagicMock(return_value=True),
|
||||
)
|
||||
default_conf['dry_run'] = False
|
||||
default_conf['trading_mode'] = 'futures'
|
||||
default_conf['margin_mode'] = 'isolated'
|
||||
default_conf['liquidation_buffer'] = 0.0
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
liq_price = exchange.get_or_calculate_liquidation_price(
|
||||
pair='NEAR/USDT:USDT',
|
||||
open_rate=18.884,
|
||||
is_short=False,
|
||||
position=0.8,
|
||||
wallet_balance=0.8,
|
||||
)
|
||||
assert liq_price == 17.47
|
||||
|
||||
default_conf['liquidation_buffer'] = 0.05
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
liq_price = exchange.get_or_calculate_liquidation_price(
|
||||
pair='NEAR/USDT:USDT',
|
||||
open_rate=18.884,
|
||||
is_short=False,
|
||||
position=0.8,
|
||||
wallet_balance=0.8,
|
||||
)
|
||||
assert liq_price == 17.540699999999998
|
||||
|
||||
|
||||
@pytest.mark.parametrize('exchange,rate_start,rate_end,d1,d2,amount,expected_fees', [
|
||||
('binance', 0, 2, "2021-09-01 01:00:00", "2021-09-01 04:00:00", 30.0, 0.0),
|
||||
('binance', 0, 2, "2021-09-01 00:00:00", "2021-09-01 08:00:00", 30.0, -0.00091409999),
|
||||
@@ -4287,7 +4227,7 @@ def test__fetch_and_calculate_funding_fees_datetime_called(
|
||||
('XLTCUSDT', 0.01, 'futures'),
|
||||
('ETH/USDT:USDT', 10, 'futures')
|
||||
])
|
||||
def test__get_contract_size(mocker, default_conf, pair, expected_size, trading_mode):
|
||||
def est__get_contract_size(mocker, default_conf, pair, expected_size, trading_mode):
|
||||
api_mock = MagicMock()
|
||||
default_conf['trading_mode'] = trading_mode
|
||||
default_conf['margin_mode'] = 'isolated'
|
||||
@@ -4306,7 +4246,7 @@ def test__get_contract_size(mocker, default_conf, pair, expected_size, trading_m
|
||||
'contractSize': '10',
|
||||
}
|
||||
})
|
||||
size = exchange._get_contract_size(pair)
|
||||
size = exchange.get_contract_size(pair)
|
||||
assert expected_size == size
|
||||
|
||||
|
||||
@@ -4538,11 +4478,12 @@ def test_liquidation_price_is_none(
|
||||
default_conf['trading_mode'] = trading_mode
|
||||
default_conf['margin_mode'] = margin_mode
|
||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||
assert exchange.get_or_calculate_liquidation_price(
|
||||
assert exchange.get_liquidation_price(
|
||||
pair='DOGE/USDT',
|
||||
open_rate=open_rate,
|
||||
is_short=is_short,
|
||||
position=71200.81144,
|
||||
amount=71200.81144,
|
||||
stake_amount=open_rate * 71200.81144,
|
||||
wallet_balance=-56354.57,
|
||||
mm_ex_1=0.10,
|
||||
upnl_ex_1=0.0
|
||||
@@ -4551,7 +4492,7 @@ def test_liquidation_price_is_none(
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
'exchange_name, is_short, trading_mode, margin_mode, wallet_balance, '
|
||||
'mm_ex_1, upnl_ex_1, maintenance_amt, position, open_rate, '
|
||||
'mm_ex_1, upnl_ex_1, maintenance_amt, amount, open_rate, '
|
||||
'mm_ratio, expected',
|
||||
[
|
||||
("binance", False, 'futures', 'isolated', 1535443.01, 0.0,
|
||||
@@ -4565,22 +4506,23 @@ def test_liquidation_price_is_none(
|
||||
])
|
||||
def test_liquidation_price(
|
||||
mocker, default_conf, exchange_name, open_rate, is_short, trading_mode,
|
||||
margin_mode, wallet_balance, mm_ex_1, upnl_ex_1, maintenance_amt, position, mm_ratio, expected
|
||||
margin_mode, wallet_balance, mm_ex_1, upnl_ex_1, maintenance_amt, amount, mm_ratio, expected
|
||||
):
|
||||
default_conf['trading_mode'] = trading_mode
|
||||
default_conf['margin_mode'] = margin_mode
|
||||
default_conf['liquidation_buffer'] = 0.0
|
||||
exchange = get_patched_exchange(mocker, default_conf, id=exchange_name)
|
||||
exchange.get_maintenance_ratio_and_amt = MagicMock(return_value=(mm_ratio, maintenance_amt))
|
||||
assert isclose(round(exchange.get_or_calculate_liquidation_price(
|
||||
assert pytest.approx(round(exchange.get_liquidation_price(
|
||||
pair='DOGE/USDT',
|
||||
open_rate=open_rate,
|
||||
is_short=is_short,
|
||||
wallet_balance=wallet_balance,
|
||||
mm_ex_1=mm_ex_1,
|
||||
upnl_ex_1=upnl_ex_1,
|
||||
position=position,
|
||||
), 2), expected)
|
||||
amount=amount,
|
||||
stake_amount=open_rate * amount,
|
||||
), 2)) == expected
|
||||
|
||||
|
||||
def test_get_max_pair_stake_amount(
|
||||
@@ -4791,6 +4733,20 @@ def test_load_leverage_tiers(mocker, default_conf, leverage_tiers, exchange_name
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize('exchange_name', EXCHANGES)
|
||||
async def test_get_market_leverage_tiers(mocker, default_conf, exchange_name):
|
||||
default_conf['exchange']['name'] = exchange_name
|
||||
await async_ccxt_exception(
|
||||
mocker,
|
||||
default_conf,
|
||||
MagicMock(),
|
||||
"get_market_leverage_tiers",
|
||||
"fetch_market_leverage_tiers",
|
||||
symbol='BTC/USDT:USDT'
|
||||
)
|
||||
|
||||
|
||||
def test_parse_leverage_tier(mocker, default_conf):
|
||||
exchange = get_patched_exchange(mocker, default_conf)
|
||||
|
||||
@@ -4811,10 +4767,10 @@ def test_parse_leverage_tier(mocker, default_conf):
|
||||
}
|
||||
|
||||
assert exchange.parse_leverage_tier(tier) == {
|
||||
"min": 0,
|
||||
"max": 100000,
|
||||
"mmr": 0.025,
|
||||
"lev": 20,
|
||||
"minNotional": 0,
|
||||
"maxNotional": 100000,
|
||||
"maintenanceMarginRate": 0.025,
|
||||
"maxLeverage": 20,
|
||||
"maintAmt": 0.0,
|
||||
}
|
||||
|
||||
@@ -4840,10 +4796,10 @@ def test_parse_leverage_tier(mocker, default_conf):
|
||||
}
|
||||
|
||||
assert exchange.parse_leverage_tier(tier2) == {
|
||||
'min': 0,
|
||||
'max': 2000,
|
||||
'mmr': 0.01,
|
||||
'lev': 75,
|
||||
'minNotional': 0,
|
||||
'maxNotional': 2000,
|
||||
'maintenanceMarginRate': 0.01,
|
||||
'maxLeverage': 75,
|
||||
"maintAmt": None,
|
||||
}
|
||||
|
||||
@@ -4911,8 +4867,8 @@ def test_get_max_leverage_futures(default_conf, mocker, leverage_tiers):
|
||||
assert exchange.get_max_leverage("BNB/BUSD", 1.0) == 20.0
|
||||
assert exchange.get_max_leverage("BNB/USDT", 100.0) == 75.0
|
||||
assert exchange.get_max_leverage("BTC/USDT", 170.30) == 125.0
|
||||
assert isclose(exchange.get_max_leverage("BNB/BUSD", 99999.9), 5.000005)
|
||||
assert isclose(exchange.get_max_leverage("BNB/USDT", 1500), 33.333333333333333)
|
||||
assert pytest.approx(exchange.get_max_leverage("BNB/BUSD", 99999.9)) == 5.000005
|
||||
assert pytest.approx(exchange.get_max_leverage("BNB/USDT", 1500)) == 33.333333333333333
|
||||
assert exchange.get_max_leverage("BTC/USDT", 300000000) == 2.0
|
||||
assert exchange.get_max_leverage("BTC/USDT", 600000000) == 1.0 # Last tier
|
||||
|
||||
@@ -4935,7 +4891,7 @@ def test__get_params(mocker, default_conf, exchange_name):
|
||||
params1 = {'test': True}
|
||||
params2 = {
|
||||
'test': True,
|
||||
'timeInForce': 'ioc',
|
||||
'timeInForce': 'IOC',
|
||||
'reduceOnly': True,
|
||||
}
|
||||
|
||||
@@ -4950,7 +4906,7 @@ def test__get_params(mocker, default_conf, exchange_name):
|
||||
side="buy",
|
||||
ordertype='market',
|
||||
reduceOnly=False,
|
||||
time_in_force='gtc',
|
||||
time_in_force='GTC',
|
||||
leverage=1.0,
|
||||
) == params1
|
||||
|
||||
@@ -4958,7 +4914,7 @@ def test__get_params(mocker, default_conf, exchange_name):
|
||||
side="buy",
|
||||
ordertype='market',
|
||||
reduceOnly=False,
|
||||
time_in_force='ioc',
|
||||
time_in_force='IOC',
|
||||
leverage=1.0,
|
||||
) == params1
|
||||
|
||||
@@ -4966,7 +4922,7 @@ def test__get_params(mocker, default_conf, exchange_name):
|
||||
side="buy",
|
||||
ordertype='limit',
|
||||
reduceOnly=False,
|
||||
time_in_force='gtc',
|
||||
time_in_force='GTC',
|
||||
leverage=1.0,
|
||||
) == params1
|
||||
|
||||
@@ -4979,11 +4935,93 @@ def test__get_params(mocker, default_conf, exchange_name):
|
||||
side="buy",
|
||||
ordertype='limit',
|
||||
reduceOnly=True,
|
||||
time_in_force='ioc',
|
||||
time_in_force='IOC',
|
||||
leverage=3.0,
|
||||
) == params2
|
||||
|
||||
|
||||
def test_get_liquidation_price1(mocker, default_conf):
|
||||
|
||||
api_mock = MagicMock()
|
||||
positions = [
|
||||
{
|
||||
'info': {},
|
||||
'symbol': 'NEAR/USDT:USDT',
|
||||
'timestamp': 1642164737148,
|
||||
'datetime': '2022-01-14T12:52:17.148Z',
|
||||
'initialMargin': 1.51072,
|
||||
'initialMarginPercentage': 0.1,
|
||||
'maintenanceMargin': 0.38916147,
|
||||
'maintenanceMarginPercentage': 0.025,
|
||||
'entryPrice': 18.884,
|
||||
'notional': 15.1072,
|
||||
'leverage': 9.97,
|
||||
'unrealizedPnl': 0.0048,
|
||||
'contracts': 8,
|
||||
'contractSize': 0.1,
|
||||
'marginRatio': None,
|
||||
'liquidationPrice': 17.47,
|
||||
'markPrice': 18.89,
|
||||
'margin_mode': 1.52549075,
|
||||
'marginType': 'isolated',
|
||||
'side': 'buy',
|
||||
'percentage': 0.003177292946409658
|
||||
}
|
||||
]
|
||||
api_mock.fetch_positions = MagicMock(return_value=positions)
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
exchange_has=MagicMock(return_value=True),
|
||||
)
|
||||
default_conf['dry_run'] = False
|
||||
default_conf['trading_mode'] = 'futures'
|
||||
default_conf['margin_mode'] = 'isolated'
|
||||
default_conf['liquidation_buffer'] = 0.0
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
liq_price = exchange.get_liquidation_price(
|
||||
pair='NEAR/USDT:USDT',
|
||||
open_rate=18.884,
|
||||
is_short=False,
|
||||
amount=0.8,
|
||||
stake_amount=18.884 * 0.8,
|
||||
)
|
||||
assert liq_price == 17.47
|
||||
|
||||
default_conf['liquidation_buffer'] = 0.05
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
liq_price = exchange.get_liquidation_price(
|
||||
pair='NEAR/USDT:USDT',
|
||||
open_rate=18.884,
|
||||
is_short=False,
|
||||
amount=0.8,
|
||||
stake_amount=18.884 * 0.8,
|
||||
)
|
||||
assert liq_price == 17.540699999999998
|
||||
|
||||
api_mock.fetch_positions = MagicMock(return_value=[])
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
liq_price = exchange.get_liquidation_price(
|
||||
pair='NEAR/USDT:USDT',
|
||||
open_rate=18.884,
|
||||
is_short=False,
|
||||
amount=0.8,
|
||||
stake_amount=18.884 * 0.8,
|
||||
)
|
||||
assert liq_price is None
|
||||
default_conf['trading_mode'] = 'margin'
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
with pytest.raises(OperationalException, match=r'.*does not support .* margin'):
|
||||
exchange.get_liquidation_price(
|
||||
pair='NEAR/USDT:USDT',
|
||||
open_rate=18.884,
|
||||
is_short=False,
|
||||
amount=0.8,
|
||||
stake_amount=18.884 * 0.8,
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize('liquidation_buffer', [0.0, 0.05])
|
||||
@pytest.mark.parametrize(
|
||||
"is_short,trading_mode,exchange_name,margin_mode,leverage,open_rate,amount,expected_liq", [
|
||||
@@ -4997,22 +5035,22 @@ def test__get_params(mocker, default_conf, exchange_name):
|
||||
(True, 'futures', 'binance', 'isolated', 5.0, 10.0, 1.0, 11.89108910891089),
|
||||
(True, 'futures', 'binance', 'isolated', 3.0, 10.0, 1.0, 13.211221122079207),
|
||||
(True, 'futures', 'binance', 'isolated', 5.0, 8.0, 1.0, 9.514851485148514),
|
||||
(True, 'futures', 'binance', 'isolated', 5.0, 10.0, 0.6, 12.557755775577558),
|
||||
(True, 'futures', 'binance', 'isolated', 5.0, 10.0, 0.6, 11.897689768976898),
|
||||
# Binance, long
|
||||
(False, 'futures', 'binance', 'isolated', 5, 10, 1.0, 8.070707070707071),
|
||||
(False, 'futures', 'binance', 'isolated', 5, 8, 1.0, 6.454545454545454),
|
||||
(False, 'futures', 'binance', 'isolated', 3, 10, 1.0, 6.717171717171718),
|
||||
(False, 'futures', 'binance', 'isolated', 5, 10, 0.6, 7.39057239057239),
|
||||
(False, 'futures', 'binance', 'isolated', 3, 10, 1.0, 6.723905723905723),
|
||||
(False, 'futures', 'binance', 'isolated', 5, 10, 0.6, 8.063973063973064),
|
||||
# Gateio/okx, short
|
||||
(True, 'futures', 'gateio', 'isolated', 5, 10, 1.0, 11.87413417771621),
|
||||
(True, 'futures', 'gateio', 'isolated', 5, 10, 2.0, 11.87413417771621),
|
||||
(True, 'futures', 'gateio', 'isolated', 3, 10, 1.0, 13.476180850346978),
|
||||
(True, 'futures', 'gateio', 'isolated', 3, 10, 1.0, 13.193482419684678),
|
||||
(True, 'futures', 'gateio', 'isolated', 5, 8, 1.0, 9.499307342172967),
|
||||
(True, 'futures', 'okx', 'isolated', 3, 10, 1.0, 13.193482419684678),
|
||||
# Gateio/okx, long
|
||||
(False, 'futures', 'gateio', 'isolated', 5.0, 10.0, 1.0, 8.085708510208207),
|
||||
(False, 'futures', 'gateio', 'isolated', 3.0, 10.0, 1.0, 6.738090425173506),
|
||||
# (True, 'futures', 'okx', 'isolated', 11.87413417771621),
|
||||
# (False, 'futures', 'okx', 'isolated', 8.085708510208207),
|
||||
(False, 'futures', 'okx', 'isolated', 3.0, 10.0, 1.0, 6.738090425173506),
|
||||
]
|
||||
)
|
||||
def test_get_liquidation_price(
|
||||
@@ -5085,7 +5123,7 @@ def test_get_liquidation_price(
|
||||
default_conf_usdt['exchange']['name'] = exchange_name
|
||||
default_conf_usdt['margin_mode'] = margin_mode
|
||||
mocker.patch('freqtrade.exchange.Gateio.validate_ordertypes')
|
||||
exchange = get_patched_exchange(mocker, default_conf_usdt)
|
||||
exchange = get_patched_exchange(mocker, default_conf_usdt, id=exchange_name)
|
||||
|
||||
exchange.get_maintenance_ratio_and_amt = MagicMock(return_value=(0.01, 0.01))
|
||||
exchange.name = exchange_name
|
||||
@@ -5096,7 +5134,9 @@ def test_get_liquidation_price(
|
||||
pair='ETH/USDT:USDT',
|
||||
open_rate=open_rate,
|
||||
amount=amount,
|
||||
leverage=leverage,
|
||||
stake_amount=amount * open_rate / leverage,
|
||||
wallet_balance=amount * open_rate / leverage,
|
||||
# leverage=leverage,
|
||||
is_short=is_short,
|
||||
)
|
||||
if expected_liq is None:
|
||||
@@ -5104,7 +5144,7 @@ def test_get_liquidation_price(
|
||||
else:
|
||||
buffer_amount = liquidation_buffer * abs(open_rate - expected_liq)
|
||||
expected_liq = expected_liq - buffer_amount if is_short else expected_liq + buffer_amount
|
||||
isclose(expected_liq, liq)
|
||||
assert pytest.approx(expected_liq) == liq
|
||||
|
||||
|
||||
@pytest.mark.parametrize('contract_size,order_amount', [
|
||||
@@ -5131,7 +5171,7 @@ def test_stoploss_contract_size(mocker, default_conf, contract_size, order_amoun
|
||||
mocker.patch('freqtrade.exchange.Exchange.price_to_precision', lambda s, x, y: y)
|
||||
|
||||
exchange = get_patched_exchange(mocker, default_conf, api_mock)
|
||||
exchange._get_contract_size = MagicMock(return_value=contract_size)
|
||||
exchange.get_contract_size = MagicMock(return_value=contract_size)
|
||||
|
||||
api_mock.create_order.reset_mock()
|
||||
order = exchange.stoploss(
|
||||
|
@@ -50,7 +50,7 @@ def test_buy_kraken_trading_agreement(default_conf, mocker):
|
||||
assert api_mock.create_order.call_args[0][2] == 'buy'
|
||||
assert api_mock.create_order.call_args[0][3] == 1
|
||||
assert api_mock.create_order.call_args[0][4] == 200
|
||||
assert api_mock.create_order.call_args[0][5] == {'timeInForce': 'ioc',
|
||||
assert api_mock.create_order.call_args[0][5] == {'timeInForce': 'IOC',
|
||||
'trading_agreement': 'agree'}
|
||||
|
||||
|
||||
|
@@ -1,4 +1,5 @@
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, PropertyMock
|
||||
|
||||
import pytest
|
||||
@@ -6,7 +7,7 @@ import pytest
|
||||
from freqtrade.enums import MarginMode, TradingMode
|
||||
from freqtrade.enums.candletype import CandleType
|
||||
from freqtrade.exchange.exchange import timeframe_to_minutes
|
||||
from tests.conftest import get_mock_coro, get_patched_exchange
|
||||
from tests.conftest import get_mock_coro, get_patched_exchange, log_has
|
||||
from tests.exchange.test_exchange import ccxt_exceptionhandlers
|
||||
|
||||
|
||||
@@ -267,7 +268,10 @@ def test_additional_exchange_init_okx(default_conf, mocker):
|
||||
"additional_exchange_init", "fetch_accounts")
|
||||
|
||||
|
||||
def test_load_leverage_tiers_okx(default_conf, mocker, markets):
|
||||
def test_load_leverage_tiers_okx(default_conf, mocker, markets, tmpdir, caplog, time_machine):
|
||||
|
||||
default_conf['datadir'] = Path(tmpdir)
|
||||
# fd_mock = mocker.patch('freqtrade.exchange.exchange.file_dump_json')
|
||||
api_mock = MagicMock()
|
||||
type(api_mock).has = PropertyMock(return_value={
|
||||
'fetchLeverageTiers': False,
|
||||
@@ -410,48 +414,66 @@ def test_load_leverage_tiers_okx(default_conf, mocker, markets):
|
||||
assert exchange._leverage_tiers == {
|
||||
'ADA/USDT:USDT': [
|
||||
{
|
||||
'min': 0,
|
||||
'max': 500,
|
||||
'mmr': 0.02,
|
||||
'lev': 75,
|
||||
'minNotional': 0,
|
||||
'maxNotional': 500,
|
||||
'maintenanceMarginRate': 0.02,
|
||||
'maxLeverage': 75,
|
||||
'maintAmt': None
|
||||
},
|
||||
{
|
||||
'min': 501,
|
||||
'max': 1000,
|
||||
'mmr': 0.025,
|
||||
'lev': 50,
|
||||
'minNotional': 501,
|
||||
'maxNotional': 1000,
|
||||
'maintenanceMarginRate': 0.025,
|
||||
'maxLeverage': 50,
|
||||
'maintAmt': None
|
||||
},
|
||||
{
|
||||
'min': 1001,
|
||||
'max': 2000,
|
||||
'mmr': 0.03,
|
||||
'lev': 20,
|
||||
'minNotional': 1001,
|
||||
'maxNotional': 2000,
|
||||
'maintenanceMarginRate': 0.03,
|
||||
'maxLeverage': 20,
|
||||
'maintAmt': None
|
||||
},
|
||||
],
|
||||
'ETH/USDT:USDT': [
|
||||
{
|
||||
'min': 0,
|
||||
'max': 2000,
|
||||
'mmr': 0.01,
|
||||
'lev': 75,
|
||||
'minNotional': 0,
|
||||
'maxNotional': 2000,
|
||||
'maintenanceMarginRate': 0.01,
|
||||
'maxLeverage': 75,
|
||||
'maintAmt': None
|
||||
},
|
||||
{
|
||||
'min': 2001,
|
||||
'max': 4000,
|
||||
'mmr': 0.015,
|
||||
'lev': 50,
|
||||
'minNotional': 2001,
|
||||
'maxNotional': 4000,
|
||||
'maintenanceMarginRate': 0.015,
|
||||
'maxLeverage': 50,
|
||||
'maintAmt': None
|
||||
},
|
||||
{
|
||||
'min': 4001,
|
||||
'max': 8000,
|
||||
'mmr': 0.02,
|
||||
'lev': 20,
|
||||
'minNotional': 4001,
|
||||
'maxNotional': 8000,
|
||||
'maintenanceMarginRate': 0.02,
|
||||
'maxLeverage': 20,
|
||||
'maintAmt': None
|
||||
},
|
||||
],
|
||||
}
|
||||
filename = (default_conf['datadir'] /
|
||||
f"futures/leverage_tiers_{default_conf['stake_currency']}.json")
|
||||
assert filename.is_file()
|
||||
|
||||
logmsg = 'Cached leverage tiers are outdated. Will update.'
|
||||
assert not log_has(logmsg, caplog)
|
||||
|
||||
api_mock.fetch_market_leverage_tiers.reset_mock()
|
||||
|
||||
exchange.load_leverage_tiers()
|
||||
assert not log_has(logmsg, caplog)
|
||||
|
||||
api_mock.fetch_market_leverage_tiers.call_count == 0
|
||||
# 2 day passes ...
|
||||
time_machine.move_to(datetime.now() + timedelta(days=2))
|
||||
exchange.load_leverage_tiers()
|
||||
|
||||
assert log_has(logmsg, caplog)
|
||||
|
@@ -1,5 +1,6 @@
|
||||
from copy import deepcopy
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import pytest
|
||||
|
||||
@@ -80,6 +81,51 @@ def get_patched_freqaimodel(mocker, freqaiconf):
|
||||
return freqaimodel
|
||||
|
||||
|
||||
def make_data_dictionary(mocker, freqai_conf):
|
||||
freqai_conf.update({"timerange": "20180110-20180130"})
|
||||
|
||||
strategy = get_patched_freqai_strategy(mocker, freqai_conf)
|
||||
exchange = get_patched_exchange(mocker, freqai_conf)
|
||||
strategy.dp = DataProvider(freqai_conf, exchange)
|
||||
strategy.freqai_info = freqai_conf.get("freqai", {})
|
||||
freqai = strategy.freqai
|
||||
freqai.live = True
|
||||
freqai.dk = FreqaiDataKitchen(freqai_conf)
|
||||
freqai.dk.pair = "ADA/BTC"
|
||||
timerange = TimeRange.parse_timerange("20180110-20180130")
|
||||
freqai.dd.load_all_pair_histories(timerange, freqai.dk)
|
||||
|
||||
freqai.dd.pair_dict = MagicMock()
|
||||
|
||||
data_load_timerange = TimeRange.parse_timerange("20180110-20180130")
|
||||
new_timerange = TimeRange.parse_timerange("20180120-20180130")
|
||||
|
||||
corr_dataframes, base_dataframes = freqai.dd.get_base_and_corr_dataframes(
|
||||
data_load_timerange, freqai.dk.pair, freqai.dk
|
||||
)
|
||||
|
||||
unfiltered_dataframe = freqai.dk.use_strategy_to_populate_indicators(
|
||||
strategy, corr_dataframes, base_dataframes, freqai.dk.pair
|
||||
)
|
||||
|
||||
unfiltered_dataframe = freqai.dk.slice_dataframe(new_timerange, unfiltered_dataframe)
|
||||
|
||||
freqai.dk.find_features(unfiltered_dataframe)
|
||||
|
||||
features_filtered, labels_filtered = freqai.dk.filter_features(
|
||||
unfiltered_dataframe,
|
||||
freqai.dk.training_features_list,
|
||||
freqai.dk.label_list,
|
||||
training_filter=True,
|
||||
)
|
||||
|
||||
data_dictionary = freqai.dk.make_train_test_datasets(features_filtered, labels_filtered)
|
||||
|
||||
data_dictionary = freqai.dk.normalize_data(data_dictionary)
|
||||
|
||||
return freqai
|
||||
|
||||
|
||||
def get_freqai_live_analyzed_dataframe(mocker, freqaiconf):
|
||||
strategy = get_patched_freqai_strategy(mocker, freqaiconf)
|
||||
exchange = get_patched_exchange(mocker, freqaiconf)
|
||||
|
@@ -5,7 +5,8 @@ from pathlib import Path
|
||||
import pytest
|
||||
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from tests.freqai.conftest import get_patched_data_kitchen
|
||||
from tests.conftest import log_has_re
|
||||
from tests.freqai.conftest import get_patched_data_kitchen, make_data_dictionary
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
@@ -66,3 +67,30 @@ def test_check_if_model_expired(mocker, freqai_conf, timestamp, expected):
|
||||
dk = get_patched_data_kitchen(mocker, freqai_conf)
|
||||
assert dk.check_if_model_expired(timestamp) == expected
|
||||
shutil.rmtree(Path(dk.full_path))
|
||||
|
||||
|
||||
def test_use_DBSCAN_to_remove_outliers(mocker, freqai_conf, caplog):
|
||||
freqai = make_data_dictionary(mocker, freqai_conf)
|
||||
# freqai_conf['freqai']['feature_parameters'].update({"outlier_protection_percentage": 1})
|
||||
freqai.dk.use_DBSCAN_to_remove_outliers(predict=False)
|
||||
assert log_has_re(
|
||||
"DBSCAN found eps of 2.42.",
|
||||
caplog,
|
||||
)
|
||||
|
||||
|
||||
def test_compute_distances(mocker, freqai_conf):
|
||||
freqai = make_data_dictionary(mocker, freqai_conf)
|
||||
freqai_conf['freqai']['feature_parameters'].update({"DI_threshold": 1})
|
||||
avg_mean_dist = freqai.dk.compute_distances()
|
||||
assert round(avg_mean_dist, 2) == 2.56
|
||||
|
||||
|
||||
def test_use_SVM_to_remove_outliers_and_outlier_protection(mocker, freqai_conf, caplog):
|
||||
freqai = make_data_dictionary(mocker, freqai_conf)
|
||||
freqai_conf['freqai']['feature_parameters'].update({"outlier_protection_percentage": 0.1})
|
||||
freqai.dk.use_SVM_to_remove_outliers(predict=False)
|
||||
assert log_has_re(
|
||||
"SVM detected 8.46%",
|
||||
caplog,
|
||||
)
|
||||
|
@@ -1,5 +1,3 @@
|
||||
from math import isclose
|
||||
|
||||
import pytest
|
||||
|
||||
from freqtrade.leverage import interest
|
||||
@@ -30,9 +28,9 @@ twentyfive_hours = FtPrecise(25.0)
|
||||
def test_interest(exchange, interest_rate, hours, expected):
|
||||
borrowed = FtPrecise(60.0)
|
||||
|
||||
assert isclose(interest(
|
||||
assert pytest.approx(float(interest(
|
||||
exchange_name=exchange,
|
||||
borrowed=borrowed,
|
||||
rate=FtPrecise(interest_rate),
|
||||
hours=hours
|
||||
), expected)
|
||||
))) == expected
|
||||
|
@@ -550,6 +550,7 @@ def test_backtest__enter_trade_futures(default_conf_usdt, fee, mocker) -> None:
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_leverage", return_value=100)
|
||||
mocker.patch("freqtrade.optimize.backtesting.price_to_precision", lambda p, *args: p)
|
||||
patch_exchange(mocker)
|
||||
default_conf_usdt['stake_amount'] = 300
|
||||
default_conf_usdt['max_open_trades'] = 2
|
||||
@@ -559,13 +560,13 @@ def test_backtest__enter_trade_futures(default_conf_usdt, fee, mocker) -> None:
|
||||
default_conf_usdt['exchange']['pair_whitelist'] = ['.*']
|
||||
backtesting = Backtesting(default_conf_usdt)
|
||||
backtesting._set_strategy(backtesting.strategylist[0])
|
||||
pair = 'UNITTEST/USDT:USDT'
|
||||
pair = 'ETH/USDT:USDT'
|
||||
row = [
|
||||
pd.Timestamp(year=2020, month=1, day=1, hour=5, minute=0),
|
||||
0.001, # Open
|
||||
0.0012, # High
|
||||
0.00099, # Low
|
||||
0.0011, # Close
|
||||
0.1, # Open
|
||||
0.12, # High
|
||||
0.099, # Low
|
||||
0.11, # Close
|
||||
1, # enter_long
|
||||
0, # exit_long
|
||||
1, # enter_short
|
||||
@@ -580,8 +581,8 @@ def test_backtest__enter_trade_futures(default_conf_usdt, fee, mocker) -> None:
|
||||
return_value=(0.01, 0.01))
|
||||
|
||||
# leverage = 5
|
||||
# ep1(trade.open_rate) = 0.001
|
||||
# position(trade.amount) = 1500000
|
||||
# ep1(trade.open_rate) = 0.1
|
||||
# position(trade.amount) = 15000
|
||||
# stake_amount = 300 -> wb = 300 / 5 = 60
|
||||
# mmr = 0.01
|
||||
# cum_b = 0.01
|
||||
@@ -591,26 +592,26 @@ def test_backtest__enter_trade_futures(default_conf_usdt, fee, mocker) -> None:
|
||||
# Binance, Long
|
||||
# liquidation_price
|
||||
# = ((wb + cum_b) - (side_1 * position * ep1)) / ((position * mmr_b) - (side_1 * position))
|
||||
# = ((300 + 0.01) - (1 * 1500000 * 0.001)) / ((1500000 * 0.01) - (1 * 1500000))
|
||||
# = ((300 + 0.01) - (1 * 15000 * 0.1)) / ((15000 * 0.01) - (1 * 15000))
|
||||
# = 0.0008080740740740741
|
||||
# freqtrade_liquidation_price = liq + (abs(open_rate - liq) * liq_buffer * side_1)
|
||||
# = 0.0008080740740740741 + ((0.001 - 0.0008080740740740741) * 0.05 * 1)
|
||||
# = 0.0008176703703703704
|
||||
# = 0.08080740740740741 + ((0.1 - 0.08080740740740741) * 0.05 * 1)
|
||||
# = 0.08176703703703704
|
||||
|
||||
trade = backtesting._enter_trade(pair, row=row, direction='long')
|
||||
assert pytest.approx(trade.liquidation_price) == 0.00081767037
|
||||
assert pytest.approx(trade.liquidation_price) == 0.081767037
|
||||
|
||||
# Binance, Short
|
||||
# liquidation_price
|
||||
# = ((wb + cum_b) - (side_1 * position * ep1)) / ((position * mmr_b) - (side_1 * position))
|
||||
# = ((300 + 0.01) - ((-1) * 1500000 * 0.001)) / ((1500000 * 0.01) - ((-1) * 1500000))
|
||||
# = ((300 + 0.01) - ((-1) * 15000 * 0.1)) / ((15000 * 0.01) - ((-1) * 15000))
|
||||
# = 0.0011881254125412541
|
||||
# freqtrade_liquidation_price = liq + (abs(open_rate - liq) * liq_buffer * side_1)
|
||||
# = 0.0011881254125412541 + (abs(0.001 - 0.0011881254125412541) * 0.05 * -1)
|
||||
# = 0.0011787191419141915
|
||||
# = 0.11881254125412541 + (abs(0.1 - 0.11881254125412541) * 0.05 * -1)
|
||||
# = 0.11787191419141915
|
||||
|
||||
trade = backtesting._enter_trade(pair, row=row, direction='short')
|
||||
assert pytest.approx(trade.liquidation_price) == 0.0011787191
|
||||
assert pytest.approx(trade.liquidation_price) == 0.11787191
|
||||
|
||||
# Stake-amount too high!
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=600.0)
|
||||
|
@@ -18,6 +18,8 @@ from tests.conftest import patch_exchange
|
||||
def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) -> None:
|
||||
default_conf['use_exit_signal'] = False
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_fee', fee)
|
||||
mocker.patch('freqtrade.optimize.backtesting.amount_to_contract_precision',
|
||||
lambda x, *args, **kwargs: round(x, 8))
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_min_pair_stake_amount", return_value=0.00001)
|
||||
mocker.patch("freqtrade.exchange.Exchange.get_max_pair_stake_amount", return_value=float('inf'))
|
||||
patch_exchange(mocker)
|
||||
|
@@ -366,6 +366,9 @@ def test_VolumePairList_refresh_empty(mocker, markets_empty, whitelist_conf):
|
||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
|
||||
{"method": "PrecisionFilter"}],
|
||||
"BTC", ['ETH/BTC', 'TKN/BTC', 'LTC/BTC', 'XRP/BTC']),
|
||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
|
||||
{"method": "PrecisionFilter"}],
|
||||
"USDT", ['ETH/USDT', 'NANO/USDT']),
|
||||
# PriceFilter and VolumePairList
|
||||
([{"method": "VolumePairList", "number_assets": 5, "sort_key": "quoteVolume"},
|
||||
{"method": "PriceFilter", "low_price_ratio": 0.03}],
|
||||
|
@@ -67,6 +67,8 @@ def generate_mock_trade(pair: str, fee: float, is_open: bool,
|
||||
trade.close(open_rate * (2 - profit_rate if is_short else profit_rate))
|
||||
trade.exit_reason = exit_reason
|
||||
|
||||
Trade.query.session.add(trade)
|
||||
Trade.commit()
|
||||
return trade
|
||||
|
||||
|
||||
@@ -125,33 +127,33 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog, is_short):
|
||||
assert not log_has_re(message, caplog)
|
||||
caplog.clear()
|
||||
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=200, min_ago_close=30, is_short=is_short,
|
||||
))
|
||||
)
|
||||
|
||||
assert not freqtrade.protections.global_stop()
|
||||
assert not log_has_re(message, caplog)
|
||||
caplog.clear()
|
||||
# This trade does not count, as it's closed too long ago
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
'BCH/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=250, min_ago_close=100, is_short=is_short,
|
||||
))
|
||||
)
|
||||
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
'ETH/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=240, min_ago_close=30, is_short=is_short,
|
||||
))
|
||||
)
|
||||
# 3 Trades closed - but the 2nd has been closed too long ago.
|
||||
assert not freqtrade.protections.global_stop()
|
||||
assert not log_has_re(message, caplog)
|
||||
caplog.clear()
|
||||
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
'LTC/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=180, min_ago_close=30, is_short=is_short,
|
||||
))
|
||||
)
|
||||
|
||||
assert freqtrade.protections.global_stop()
|
||||
assert log_has_re(message, caplog)
|
||||
@@ -186,25 +188,25 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair
|
||||
assert not log_has_re(message, caplog)
|
||||
caplog.clear()
|
||||
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
pair, fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=200, min_ago_close=30, profit_rate=0.9, is_short=is_short
|
||||
))
|
||||
)
|
||||
|
||||
assert not freqtrade.protections.stop_per_pair(pair)
|
||||
assert not freqtrade.protections.global_stop()
|
||||
assert not log_has_re(message, caplog)
|
||||
caplog.clear()
|
||||
# This trade does not count, as it's closed too long ago
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
pair, fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=250, min_ago_close=100, profit_rate=0.9, is_short=is_short
|
||||
))
|
||||
)
|
||||
# Trade does not count for per pair stop as it's the wrong pair.
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
'ETH/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=240, min_ago_close=30, profit_rate=0.9, is_short=is_short
|
||||
))
|
||||
)
|
||||
# 3 Trades closed - but the 2nd has been closed too long ago.
|
||||
assert not freqtrade.protections.stop_per_pair(pair)
|
||||
assert freqtrade.protections.global_stop() != only_per_pair
|
||||
@@ -216,10 +218,10 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair
|
||||
caplog.clear()
|
||||
|
||||
# Trade does not count potentially, as it's in the wrong direction
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
pair, fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=150, min_ago_close=25, profit_rate=0.9, is_short=not is_short
|
||||
))
|
||||
)
|
||||
freqtrade.protections.stop_per_pair(pair)
|
||||
assert freqtrade.protections.global_stop() != only_per_pair
|
||||
assert PairLocks.is_pair_locked(pair, side=check_side) != (only_per_side and only_per_pair)
|
||||
@@ -231,10 +233,10 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair
|
||||
caplog.clear()
|
||||
|
||||
# 2nd Trade that counts with correct pair
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
pair, fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=180, min_ago_close=30, profit_rate=0.9, is_short=is_short
|
||||
))
|
||||
)
|
||||
|
||||
freqtrade.protections.stop_per_pair(pair)
|
||||
assert freqtrade.protections.global_stop() != only_per_pair
|
||||
@@ -259,20 +261,20 @@ def test_CooldownPeriod(mocker, default_conf, fee, caplog):
|
||||
assert not log_has_re(message, caplog)
|
||||
caplog.clear()
|
||||
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=200, min_ago_close=30,
|
||||
))
|
||||
)
|
||||
|
||||
assert not freqtrade.protections.global_stop()
|
||||
assert freqtrade.protections.stop_per_pair('XRP/BTC')
|
||||
assert PairLocks.is_pair_locked('XRP/BTC')
|
||||
assert not PairLocks.is_global_lock()
|
||||
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
'ETH/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value,
|
||||
min_ago_open=205, min_ago_close=35,
|
||||
))
|
||||
)
|
||||
|
||||
assert not freqtrade.protections.global_stop()
|
||||
assert not PairLocks.is_pair_locked('ETH/BTC')
|
||||
@@ -300,10 +302,10 @@ def test_LowProfitPairs(mocker, default_conf, fee, caplog, only_per_side):
|
||||
assert not log_has_re(message, caplog)
|
||||
caplog.clear()
|
||||
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=800, min_ago_close=450, profit_rate=0.9,
|
||||
))
|
||||
)
|
||||
|
||||
Trade.commit()
|
||||
# Not locked with 1 trade
|
||||
@@ -312,10 +314,10 @@ def test_LowProfitPairs(mocker, default_conf, fee, caplog, only_per_side):
|
||||
assert not PairLocks.is_pair_locked('XRP/BTC')
|
||||
assert not PairLocks.is_global_lock()
|
||||
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=200, min_ago_close=120, profit_rate=0.9,
|
||||
))
|
||||
)
|
||||
|
||||
Trade.commit()
|
||||
# Not locked with 1 trade (first trade is outside of lookback_period)
|
||||
@@ -325,19 +327,19 @@ def test_LowProfitPairs(mocker, default_conf, fee, caplog, only_per_side):
|
||||
assert not PairLocks.is_global_lock()
|
||||
|
||||
# Add positive trade
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value,
|
||||
min_ago_open=20, min_ago_close=10, profit_rate=1.15, is_short=True
|
||||
))
|
||||
)
|
||||
Trade.commit()
|
||||
assert freqtrade.protections.stop_per_pair('XRP/BTC') != only_per_side
|
||||
assert not PairLocks.is_pair_locked('XRP/BTC', side='*')
|
||||
assert PairLocks.is_pair_locked('XRP/BTC', side='long') == only_per_side
|
||||
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=110, min_ago_close=21, profit_rate=0.8,
|
||||
))
|
||||
)
|
||||
Trade.commit()
|
||||
|
||||
# Locks due to 2nd trade
|
||||
@@ -365,36 +367,38 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog):
|
||||
assert not freqtrade.protections.stop_per_pair('XRP/BTC')
|
||||
caplog.clear()
|
||||
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=1000, min_ago_close=900, profit_rate=1.1,
|
||||
))
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
)
|
||||
generate_mock_trade(
|
||||
'ETH/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=1000, min_ago_close=900, profit_rate=1.1,
|
||||
))
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
)
|
||||
generate_mock_trade(
|
||||
'NEO/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=1000, min_ago_close=900, profit_rate=1.1,
|
||||
))
|
||||
)
|
||||
Trade.commit()
|
||||
# No losing trade yet ... so max_drawdown will raise exception
|
||||
assert not freqtrade.protections.global_stop()
|
||||
assert not freqtrade.protections.stop_per_pair('XRP/BTC')
|
||||
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=500, min_ago_close=400, profit_rate=0.9,
|
||||
))
|
||||
)
|
||||
# Not locked with one trade
|
||||
assert not freqtrade.protections.global_stop()
|
||||
assert not freqtrade.protections.stop_per_pair('XRP/BTC')
|
||||
assert not PairLocks.is_pair_locked('XRP/BTC')
|
||||
assert not PairLocks.is_global_lock()
|
||||
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.STOP_LOSS.value,
|
||||
min_ago_open=1200, min_ago_close=1100, profit_rate=0.5,
|
||||
))
|
||||
)
|
||||
Trade.commit()
|
||||
|
||||
# Not locked with 1 trade (2nd trade is outside of lookback_period)
|
||||
assert not freqtrade.protections.global_stop()
|
||||
@@ -404,20 +408,22 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog):
|
||||
assert not log_has_re(message, caplog)
|
||||
|
||||
# Winning trade ... (should not lock, does not change drawdown!)
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value,
|
||||
min_ago_open=320, min_ago_close=410, profit_rate=1.5,
|
||||
))
|
||||
)
|
||||
Trade.commit()
|
||||
assert not freqtrade.protections.global_stop()
|
||||
assert not PairLocks.is_global_lock()
|
||||
|
||||
caplog.clear()
|
||||
|
||||
# Add additional negative trade, causing a loss of > 15%
|
||||
Trade.query.session.add(generate_mock_trade(
|
||||
generate_mock_trade(
|
||||
'XRP/BTC', fee.return_value, False, exit_reason=ExitType.ROI.value,
|
||||
min_ago_open=20, min_ago_close=10, profit_rate=0.8,
|
||||
))
|
||||
)
|
||||
Trade.commit()
|
||||
assert not freqtrade.protections.stop_per_pair('XRP/BTC')
|
||||
# local lock not supported
|
||||
assert not PairLocks.is_pair_locked('XRP/BTC')
|
||||
|
@@ -663,7 +663,7 @@ def test_rpc_stop(mocker, default_conf) -> None:
|
||||
assert freqtradebot.state == State.STOPPED
|
||||
|
||||
|
||||
def test_rpc_stopbuy(mocker, default_conf) -> None:
|
||||
def test_rpc_stopentry(mocker, default_conf) -> None:
|
||||
mocker.patch('freqtrade.rpc.telegram.Telegram', MagicMock())
|
||||
mocker.patch.multiple(
|
||||
'freqtrade.exchange.Exchange',
|
||||
@@ -676,8 +676,8 @@ def test_rpc_stopbuy(mocker, default_conf) -> None:
|
||||
freqtradebot.state = State.RUNNING
|
||||
|
||||
assert freqtradebot.config['max_open_trades'] != 0
|
||||
result = rpc._rpc_stopbuy()
|
||||
assert {'status': 'No more buy will occur from now. Run /reload_config to reset.'} == result
|
||||
result = rpc._rpc_stopentry()
|
||||
assert {'status': 'No more entries will occur from now. Run /reload_config to reset.'} == result
|
||||
assert freqtradebot.config['max_open_trades'] == 0
|
||||
|
||||
|
||||
|
@@ -422,13 +422,20 @@ def test_api_reloadconf(botclient):
|
||||
assert ftbot.state == State.RELOAD_CONFIG
|
||||
|
||||
|
||||
def test_api_stopbuy(botclient):
|
||||
def test_api_stopentry(botclient):
|
||||
ftbot, client = botclient
|
||||
assert ftbot.config['max_open_trades'] != 0
|
||||
|
||||
rc = client_post(client, f"{BASE_URI}/stopbuy")
|
||||
assert_response(rc)
|
||||
assert rc.json() == {'status': 'No more buy will occur from now. Run /reload_config to reset.'}
|
||||
assert rc.json() == {
|
||||
'status': 'No more entries will occur from now. Run /reload_config to reset.'}
|
||||
assert ftbot.config['max_open_trades'] == 0
|
||||
|
||||
rc = client_post(client, f"{BASE_URI}/stopentry")
|
||||
assert_response(rc)
|
||||
assert rc.json() == {
|
||||
'status': 'No more entries will occur from now. Run /reload_config to reset.'}
|
||||
assert ftbot.config['max_open_trades'] == 0
|
||||
|
||||
|
||||
|
@@ -103,7 +103,8 @@ def test_telegram_init(default_conf, mocker, caplog) -> None:
|
||||
"['stats'], ['daily'], ['weekly'], ['monthly'], "
|
||||
"['count'], ['locks'], ['unlock', 'delete_locks'], "
|
||||
"['reload_config', 'reload_conf'], ['show_config', 'show_conf'], "
|
||||
"['stopbuy'], ['whitelist'], ['blacklist'], ['blacklist_delete', 'bl_delete'], "
|
||||
"['stopbuy', 'stopentry'], ['whitelist'], ['blacklist'], "
|
||||
"['blacklist_delete', 'bl_delete'], "
|
||||
"['logs'], ['edge'], ['health'], ['help'], ['version']"
|
||||
"]")
|
||||
|
||||
@@ -896,10 +897,10 @@ def test_stopbuy_handle(default_conf, update, mocker) -> None:
|
||||
telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf)
|
||||
|
||||
assert freqtradebot.config['max_open_trades'] != 0
|
||||
telegram._stopbuy(update=update, context=MagicMock())
|
||||
telegram._stopentry(update=update, context=MagicMock())
|
||||
assert freqtradebot.config['max_open_trades'] == 0
|
||||
assert msg_mock.call_count == 1
|
||||
assert 'No more buy will occur from now. Run /reload_config to reset.' \
|
||||
assert 'No more entries will occur from now. Run /reload_config to reset.' \
|
||||
in msg_mock.call_args_list[0][0][0]
|
||||
|
||||
|
||||
|
@@ -12,7 +12,9 @@ from freqtrade.configuration import TimeRange
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.data.history import load_data
|
||||
from freqtrade.enums import ExitCheckTuple, ExitType, SignalDirection
|
||||
from freqtrade.enums.hyperoptstate import HyperoptState
|
||||
from freqtrade.exceptions import OperationalException, StrategyError
|
||||
from freqtrade.optimize.hyperopt_tools import HyperoptStateContainer
|
||||
from freqtrade.optimize.space import SKDecimal
|
||||
from freqtrade.persistence import PairLocks, Trade
|
||||
from freqtrade.resolvers import StrategyResolver
|
||||
@@ -859,7 +861,9 @@ def test_strategy_safe_wrapper_trade_copy(fee):
|
||||
|
||||
|
||||
def test_hyperopt_parameters():
|
||||
HyperoptStateContainer.set_state(HyperoptState.INDICATORS)
|
||||
from skopt.space import Categorical, Integer, Real
|
||||
|
||||
with pytest.raises(OperationalException, match=r"Name is determined.*"):
|
||||
IntParameter(low=0, high=5, default=1, name='hello')
|
||||
|
||||
@@ -937,6 +941,12 @@ def test_hyperopt_parameters():
|
||||
|
||||
assert list(boolpar.range) == [True, False]
|
||||
|
||||
HyperoptStateContainer.set_state(HyperoptState.OPTIMIZE)
|
||||
assert len(list(intpar.range)) == 1
|
||||
assert len(list(fltpar.range)) == 1
|
||||
assert len(list(catpar.range)) == 1
|
||||
assert len(list(boolpar.range)) == 1
|
||||
|
||||
|
||||
def test_auto_hyperopt_interface(default_conf):
|
||||
default_conf.update({'strategy': 'HyperoptableStrategyV2'})
|
||||
|
@@ -1,5 +1,3 @@
|
||||
from math import isclose
|
||||
|
||||
import numpy as np
|
||||
import pandas as pd
|
||||
import pytest
|
||||
@@ -165,7 +163,7 @@ def test_stoploss_from_open():
|
||||
or (side == 'short' and expected_stop_price < current_price)):
|
||||
assert stoploss == 0
|
||||
else:
|
||||
assert isclose(stop_price, expected_stop_price, rel_tol=0.00001)
|
||||
assert pytest.approx(stop_price) == expected_stop_price
|
||||
|
||||
|
||||
def test_stoploss_from_absolute():
|
||||
|
@@ -48,6 +48,10 @@ def test_search_all_strategies_with_failed():
|
||||
assert len([x for x in strategies if x['class'] is not None]) == 9
|
||||
assert len([x for x in strategies if x['class'] is None]) == 1
|
||||
|
||||
directory = Path(__file__).parent / "strats_nonexistingdir"
|
||||
strategies = StrategyResolver.search_all_objects(directory, enum_failed=True)
|
||||
assert len(strategies) == 0
|
||||
|
||||
|
||||
def test_load_strategy(default_conf, result):
|
||||
default_conf.update({'strategy': 'SampleStrategy',
|
||||
@@ -271,8 +275,8 @@ def test_strategy_override_order_tif(caplog, default_conf):
|
||||
caplog.set_level(logging.INFO)
|
||||
|
||||
order_time_in_force = {
|
||||
'entry': 'fok',
|
||||
'exit': 'gtc',
|
||||
'entry': 'FOK',
|
||||
'exit': 'GTC',
|
||||
}
|
||||
|
||||
default_conf.update({
|
||||
@@ -286,11 +290,11 @@ def test_strategy_override_order_tif(caplog, default_conf):
|
||||
assert strategy.order_time_in_force[method] == order_time_in_force[method]
|
||||
|
||||
assert log_has("Override strategy 'order_time_in_force' with value in config file:"
|
||||
" {'entry': 'fok', 'exit': 'gtc'}.", caplog)
|
||||
" {'entry': 'FOK', 'exit': 'GTC'}.", caplog)
|
||||
|
||||
default_conf.update({
|
||||
'strategy': CURRENT_TEST_STRATEGY,
|
||||
'order_time_in_force': {'entry': 'fok'}
|
||||
'order_time_in_force': {'entry': 'FOK'}
|
||||
})
|
||||
# Raise error for invalid configuration
|
||||
with pytest.raises(ImportError,
|
||||
|
@@ -973,17 +973,17 @@ def test_validate_time_in_force(default_conf, caplog) -> None:
|
||||
conf = deepcopy(default_conf)
|
||||
conf['order_time_in_force'] = {
|
||||
'buy': 'gtc',
|
||||
'sell': 'gtc',
|
||||
'sell': 'GTC',
|
||||
}
|
||||
validate_config_consistency(conf)
|
||||
assert log_has_re(r"DEPRECATED: Using 'buy' and 'sell' for time_in_force is.*", caplog)
|
||||
assert conf['order_time_in_force']['entry'] == 'gtc'
|
||||
assert conf['order_time_in_force']['exit'] == 'gtc'
|
||||
assert conf['order_time_in_force']['exit'] == 'GTC'
|
||||
|
||||
conf = deepcopy(default_conf)
|
||||
conf['order_time_in_force'] = {
|
||||
'buy': 'gtc',
|
||||
'sell': 'gtc',
|
||||
'buy': 'GTC',
|
||||
'sell': 'GTC',
|
||||
}
|
||||
conf['trading_mode'] = 'futures'
|
||||
with pytest.raises(OperationalException,
|
||||
|
@@ -473,8 +473,6 @@ def test_create_trade_no_signal(default_conf_usdt, fee, mocker) -> None:
|
||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||
patch_get_signal(freqtrade, enter_long=False, exit_long=False)
|
||||
|
||||
Trade.query = MagicMock()
|
||||
Trade.query.filter = MagicMock()
|
||||
assert not freqtrade.create_trade('ETH/USDT')
|
||||
|
||||
|
||||
@@ -677,6 +675,7 @@ def test_process_trade_no_whitelist_pair(default_conf_usdt, ticker_usdt, limit_b
|
||||
open_rate=0.001,
|
||||
exchange='binance',
|
||||
))
|
||||
Trade.commit()
|
||||
|
||||
assert pair not in freqtrade.active_pair_whitelist
|
||||
freqtrade.process()
|
||||
@@ -1052,8 +1051,6 @@ def test_add_stoploss_on_exchange(mocker, default_conf_usdt, limit_order, is_sho
|
||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
|
||||
mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=order)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
|
||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
|
||||
return_value=order['amount'])
|
||||
|
||||
stoploss = MagicMock(return_value={'id': 13434334})
|
||||
mocker.patch('freqtrade.exchange.Binance.stoploss', stoploss)
|
||||
@@ -1876,8 +1873,6 @@ def test_exit_positions(mocker, default_conf_usdt, limit_order, is_short, caplog
|
||||
mocker.patch('freqtrade.exchange.Exchange.fetch_order',
|
||||
return_value=limit_order[entry_side(is_short)])
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
|
||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
|
||||
return_value=limit_order[entry_side(is_short)]['amount'])
|
||||
|
||||
trade = MagicMock()
|
||||
trade.is_short = is_short
|
||||
@@ -1887,14 +1882,13 @@ def test_exit_positions(mocker, default_conf_usdt, limit_order, is_short, caplog
|
||||
n = freqtrade.exit_positions(trades)
|
||||
assert n == 0
|
||||
# Test amount not modified by fee-logic
|
||||
assert not log_has(
|
||||
'Applying fee to amount for Trade {} from 30.0 to 90.81'.format(trade), caplog
|
||||
)
|
||||
assert not log_has_re(r'Applying fee to amount for Trade .*', caplog)
|
||||
|
||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81)
|
||||
gra = mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=0.0)
|
||||
# test amount modified by fee-logic
|
||||
n = freqtrade.exit_positions(trades)
|
||||
assert n == 0
|
||||
assert gra.call_count == 0
|
||||
|
||||
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
@@ -1928,8 +1922,7 @@ def test_update_trade_state(mocker, default_conf_usdt, limit_order, is_short, ca
|
||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.handle_trade', MagicMock(return_value=True))
|
||||
mocker.patch('freqtrade.exchange.Exchange.fetch_order', return_value=order)
|
||||
mocker.patch('freqtrade.exchange.Exchange.get_trades_for_order', return_value=[])
|
||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount',
|
||||
return_value=order['amount'])
|
||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=0.0)
|
||||
order_id = order['id']
|
||||
|
||||
trade = Trade(
|
||||
@@ -1961,11 +1954,11 @@ def test_update_trade_state(mocker, default_conf_usdt, limit_order, is_short, ca
|
||||
assert trade.amount == order['amount']
|
||||
|
||||
trade.open_order_id = order_id
|
||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=90.81)
|
||||
assert trade.amount != 90.81
|
||||
mocker.patch('freqtrade.freqtradebot.FreqtradeBot.get_real_amount', return_value=0.01)
|
||||
assert trade.amount == 30.0
|
||||
# test amount modified by fee-logic
|
||||
freqtrade.update_trade_state(trade, order_id)
|
||||
assert trade.amount == 90.81
|
||||
assert trade.amount == 29.99
|
||||
assert trade.open_order_id is None
|
||||
|
||||
trade.is_open = True
|
||||
@@ -2414,6 +2407,7 @@ def test_manage_open_orders_entry_usercustom(
|
||||
open_trade.orders[0].side = 'sell' if is_short else 'buy'
|
||||
open_trade.orders[0].ft_order_side = 'sell' if is_short else 'buy'
|
||||
Trade.query.session.add(open_trade)
|
||||
Trade.commit()
|
||||
|
||||
# Ensure default is to return empty (so not mocked yet)
|
||||
freqtrade.manage_open_orders()
|
||||
@@ -2472,6 +2466,7 @@ def test_manage_open_orders_entry(
|
||||
|
||||
open_trade.is_short = is_short
|
||||
Trade.query.session.add(open_trade)
|
||||
Trade.commit()
|
||||
|
||||
freqtrade.strategy.check_entry_timeout = MagicMock(return_value=False)
|
||||
freqtrade.strategy.adjust_entry_price = MagicMock(return_value=1234)
|
||||
@@ -2509,6 +2504,7 @@ def test_adjust_entry_cancel(
|
||||
|
||||
open_trade.is_short = is_short
|
||||
Trade.query.session.add(open_trade)
|
||||
Trade.commit()
|
||||
|
||||
# Timeout to not interfere
|
||||
freqtrade.strategy.ft_check_timed_out = MagicMock(return_value=False)
|
||||
@@ -2549,6 +2545,7 @@ def test_adjust_entry_maintain_replace(
|
||||
|
||||
open_trade.is_short = is_short
|
||||
Trade.query.session.add(open_trade)
|
||||
Trade.commit()
|
||||
|
||||
# Timeout to not interfere
|
||||
freqtrade.strategy.ft_check_timed_out = MagicMock(return_value=False)
|
||||
@@ -2601,6 +2598,7 @@ def test_check_handle_cancelled_buy(
|
||||
open_trade.orders = []
|
||||
open_trade.is_short = is_short
|
||||
Trade.query.session.add(open_trade)
|
||||
Trade.commit()
|
||||
|
||||
# check it does cancel buy orders over the time limit
|
||||
freqtrade.manage_open_orders()
|
||||
@@ -2631,6 +2629,7 @@ def test_manage_open_orders_buy_exception(
|
||||
|
||||
open_trade.is_short = is_short
|
||||
Trade.query.session.add(open_trade)
|
||||
Trade.commit()
|
||||
|
||||
# check it does cancel buy orders over the time limit
|
||||
freqtrade.manage_open_orders()
|
||||
@@ -2672,6 +2671,7 @@ def test_manage_open_orders_exit_usercustom(
|
||||
open_trade_usdt.is_open = False
|
||||
|
||||
Trade.query.session.add(open_trade_usdt)
|
||||
Trade.commit()
|
||||
# Ensure default is false
|
||||
freqtrade.manage_open_orders()
|
||||
assert cancel_order_mock.call_count == 0
|
||||
@@ -2754,6 +2754,7 @@ def test_manage_open_orders_exit(
|
||||
open_trade_usdt.is_short = is_short
|
||||
|
||||
Trade.query.session.add(open_trade_usdt)
|
||||
Trade.commit()
|
||||
|
||||
freqtrade.strategy.check_exit_timeout = MagicMock(return_value=False)
|
||||
freqtrade.strategy.check_entry_timeout = MagicMock(return_value=False)
|
||||
@@ -2794,6 +2795,7 @@ def test_check_handle_cancelled_exit(
|
||||
open_trade_usdt.is_short = is_short
|
||||
|
||||
Trade.query.session.add(open_trade_usdt)
|
||||
Trade.commit()
|
||||
|
||||
# check it does cancel sell orders over the time limit
|
||||
freqtrade.manage_open_orders()
|
||||
@@ -2830,6 +2832,7 @@ def test_manage_open_orders_partial(
|
||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||
prior_stake = open_trade.stake_amount
|
||||
Trade.query.session.add(open_trade)
|
||||
Trade.commit()
|
||||
|
||||
# check it does cancel buy orders over the time limit
|
||||
# note this is for a partially-complete buy order
|
||||
@@ -2874,6 +2877,7 @@ def test_manage_open_orders_partial_fee(
|
||||
open_trade.fee_open = fee()
|
||||
open_trade.fee_close = fee()
|
||||
Trade.query.session.add(open_trade)
|
||||
Trade.commit()
|
||||
# cancelling a half-filled order should update the amount to the bought amount
|
||||
# and apply fees if necessary.
|
||||
freqtrade.manage_open_orders()
|
||||
@@ -2923,6 +2927,7 @@ def test_manage_open_orders_partial_except(
|
||||
open_trade.fee_open = fee()
|
||||
open_trade.fee_close = fee()
|
||||
Trade.query.session.add(open_trade)
|
||||
Trade.commit()
|
||||
# cancelling a half-filled order should update the amount to the bought amount
|
||||
# and apply fees if necessary.
|
||||
freqtrade.manage_open_orders()
|
||||
@@ -2961,6 +2966,7 @@ def test_manage_open_orders_exception(default_conf_usdt, ticker_usdt, open_trade
|
||||
freqtrade = FreqtradeBot(default_conf_usdt)
|
||||
|
||||
Trade.query.session.add(open_trade_usdt)
|
||||
Trade.commit()
|
||||
|
||||
caplog.clear()
|
||||
freqtrade.manage_open_orders()
|
||||
@@ -4256,10 +4262,10 @@ def test_get_real_amount_quote(default_conf_usdt, trades_for_order, buy_order_fe
|
||||
caplog.clear()
|
||||
order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
|
||||
# Amount is reduced by "fee"
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) == amount - (amount * 0.001)
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) == (amount * 0.001)
|
||||
assert log_has(
|
||||
'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, is_short=False,'
|
||||
' leverage=1.0, open_rate=0.24544100, open_since=closed) (from 8.0 to 7.992).',
|
||||
' leverage=1.0, open_rate=0.24544100, open_since=closed), fee=0.008.',
|
||||
caplog
|
||||
)
|
||||
|
||||
@@ -4284,7 +4290,7 @@ def test_get_real_amount_quote_dust(default_conf_usdt, trades_for_order, buy_ord
|
||||
walletmock.reset_mock()
|
||||
order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
|
||||
# Amount is kept as is
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) == amount
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) is None
|
||||
assert walletmock.call_count == 1
|
||||
assert log_has_re(r'Fee amount for Trade.* was in base currency '
|
||||
'- Eating Fee 0.008 into dust', caplog)
|
||||
@@ -4307,7 +4313,7 @@ def test_get_real_amount_no_trade(default_conf_usdt, buy_order_fee, caplog, mock
|
||||
|
||||
order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
|
||||
# Amount is reduced by "fee"
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) == amount
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) is None
|
||||
assert log_has(
|
||||
'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
|
||||
'is_short=False, leverage=1.0, open_rate=0.24544100, open_since=closed) failed: '
|
||||
@@ -4331,8 +4337,7 @@ def test_get_real_amount_no_trade(default_conf_usdt, buy_order_fee, caplog, mock
|
||||
# from order
|
||||
({'cost': 0.004, 'currency': 'LTC'}, 0.004, False, (
|
||||
'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
|
||||
'is_short=False, leverage=1.0, open_rate=0.24544100, open_since=closed) (from'
|
||||
' 8.0 to 7.996).'
|
||||
'is_short=False, leverage=1.0, open_rate=0.24544100, open_since=closed), fee=0.004.'
|
||||
)),
|
||||
# invalid, no currency in from fee dict
|
||||
({'cost': 0.008, 'currency': None}, 0, True, None),
|
||||
@@ -4364,7 +4369,11 @@ def test_get_real_amount(
|
||||
|
||||
caplog.clear()
|
||||
order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
|
||||
assert freqtrade.get_real_amount(trade, buy_order, order_obj) == amount - fee_reduction_amount
|
||||
res = freqtrade.get_real_amount(trade, buy_order, order_obj)
|
||||
if fee_reduction_amount == 0:
|
||||
assert res is None
|
||||
else:
|
||||
assert res == fee_reduction_amount
|
||||
|
||||
if expected_log:
|
||||
assert log_has(expected_log, caplog)
|
||||
@@ -4410,14 +4419,14 @@ def test_get_real_amount_multi(
|
||||
return_value={'ask': 0.19, 'last': 0.2})
|
||||
|
||||
# Amount is reduced by "fee"
|
||||
expected_amount = amount - (amount * fee_reduction_amount)
|
||||
expected_amount = amount * fee_reduction_amount
|
||||
order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
|
||||
assert freqtrade.get_real_amount(trade, buy_order_fee, order_obj) == expected_amount
|
||||
assert log_has(
|
||||
(
|
||||
'Applying fee on amount for Trade(id=None, pair=LTC/ETH, amount=8.00000000, '
|
||||
'is_short=False, leverage=1.0, open_rate=0.24544100, open_since=closed) '
|
||||
f'(from 8.0 to {expected_log_amount}).'
|
||||
'is_short=False, leverage=1.0, open_rate=0.24544100, open_since=closed), '
|
||||
f'fee={expected_amount}.'
|
||||
),
|
||||
caplog
|
||||
)
|
||||
@@ -4450,7 +4459,7 @@ def test_get_real_amount_invalid_order(default_conf_usdt, trades_for_order, buy_
|
||||
|
||||
order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
|
||||
# Amount does not change
|
||||
assert freqtrade.get_real_amount(trade, limit_buy_order_usdt, order_obj) == amount
|
||||
assert freqtrade.get_real_amount(trade, limit_buy_order_usdt, order_obj) is None
|
||||
|
||||
|
||||
def test_get_real_amount_fees_order(default_conf_usdt, market_buy_order_usdt_doublefee,
|
||||
@@ -4473,7 +4482,7 @@ def test_get_real_amount_fees_order(default_conf_usdt, market_buy_order_usdt_dou
|
||||
# Amount does not change
|
||||
assert trade.fee_open == 0.0025
|
||||
order_obj = Order.parse_from_ccxt_object(market_buy_order_usdt_doublefee, 'LTC/ETH', 'buy')
|
||||
assert freqtrade.get_real_amount(trade, market_buy_order_usdt_doublefee, order_obj) == 30.0
|
||||
assert freqtrade.get_real_amount(trade, market_buy_order_usdt_doublefee, order_obj) is None
|
||||
assert tfo_mock.call_count == 0
|
||||
# Fetch fees from trades dict if available to get "proper" values
|
||||
assert round(trade.fee_open, 4) == 0.001
|
||||
@@ -4525,7 +4534,7 @@ def test_get_real_amount_wrong_amount_rounding(default_conf_usdt, trades_for_ord
|
||||
order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
|
||||
# Amount changes by fee amount.
|
||||
assert pytest.approx(freqtrade.get_real_amount(
|
||||
trade, limit_buy_order_usdt, order_obj)) == amount - (amount * 0.001)
|
||||
trade, limit_buy_order_usdt, order_obj)) == (amount * 0.001)
|
||||
|
||||
|
||||
def test_get_real_amount_open_trade_usdt(default_conf_usdt, fee, mocker):
|
||||
@@ -4547,7 +4556,7 @@ def test_get_real_amount_open_trade_usdt(default_conf_usdt, fee, mocker):
|
||||
}
|
||||
freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt)
|
||||
order_obj = Order.parse_from_ccxt_object(order, 'LTC/ETH', 'buy')
|
||||
assert freqtrade.get_real_amount(trade, order, order_obj) == amount
|
||||
assert freqtrade.get_real_amount(trade, order, order_obj) is None
|
||||
|
||||
|
||||
def test_get_real_amount_in_point(default_conf_usdt, buy_order_fee, fee, mocker, caplog):
|
||||
@@ -4604,7 +4613,7 @@ def test_get_real_amount_in_point(default_conf_usdt, buy_order_fee, fee, mocker,
|
||||
|
||||
order_obj = Order.parse_from_ccxt_object(buy_order_fee, 'LTC/ETH', 'buy')
|
||||
res = freqtrade.get_real_amount(trade, limit_buy_order_usdt, order_obj)
|
||||
assert res == amount
|
||||
assert res is None
|
||||
assert trade.fee_open_currency is None
|
||||
assert trade.fee_open_cost is None
|
||||
message = "Not updating buy-fee - rate: None, POINT."
|
||||
@@ -4612,7 +4621,7 @@ def test_get_real_amount_in_point(default_conf_usdt, buy_order_fee, fee, mocker,
|
||||
caplog.clear()
|
||||
freqtrade.config['exchange']['unknown_fee_rate'] = 1
|
||||
res = freqtrade.get_real_amount(trade, limit_buy_order_usdt, order_obj)
|
||||
assert res == amount
|
||||
assert res is None
|
||||
assert trade.fee_open_currency == 'POINT'
|
||||
assert pytest.approx(trade.fee_open_cost) == 0.3046651026
|
||||
assert trade.fee_open == 0.002
|
||||
@@ -4621,12 +4630,12 @@ def test_get_real_amount_in_point(default_conf_usdt, buy_order_fee, fee, mocker,
|
||||
|
||||
|
||||
@pytest.mark.parametrize('amount,fee_abs,wallet,amount_exp', [
|
||||
(8.0, 0.0, 10, 8),
|
||||
(8.0, 0.0, 0, 8),
|
||||
(8.0, 0.1, 0, 7.9),
|
||||
(8.0, 0.1, 10, 8),
|
||||
(8.0, 0.1, 8.0, 8.0),
|
||||
(8.0, 0.1, 7.9, 7.9),
|
||||
(8.0, 0.0, 10, None),
|
||||
(8.0, 0.0, 0, None),
|
||||
(8.0, 0.1, 0, 0.1),
|
||||
(8.0, 0.1, 10, None),
|
||||
(8.0, 0.1, 8.0, None),
|
||||
(8.0, 0.1, 7.9, 0.1),
|
||||
])
|
||||
def test_apply_fee_conditional(default_conf_usdt, fee, mocker,
|
||||
amount, fee_abs, wallet, amount_exp):
|
||||
|
@@ -1,7 +1,6 @@
|
||||
# pragma pylint: disable=missing-docstring, C0103
|
||||
import logging
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from math import isclose
|
||||
from pathlib import Path
|
||||
from types import FunctionType
|
||||
from unittest.mock import MagicMock
|
||||
@@ -630,9 +629,9 @@ def test_calc_open_close_trade_price(
|
||||
trade.open_rate = 2.0
|
||||
trade.close_rate = 2.2
|
||||
trade.recalc_open_trade_value()
|
||||
assert isclose(trade._calc_open_trade_value(trade.amount, trade.open_rate), open_value)
|
||||
assert isclose(trade.calc_close_trade_value(trade.close_rate), close_value)
|
||||
assert isclose(trade.calc_profit(trade.close_rate), round(profit, 8))
|
||||
assert pytest.approx(trade._calc_open_trade_value(trade.amount, trade.open_rate)) == open_value
|
||||
assert pytest.approx(trade.calc_close_trade_value(trade.close_rate)) == close_value
|
||||
assert pytest.approx(trade.calc_profit(trade.close_rate)) == round(profit, 8)
|
||||
assert pytest.approx(trade.calc_profit_ratio(trade.close_rate)) == profit_ratio
|
||||
|
||||
|
||||
@@ -1387,6 +1386,7 @@ def test_migrate_new(mocker, default_conf, fee, caplog):
|
||||
assert log_has("trying trades_bak2", caplog)
|
||||
assert log_has("Running database migration for trades - backup: trades_bak2, orders_bak0",
|
||||
caplog)
|
||||
assert log_has("Database migration finished.", caplog)
|
||||
assert pytest.approx(trade.open_trade_value) == trade._calc_open_trade_value(
|
||||
trade.amount, trade.open_rate)
|
||||
assert trade.close_profit_abs is None
|
||||
@@ -1688,6 +1688,7 @@ def test_get_open(fee, is_short, use_db):
|
||||
|
||||
create_mock_trades(fee, is_short, use_db)
|
||||
assert len(Trade.get_open_trades()) == 4
|
||||
assert Trade.get_open_trade_count() == 4
|
||||
|
||||
Trade.use_db = True
|
||||
|
||||
@@ -1700,6 +1701,7 @@ def test_get_open_lev(fee, use_db):
|
||||
|
||||
create_mock_trades_with_leverage(fee, use_db)
|
||||
assert len(Trade.get_open_trades()) == 5
|
||||
assert Trade.get_open_trade_count() == 5
|
||||
|
||||
Trade.use_db = True
|
||||
|
||||
@@ -1885,6 +1887,7 @@ def test_stoploss_reinitialization(default_conf, fee):
|
||||
assert trade.initial_stop_loss == 0.95
|
||||
assert trade.initial_stop_loss_pct == -0.05
|
||||
Trade.query.session.add(trade)
|
||||
Trade.commit()
|
||||
|
||||
# Lower stoploss
|
||||
Trade.stoploss_reinitialization(0.06)
|
||||
@@ -1946,6 +1949,7 @@ def test_stoploss_reinitialization_leverage(default_conf, fee):
|
||||
assert trade.initial_stop_loss == 0.98
|
||||
assert trade.initial_stop_loss_pct == -0.1
|
||||
Trade.query.session.add(trade)
|
||||
Trade.commit()
|
||||
|
||||
# Lower stoploss
|
||||
Trade.stoploss_reinitialization(0.15)
|
||||
@@ -2007,6 +2011,7 @@ def test_stoploss_reinitialization_short(default_conf, fee):
|
||||
assert trade.initial_stop_loss == 1.02
|
||||
assert trade.initial_stop_loss_pct == -0.1
|
||||
Trade.query.session.add(trade)
|
||||
Trade.commit()
|
||||
# Lower stoploss
|
||||
Trade.stoploss_reinitialization(-0.15)
|
||||
trades = Trade.get_open_trades()
|
||||
@@ -2888,8 +2893,8 @@ def test_order_to_ccxt(limit_buy_order_open):
|
||||
(('buy', 100, 9), (200.0, 8.5, 1700.0, 0.0, None, None)),
|
||||
(('sell', 100, 10), (100.0, 8.5, 850.0, 150.0, 150.0, 0.17647059)),
|
||||
(('buy', 150, 11), (250.0, 10, 2500.0, 150.0, 150.0, 0.17647059)),
|
||||
(('sell', 100, 12), (150.0, 10.0, 1500.0, 350.0, 350.0, 0.2)),
|
||||
(('sell', 150, 14), (150.0, 10.0, 1500.0, 950.0, 950.0, 0.40)),
|
||||
(('sell', 100, 12), (150.0, 10.0, 1500.0, 350.0, 200.0, 0.2)),
|
||||
(('sell', 150, 14), (150.0, 10.0, 1500.0, 950.0, 600.0, 0.40)),
|
||||
],
|
||||
'end_profit': 950.0,
|
||||
'end_profit_ratio': 0.283582,
|
||||
@@ -2954,9 +2959,8 @@ def test_recalc_trade_from_orders_dca(data) -> None:
|
||||
assert trade.amount == result[0]
|
||||
assert trade.open_rate == result[1]
|
||||
assert trade.stake_amount == result[2]
|
||||
# TODO: enable the below.
|
||||
assert pytest.approx(trade.realized_profit) == result[3]
|
||||
# assert pytest.approx(trade.close_profit_abs) == result[4]
|
||||
assert pytest.approx(trade.close_profit_abs) == result[4]
|
||||
assert pytest.approx(trade.close_profit) == result[5]
|
||||
|
||||
trade.close(price)
|
||||
|
Reference in New Issue
Block a user