diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 1ff65660003..d20e5d78d5f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -24,7 +24,7 @@ repos: - types-filelock==3.2.7 - types-requests==2.33.0.20260712 - types-tabulate==0.10.0.20260508 - - types-python-dateutil==2.9.0.20260716 + - types-python-dateutil==2.9.0.20260807 - scipy-stubs==1.17.1.5 - SQLAlchemy==2.0.51 # stages: [push] diff --git a/build_helpers/schema.json b/build_helpers/schema.json index 11f6a6727bb..bc1985b17e5 100644 --- a/build_helpers/schema.json +++ b/build_helpers/schema.json @@ -1275,34 +1275,55 @@ }, "key": { "description": "API key for the exchange. Recommended to be set via environment variable FREQTRADE__EXCHANGE__KEY", - "type": "string", - "default": "" + "type": [ + "string", + "null" + ], + "default": null }, "secret": { "description": "API secret for the exchange. Recommended to be set via environment variable FREQTRADE__EXCHANGE__SECRET", - "type": "string", - "default": "" + "type": [ + "string", + "null" + ], + "default": null }, "password": { "description": "Password for the exchange, if required. Recommended to be set via environment variable FREQTRADE__EXCHANGE__PASSWORD", - "type": "string", - "default": "" + "type": [ + "string", + "null" + ], + "default": null }, "uid": { "description": "User ID for the exchange, if required. Recommended to be set via environment variable FREQTRADE__EXCHANGE__UID", - "type": "string" + "type": [ + "string", + "null" + ] }, "account_id": { "description": "Account ID for the exchange, if required. Recommended to be set via environment variable FREQTRADE__EXCHANGE__ACCOUNT_ID", - "type": "string" + "type": [ + "string", + "null" + ] }, "wallet_address": { "description": "Wallet address for the exchange, if required. Usually used by DEX exchanges. Recommended to be set via environment variable FREQTRADE__EXCHANGE__WALLET_ADDRESS", - "type": "string" + "type": [ + "string", + "null" + ] }, "private_key": { "description": "Private key for the exchange, if required. Usually used by DEX exchanges. Recommended to be set via environment variable FREQTRADE__EXCHANGE__PRIVATE_KEY", - "type": "string" + "type": [ + "string", + "null" + ] }, "pair_whitelist": { "description": "List of whitelisted trading pairs.", diff --git a/freqtrade/config_schema/config_schema.py b/freqtrade/config_schema/config_schema.py index 2e0903f4f9e..aaee625d302 100644 --- a/freqtrade/config_schema/config_schema.py +++ b/freqtrade/config_schema/config_schema.py @@ -940,37 +940,37 @@ "description": ( f"API key for the exchange. {__VIA_ENV} FREQTRADE__EXCHANGE__KEY" ), - "type": "string", - "default": "", + "type": ["string", "null"], + "default": None, }, "secret": { "description": ( f"API secret for the exchange. {__VIA_ENV} FREQTRADE__EXCHANGE__SECRET" ), - "type": "string", - "default": "", + "type": ["string", "null"], + "default": None, }, "password": { "description": ( "Password for the exchange, if required. " f"{__VIA_ENV} FREQTRADE__EXCHANGE__PASSWORD" ), - "type": "string", - "default": "", + "type": ["string", "null"], + "default": None, }, "uid": { "description": ( "User ID for the exchange, if required. " f"{__VIA_ENV} FREQTRADE__EXCHANGE__UID" ), - "type": "string", + "type": ["string", "null"], }, "account_id": { "description": ( "Account ID for the exchange, if required. " f"{__VIA_ENV} FREQTRADE__EXCHANGE__ACCOUNT_ID" ), - "type": "string", + "type": ["string", "null"], }, "wallet_address": { "description": ( @@ -978,14 +978,14 @@ "Usually used by DEX exchanges. " f"{__VIA_ENV} FREQTRADE__EXCHANGE__WALLET_ADDRESS" ), - "type": "string", + "type": ["string", "null"], }, "private_key": { "description": ( "Private key for the exchange, if required. Usually used by DEX exchanges. " f"{__VIA_ENV} FREQTRADE__EXCHANGE__PRIVATE_KEY" ), - "type": "string", + "type": ["string", "null"], }, "pair_whitelist": { "description": "List of whitelisted trading pairs.", diff --git a/freqtrade/configuration/config_secrets.py b/freqtrade/configuration/config_secrets.py index 797caba2342..c36042c3b25 100644 --- a/freqtrade/configuration/config_secrets.py +++ b/freqtrade/configuration/config_secrets.py @@ -64,4 +64,4 @@ def remove_exchange_credentials(exchange_config: ExchangeConfig, dry_run: bool) if "." in key: key1 = key.removeprefix("exchange.") if key1 in exchange_config: - exchange_config[key1] = "" + exchange_config[key1] = None diff --git a/freqtrade/constants.py b/freqtrade/constants.py index e86dde00d7d..dafbd411e6b 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -196,8 +196,8 @@ "dry_run": True, "exchange": { "name": "", - "key": "", - "secret": "", + "key": None, + "secret": None, "pair_whitelist": [], "ccxt_async_config": {}, }, diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index ff66c230ebd..b33690a168e 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -399,8 +399,8 @@ def _init_ccxt( ), "secret": exchange_config.get("secret"), "password": exchange_config.get("password"), - "uid": exchange_config.get("uid", ""), - "accountId": exchange_config.get("account_id", exchange_config.get("accountId", "")), + "uid": exchange_config.get("uid"), + "accountId": exchange_config.get("account_id", exchange_config.get("accountId")), # DEX attributes: "walletAddress": exchange_config.get( "wallet_address", exchange_config.get("walletAddress") diff --git a/requirements-dev.txt b/requirements-dev.txt index 4d7aaea9b8e..89275346e0c 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,7 +6,7 @@ -r requirements-freqai-rl.txt -r docs/requirements-docs.txt -ruff==0.16.1 +ruff==0.16.2 mypy==2.3.0 pre-commit==4.6.1 pytest==9.1.1 @@ -17,7 +17,7 @@ pytest-random-order==1.2.0 pytest-timeout==2.4.0 pytest-xdist==3.8.0 # For datetime mocking -time-machine==3.3.0 +time-machine==3.3.1 # Convert jupyter notebooks to markdown documents nbconvert==7.17.1 @@ -28,7 +28,7 @@ types-cachetools==7.0.0.20260713 types-filelock==3.2.7 types-requests==2.33.0.20260712 types-tabulate==0.10.0.20260508 -types-python-dateutil==2.9.0.20260716 +types-python-dateutil==2.9.0.20260807 pip-audit==2.10.1 # For build step in CI build==1.5.0 diff --git a/requirements.txt b/requirements.txt index 7347549a988..2785f62ff85 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,7 +8,7 @@ ft-pandas-ta==0.3.16 ta-lib==0.7.1 technical==1.7.0 -ccxt==4.5.71 +ccxt==4.5.73 cryptography==50.0.0 aiohttp==3.14.3 SQLAlchemy==2.0.51 @@ -60,4 +60,4 @@ websockets==17.0.1 janus==2.0.0 ast-comments==1.3.0 -packaging==26.2 +packaging==26.3 diff --git a/tests/exchange_online/conftest.py b/tests/exchange_online/conftest.py index 623da685f40..08ff88ac201 100644 --- a/tests/exchange_online/conftest.py +++ b/tests/exchange_online/conftest.py @@ -773,8 +773,8 @@ class TestExchangeOnlineSetup(TypedDict): def exchange_conf(): config = get_default_conf_usdt((Path(__file__).parent / "testdata").resolve()) config["exchange"]["pair_whitelist"] = [] - config["exchange"]["key"] = "" - config["exchange"]["secret"] = "" + config["exchange"]["apiKey"] = None + config["exchange"]["secret"] = None config["dry_run"] = False config["entry_pricing"]["use_order_book"] = True config["exit_pricing"]["use_order_book"] = True diff --git a/tests/freqtradebot/test_freqtradebot.py b/tests/freqtradebot/test_freqtradebot.py index 0fdfdbb74ad..0176c33ac59 100644 --- a/tests/freqtradebot/test_freqtradebot.py +++ b/tests/freqtradebot/test_freqtradebot.py @@ -188,16 +188,16 @@ def test_load_strategy_no_keys(default_conf_usdt, mocker, runmode, caplog) -> No strategy_config = freqtrade.strategy.config assert id(strategy_config["exchange"]) == id(conf["exchange"]) # Keys have been removed and are not passed to the exchange - assert strategy_config["exchange"]["key"] == "" - assert strategy_config["exchange"]["secret"] == "" + assert strategy_config["exchange"]["key"] is None + assert strategy_config["exchange"]["secret"] is None assert erm.call_count == 1 ex_conf = erm.call_args_list[0][1]["exchange_config"] assert id(ex_conf) != id(conf["exchange"]) # Keys are still present - assert ex_conf["key"] != "" + assert ex_conf["key"] is not None assert ex_conf["key"] == default_conf_usdt["exchange"]["key"] - assert ex_conf["secret"] != "" + assert ex_conf["secret"] is not None assert ex_conf["secret"] == default_conf_usdt["exchange"]["secret"] diff --git a/tests/test_configuration.py b/tests/test_configuration.py index e58a7176aa5..dc8e1974dc9 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -1118,9 +1118,9 @@ def test_load_config_stoploss_exchange_limit_ratio(all_conf) -> None: @pytest.mark.parametrize( "keys", [ - ("exchange", "key", ""), - ("exchange", "secret", ""), - ("exchange", "password", ""), + ("exchange", "key", None), + ("exchange", "secret", None), + ("exchange", "password", None), ], ) def test_load_config_default_subkeys(all_conf, keys) -> None: @@ -1613,11 +1613,11 @@ def test_remove_exchange_credentials(default_conf) -> None: conf = deepcopy(default_conf) remove_exchange_credentials(conf["exchange"], False) - assert conf["exchange"]["key"] != "" - assert conf["exchange"]["secret"] != "" + assert conf["exchange"]["key"] is not None + assert conf["exchange"]["secret"] is not None remove_exchange_credentials(conf["exchange"], True) - assert conf["exchange"]["key"] == "" - assert conf["exchange"]["secret"] == "" - assert conf["exchange"].get("password", "") == "" - assert conf["exchange"].get("uid", "") == "" + assert conf["exchange"]["key"] is None + assert conf["exchange"]["secret"] is None + assert conf["exchange"].get("password") is None + assert conf["exchange"].get("uid") is None