Merge branch 'develop' into fix/mutable_defaults
This commit is contained in:
@@ -1,8 +1,10 @@
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
from tests.conftest import is_arm, is_mac
|
||||
|
||||
MAXIMUM_STARTUP_TIME = 0.5
|
||||
|
||||
MAXIMUM_STARTUP_TIME = 0.7 if is_mac() and not is_arm() else 0.5
|
||||
|
||||
|
||||
def test_startup_time():
|
||||
@@ -14,4 +16,5 @@ def test_startup_time():
|
||||
elapsed = time.time() - start
|
||||
assert elapsed < MAXIMUM_STARTUP_TIME, (
|
||||
"The startup time is too long, try to use lazy import in the command entry function"
|
||||
f" (maximum {MAXIMUM_STARTUP_TIME}s, got {elapsed}s)"
|
||||
)
|
||||
|
||||
@@ -30,6 +30,7 @@ from freqtrade.data.metrics import (
|
||||
calculate_max_drawdown,
|
||||
calculate_sharpe,
|
||||
calculate_sortino,
|
||||
calculate_sqn,
|
||||
calculate_underwater,
|
||||
combine_dataframes_with_mean,
|
||||
combined_dataframes_with_rel_mean,
|
||||
@@ -457,6 +458,42 @@ def test_calculate_calmar(testdatadir):
|
||||
assert pytest.approx(calmar) == 559.040508
|
||||
|
||||
|
||||
def test_calculate_sqn(testdatadir):
|
||||
filename = testdatadir / "backtest_results/backtest-result.json"
|
||||
bt_data = load_backtest_data(filename)
|
||||
|
||||
sqn = calculate_sqn(DataFrame(), 0)
|
||||
assert sqn == 0.0
|
||||
|
||||
sqn = calculate_sqn(
|
||||
bt_data,
|
||||
0.01,
|
||||
)
|
||||
assert isinstance(sqn, float)
|
||||
assert pytest.approx(sqn) == 3.2991
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"profits,starting_balance,expected_sqn,description",
|
||||
[
|
||||
([1.0, -0.5, 2.0, -1.0, 0.5, 1.5, -0.5, 1.0], 100, 1.3229, "Mixed profits/losses"),
|
||||
([], 100, 0.0, "Empty dataframe"),
|
||||
([1.0, 0.5, 2.0, 1.5, 0.8], 100, 4.3657, "All winning trades"),
|
||||
([-1.0, -0.5, -2.0, -1.5, -0.8], 100, -4.3657, "All losing trades"),
|
||||
([1.0], 100, -100, "Single trade"),
|
||||
],
|
||||
)
|
||||
def test_calculate_sqn_cases(profits, starting_balance, expected_sqn, description):
|
||||
"""
|
||||
Test SQN calculation with various scenarios:
|
||||
"""
|
||||
trades = DataFrame({"profit_abs": profits})
|
||||
sqn = calculate_sqn(trades, starting_balance=starting_balance)
|
||||
|
||||
assert isinstance(sqn, float)
|
||||
assert pytest.approx(sqn, rel=1e-4) == expected_sqn
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"start,end,days, expected",
|
||||
[
|
||||
|
||||
@@ -6,6 +6,7 @@ import ccxt
|
||||
import pandas as pd
|
||||
import pytest
|
||||
|
||||
from freqtrade.data.converter.trade_converter import trades_dict_to_list
|
||||
from freqtrade.enums import CandleType, MarginMode, TradingMode
|
||||
from freqtrade.exceptions import DependencyException, InvalidOrderException, OperationalException
|
||||
from freqtrade.exchange.exchange_utils_timeframe import timeframe_to_seconds
|
||||
@@ -1002,6 +1003,7 @@ def test_get_maintenance_ratio_and_amt_binance(
|
||||
|
||||
|
||||
async def test__async_get_trade_history_id_binance(default_conf_usdt, mocker, fetch_trades_result):
|
||||
default_conf_usdt["exchange"]["only_from_ccxt"] = True
|
||||
exchange = get_patched_exchange(mocker, default_conf_usdt, exchange="binance")
|
||||
|
||||
async def mock_get_trade_hist(pair, *args, **kwargs):
|
||||
@@ -1056,3 +1058,53 @@ async def test__async_get_trade_history_id_binance(default_conf_usdt, mocker, fe
|
||||
|
||||
# Clean up event loop to avoid warnings
|
||||
exchange.close()
|
||||
|
||||
|
||||
async def test__async_get_trade_history_id_binance_fast(
|
||||
default_conf_usdt, mocker, fetch_trades_result
|
||||
):
|
||||
default_conf_usdt["exchange"]["only_from_ccxt"] = False
|
||||
exchange = get_patched_exchange(mocker, default_conf_usdt, exchange="binance")
|
||||
|
||||
async def mock_get_trade_hist(pair, *args, **kwargs):
|
||||
if "since" in kwargs:
|
||||
pass
|
||||
# older than initial call
|
||||
# if kwargs["since"] < 1565798399752:
|
||||
# return []
|
||||
# else:
|
||||
# # Don't expect to get here
|
||||
# raise ValueError("Unexpected call")
|
||||
# # return fetch_trades_result[:-2]
|
||||
elif kwargs.get("params", {}).get(exchange._trades_pagination_arg) == "0":
|
||||
# Return first 3
|
||||
return fetch_trades_result[:-2]
|
||||
# elif kwargs.get("params", {}).get(exchange._trades_pagination_arg) in (
|
||||
# fetch_trades_result[-3]["id"],
|
||||
# 1565798399752,
|
||||
# ):
|
||||
# # Return 2
|
||||
# return fetch_trades_result[-3:-1]
|
||||
# else:
|
||||
# # Return last 2
|
||||
# return fetch_trades_result[-2:]
|
||||
|
||||
pair = "ETH/BTC"
|
||||
mocker.patch(
|
||||
"freqtrade.exchange.binance.download_archive_trades",
|
||||
return_value=(pair, trades_dict_to_list(fetch_trades_result[-2:])),
|
||||
)
|
||||
|
||||
exchange._api_async.fetch_trades = MagicMock(side_effect=mock_get_trade_hist)
|
||||
|
||||
ret = await exchange._async_get_trade_history(
|
||||
pair,
|
||||
since=fetch_trades_result[0]["timestamp"],
|
||||
until=fetch_trades_result[-1]["timestamp"] - 1,
|
||||
)
|
||||
|
||||
assert ret[0] == pair
|
||||
assert isinstance(ret[1], list)
|
||||
|
||||
# Clean up event loop to avoid warnings
|
||||
exchange.close()
|
||||
|
||||
@@ -14,11 +14,15 @@ from freqtrade.enums import CandleType
|
||||
from freqtrade.exchange.binance_public_data import (
|
||||
BadHttpStatus,
|
||||
Http404,
|
||||
binance_vision_trades_zip_url,
|
||||
binance_vision_zip_name,
|
||||
download_archive_ohlcv,
|
||||
download_archive_trades,
|
||||
get_daily_ohlcv,
|
||||
get_daily_trades,
|
||||
)
|
||||
from freqtrade.util.datetime_helpers import dt_ts, dt_utc
|
||||
from ft_client.test_client.test_rest_client import log_has_re
|
||||
|
||||
|
||||
@pytest.fixture(scope="module")
|
||||
@@ -337,3 +341,156 @@ async def test_get_daily_ohlcv(mocker, testdatadir):
|
||||
with pytest.raises(zipfile.BadZipFile):
|
||||
df = await get_daily_ohlcv(symbol, timeframe, CandleType.SPOT, date, session)
|
||||
assert get.call_count == 4 # 1 + 3 default retries
|
||||
|
||||
|
||||
async def test_download_archive_trades(mocker, caplog):
|
||||
pair = "BTC/USDT"
|
||||
|
||||
since_ms = dt_ts(dt_utc(2020, 1, 1))
|
||||
until_ms = dt_ts(dt_utc(2020, 1, 2))
|
||||
markets = {"BTC/USDT": {"id": "BTCUSDT"}, "BTC/USDT:USDT": {"id": "BTCUSDT"}}
|
||||
|
||||
mocker.patch("freqtrade.exchange.binance_public_data.get_daily_trades", return_value=[[2, 3]])
|
||||
|
||||
pair1, res = await download_archive_trades(
|
||||
CandleType.SPOT, pair, since_ms=since_ms, until_ms=until_ms, markets=markets
|
||||
)
|
||||
assert pair1 == pair
|
||||
assert res == [[2, 3], [2, 3]]
|
||||
|
||||
mocker.patch(
|
||||
"freqtrade.exchange.binance_public_data.get_daily_trades",
|
||||
side_effect=Http404("xxx", dt_utc(2020, 1, 1), "http://example.com/something"),
|
||||
)
|
||||
|
||||
pair1, res = await download_archive_trades(
|
||||
CandleType.SPOT, pair, since_ms=since_ms, until_ms=until_ms, markets=markets
|
||||
)
|
||||
|
||||
assert pair1 == pair
|
||||
assert res == []
|
||||
# exit on day 1
|
||||
assert log_has_re("Fast download is unavailable", caplog)
|
||||
|
||||
# Test fail on day 2
|
||||
caplog.clear()
|
||||
mocker.patch(
|
||||
"freqtrade.exchange.binance_public_data.get_daily_trades",
|
||||
side_effect=[
|
||||
[[2, 3]],
|
||||
[[2, 3]],
|
||||
Http404("xxx", dt_utc(2020, 1, 2), "http://example.com/something"),
|
||||
[[2, 3]],
|
||||
],
|
||||
)
|
||||
# Download 3 days
|
||||
until_ms = dt_ts(dt_utc(2020, 1, 3))
|
||||
|
||||
pair1, res = await download_archive_trades(
|
||||
CandleType.SPOT, pair, since_ms=since_ms, until_ms=until_ms, markets=markets
|
||||
)
|
||||
|
||||
assert pair1 == pair
|
||||
assert res == [[2, 3], [2, 3]]
|
||||
assert log_has_re(r"Binance fast download .*stopped", caplog)
|
||||
|
||||
|
||||
async def test_download_archive_trades_exception(mocker, caplog):
|
||||
pair = "BTC/USDT"
|
||||
|
||||
since_ms = dt_ts(dt_utc(2020, 1, 1))
|
||||
until_ms = dt_ts(dt_utc(2020, 1, 2))
|
||||
|
||||
markets = {"BTC/USDT": {"id": "BTCUSDT"}, "BTC/USDT:USDT": {"id": "BTCUSDT"}}
|
||||
mocker.patch(
|
||||
"freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get", side_effect=RuntimeError
|
||||
)
|
||||
|
||||
pair1, res = await download_archive_trades(
|
||||
CandleType.SPOT, pair, since_ms=since_ms, until_ms=until_ms, markets=markets
|
||||
)
|
||||
|
||||
assert pair1 == pair
|
||||
assert res == []
|
||||
mocker.patch(
|
||||
"freqtrade.exchange.binance_public_data._download_archive_trades", side_effect=RuntimeError
|
||||
)
|
||||
|
||||
await download_archive_trades(
|
||||
CandleType.SPOT, pair, since_ms=since_ms, until_ms=until_ms, markets=markets
|
||||
)
|
||||
assert pair1 == pair
|
||||
assert res == []
|
||||
assert log_has_re("An exception occurred during fast trades download", caplog)
|
||||
|
||||
|
||||
async def test_binance_vision_trades_zip_url():
|
||||
url = binance_vision_trades_zip_url("BTCUSDT", CandleType.SPOT, dt_utc(2023, 10, 27))
|
||||
assert (
|
||||
url == "https://data.binance.vision/data/spot/daily/aggTrades/"
|
||||
"BTCUSDT/BTCUSDT-aggTrades-2023-10-27.zip"
|
||||
)
|
||||
|
||||
url = binance_vision_trades_zip_url("BTCUSDT", CandleType.FUTURES, dt_utc(2023, 10, 28))
|
||||
assert (
|
||||
url == "https://data.binance.vision/data/futures/um/daily/aggTrades/"
|
||||
"BTCUSDT/BTCUSDT-aggTrades-2023-10-28.zip"
|
||||
)
|
||||
|
||||
|
||||
async def test_get_daily_trades(mocker, testdatadir):
|
||||
symbol = "PEPEUSDT"
|
||||
symbol_futures = "APEUSDT"
|
||||
date = dt_utc(2024, 10, 28).date()
|
||||
first_date = 1729987202368
|
||||
last_date = 1730073596350
|
||||
|
||||
async with aiohttp.ClientSession() as session:
|
||||
spot_path = (
|
||||
testdatadir / "binance/binance_public_data/spot-PEPEUSDT-aggTrades-2024-10-27.zip"
|
||||
)
|
||||
get = mocker.patch(
|
||||
"freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get",
|
||||
return_value=MockResponse(spot_path.read_bytes(), 200),
|
||||
)
|
||||
res = await get_daily_trades(symbol, CandleType.SPOT, date, session)
|
||||
assert get.call_count == 1
|
||||
assert res[0][0] == first_date
|
||||
assert res[-1][0] == last_date
|
||||
|
||||
futures_path = (
|
||||
testdatadir / "binance/binance_public_data/futures-APEUSDT-aggTrades-2024-10-18.zip"
|
||||
)
|
||||
get = mocker.patch(
|
||||
"freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get",
|
||||
return_value=MockResponse(futures_path.read_bytes(), 200),
|
||||
)
|
||||
res_fut = await get_daily_trades(symbol_futures, CandleType.FUTURES, date, session)
|
||||
assert get.call_count == 1
|
||||
assert res_fut[0][0] == 1729209603958
|
||||
assert res_fut[-1][0] == 1729295981272
|
||||
|
||||
get = mocker.patch(
|
||||
"freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get",
|
||||
return_value=MockResponse(b"", 404),
|
||||
)
|
||||
with pytest.raises(Http404):
|
||||
await get_daily_trades(symbol, CandleType.SPOT, date, session, retry_delay=0)
|
||||
assert get.call_count == 1
|
||||
|
||||
get = mocker.patch(
|
||||
"freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get",
|
||||
return_value=MockResponse(b"", 500),
|
||||
)
|
||||
mocker.patch("asyncio.sleep")
|
||||
with pytest.raises(BadHttpStatus):
|
||||
await get_daily_trades(symbol, CandleType.SPOT, date, session)
|
||||
assert get.call_count == 4 # 1 + 3 default retries
|
||||
|
||||
get = mocker.patch(
|
||||
"freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get",
|
||||
return_value=MockResponse(b"nop", 200),
|
||||
)
|
||||
with pytest.raises(zipfile.BadZipFile):
|
||||
await get_daily_trades(symbol, CandleType.SPOT, date, session)
|
||||
assert get.call_count == 4 # 1 + 3 default retries
|
||||
|
||||
@@ -2373,6 +2373,8 @@ def test_refresh_latest_trades(
|
||||
caplog.set_level(logging.DEBUG)
|
||||
use_trades_conf = default_conf
|
||||
use_trades_conf["exchange"]["use_public_trades"] = True
|
||||
use_trades_conf["exchange"]["only_from_ccxt"] = True
|
||||
|
||||
use_trades_conf["datadir"] = tmp_path
|
||||
use_trades_conf["orderflow"] = {"max_candles": 1500}
|
||||
exchange = get_patched_exchange(mocker, use_trades_conf)
|
||||
@@ -3365,6 +3367,7 @@ async def test__async_fetch_trades_contract_size(
|
||||
async def test__async_get_trade_history_id(
|
||||
default_conf, mocker, exchange_name, fetch_trades_result
|
||||
):
|
||||
default_conf["exchange"]["only_from_ccxt"] = True
|
||||
exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name)
|
||||
if exchange._trades_pagination != "id":
|
||||
exchange.close()
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
# pragma pylint: disable=missing-docstring, protected-access, invalid-name
|
||||
from datetime import datetime, timedelta, timezone
|
||||
from math import isnan, nan
|
||||
|
||||
import pytest
|
||||
from ccxt import (
|
||||
@@ -321,6 +322,7 @@ def test_amount_to_precision(
|
||||
(2.9977, TICK_SIZE, 0.005, 3.0, ROUND),
|
||||
(234.24, TICK_SIZE, 0.5, 234.0, ROUND),
|
||||
(234.26, TICK_SIZE, 0.5, 234.5, ROUND),
|
||||
(nan, TICK_SIZE, 3, nan, ROUND),
|
||||
# Tests for TRUNCATTE
|
||||
(2.34559, DECIMAL_PLACES, 4, 2.3455, TRUNCATE),
|
||||
(2.34559, DECIMAL_PLACES, 5, 2.34559, TRUNCATE),
|
||||
@@ -359,10 +361,11 @@ def test_amount_to_precision(
|
||||
],
|
||||
)
|
||||
def test_price_to_precision(price, precision_mode, precision, expected, rounding_mode):
|
||||
assert (
|
||||
price_to_precision(price, precision, precision_mode, rounding_mode=rounding_mode)
|
||||
== expected
|
||||
)
|
||||
result = price_to_precision(price, precision, precision_mode, rounding_mode=rounding_mode)
|
||||
if not isnan(expected):
|
||||
assert result == expected
|
||||
else:
|
||||
assert isnan(result)
|
||||
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
|
||||
@@ -3743,8 +3743,9 @@ def test_trailing_stop_loss_positive(
|
||||
|
||||
@pytest.mark.parametrize("is_short", [False, True])
|
||||
def test_disable_ignore_roi_if_entry_signal(
|
||||
default_conf_usdt, limit_order, limit_order_open, is_short, fee, mocker
|
||||
default_conf_usdt, limit_order, limit_order_open, is_short, fee, mocker, time_machine
|
||||
) -> None:
|
||||
time_machine.move_to("2025-01-10 08:00:16 +00:00")
|
||||
patch_RPCManager(mocker)
|
||||
patch_exchange(mocker)
|
||||
eside = entry_side(is_short)
|
||||
@@ -3773,6 +3774,13 @@ def test_disable_ignore_roi_if_entry_signal(
|
||||
patch_get_signal(freqtrade, enter_long=not is_short, enter_short=is_short, exit_short=is_short)
|
||||
assert freqtrade.handle_trade(trade) is True
|
||||
|
||||
# Test if entry-signal is absent
|
||||
patch_get_signal(freqtrade)
|
||||
# Signal was evaluated already - no action.
|
||||
assert freqtrade.handle_trade(trade) is False
|
||||
|
||||
# Move to after the candle expired
|
||||
time_machine.shift(timedelta(hours=5))
|
||||
# Test if entry-signal is absent
|
||||
patch_get_signal(freqtrade)
|
||||
assert freqtrade.handle_trade(trade) is True
|
||||
|
||||
@@ -436,6 +436,7 @@ def test_dca_order_adjust(default_conf_usdt, ticker_usdt, leverage, fee, mocker)
|
||||
|
||||
# Replace new order with diff. order at a lower price
|
||||
freqtrade.strategy.adjust_entry_price = MagicMock(return_value=1.95)
|
||||
freqtrade.strategy.adjust_exit_price = MagicMock(side_effect=ValueError)
|
||||
freqtrade.strategy.adjust_trade_position = MagicMock(return_value=None)
|
||||
freqtrade.process()
|
||||
trade = Trade.get_trades().first()
|
||||
@@ -445,6 +446,8 @@ def test_dca_order_adjust(default_conf_usdt, ticker_usdt, leverage, fee, mocker)
|
||||
assert pytest.approx(trade.stake_amount) == 60
|
||||
assert trade.orders[-1].price == 1.95
|
||||
assert pytest.approx(trade.orders[-1].cost) == 120 * leverage
|
||||
assert freqtrade.strategy.adjust_entry_price.call_count == 1
|
||||
assert freqtrade.strategy.adjust_exit_price.call_count == 0
|
||||
|
||||
# Fill DCA order
|
||||
freqtrade.strategy.adjust_trade_position = MagicMock(return_value=None)
|
||||
@@ -469,6 +472,7 @@ def test_dca_order_adjust(default_conf_usdt, ticker_usdt, leverage, fee, mocker)
|
||||
mocker.patch(f"{EXMS}._dry_is_price_crossed", return_value=False)
|
||||
freqtrade.strategy.custom_exit = MagicMock(return_value="Exit now")
|
||||
freqtrade.strategy.adjust_entry_price = MagicMock(return_value=2.02)
|
||||
freqtrade.strategy.adjust_exit_price = MagicMock(side_effect=ValueError)
|
||||
freqtrade.process()
|
||||
trade = Trade.get_trades().first()
|
||||
assert len(trade.orders) == 5
|
||||
@@ -478,8 +482,9 @@ def test_dca_order_adjust(default_conf_usdt, ticker_usdt, leverage, fee, mocker)
|
||||
assert pytest.approx(trade.amount) == 91.689215 * leverage
|
||||
assert pytest.approx(trade.orders[-1].amount) == 91.689215 * leverage
|
||||
assert freqtrade.strategy.adjust_entry_price.call_count == 0
|
||||
assert freqtrade.strategy.adjust_exit_price.call_count == 0
|
||||
|
||||
# Process again, should not adjust entry price
|
||||
# Process again, should not adjust price
|
||||
freqtrade.process()
|
||||
trade = Trade.get_trades().first()
|
||||
|
||||
@@ -490,6 +495,21 @@ def test_dca_order_adjust(default_conf_usdt, ticker_usdt, leverage, fee, mocker)
|
||||
assert trade.orders[-1].price == 2.02
|
||||
# Adjust entry price cannot be called - this is an exit order
|
||||
assert freqtrade.strategy.adjust_entry_price.call_count == 0
|
||||
assert freqtrade.strategy.adjust_exit_price.call_count == 1
|
||||
|
||||
freqtrade.strategy.adjust_exit_price = MagicMock(return_value=2.03)
|
||||
|
||||
# Process again, should adjust exit price
|
||||
freqtrade.process()
|
||||
trade = Trade.get_trades().first()
|
||||
|
||||
assert trade.orders[-2].status == "canceled"
|
||||
assert len(trade.orders) == 6
|
||||
assert trade.orders[-1].side == trade.exit_side
|
||||
assert trade.orders[-1].status == "open"
|
||||
assert trade.orders[-1].price == 2.03
|
||||
assert freqtrade.strategy.adjust_entry_price.call_count == 0
|
||||
assert freqtrade.strategy.adjust_exit_price.call_count == 1
|
||||
|
||||
|
||||
@pytest.mark.parametrize("leverage", [1, 2])
|
||||
|
||||
@@ -45,6 +45,7 @@ class BTContainer(NamedTuple):
|
||||
leverage: float = 1.0
|
||||
timeout: int | None = None
|
||||
adjust_entry_price: float | None = None
|
||||
adjust_exit_price: float | None = None
|
||||
adjust_trade_position: list[float] | None = None
|
||||
|
||||
|
||||
|
||||
@@ -1217,6 +1217,46 @@ tc57 = BTContainer(
|
||||
],
|
||||
)
|
||||
|
||||
# Test 58: Custom-exit-price short - below all candles
|
||||
tc58 = BTContainer(
|
||||
data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 0, 0, 1, 0],
|
||||
[1, 5000, 5200, 4951, 5000, 6172, 0, 0, 0, 0], # enter trade (signal on last candle)
|
||||
[2, 4900, 5250, 4900, 5100, 6172, 0, 0, 0, 1], # Exit - delayed
|
||||
[3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 0], #
|
||||
[4, 4750, 5100, 4350, 4750, 6172, 0, 0, 0, 0],
|
||||
],
|
||||
stop_loss=-0.10,
|
||||
roi={"0": 1.00},
|
||||
profit_perc=-0.01,
|
||||
use_exit_signal=True,
|
||||
timeout=1000,
|
||||
custom_exit_price=4300,
|
||||
adjust_exit_price=5050,
|
||||
trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4, is_short=True)],
|
||||
)
|
||||
|
||||
# Test 59: Custom-exit-price above all candles - readjust order
|
||||
tc59 = BTContainer(
|
||||
data=[
|
||||
# D O H L C V EL XL ES Xs BT
|
||||
[0, 5000, 5050, 4950, 5000, 6172, 1, 0],
|
||||
[1, 5000, 5500, 4951, 5000, 6172, 0, 0],
|
||||
[2, 4900, 5250, 4500, 5100, 6172, 0, 1], # exit
|
||||
[3, 5100, 5100, 4650, 4750, 6172, 0, 0], # order readjust
|
||||
[4, 4750, 4950, 4350, 4750, 6172, 0, 0],
|
||||
],
|
||||
stop_loss=-0.2,
|
||||
roi={"0": 0.10},
|
||||
profit_perc=-0.02,
|
||||
use_exit_signal=True,
|
||||
timeout=1000,
|
||||
custom_exit_price=5300,
|
||||
adjust_exit_price=4900,
|
||||
trades=[BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=4, is_short=False)],
|
||||
)
|
||||
|
||||
|
||||
TESTS = [
|
||||
tc0,
|
||||
@@ -1277,6 +1317,8 @@ TESTS = [
|
||||
tc55,
|
||||
tc56,
|
||||
tc57,
|
||||
tc58,
|
||||
tc59,
|
||||
]
|
||||
|
||||
|
||||
@@ -1330,6 +1372,8 @@ def test_backtest_results(default_conf, mocker, caplog, data: BTContainer) -> No
|
||||
)
|
||||
if data.adjust_entry_price:
|
||||
backtesting.strategy.adjust_entry_price = MagicMock(return_value=data.adjust_entry_price)
|
||||
if data.adjust_exit_price:
|
||||
backtesting.strategy.adjust_exit_price = MagicMock(return_value=data.adjust_exit_price)
|
||||
|
||||
backtesting.strategy.use_custom_stoploss = data.use_custom_stoploss
|
||||
backtesting.strategy.leverage = lambda **kwargs: data.leverage
|
||||
|
||||
@@ -577,7 +577,7 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers, proxy_coin, marg
|
||||
"symbol": "ETH/USDT:USDT",
|
||||
"timestamp": None,
|
||||
"datetime": None,
|
||||
"initialMargin": 0.0,
|
||||
"initialMargin": 20,
|
||||
"initialMarginPercentage": None,
|
||||
"maintenanceMargin": 0.0,
|
||||
"maintenanceMarginPercentage": 0.005,
|
||||
@@ -590,8 +590,9 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers, proxy_coin, marg
|
||||
"marginRatio": None,
|
||||
"liquidationPrice": 0.0,
|
||||
"markPrice": 2896.41,
|
||||
"collateral": 20,
|
||||
"marginType": "isolated",
|
||||
# Collateral is in USDT - and can be higher than position size in cross mode
|
||||
"collateral": 50,
|
||||
"marginType": "cross",
|
||||
"side": "short",
|
||||
"percentage": None,
|
||||
}
|
||||
|
||||
@@ -21,10 +21,12 @@ class TestStrategyNoImplementSell(TestStrategyNoImplements):
|
||||
return super().populate_entry_trend(dataframe, metadata)
|
||||
|
||||
|
||||
class TestStrategyImplementCustomSell(TestStrategyNoImplementSell):
|
||||
class TestStrategyImplementEmptyWorking(TestStrategyNoImplementSell):
|
||||
def populate_exit_trend(self, dataframe: DataFrame, metadata: dict) -> DataFrame:
|
||||
return super().populate_exit_trend(dataframe, metadata)
|
||||
|
||||
|
||||
class TestStrategyImplementCustomSell(TestStrategyImplementEmptyWorking):
|
||||
def custom_sell(
|
||||
self,
|
||||
pair: str,
|
||||
@@ -55,3 +57,34 @@ class TestStrategyImplementSellTimeout(TestStrategyNoImplementSell):
|
||||
self, pair: str, trade, order: Order, current_time: datetime, **kwargs
|
||||
) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
class TestStrategyAdjustOrderPrice(TestStrategyImplementEmptyWorking):
|
||||
def adjust_entry_price(
|
||||
self,
|
||||
trade,
|
||||
order,
|
||||
pair,
|
||||
current_time,
|
||||
proposed_rate,
|
||||
current_order_rate,
|
||||
entry_tag,
|
||||
side,
|
||||
**kwargs,
|
||||
):
|
||||
return proposed_rate
|
||||
|
||||
def adjust_order_price(
|
||||
self,
|
||||
trade,
|
||||
order,
|
||||
pair,
|
||||
current_time,
|
||||
proposed_rate,
|
||||
current_order_rate,
|
||||
entry_tag,
|
||||
side,
|
||||
is_entry,
|
||||
**kwargs,
|
||||
):
|
||||
return proposed_rate
|
||||
|
||||
@@ -460,6 +460,10 @@ def test_missing_implements(default_conf, caplog):
|
||||
):
|
||||
StrategyResolver.load_strategy(default_conf)
|
||||
|
||||
default_conf["strategy"] = "TestStrategyAdjustOrderPrice"
|
||||
with pytest.raises(OperationalException, match=r"If you implement `adjust_order_price`.*"):
|
||||
StrategyResolver.load_strategy(default_conf)
|
||||
|
||||
|
||||
def test_call_deprecated_function(default_conf):
|
||||
default_location = Path(__file__).parent / "strats/broken_strats/"
|
||||
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
Reference in New Issue
Block a user