Merge remote-tracking branch 'upstream/develop' into feature/fetch-public-trades

This commit is contained in:
Joe Schr 2024-06-27 15:23:12 +02:00
commit b2bcac8447
26 changed files with 1123 additions and 367 deletions

View File

@ -55,7 +55,7 @@ jobs:
- name: Installation - *nix - name: Installation - *nix
run: | run: |
python -m pip install --upgrade pip wheel python -m pip install --upgrade "pip<=24.0" wheel
export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH
export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_LIBRARY_PATH=${HOME}/dependencies/lib
export TA_INCLUDE_PATH=${HOME}/dependencies/include export TA_INCLUDE_PATH=${HOME}/dependencies/include
@ -192,7 +192,7 @@ jobs:
- name: Installation (python) - name: Installation (python)
run: | run: |
python -m pip install --upgrade pip wheel python -m pip install --upgrade "pip<=24.0" wheel
export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH
export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_LIBRARY_PATH=${HOME}/dependencies/lib
export TA_INCLUDE_PATH=${HOME}/dependencies/include export TA_INCLUDE_PATH=${HOME}/dependencies/include
@ -422,7 +422,7 @@ jobs:
- name: Installation - *nix - name: Installation - *nix
run: | run: |
python -m pip install --upgrade pip wheel python -m pip install --upgrade "pip<=24.0" wheel
export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH
export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_LIBRARY_PATH=${HOME}/dependencies/lib
export TA_INCLUDE_PATH=${HOME}/dependencies/include export TA_INCLUDE_PATH=${HOME}/dependencies/include

View File

@ -16,10 +16,10 @@ repos:
additional_dependencies: additional_dependencies:
- types-cachetools==5.3.0.7 - types-cachetools==5.3.0.7
- types-filelock==3.2.7 - types-filelock==3.2.7
- types-requests==2.32.0.20240602 - types-requests==2.32.0.20240622
- types-tabulate==0.9.0.20240106 - types-tabulate==0.9.0.20240106
- types-python-dateutil==2.9.0.20240316 - types-python-dateutil==2.9.0.20240316
- SQLAlchemy==2.0.30 - SQLAlchemy==2.0.31
# stages: [push] # stages: [push]
- repo: https://github.com/pycqa/isort - repo: https://github.com/pycqa/isort
@ -31,7 +31,7 @@ repos:
- repo: https://github.com/charliermarsh/ruff-pre-commit - repo: https://github.com/charliermarsh/ruff-pre-commit
# Ruff version. # Ruff version.
rev: 'v0.4.9' rev: 'v0.4.10'
hooks: hooks:
- id: ruff - id: ruff

View File

