mirror of
https://github.com/freqtrade/freqtrade.git
synced 2025-11-29 08:33:07 +00:00
Merge branch 'freqtrade:develop' into feat/telegram-profit-direction
This commit is contained in:
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
5
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -1,5 +1,10 @@
|
||||
<!-- Thank you for sending your pull request. But first, have you included
|
||||
unit tests, and is your code PEP8 conformant? [More details](https://github.com/freqtrade/freqtrade/blob/develop/CONTRIBUTING.md)
|
||||
|
||||
Did you use AI to create your changes?
|
||||
If so, please state it clearly in the PR description (failing to do so may result in your PR being closed).
|
||||
|
||||
Also, please do a line by line review of the changes you made before submitting the PR, reverting all unnecessary changes.
|
||||
-->
|
||||
## Summary
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ repos:
|
||||
- types-filelock==3.2.7
|
||||
- types-requests==2.32.4.20250611
|
||||
- types-tabulate==0.9.0.20241207
|
||||
- types-python-dateutil==2.9.0.20250516
|
||||
- types-python-dateutil==2.9.0.20250708
|
||||
- scipy-stubs==1.16.0.2
|
||||
- SQLAlchemy==2.0.41
|
||||
# stages: [push]
|
||||
@@ -44,7 +44,7 @@ repos:
|
||||
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
# Ruff version.
|
||||
rev: 'v0.12.2'
|
||||
rev: 'v0.12.3'
|
||||
hooks:
|
||||
- id: ruff
|
||||
- id: ruff-format
|
||||
@@ -70,7 +70,7 @@ repos:
|
||||
)$
|
||||
|
||||
- repo: https://github.com/stefmolin/exif-stripper
|
||||
rev: 1.0.0
|
||||
rev: 1.1.0
|
||||
hooks:
|
||||
- id: strip-exif
|
||||
|
||||
|
||||
@@ -159,6 +159,14 @@ This warning can point to one of the below problems:
|
||||
* Barely traded pair -> Check the pair on the exchange webpage, look at the timeframe your strategy uses. If the pair does not have any volume in some candles (usually visualized with a "volume 0" bar, and a "_" as candle), this pair did not have any trades in this timeframe. These pairs should ideally be avoided, as they can cause problems with order-filling.
|
||||
* API problem -> API returns wrong data (this only here for completeness, and should not happen with supported exchanges).
|
||||
|
||||
### I get the message "Couldn't reuse watch for xxx" in the log
|
||||
|
||||
This is an informational message that the bot tried to use candles from the websocket, but the exchange didn't provide the right information.
|
||||
This can happen if there was an interruption to the websocket connection - or if the pair didn't have any trades happen in the timeframe you are using.
|
||||
|
||||
Freqtrade will handle this gracefully by falling back to the REST api.
|
||||
While this makes the iteration slightly slower (due to the REST Api call) - it will not cause any problems to the bot's operation.
|
||||
|
||||
### I'm getting the "Exchange XXX does not support market orders." message and cannot run my strategy
|
||||
|
||||
As the message says, your exchange does not support market orders and you have one of the [order types](configuration.md/#understand-order_types) set to "market". Your strategy was probably written with other exchanges in mind and sets "market" orders for "stoploss" orders, which is correct and preferable for most of the exchanges supporting market orders (but not for Gate.io).
|
||||
|
||||
@@ -18,10 +18,7 @@ from freqtrade.constants import Config
|
||||
from freqtrade.enums import (
|
||||
NON_UTIL_MODES,
|
||||
TRADE_MODES,
|
||||
CandleType,
|
||||
MarginMode,
|
||||
RunMode,
|
||||
TradingMode,
|
||||
)
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.loggers import setup_logging
|
||||
@@ -397,11 +394,6 @@ class Configuration:
|
||||
self._args_to_config(
|
||||
config, argname="trading_mode", logstring="Detected --trading-mode: {}"
|
||||
)
|
||||
config["candle_type_def"] = CandleType.get_default(
|
||||
config.get("trading_mode", "spot") or "spot"
|
||||
)
|
||||
config["trading_mode"] = TradingMode(config.get("trading_mode", "spot") or "spot")
|
||||
config["margin_mode"] = MarginMode(config.get("margin_mode", "") or "")
|
||||
self._args_to_config(
|
||||
config, argname="candle_types", logstring="Detected --candle-types: {}"
|
||||
)
|
||||
|
||||
@@ -44,4 +44,5 @@ from freqtrade.exchange.kraken import Kraken
|
||||
from freqtrade.exchange.kucoin import Kucoin
|
||||
from freqtrade.exchange.lbank import Lbank
|
||||
from freqtrade.exchange.luno import Luno
|
||||
from freqtrade.exchange.modetrade import Modetrade
|
||||
from freqtrade.exchange.okx import Okx
|
||||
|
||||
@@ -63,7 +63,7 @@ class Binance(Exchange):
|
||||
}
|
||||
|
||||
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
|
||||
# TradingMode.SPOT always supported and not required in this list
|
||||
(TradingMode.SPOT, MarginMode.NONE),
|
||||
# (TradingMode.MARGIN, MarginMode.CROSS),
|
||||
(TradingMode.FUTURES, MarginMode.CROSS),
|
||||
(TradingMode.FUTURES, MarginMode.ISOLATED),
|
||||
|
||||
@@ -64,9 +64,9 @@ class Bybit(Exchange):
|
||||
}
|
||||
|
||||
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
|
||||
# TradingMode.SPOT always supported and not required in this list
|
||||
(TradingMode.SPOT, MarginMode.NONE),
|
||||
(TradingMode.FUTURES, MarginMode.ISOLATED),
|
||||
# (TradingMode.FUTURES, MarginMode.CROSS),
|
||||
(TradingMode.FUTURES, MarginMode.ISOLATED)
|
||||
]
|
||||
|
||||
@property
|
||||
|
||||
@@ -169,7 +169,8 @@ class Exchange:
|
||||
_ft_has_futures: FtHas = {}
|
||||
|
||||
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
|
||||
# TradingMode.SPOT always supported and not required in this list
|
||||
# Non-defined exchanges only support spot mode.
|
||||
(TradingMode.SPOT, MarginMode.NONE),
|
||||
]
|
||||
|
||||
def __init__(
|
||||
@@ -198,13 +199,19 @@ class Exchange:
|
||||
self.loop = self._init_async_loop()
|
||||
self._config: Config = {}
|
||||
|
||||
self._config.update(config)
|
||||
|
||||
# Leverage properties
|
||||
self.trading_mode: TradingMode = config.get("trading_mode", TradingMode.SPOT)
|
||||
self.margin_mode: MarginMode = (
|
||||
MarginMode(config.get("margin_mode")) if config.get("margin_mode") else MarginMode.NONE
|
||||
self.trading_mode: TradingMode = TradingMode(
|
||||
config.get("trading_mode", self._supported_trading_mode_margin_pairs[0][0])
|
||||
)
|
||||
self.margin_mode: MarginMode = MarginMode(
|
||||
MarginMode(config.get("margin_mode"))
|
||||
if config.get("margin_mode")
|
||||
else self._supported_trading_mode_margin_pairs[0][1]
|
||||
)
|
||||
config["trading_mode"] = self.trading_mode
|
||||
config["margin_mode"] = self.margin_mode
|
||||
config["candle_type_def"] = CandleType.get_default(self.trading_mode)
|
||||
self._config.update(config)
|
||||
self.liquidation_buffer = config.get("liquidation_buffer", 0.05)
|
||||
|
||||
exchange_conf: ExchangeConfig = exchange_config if exchange_config else config["exchange"]
|
||||
@@ -2596,10 +2603,12 @@ class Exchange:
|
||||
if ticks and cache:
|
||||
idx = -2 if drop_incomplete and len(ticks) > 1 else -1
|
||||
self._pairs_last_refresh_time[(pair, timeframe, c_type)] = ticks[idx][0]
|
||||
# keeping parsed dataframe in cache
|
||||
has_cache = cache and (pair, timeframe, c_type) in self._klines
|
||||
# in case of existing cache, fill_missing happens after concatenation
|
||||
ohlcv_df = ohlcv_to_dataframe(
|
||||
ticks, timeframe, pair=pair, fill_missing=True, drop_incomplete=drop_incomplete
|
||||
ticks, timeframe, pair=pair, fill_missing=not has_cache, drop_incomplete=drop_incomplete
|
||||
)
|
||||
# keeping parsed dataframe in cache
|
||||
if cache:
|
||||
if (pair, timeframe, c_type) in self._klines:
|
||||
old = self._klines[(pair, timeframe, c_type)]
|
||||
|
||||
@@ -27,7 +27,7 @@ from freqtrade.exchange.common import (
|
||||
SUPPORTED_EXCHANGES,
|
||||
)
|
||||
from freqtrade.exchange.exchange_utils_timeframe import timeframe_to_minutes, timeframe_to_prev_date
|
||||
from freqtrade.ft_types import ValidExchangesType
|
||||
from freqtrade.ft_types import TradeModeType, ValidExchangesType
|
||||
from freqtrade.util import FtPrecise
|
||||
|
||||
|
||||
@@ -110,7 +110,7 @@ def _build_exchange_list_entry(
|
||||
"trade_modes": [{"trading_mode": "spot", "margin_mode": ""}],
|
||||
}
|
||||
if resolved := exchangeClasses.get(mapped_exchange_name):
|
||||
supported_modes = [{"trading_mode": "spot", "margin_mode": ""}] + [
|
||||
supported_modes: list[TradeModeType] = [
|
||||
{"trading_mode": tm.value, "margin_mode": mm.value}
|
||||
for tm, mm in resolved["class"]._supported_trading_mode_margin_pairs
|
||||
]
|
||||
|
||||
@@ -55,10 +55,10 @@ class Gate(Exchange):
|
||||
}
|
||||
|
||||
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
|
||||
# TradingMode.SPOT always supported and not required in this list
|
||||
(TradingMode.SPOT, MarginMode.NONE),
|
||||
# (TradingMode.MARGIN, MarginMode.CROSS),
|
||||
# (TradingMode.FUTURES, MarginMode.CROSS),
|
||||
(TradingMode.FUTURES, MarginMode.ISOLATED)
|
||||
(TradingMode.FUTURES, MarginMode.ISOLATED),
|
||||
]
|
||||
|
||||
@retrier
|
||||
|
||||
@@ -28,6 +28,7 @@ class Hyperliquid(Exchange):
|
||||
"stoploss_on_exchange": False,
|
||||
"exchange_has_overrides": {"fetchTrades": False},
|
||||
"marketOrderRequiresPrice": True,
|
||||
"ws_enabled": True,
|
||||
}
|
||||
_ft_has_futures: FtHas = {
|
||||
"stoploss_on_exchange": True,
|
||||
@@ -40,7 +41,8 @@ class Hyperliquid(Exchange):
|
||||
}
|
||||
|
||||
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
|
||||
(TradingMode.FUTURES, MarginMode.ISOLATED)
|
||||
(TradingMode.SPOT, MarginMode.NONE),
|
||||
(TradingMode.FUTURES, MarginMode.ISOLATED),
|
||||
]
|
||||
|
||||
@property
|
||||
|
||||
@@ -35,7 +35,7 @@ class Kraken(Exchange):
|
||||
}
|
||||
|
||||
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
|
||||
# TradingMode.SPOT always supported and not required in this list
|
||||
(TradingMode.SPOT, MarginMode.NONE),
|
||||
# (TradingMode.MARGIN, MarginMode.CROSS),
|
||||
# (TradingMode.FUTURES, MarginMode.CROSS)
|
||||
]
|
||||
|
||||
27
freqtrade/exchange/modetrade.py
Normal file
27
freqtrade/exchange/modetrade.py
Normal file
@@ -0,0 +1,27 @@
|
||||
import logging
|
||||
|
||||
# from freqtrade.enums import MarginMode, TradingMode
|
||||
from freqtrade.exchange import Exchange
|
||||
from freqtrade.exchange.exchange_types import FtHas
|
||||
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class Modetrade(Exchange):
|
||||
"""
|
||||
MOdetrade exchange class. Contains adjustments needed for Freqtrade to work
|
||||
with this exchange.
|
||||
|
||||
Please note that this exchange is not included in the list of exchanges
|
||||
officially supported by the Freqtrade development team. So some features
|
||||
may still not work as expected.
|
||||
"""
|
||||
|
||||
_ft_has: FtHas = {
|
||||
"always_require_api_keys": True, # Requires API keys to fetch candles
|
||||
}
|
||||
|
||||
# _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
|
||||
# (TradingMode.FUTURES, MarginMode.ISOLATED),
|
||||
# ]
|
||||
@@ -49,7 +49,7 @@ class Okx(Exchange):
|
||||
}
|
||||
|
||||
_supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [
|
||||
# TradingMode.SPOT always supported and not required in this list
|
||||
(TradingMode.SPOT, MarginMode.NONE),
|
||||
# (TradingMode.MARGIN, MarginMode.CROSS),
|
||||
# (TradingMode.FUTURES, MarginMode.CROSS),
|
||||
(TradingMode.FUTURES, MarginMode.ISOLATED),
|
||||
|
||||
@@ -8,4 +8,4 @@ from freqtrade.ft_types.backtest_result_type import (
|
||||
get_BacktestResultType_default,
|
||||
)
|
||||
from freqtrade.ft_types.plot_annotation_type import AnnotationType
|
||||
from freqtrade.ft_types.valid_exchanges_type import ValidExchangesType
|
||||
from freqtrade.ft_types.valid_exchanges_type import TradeModeType, ValidExchangesType
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
# Requirements for freqtrade client library
|
||||
requests==2.32.4
|
||||
python-rapidjson==1.20
|
||||
python-rapidjson==1.21
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
-r requirements-freqai-rl.txt
|
||||
-r docs/requirements-docs.txt
|
||||
|
||||
ruff==0.12.2
|
||||
ruff==0.12.3
|
||||
mypy==1.16.1
|
||||
pre-commit==4.2.0
|
||||
pytest==8.4.1
|
||||
@@ -29,4 +29,4 @@ types-cachetools==6.0.0.20250525
|
||||
types-filelock==3.2.7
|
||||
types-requests==2.32.4.20250611
|
||||
types-tabulate==0.9.0.20241207
|
||||
types-python-dateutil==2.9.0.20250516
|
||||
types-python-dateutil==2.9.0.20250708
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
numpy==2.3.1
|
||||
pandas==2.3.0
|
||||
pandas==2.3.1
|
||||
bottleneck==1.5.0
|
||||
numexpr==2.11.0
|
||||
# Indicator libraries
|
||||
@@ -7,9 +7,9 @@ ft-pandas-ta==0.3.15
|
||||
ta-lib==0.5.5
|
||||
technical==1.5.1
|
||||
|
||||
ccxt==4.4.92
|
||||
ccxt==4.4.94
|
||||
cryptography==45.0.5
|
||||
aiohttp==3.12.13
|
||||
aiohttp==3.12.14
|
||||
SQLAlchemy==2.0.41
|
||||
python-telegram-bot==22.2
|
||||
# can't be hard-pinned due to telegram-bot pinning httpx with ~
|
||||
@@ -18,7 +18,7 @@ humanize==4.12.3
|
||||
cachetools==6.1.0
|
||||
requests==2.32.4
|
||||
urllib3==2.5.0
|
||||
certifi==2025.6.15
|
||||
certifi==2025.7.14
|
||||
jsonschema==4.24.0
|
||||
tabulate==0.9.0
|
||||
pycoingecko==3.2.0
|
||||
@@ -28,7 +28,7 @@ rich==14.0.0
|
||||
pyarrow==20.0.0; platform_machine != 'armv7l'
|
||||
|
||||
# Load ticker files 30% faster
|
||||
python-rapidjson==1.20
|
||||
python-rapidjson==1.21
|
||||
# Properly format api responses
|
||||
orjson==3.10.18
|
||||
|
||||
@@ -36,7 +36,7 @@ orjson==3.10.18
|
||||
sdnotify==0.3.2
|
||||
|
||||
# API Server
|
||||
fastapi==0.115.14
|
||||
fastapi==0.116.1
|
||||
pydantic==2.11.7
|
||||
uvicorn==0.35.0
|
||||
pyjwt==2.10.1
|
||||
|
||||
@@ -258,6 +258,7 @@ def patch_exchange(
|
||||
"._supported_trading_mode_margin_pairs",
|
||||
PropertyMock(
|
||||
return_value=[
|
||||
(TradingMode.SPOT, MarginMode.NONE),
|
||||
(TradingMode.MARGIN, MarginMode.CROSS),
|
||||
(TradingMode.MARGIN, MarginMode.ISOLATED),
|
||||
(TradingMode.FUTURES, MarginMode.CROSS),
|
||||
|
||||
@@ -10,7 +10,6 @@ from freqtrade.configuration.timerange import TimeRange
|
||||
from freqtrade.data import history
|
||||
from freqtrade.data.dataprovider import DataProvider
|
||||
from freqtrade.enums import RunMode
|
||||
from freqtrade.enums.candletype import CandleType
|
||||
from freqtrade.exceptions import OperationalException
|
||||
from freqtrade.freqai.data_kitchen import FreqaiDataKitchen
|
||||
from freqtrade.optimize.backtesting import Backtesting
|
||||
@@ -142,7 +141,7 @@ def test_freqai_backtest_consistent_timerange(mocker, freqai_conf):
|
||||
|
||||
gbs = mocker.patch("freqtrade.optimize.backtesting.generate_backtest_stats")
|
||||
|
||||
freqai_conf["candle_type_def"] = CandleType.FUTURES
|
||||
freqai_conf["trading_mode"] = "futures"
|
||||
freqai_conf.get("exchange", {}).update({"pair_whitelist": ["XRP/USDT:USDT"]})
|
||||
freqai_conf.get("freqai", {}).get("feature_parameters", {}).update(
|
||||
{"include_timeframes": ["5m", "1h"], "include_corr_pairlist": []}
|
||||
|
||||
@@ -354,6 +354,7 @@ def test_informative_decorator(mocker, default_conf_usdt, trading_mode):
|
||||
default_conf_usdt["strategy"] = "InformativeDecoratorTest"
|
||||
strategy = StrategyResolver.load_strategy(default_conf_usdt)
|
||||
exchange = get_patched_exchange(mocker, default_conf_usdt)
|
||||
default_conf_usdt["candle_type_def"] = candle_def
|
||||
strategy.dp = DataProvider({}, exchange, None)
|
||||
mocker.patch.object(
|
||||
strategy.dp, "current_whitelist", return_value=["XRP/USDT", "LTC/USDT", "NEO/USDT"]
|
||||
|
||||
Reference in New Issue
Block a user