@ -25,7 +25,7 @@ FROM base as python-deps
RUN apt-get update \ RUN apt-get update \
&& apt-get -y install build-essential libssl-dev git libffi-dev libgfortran5 pkg-config cmake gcc \ && apt-get -y install build-essential libssl-dev git libffi-dev libgfortran5 pkg-config cmake gcc \
&& apt-get clean \ && apt-get clean \
&& pip install --upgrade pip wheel && pip install --upgrade "pip<=24.0" wheel
# Install TA-lib # Install TA-lib
COPY build_helpers/* /tmp/ COPY build_helpers/* /tmp/
@ -35,7 +35,7 @@ ENV LD_LIBRARY_PATH /usr/local/lib
# Install dependencies # Install dependencies
COPY --chown=ftuser:ftuser requirements.txt requirements-hyperopt.txt /freqtrade/ COPY --chown=ftuser:ftuser requirements.txt requirements-hyperopt.txt /freqtrade/
USER ftuser USER ftuser
RUN pip install --user --no-cache-dir numpy \ RUN pip install --user --no-cache-dir "numpy<2.0" \
&& pip install --user --no-cache-dir -r requirements-hyperopt.txt && pip install --user --no-cache-dir -r requirements-hyperopt.txt
# Copy dependencies to runtime-image # Copy dependencies to runtime-image

View File

@ -1,6 +1,6 @@
# vendored Wheels compiled via https://github.com/xmatthias/ta-lib-python/tree/ta_bundled_040 # vendored Wheels compiled via https://github.com/xmatthias/ta-lib-python/tree/ta_bundled_040
python -m pip install --upgrade pip wheel python -m pip install --upgrade "pip<=24.0" wheel
$pyv = python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" $pyv = python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')"

View File

@ -17,7 +17,7 @@ RUN mkdir /freqtrade \
&& chown ftuser:ftuser /freqtrade \ && chown ftuser:ftuser /freqtrade \
# Allow sudoers # Allow sudoers
&& echo "ftuser ALL=(ALL) NOPASSWD: /bin/chown" >> /etc/sudoers \ && echo "ftuser ALL=(ALL) NOPASSWD: /bin/chown" >> /etc/sudoers \
&& pip install --upgrade pip && pip install --upgrade "pip<=24.0"
WORKDIR /freqtrade WORKDIR /freqtrade

View File

@ -373,7 +373,7 @@ Filters low-value coins which would not allow setting stoplosses.
Namely, pairs are blacklisted if a variance of one percent or more in the stop price would be caused by precision rounding on the exchange, i.e. `rounded(stop_price) <= rounded(stop_price * 0.99)`. The idea is to avoid coins with a value VERY close to their lower trading boundary, not allowing setting of proper stoploss. Namely, pairs are blacklisted if a variance of one percent or more in the stop price would be caused by precision rounding on the exchange, i.e. `rounded(stop_price) <= rounded(stop_price * 0.99)`. The idea is to avoid coins with a value VERY close to their lower trading boundary, not allowing setting of proper stoploss.
!!! Tip "PerformanceFilter is pointless for futures trading" !!! Tip "PrecisionFilter is pointless for futures trading"
The above does not apply to shorts. And for longs, in theory the trade will be liquidated first. The above does not apply to shorts. And for longs, in theory the trade will be liquidated first.
!!! Warning "Backtesting" !!! Warning "Backtesting"

View File

@ -2,6 +2,14 @@
This page explains how to plot prices, indicators and profits. This page explains how to plot prices, indicators and profits.
!!! Warning "Deprecated"
The commands described in this page (`plot-dataframe`, `plot-profit`) should be considered deprecated and are in maintenance mode.
This is mostly for the performance problems even medium sized plots can cause, but also because "store a file and open it in a browser" isn't very intuitive from a UI perspective.
While there are no immediate plans to remove them, they are not actively maintained - and may be removed short-term should major changes be required to keep them working.
Please use [FreqUI](freq-ui.md) for plotting needs, which doesn't struggle with the same performance problems.
## Installation / Setup ## Installation / Setup
Plotting modules use the Plotly library. You can install / upgrade this by running the following command: Plotting modules use the Plotly library. You can install / upgrade this by running the following command:

View File

@ -165,7 +165,9 @@ E.g. If the `current_rate` is 200 USD, then returning `0.02` will set the stoplo
During backtesting, `current_rate` (and `current_profit`) are provided against the candle's high (or low for short trades) - while the resulting stoploss is evaluated against the candle's low (or high for short trades). During backtesting, `current_rate` (and `current_profit`) are provided against the candle's high (or low for short trades) - while the resulting stoploss is evaluated against the candle's low (or high for short trades).
The absolute value of the return value is used (the sign is ignored), so returning `0.05` or `-0.05` have the same result, a stoploss 5% below the current price. The absolute value of the return value is used (the sign is ignored), so returning `0.05` or `-0.05` have the same result, a stoploss 5% below the current price.
Returning None will be interpreted as "no desire to change", and is the only safe way to return when you'd like to not modify the stoploss. Returning `None` will be interpreted as "no desire to change", and is the only safe way to return when you'd like to not modify the stoploss.
`NaN` and `inf` values are considered invalid and will be ignored (identical to `None`).
Stoploss on exchange works similar to `trailing_stop`, and the stoploss on exchange is updated as configured in `stoploss_on_exchange_interval` ([More details about stoploss on exchange](stoploss.md#stop-loss-on-exchangefreqtrade)). Stoploss on exchange works similar to `trailing_stop`, and the stoploss on exchange is updated as configured in `stoploss_on_exchange_interval` ([More details about stoploss on exchange](stoploss.md#stop-loss-on-exchangefreqtrade)).

View File

@ -618,6 +618,11 @@ def download_data_main(config: Config) -> None:
# Start downloading # Start downloading
try: try:
if config.get("download_trades"): if config.get("download_trades"):
if not exchange.get_option("trades_has_history", True):
raise OperationalException(
f"Trade history not available for {exchange.name}. "
"You cannot use --dl-trades for this exchange."
)
pairs_not_available = refresh_backtest_trades_data( pairs_not_available = refresh_backtest_trades_data(
exchange, exchange,
pairs=expanded_pairs, pairs=expanded_pairs,

View File

@ -28,6 +28,7 @@ class Binance(Exchange):
"ohlcv_candle_limit": 1000, "ohlcv_candle_limit": 1000,
"trades_pagination": "id", "trades_pagination": "id",
"trades_pagination_arg": "fromId", "trades_pagination_arg": "fromId",
"trades_has_history": True,
"l2_limit_range": [5, 10, 20, 50, 100, 500, 1000], "l2_limit_range": [5, 10, 20, 50, 100, 500, 1000],
} }
_ft_has_futures: Dict = { _ft_has_futures: Dict = {

File diff suppressed because it is too large Load Diff

View File

@ -20,4 +20,5 @@ class Bingx(Exchange):
"stoploss_on_exchange": True, "stoploss_on_exchange": True,
"stoploss_order_types": {"limit": "limit", "market": "market"}, "stoploss_order_types": {"limit": "limit", "market": "market"},
"order_time_in_force": ["GTC", "IOC", "PO"], "order_time_in_force": ["GTC", "IOC", "PO"],
"trades_has_history": False, # Endpoint doesn't seem to support pagination
} }

View File

@ -18,4 +18,5 @@ class Bitmart(Exchange):
_ft_has: Dict = { _ft_has: Dict = {
"stoploss_on_exchange": False, # Bitmart API does not support stoploss orders "stoploss_on_exchange": False, # Bitmart API does not support stoploss orders
"ohlcv_candle_limit": 200, "ohlcv_candle_limit": 200,
"trades_has_history": False, # Endpoint doesn't seem to support pagination
} }

View File

@ -33,6 +33,7 @@ class Bybit(Exchange):
"ohlcv_candle_limit": 1000, "ohlcv_candle_limit": 1000,
"ohlcv_has_history": True, "ohlcv_has_history": True,
"order_time_in_force": ["GTC", "FOK", "IOC", "PO"], "order_time_in_force": ["GTC", "FOK", "IOC", "PO"],
"trades_has_history": False, # Endpoint doesn't support pagination
} }
_ft_has_futures: Dict = { _ft_has_futures: Dict = {
"ohlcv_has_history": True, "ohlcv_has_history": True,

View File

@ -125,6 +125,7 @@ class Exchange:
"trades_limit": 1000, # Limit for 1 call to fetch_trades "trades_limit": 1000, # Limit for 1 call to fetch_trades
"trades_pagination": "time", # Possible are "time" or "id" "trades_pagination": "time", # Possible are "time" or "id"
"trades_pagination_arg": "since", "trades_pagination_arg": "since",
"trades_has_history": False,
"l2_limit_range": None, "l2_limit_range": None,
"l2_limit_range_required": True, # Allow Empty L2 limit (kucoin) "l2_limit_range_required": True, # Allow Empty L2 limit (kucoin)
"mark_ohlcv_price": "mark", "mark_ohlcv_price": "mark",

View File

@ -31,6 +31,7 @@ class Gate(Exchange):
"stop_price_param": "stopPrice", "stop_price_param": "stopPrice",
"stop_price_prop": "stopPrice", "stop_price_prop": "stopPrice",
"marketOrderRequiresPrice": True, "marketOrderRequiresPrice": True,
"trades_has_history": False, # Endpoint would support this - but ccxt doesn't.
} }
_ft_has_futures: Dict = { _ft_has_futures: Dict = {

View File

@ -28,6 +28,7 @@ class Htx(Exchange):
"1w": 500, "1w": 500,
"1M": 500, "1M": 500,
}, },
"trades_has_history": False, # Endpoint doesn't have a "since" parameter
} }
def _get_stop_params(self, side: BuySell, ordertype: str, stop_price: float) -> Dict: def _get_stop_params(self, side: BuySell, ordertype: str, stop_price: float) -> Dict:

View File

@ -31,6 +31,7 @@ class Kraken(Exchange):
"trades_pagination": "id", "trades_pagination": "id",
"trades_pagination_arg": "since", "trades_pagination_arg": "since",
"trades_pagination_overlap": False, "trades_pagination_overlap": False,
"trades_has_history": True,
"mark_ohlcv_timeframe": "4h", "mark_ohlcv_timeframe": "4h",
} }

View File

@ -33,6 +33,7 @@ class Okx(Exchange):
"funding_fee_timeframe": "8h", "funding_fee_timeframe": "8h",
"stoploss_order_types": {"limit": "limit"}, "stoploss_order_types": {"limit": "limit"},
"stoploss_on_exchange": True, "stoploss_on_exchange": True,
"trades_has_history": False, # Endpoint doesn't have a "since" parameter
} }
_ft_has_futures: Dict = { _ft_has_futures: Dict = {
"tickers_have_quoteVolume": False, "tickers_have_quoteVolume": False,

View File

@ -6,6 +6,7 @@ This module defines the interface to apply for strategies
import logging import logging
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from math import isinf, isnan
from typing import Dict, List, Optional, Tuple, Union from typing import Dict, List, Optional, Tuple, Union
from pandas import DataFrame from pandas import DataFrame
@ -1425,7 +1426,9 @@ class IStrategy(ABC, HyperStrategyMixin):
after_fill=after_fill, after_fill=after_fill,
) )
# Sanity check - error cases will return None # Sanity check - error cases will return None
if stop_loss_value_custom: if stop_loss_value_custom and not (
isnan(stop_loss_value_custom) or isinf(stop_loss_value_custom)
):
stop_loss_value = stop_loss_value_custom stop_loss_value = stop_loss_value_custom
trade.adjust_stop_loss( trade.adjust_stop_loss(
bound or current_rate, stop_loss_value, allow_refresh=after_fill bound or current_rate, stop_loss_value, allow_refresh=after_fill

View File

@ -7,7 +7,7 @@
-r docs/requirements-docs.txt -r docs/requirements-docs.txt
coveralls==4.0.1 coveralls==4.0.1
ruff==0.4.9 ruff==0.4.10
mypy==1.10.0 mypy==1.10.0
pre-commit==3.7.1 pre-commit==3.7.1
pytest==8.2.2 pytest==8.2.2
@ -26,6 +26,6 @@ nbconvert==7.16.4
# mypy types # mypy types
types-cachetools==5.3.0.7 types-cachetools==5.3.0.7
types-filelock==3.2.7 types-filelock==3.2.7
types-requests==2.32.0.20240602 types-requests==2.32.0.20240622
types-tabulate==0.9.0.20240106 types-tabulate==0.9.0.20240106
types-python-dateutil==2.9.0.20240316 types-python-dateutil==2.9.0.20240316

View File

@ -5,4 +5,4 @@
scipy==1.13.1 scipy==1.13.1
scikit-learn==1.5.0 scikit-learn==1.5.0
ft-scikit-optimize==0.9.2 ft-scikit-optimize==0.9.2
filelock==3.15.1 filelock==3.15.4

View File

@ -1,13 +1,13 @@
numpy==1.26.4 numpy==1.26.4
pandas==2.2.2 pandas==2.2.2
bottleneck==1.3.8 bottleneck==1.4.0
numexpr==2.10.0 numexpr==2.10.1
pandas-ta==0.3.14b pandas-ta==0.3.14b
ccxt==4.3.46 ccxt==4.3.50
cryptography==42.0.8 cryptography==42.0.8
aiohttp==3.9.5 aiohttp==3.9.5
SQLAlchemy==2.0.30 SQLAlchemy==2.0.31
python-telegram-bot==21.3 python-telegram-bot==21.3
# can't be hard-pinned due to telegram-bot pinning httpx with ~ # can't be hard-pinned due to telegram-bot pinning httpx with ~
httpx>=0.24.1 httpx>=0.24.1
@ -43,7 +43,7 @@ pydantic==2.7.4
uvicorn==0.30.1 uvicorn==0.30.1
pyjwt==2.8.0 pyjwt==2.8.0
aiofiles==23.2.1 aiofiles==23.2.1
psutil==5.9.8 psutil==6.0.0
# Support for colorized terminal output # Support for colorized terminal output
colorama==0.4.6 colorama==0.4.6

View File

@ -49,7 +49,7 @@ function updateenv() {
source .venv/bin/activate source .venv/bin/activate
SYS_ARCH=$(uname -m) SYS_ARCH=$(uname -m)
echo "pip install in-progress. Please wait..." echo "pip install in-progress. Please wait..."
${PYTHON} -m pip install --upgrade pip wheel setuptools ${PYTHON} -m pip install --upgrade "pip<=24.0" wheel setuptools
REQUIREMENTS_HYPEROPT="" REQUIREMENTS_HYPEROPT=""
REQUIREMENTS_PLOT="" REQUIREMENTS_PLOT=""
REQUIREMENTS_FREQAI="" REQUIREMENTS_FREQAI=""

View File

@ -83,6 +83,12 @@ def test_download_data_main_trades(mocker):
assert dl_mock.call_count == 1 assert dl_mock.call_count == 1
assert convert_mock.call_count == 1 assert convert_mock.call_count == 1
# Exchange that doesn't support historic downloads
config["exchange"]["name"] = "bybit"
with pytest.raises(OperationalException, match=r"Trade history not available for .*"):
config
download_data_main(config)
def test_download_data_main_data_invalid(mocker): def test_download_data_main_data_invalid(mocker):
patch_exchange(mocker, id="kraken") patch_exchange(mocker, id="kraken")

View File

@ -1,5 +1,6 @@
# pragma pylint: disable=missing-docstring, C0103 # pragma pylint: disable=missing-docstring, C0103
import logging import logging
import math
from datetime import datetime, timedelta, timezone from datetime import datetime, timedelta, timezone
from pathlib import Path from pathlib import Path
from unittest.mock import MagicMock from unittest.mock import MagicMock
@ -458,55 +459,66 @@ def test_min_roi_reached3(default_conf, fee) -> None:
ExitType.TRAILING_STOP_LOSS, ExitType.TRAILING_STOP_LOSS,
None, None,
), ),
(0.01, 0.96, ExitType.NONE, None, True, False, 0.05, 1, ExitType.NONE, None), (0.01, 0.96, ExitType.NONE, None, True, False, 0.05, 0.998, ExitType.NONE, None),
(0.05, 1, ExitType.NONE, None, True, False, -0.01, 1, ExitType.TRAILING_STOP_LOSS, None), (
0.05,
0.998,
ExitType.NONE,
None,
True,
False,
-0.01,
0.998,
ExitType.TRAILING_STOP_LOSS,
None,
),
# Default custom case - trails with 10% # Default custom case - trails with 10%
(0.05, 0.95, ExitType.NONE, None, False, True, -0.02, 0.95, ExitType.NONE, None), (0.05, 0.945, ExitType.NONE, None, False, True, -0.02, 0.945, ExitType.NONE, None),
( (
0.05, 0.05,
0.95, 0.945,
ExitType.NONE, ExitType.NONE,
None, None,
False, False,
True, True,
-0.06, -0.06,
0.95, 0.945,
ExitType.TRAILING_STOP_LOSS, ExitType.TRAILING_STOP_LOSS,
None, None,
), ),
( (
0.05, 0.05,
1, 0.998,
ExitType.NONE, ExitType.NONE,
None, None,
False, False,
True, True,
-0.06, -0.06,
1, 0.998,
ExitType.TRAILING_STOP_LOSS, ExitType.TRAILING_STOP_LOSS,
lambda **kwargs: -0.05, lambda **kwargs: -0.05,
), ),
( (
0.05, 0.05,
1, 0.998,
ExitType.NONE, ExitType.NONE,
None, None,
False, False,
True, True,
0.09, 0.09,
1.04, 1.036,
ExitType.NONE, ExitType.NONE,
lambda **kwargs: -0.05, lambda **kwargs: -0.05,
), ),
( (
0.05, 0.05,
0.95, 0.945,
ExitType.NONE, ExitType.NONE,
None, None,
False, False,
True, True,
0.09, 0.09,
0.98, 0.981,
ExitType.NONE, ExitType.NONE,
lambda current_profit, **kwargs: ( lambda current_profit, **kwargs: (
-0.1 if current_profit < 0.6 else -(current_profit * 2) -0.1 if current_profit < 0.6 else -(current_profit * 2)
@ -525,6 +537,19 @@ def test_min_roi_reached3(default_conf, fee) -> None:
ExitType.NONE, ExitType.NONE,
lambda **kwargs: None, lambda **kwargs: None,
), ),
# Error case - Returning inf.
(
0.05,
0.9,
ExitType.NONE,
None,
False,
True,
0.09,
0.9,
ExitType.NONE,
lambda **kwargs: math.inf,
),
], ],
) )
def test_ft_stoploss_reached( def test_ft_stoploss_reached(
@ -552,6 +577,8 @@ def test_ft_stoploss_reached(
exchange="binance", exchange="binance",
open_rate=1, open_rate=1,
liquidation_price=liq, liquidation_price=liq,
price_precision=4,
precision_mode=2,
) )
trade.adjust_min_max_rates(trade.open_rate, trade.open_rate) trade.adjust_min_max_rates(trade.open_rate, trade.open_rate)
strategy.trailing_stop = trailing strategy.trailing_stop = trailing
@ -577,7 +604,7 @@ def test_ft_stoploss_reached(
assert sl_flag.exit_flag is False assert sl_flag.exit_flag is False
else: else:
assert sl_flag.exit_flag is True assert sl_flag.exit_flag is True
assert round(trade.stop_loss, 2) == adjusted assert round(trade.stop_loss, 3) == adjusted
current_rate2 = trade.open_rate * (1 + profit2) current_rate2 = trade.open_rate * (1 + profit2)
sl_flag = strategy.ft_stoploss_reached( sl_flag = strategy.ft_stoploss_reached(
@ -593,7 +620,7 @@ def test_ft_stoploss_reached(
assert sl_flag.exit_flag is False assert sl_flag.exit_flag is False
else: else:
assert sl_flag.exit_flag is True assert sl_flag.exit_flag is True
assert round(trade.stop_loss, 2) == adjusted2 assert round(trade.stop_loss, 3) == adjusted2
strategy.custom_stoploss = original_stopvalue strategy.custom_stoploss = original_stopvalue