diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9a38e7d8..39f1dbe22 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,7 +25,7 @@ jobs: strategy: matrix: os: [ "ubuntu-20.04", "ubuntu-22.04", "ubuntu-24.04" ] - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v4 @@ -72,7 +72,7 @@ jobs: pytest --random-order --cov=freqtrade --cov=freqtrade_client --cov-config=.coveragerc - name: Coveralls - if: (runner.os == 'Linux' && matrix.python-version == '3.10' && matrix.os == 'ubuntu-22.04') + if: (runner.os == 'Linux' && matrix.python-version == '3.12' && matrix.os == 'ubuntu-22.04') env: # Coveralls token. Not used as secret due to github not providing secrets to forked repositories COVERALLS_REPO_TOKEN: 6D1m0xupS3FgutfuGao8keFf9Hc0FpIXu @@ -139,10 +139,7 @@ jobs: strategy: matrix: os: [ "macos-12", "macos-13", "macos-14" ] - python-version: ["3.9", "3.10", "3.11", "3.12"] - exclude: - - os: "macos-14" - python-version: "3.9" + python-version: ["3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v4 @@ -263,7 +260,7 @@ jobs: strategy: matrix: os: [ windows-latest ] - python-version: ["3.9", "3.10", "3.11", "3.12"] + python-version: ["3.10", "3.11", "3.12"] steps: - uses: actions/checkout@v4 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 57a5ad437..3f063b144 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -31,7 +31,7 @@ repos: - repo: https://github.com/charliermarsh/ruff-pre-commit # Ruff version. - rev: 'v0.6.7' + rev: 'v0.6.8' hooks: - id: ruff - id: ruff-format diff --git a/README.md b/README.md index 317a6cfdf..9f4fc51fb 100644 --- a/README.md +++ b/README.md @@ -61,7 +61,7 @@ Please find the complete documentation on the [freqtrade website](https://www.fr ## Features -- [x] **Based on Python 3.9+**: For botting on any operating system - Windows, macOS and Linux. +- [x] **Based on Python 3.10+**: For botting on any operating system - Windows, macOS and Linux. - [x] **Persistence**: Persistence is achieved through sqlite. - [x] **Dry-run**: Run the bot without paying money. - [x] **Backtesting**: Run a simulation of your buy/sell strategy. @@ -218,7 +218,7 @@ To run this bot we recommend you a cloud instance with a minimum of: ### Software requirements -- [Python >= 3.9](http://docs.python-guide.org/en/latest/starting/installation/) +- [Python >= 3.10](http://docs.python-guide.org/en/latest/starting/installation/) - [pip](https://pip.pypa.io/en/stable/installing/) - [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) - [TA-Lib](https://ta-lib.github.io/ta-lib-python/) diff --git a/build_helpers/TA_Lib-0.4.32-cp39-cp39-linux_armv7l.whl b/build_helpers/TA_Lib-0.4.32-cp39-cp39-linux_armv7l.whl deleted file mode 100644 index 03bc79df8..000000000 Binary files a/build_helpers/TA_Lib-0.4.32-cp39-cp39-linux_armv7l.whl and /dev/null differ diff --git a/build_helpers/TA_Lib-0.4.32-cp39-cp39-win_amd64.whl b/build_helpers/TA_Lib-0.4.32-cp39-cp39-win_amd64.whl deleted file mode 100644 index f0c46dafe..000000000 Binary files a/build_helpers/TA_Lib-0.4.32-cp39-cp39-win_amd64.whl and /dev/null differ diff --git a/build_helpers/schema.json b/build_helpers/schema.json index 8438dc3a0..6a73e75b0 100644 --- a/build_helpers/schema.json +++ b/build_helpers/schema.json @@ -579,57 +579,6 @@ ] } }, - "protections": { - "description": "Configuration for various protections.", - "type": "array", - "items": { - "type": "object", - "properties": { - "method": { - "description": "Method used for the protection.", - "type": "string", - "enum": [ - "CooldownPeriod", - "LowProfitPairs", - "MaxDrawdown", - "StoplossGuard" - ] - }, - "stop_duration": { - "description": "Duration to lock the pair after a protection is triggered, in minutes.", - "type": "number", - "minimum": 0.0 - }, - "stop_duration_candles": { - "description": "Duration to lock the pair after a protection is triggered, in number of candles.", - "type": "number", - "minimum": 0 - }, - "unlock_at": { - "description": "Time when trading will be unlocked regularly. Format: HH:MM", - "type": "string" - }, - "trade_limit": { - "description": "Minimum number of trades required during lookback period.", - "type": "number", - "minimum": 1 - }, - "lookback_period": { - "description": "Period to look back for protection checks, in minutes.", - "type": "number", - "minimum": 1 - }, - "lookback_period_candles": { - "description": "Period to look back for protection checks, in number of candles.", - "type": "number", - "minimum": 1 - } - }, - "required": [ - "method" - ] - } - }, "telegram": { "description": "Telegram settings.", "type": "object", diff --git a/docs/configuration.md b/docs/configuration.md index b05b1dcaa..074c9a577 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -229,7 +229,6 @@ Mandatory parameters are marked as **Required**, which means that they are requi | | **Plugins** | `edge.*` | Please refer to [edge configuration document](edge.md) for detailed explanation of all possible configuration options. | `pairlists` | Define one or more pairlists to be used. [More information](plugins.md#pairlists-and-pairlist-handlers).
*Defaults to `StaticPairList`.*
**Datatype:** List of Dicts -| `protections` | Define one or more protections to be used. [More information](plugins.md#protections).
**Datatype:** List of Dicts | | **Telegram** | `telegram.enabled` | Enable the usage of Telegram.
**Datatype:** Boolean | `telegram.token` | Your Telegram bot token. Only required if `telegram.enabled` is `true`.
**Keep it in secret, do not disclose publicly.**
**Datatype:** String diff --git a/docs/deprecated.md b/docs/deprecated.md index 6719ce56d..5357acc62 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -75,7 +75,10 @@ Webhook terminology changed from "sell" to "exit", and from "buy" to "entry", re * `webhooksellfill`, `webhookexitfill` -> `exit_fill` * `webhooksellcancel`, `webhookexitcancel` -> `exit_cancel` - ## Removal of `populate_any_indicators` version 2023.3 saw the removal of `populate_any_indicators` in favor of split methods for feature engineering and targets. Please read the [migration document](strategy_migration.md#freqai-strategy) for full details. + +## Removal of `protections` from configuration + + Setting protections from the configuration via `"protections": [],` has been removed in 2024.10, after having raised deprecation warnings for over 3 years. diff --git a/docs/developer.md b/docs/developer.md index 127e8e5d5..401de07c1 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -241,7 +241,6 @@ No protection should use datetime directly, but use the provided `date_now` vari !!! Tip "Writing a new Protection" Best copy one of the existing Protections to have a good example. - Don't forget to register your protection in `constants.py` under the variable `AVAILABLE_PROTECTIONS` - otherwise it will not be selectable. #### Implementation of a new protection diff --git a/docs/hyperopt.md b/docs/hyperopt.md index f88928344..43085029c 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -445,7 +445,6 @@ While this strategy is most likely too simple to provide consistent profit, it s Whether you are using `.range` functionality or the alternatives above, you should try to use space ranges as small as possible since this will improve CPU/RAM usage. - ## Optimizing protections Freqtrade can also optimize protections. How you optimize protections is up to you, and the following should be considered as example only. diff --git a/docs/includes/pairlists.md b/docs/includes/pairlists.md index 804190e24..8d79a7bc1 100644 --- a/docs/includes/pairlists.md +++ b/docs/includes/pairlists.md @@ -360,14 +360,21 @@ The optional `bearer_token` will be included in the requests Authorization Heade "method": "MarketCapPairList", "number_assets": 20, "max_rank": 50, - "refresh_period": 86400 + "refresh_period": 86400, + "categories": ["layer-1"] } ] ``` `number_assets` defines the maximum number of pairs returned by the pairlist. `max_rank` will determine the maximum rank used in creating/filtering the pairlist. It's expected that some coins within the top `max_rank` marketcap will not be included in the resulting pairlist since not all pairs will have active trading pairs in your preferred market/stake/exchange combination. -`refresh_period` setting defines the period (in seconds) at which the marketcap rank data will be refreshed. Defaults to 86,400s (1 day). The pairlist cache (`refresh_period`) is applicable on both generating pairlists (first position in the list) and filtering instances (not the first position in the list). +The `refresh_period` setting defines the interval (in seconds) at which the marketcap rank data will be refreshed. The default is 86,400 seconds (1 day). The pairlist cache (`refresh_period`) applies to both generating pairlists (when in the first position in the list) and filtering instances (when not in the first position in the list). + +The `categories` setting specifies the [coingecko categories](https://www.coingecko.com/en/categories) from which to select coins from. The default is an empty list `[]`, meaning no category filtering is applied. +If an incorrect category string is chosen, the plugin will print the available categories from CoinGecko and fail. The category should be the ID of the category, for example, for `https://www.coingecko.com/en/categories/layer-1`, the category ID would be `layer-1`. You can pass multiple categories such as `["layer-1", "meme-token"]` to select from several categories. + +!!! Warning "Many categories" + Each added category corresponds to one API call to CoinGecko. The more categories you add, the longer the pairlist generation will take, potentially causing rate limit issues. #### AgeFilter diff --git a/docs/includes/protections.md b/docs/includes/protections.md index a4cb9d3cc..c32846165 100644 --- a/docs/includes/protections.md +++ b/docs/includes/protections.md @@ -1,24 +1,16 @@ ## Protections -!!! Warning "Beta feature" - This feature is still in it's testing phase. Should you notice something you think is wrong please let us know via Discord or via Github Issue. - Protections will protect your strategy from unexpected events and market conditions by temporarily stop trading for either one pair, or for all pairs. All protection end times are rounded up to the next candle to avoid sudden, unexpected intra-candle buys. -!!! Note +!!! Tip "Usage tips" Not all Protections will work for all strategies, and parameters will need to be tuned for your strategy to improve performance. -!!! Tip Each Protection can be configured multiple times with different parameters, to allow different levels of protection (short-term / long-term). !!! Note "Backtesting" Protections are supported by backtesting and hyperopt, but must be explicitly enabled by using the `--enable-protections` flag. -!!! Warning "Setting protections from the configuration" - Setting protections from the configuration via `"protections": [],` key should be considered deprecated and will be removed in a future version. - It is also no longer guaranteed that your protections apply to the strategy in cases where the strategy defines [protections as property](hyperopt.md#optimizing-protections). - ### Available Protections * [`StoplossGuard`](#stoploss-guard) Stop trading if a certain amount of stoploss occurred within a certain time window. diff --git a/docs/index.md b/docs/index.md index f2d1482c9..d6dca4880 100644 --- a/docs/index.md +++ b/docs/index.md @@ -85,7 +85,7 @@ To run this bot we recommend you a linux cloud instance with a minimum of: Alternatively -- Python 3.9+ +- Python 3.10+ - pip (pip3) - git - TA-Lib diff --git a/docs/installation.md b/docs/installation.md index f86043fb3..02cbb7f3e 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -24,7 +24,7 @@ The easiest way to install and run Freqtrade is to clone the bot Github reposito The `stable` branch contains the code of the last release (done usually once per month on an approximately one week old snapshot of the `develop` branch to prevent packaging bugs, so potentially it's more stable). !!! Note - Python3.9 or higher and the corresponding `pip` are assumed to be available. The install-script will warn you and stop if that's not the case. `git` is also needed to clone the Freqtrade repository. + Python3.10 or higher and the corresponding `pip` are assumed to be available. The install-script will warn you and stop if that's not the case. `git` is also needed to clone the Freqtrade repository. Also, python headers (`python-dev` / `python-devel`) must be available for the installation to complete successfully. !!! Warning "Up-to-date clock" @@ -42,7 +42,7 @@ These requirements apply to both [Script Installation](#script-installation) and ### Install guide -* [Python >= 3.9](http://docs.python-guide.org/en/latest/starting/installation/) +* [Python >= 3.10](http://docs.python-guide.org/en/latest/starting/installation/) * [pip](https://pip.pypa.io/en/stable/installing/) * [git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) * [virtualenv](https://virtualenv.pypa.io/en/stable/installation.html) (Recommended) @@ -54,7 +54,7 @@ We've included/collected install instructions for Ubuntu, MacOS, and Windows. Th OS Specific steps are listed first, the common section below is necessary for all systems. !!! Note - Python3.9 or higher and the corresponding pip are assumed to be available. + Python3.10 or higher and the corresponding pip are assumed to be available. === "Debian/Ubuntu" #### Install necessary dependencies @@ -69,7 +69,7 @@ OS Specific steps are listed first, the common section below is necessary for al === "RaspberryPi/Raspbian" The following assumes the latest [Raspbian Buster lite image](https://www.raspberrypi.org/downloads/raspbian/). - This image comes with python3.9 preinstalled, making it easy to get freqtrade up and running. + This image comes with python3.11 preinstalled, making it easy to get freqtrade up and running. Tested using a Raspberry Pi 3 with the Raspbian Buster lite image, all updates applied. @@ -169,7 +169,7 @@ You can as well update, configure and reset the codebase of your bot with `./scr ** --install ** With this option, the script will install the bot and most dependencies: -You will need to have git and python3.9+ installed beforehand for this to work. +You will need to have git and python3.10+ installed beforehand for this to work. * Mandatory software as: `ta-lib` * Setup your virtualenv under `.venv/` diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 141490354..a58589144 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,7 +1,7 @@ markdown==3.7 mkdocs==1.6.1 -mkdocs-material==9.5.36 +mkdocs-material==9.5.39 mdx_truly_sane_lists==1.3 -pymdown-extensions==10.10.1 +pymdown-extensions==10.11.1 jinja2==3.1.4 mike==2.1.3 diff --git a/docs/windows_installation.md b/docs/windows_installation.md index 5e28d98fb..c824ee5de 100644 --- a/docs/windows_installation.md +++ b/docs/windows_installation.md @@ -5,7 +5,7 @@ We **strongly** recommend that Windows users use [Docker](docker_quickstart.md) If that is not possible, try using the Windows Linux subsystem (WSL) - for which the Ubuntu instructions should work. Otherwise, please follow the instructions below. -All instructions assume that python 3.9+ is installed and available. +All instructions assume that python 3.10+ is installed and available. ## Clone the git repository @@ -42,7 +42,7 @@ cd freqtrade Install ta-lib according to the [ta-lib documentation](https://github.com/TA-Lib/ta-lib-python#windows). -As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), Freqtrade provides these dependencies (in the binary wheel format) for the latest 3 Python versions (3.9, 3.10, 3.11 and 3.12) and for 64bit Windows. +As compiling from source on windows has heavy dependencies (requires a partial visual studio installation), Freqtrade provides these dependencies (in the binary wheel format) for the latest 3 Python versions (3.10, 3.11 and 3.12) and for 64bit Windows. These Wheels are also used by CI running on windows, and are therefore tested together with freqtrade. Other versions must be downloaded from the above link. diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index ba26f214d..08667e827 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,6 +1,6 @@ """Freqtrade bot""" -__version__ = "2024.9-dev" +__version__ = "2024.10-dev" if "dev" in __version__: from pathlib import Path diff --git a/freqtrade/__main__.py b/freqtrade/__main__.py index f39321c83..caa26ce0a 100755 --- a/freqtrade/__main__.py +++ b/freqtrade/__main__.py @@ -3,7 +3,7 @@ __main__.py for Freqtrade To launch Freqtrade as a module -> python -m freqtrade (with Python >= 3.9) +> python -m freqtrade (with Python >= 3.10) """ from freqtrade import main diff --git a/freqtrade/commands/strategy_utils_commands.py b/freqtrade/commands/strategy_utils_commands.py index 761a7262c..7ac0e77e8 100644 --- a/freqtrade/commands/strategy_utils_commands.py +++ b/freqtrade/commands/strategy_utils_commands.py @@ -1,5 +1,4 @@ import logging -import sys import time from pathlib import Path from typing import Any, Dict @@ -20,9 +19,6 @@ def start_strategy_update(args: Dict[str, Any]) -> None: :return: None """ - if sys.version_info == (3, 8): # pragma: no cover - sys.exit("Freqtrade strategy updater requires Python version >= 3.9") - config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) strategy_objs = StrategyResolver.search_all_objects( diff --git a/freqtrade/configuration/config_schema.py b/freqtrade/configuration/config_schema.py index cd349daed..13f8b8703 100644 --- a/freqtrade/configuration/config_schema.py +++ b/freqtrade/configuration/config_schema.py @@ -4,7 +4,6 @@ from typing import Dict from freqtrade.constants import ( AVAILABLE_DATAHANDLERS, AVAILABLE_PAIRLISTS, - AVAILABLE_PROTECTIONS, BACKTEST_BREAKDOWNS, DRY_RUN_WALLET, EXPORT_OPTIONS, @@ -449,60 +448,6 @@ CONF_SCHEMA = { "required": ["method"], }, }, - "protections": { - "description": "Configuration for various protections.", - "type": "array", - "items": { - "type": "object", - "properties": { - "method": { - "description": "Method used for the protection.", - "type": "string", - "enum": AVAILABLE_PROTECTIONS, - }, - "stop_duration": { - "description": ( - "Duration to lock the pair after a protection is triggered, " - "in minutes." - ), - "type": "number", - "minimum": 0.0, - }, - "stop_duration_candles": { - "description": ( - "Duration to lock the pair after a protection is triggered, in " - "number of candles." - ), - "type": "number", - "minimum": 0, - }, - "unlock_at": { - "description": ( - "Time when trading will be unlocked regularly. Format: HH:MM" - ), - "type": "string", - }, - "trade_limit": { - "description": "Minimum number of trades required during lookback period.", - "type": "number", - "minimum": 1, - }, - "lookback_period": { - "description": "Period to look back for protection checks, in minutes.", - "type": "number", - "minimum": 1, - }, - "lookback_period_candles": { - "description": ( - "Period to look back for protection checks, in number " "of candles." - ), - "type": "number", - "minimum": 1, - }, - }, - "required": ["method"], - }, - }, # RPC section "telegram": { "description": "Telegram settings.", diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index 6a14841ff..536feb535 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -1,7 +1,6 @@ import logging from collections import Counter from copy import deepcopy -from datetime import datetime from typing import Any, Dict from jsonschema import Draft4Validator, validators @@ -84,7 +83,6 @@ def validate_config_consistency(conf: Dict[str, Any], *, preliminary: bool = Fal _validate_price_config(conf) _validate_edge(conf) _validate_whitelist(conf) - _validate_protections(conf) _validate_unlimited_amount(conf) _validate_ask_orderbook(conf) _validate_freqai_hyperopt(conf) @@ -196,41 +194,6 @@ def _validate_whitelist(conf: Dict[str, Any]) -> None: raise ConfigurationError("StaticPairList requires pair_whitelist to be set.") -def _validate_protections(conf: Dict[str, Any]) -> None: - """ - Validate protection configuration validity - """ - - for prot in conf.get("protections", []): - parsed_unlock_at = None - if (config_unlock_at := prot.get("unlock_at")) is not None: - try: - parsed_unlock_at = datetime.strptime(config_unlock_at, "%H:%M") - except ValueError: - raise ConfigurationError(f"Invalid date format for unlock_at: {config_unlock_at}.") - - if "stop_duration" in prot and "stop_duration_candles" in prot: - raise ConfigurationError( - "Protections must specify either `stop_duration` or `stop_duration_candles`.\n" - f"Please fix the protection {prot.get('method')}." - ) - - if "lookback_period" in prot and "lookback_period_candles" in prot: - raise ConfigurationError( - "Protections must specify either `lookback_period` or `lookback_period_candles`.\n" - f"Please fix the protection {prot.get('method')}." - ) - - if parsed_unlock_at is not None and ( - "stop_duration" in prot or "stop_duration_candles" in prot - ): - raise ConfigurationError( - "Protections must specify either `unlock_at`, `stop_duration` or " - "`stop_duration_candles`.\n" - f"Please fix the protection {prot.get('method')}." - ) - - def _validate_ask_orderbook(conf: Dict[str, Any]) -> None: ask_strategy = conf.get("exit_pricing", {}) ob_min = ask_strategy.get("order_book_min") diff --git a/freqtrade/configuration/deprecated_settings.py b/freqtrade/configuration/deprecated_settings.py index 6a0901ed7..c4d78e588 100644 --- a/freqtrade/configuration/deprecated_settings.py +++ b/freqtrade/configuration/deprecated_settings.py @@ -177,4 +177,6 @@ def process_temporary_deprecated_settings(config: Config) -> None: ) if "protections" in config: - logger.warning("DEPRECATED: Setting 'protections' in the configuration is deprecated.") + raise ConfigurationError( + "DEPRECATED: Setting 'protections' in the configuration is deprecated." + ) diff --git a/freqtrade/configuration/directory_operations.py b/freqtrade/configuration/directory_operations.py index 3e6ed92ed..448cf1acd 100644 --- a/freqtrade/configuration/directory_operations.py +++ b/freqtrade/configuration/directory_operations.py @@ -82,6 +82,11 @@ def create_userdata_dir(directory: str, create_dir: bool = False) -> Path: for f in sub_dirs: subfolder = folder / f if not subfolder.is_dir(): + if subfolder.exists() or subfolder.is_symlink(): + raise OperationalException( + f"File `{subfolder}` exists already and is not a directory. " + "Freqtrade requires this to be a directory." + ) subfolder.mkdir(parents=False) return folder diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 120f463f3..f1c44d204 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -57,7 +57,6 @@ AVAILABLE_PAIRLISTS = [ "SpreadFilter", "VolatilityFilter", ] -AVAILABLE_PROTECTIONS = ["CooldownPeriod", "LowProfitPairs", "MaxDrawdown", "StoplossGuard"] AVAILABLE_DATAHANDLERS = ["json", "jsongz", "hdf5", "feather", "parquet"] BACKTEST_BREAKDOWNS = ["day", "week", "month"] BACKTEST_CACHE_AGE = ["none", "day", "week", "month"] diff --git a/freqtrade/exchange/binance_leverage_tiers.json b/freqtrade/exchange/binance_leverage_tiers.json index 8b0882d64..c0eac5b0e 100644 --- a/freqtrade/exchange/binance_leverage_tiers.json +++ b/freqtrade/exchange/binance_leverage_tiers.json @@ -1628,13 +1628,13 @@ "tier": 3.0, "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 20000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "20000", + "notionalCap": "50000", "notionalFloor": "10000", "maintMarginRatio": "0.02", "cum": "75.0" @@ -1643,97 +1643,97 @@ { "tier": 4.0, "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 40000.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "4", "initialLeverage": "20", - "notionalCap": "40000", - "notionalFloor": "20000", + "notionalCap": "100000", + "notionalFloor": "50000", "maintMarginRatio": "0.025", - "cum": "175.0" + "cum": "325.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 40000.0, - "maxNotional": 200000.0, + "minNotional": 100000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "200000", - "notionalFloor": "40000", + "notionalCap": "500000", + "notionalFloor": "100000", "maintMarginRatio": "0.05", - "cum": "1175.0" + "cum": "2825.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 400000.0, + "minNotional": 500000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "400000", - "notionalFloor": "200000", + "notionalCap": "1000000", + "notionalFloor": "500000", "maintMarginRatio": "0.1", - "cum": "11175.0" + "cum": "27825.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 500000.0, + "minNotional": 1000000.0, + "maxNotional": 1250000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "500000", - "notionalFloor": "400000", + "notionalCap": "1250000", + "notionalFloor": "1000000", "maintMarginRatio": "0.125", - "cum": "21175.0" + "cum": "52825.0" } }, { "tier": 8.0, "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 1250000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "2500000", + "notionalFloor": "1250000", "maintMarginRatio": "0.25", - "cum": "83675.0" + "cum": "209075.0" } }, { "tier": 9.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "333675.0" + "cum": "834075.0" } } ], @@ -10301,6 +10301,152 @@ } } ], + "CATI/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "10000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "100000", + "notionalFloor": "20000", + "maintMarginRatio": "0.02", + "cum": "150.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "200000", + "notionalFloor": "100000", + "maintMarginRatio": "0.025", + "cum": "650.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "1000000", + "notionalFloor": "200000", + "maintMarginRatio": "0.05", + "cum": "5650.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "55650.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 2500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "2500000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "105650.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 2500000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "5000000", + "notionalFloor": "2500000", + "maintMarginRatio": "0.25", + "cum": "418150.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.5", + "cum": "1668150.0" + } + } + ], "CELO/USDT:USDT": [ { "tier": 1.0, @@ -11006,112 +11152,144 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 20.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "20", - "notionalCap": "5000", + "initialLeverage": "75", + "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.02", + "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 15.0, + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": "2", - "initialLeverage": "15", - "notionalCap": "25000", - "notionalFloor": "5000", - "maintMarginRatio": "0.025", - "cum": "25.0" + "initialLeverage": "50", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 200000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 20000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "3", - "initialLeverage": "10", - "notionalCap": "200000", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "650.0" + "initialLeverage": "25", + "notionalCap": "100000", + "notionalFloor": "20000", + "maintMarginRatio": "0.02", + "cum": "150.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 100000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "4", - "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "200000", - "maintMarginRatio": "0.1", - "cum": "10650.0" + "initialLeverage": "20", + "notionalCap": "200000", + "notionalFloor": "100000", + "maintMarginRatio": "0.025", + "cum": "650.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 500000.0, + "minNotional": 200000.0, "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "5", - "initialLeverage": "4", + "initialLeverage": "10", "notionalCap": "1000000", - "notionalFloor": "500000", - "maintMarginRatio": "0.125", - "cum": "23150.0" + "notionalFloor": "200000", + "maintMarginRatio": "0.05", + "cum": "5650.0" } }, { "tier": 6.0, "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 3000000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "6", - "initialLeverage": "2", - "notionalCap": "3000000", + "initialLeverage": "5", + "notionalCap": "2000000", "notionalFloor": "1000000", - "maintMarginRatio": "0.25", - "cum": "148150.0" + "maintMarginRatio": "0.1", + "cum": "55650.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 3000000.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "2500000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "105650.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 2500000.0, "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "5000000", + "notionalFloor": "2500000", + "maintMarginRatio": "0.25", + "cum": "418150.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "7", + "bracket": "9", "initialLeverage": "1", - "notionalCap": "5000000", - "notionalFloor": "3000000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", - "cum": "898150.0" + "cum": "1668150.0" } } ], @@ -16267,6 +16445,152 @@ } } ], + "FIDA/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 40000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "40000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "175.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 40000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "40000", + "maintMarginRatio": "0.05", + "cum": "1175.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "11175.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "21175.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83675.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "333675.0" + } + } + ], "FIL/USDC:USDC": [ { "tier": 1.0, @@ -16575,6 +16899,152 @@ } } ], + "FIO/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 40000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "40000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "175.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 40000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "40000", + "maintMarginRatio": "0.05", + "cum": "1175.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "11175.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "21175.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83675.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "333675.0" + } + } + ], "FLM/USDT:USDT": [ { "tier": 1.0, @@ -17697,6 +18167,152 @@ } } ], + "GHST/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 40000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "40000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "175.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 40000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "40000", + "maintMarginRatio": "0.05", + "cum": "1175.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "11175.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "21175.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83675.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "333675.0" + } + } + ], "GLM/USDT:USDT": [ { "tier": 1.0, @@ -22739,6 +23355,152 @@ } } ], + "LOKA/USDT:USDT": [ + { + "tier": 1.0, + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, + "info": { + "bracket": "1", + "initialLeverage": "75", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 40000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "40000", + "notionalFloor": "20000", + "maintMarginRatio": "0.025", + "cum": "175.0" + } + }, + { + "tier": 5.0, + "currency": "USDT", + "minNotional": 40000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "40000", + "maintMarginRatio": "0.05", + "cum": "1175.0" + } + }, + { + "tier": 6.0, + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "11175.0" + } + }, + { + "tier": 7.0, + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.125", + "cum": "21175.0" + } + }, + { + "tier": 8.0, + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "83675.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "333675.0" + } + } + ], "LOOM/USDT:USDT": [ { "tier": 1.0, @@ -25932,13 +26694,13 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, "maxLeverage": 75.0, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "5000", + "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" @@ -25947,129 +26709,129 @@ { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, + "minNotional": 10000.0, + "maxNotional": 40000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "10000", - "notionalFloor": "5000", + "notionalCap": "40000", + "notionalFloor": "10000", "maintMarginRatio": "0.015", - "cum": "25.0" + "cum": "50.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 20000.0, + "minNotional": 40000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "20000", - "notionalFloor": "10000", + "notionalCap": "200000", + "notionalFloor": "40000", "maintMarginRatio": "0.02", - "cum": "75.0" + "cum": "250.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 40000.0, + "minNotional": 200000.0, + "maxNotional": 400000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "4", "initialLeverage": "20", - "notionalCap": "40000", - "notionalFloor": "20000", + "notionalCap": "400000", + "notionalFloor": "200000", "maintMarginRatio": "0.025", - "cum": "175.0" + "cum": "1250.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 40000.0, - "maxNotional": 200000.0, + "minNotional": 400000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "200000", - "notionalFloor": "40000", + "notionalCap": "2000000", + "notionalFloor": "400000", "maintMarginRatio": "0.05", - "cum": "1175.0" + "cum": "11250.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 400000.0, + "minNotional": 2000000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "400000", - "notionalFloor": "200000", + "notionalCap": "4000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.1", - "cum": "11175.0" + "cum": "111250.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 500000.0, + "minNotional": 4000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "500000", - "notionalFloor": "400000", + "notionalCap": "5000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.125", - "cum": "21175.0" + "cum": "211250.0" } }, { "tier": 8.0, "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.25", - "cum": "83675.0" + "cum": "836250.0" } }, { "tier": 9.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.5", - "cum": "333675.0" + "cum": "3336250.0" } } ], @@ -29930,13 +30692,13 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, "maxLeverage": 75.0, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "5000", + "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" @@ -29945,129 +30707,129 @@ { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, + "minNotional": 10000.0, + "maxNotional": 20000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "10000", - "notionalFloor": "5000", + "notionalCap": "20000", + "notionalFloor": "10000", "maintMarginRatio": "0.015", - "cum": "25.0" + "cum": "50.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 20000.0, + "minNotional": 20000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "20000", - "notionalFloor": "10000", + "notionalCap": "100000", + "notionalFloor": "20000", "maintMarginRatio": "0.02", - "cum": "75.0" + "cum": "150.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 40000.0, + "minNotional": 100000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "4", "initialLeverage": "20", - "notionalCap": "40000", - "notionalFloor": "20000", + "notionalCap": "200000", + "notionalFloor": "100000", "maintMarginRatio": "0.025", - "cum": "175.0" + "cum": "650.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 40000.0, - "maxNotional": 200000.0, + "minNotional": 200000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "200000", - "notionalFloor": "40000", + "notionalCap": "1000000", + "notionalFloor": "200000", "maintMarginRatio": "0.05", - "cum": "1175.0" + "cum": "5650.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 400000.0, + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "400000", - "notionalFloor": "200000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.1", - "cum": "11175.0" + "cum": "55650.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 500000.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "500000", - "notionalFloor": "400000", + "notionalCap": "2500000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", - "cum": "21175.0" + "cum": "105650.0" } }, { "tier": 8.0, "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.25", - "cum": "83675.0" + "cum": "418150.0" } }, { "tier": 9.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", - "cum": "333675.0" + "cum": "1668150.0" } } ], @@ -32760,128 +33522,144 @@ "tier": 1.0, "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "75", + "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.01", "cum": "0.0" } }, { "tier": 2.0, "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": "2", - "initialLeverage": "25", - "notionalCap": "25000", - "notionalFloor": "5000", - "maintMarginRatio": "0.02", - "cum": "25.0" + "initialLeverage": "50", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 30000.0, + "maxNotional": 150000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "3", - "initialLeverage": "20", - "notionalCap": "50000", - "notionalFloor": "25000", - "maintMarginRatio": "0.025", - "cum": "150.0" + "initialLeverage": "25", + "notionalCap": "150000", + "notionalFloor": "30000", + "maintMarginRatio": "0.02", + "cum": "200.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 150000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "4", - "initialLeverage": "10", - "notionalCap": "500000", - "notionalFloor": "50000", - "maintMarginRatio": "0.05", - "cum": "1400.0" + "initialLeverage": "20", + "notionalCap": "300000", + "notionalFloor": "150000", + "maintMarginRatio": "0.025", + "cum": "950.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 300000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "5", - "initialLeverage": "5", - "notionalCap": "1000000", - "notionalFloor": "500000", - "maintMarginRatio": "0.1", - "cum": "26400.0" + "initialLeverage": "10", + "notionalCap": "1500000", + "notionalFloor": "300000", + "maintMarginRatio": "0.05", + "cum": "8450.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 1250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "6", - "initialLeverage": "4", - "notionalCap": "1250000", - "notionalFloor": "1000000", - "maintMarginRatio": "0.125", - "cum": "51400.0" + "initialLeverage": "5", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.1", + "cum": "83450.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 1250000.0, - "maxNotional": 2500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, + "minNotional": 3000000.0, + "maxNotional": 3750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "7", - "initialLeverage": "2", - "notionalCap": "2500000", - "notionalFloor": "1250000", - "maintMarginRatio": "0.25", - "cum": "207650.0" + "initialLeverage": "4", + "notionalCap": "3750000", + "notionalFloor": "3000000", + "maintMarginRatio": "0.125", + "cum": "158450.0" } }, { "tier": 8.0, "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 3750000.0, + "maxNotional": 7500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "7500000", + "notionalFloor": "3750000", + "maintMarginRatio": "0.25", + "cum": "627200.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 7500000.0, + "maxNotional": 15000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "1", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "15000000", + "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "832650.0" + "cum": "2502200.0" } } ], @@ -37915,14 +38693,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "75", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.01", "cum": "0.0" } }, @@ -37930,112 +38708,128 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": "2", - "initialLeverage": "25", - "notionalCap": "20000", + "initialLeverage": "50", + "notionalCap": "10000", "notionalFloor": "5000", - "maintMarginRatio": "0.02", + "maintMarginRatio": "0.015", "cum": "25.0" } }, { "tier": 3.0, "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 10000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "3", - "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "20000", - "maintMarginRatio": "0.025", - "cum": "125.0" + "initialLeverage": "25", + "notionalCap": "50000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 200000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "4", - "initialLeverage": "10", - "notionalCap": "200000", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "750.0" + "initialLeverage": "20", + "notionalCap": "100000", + "notionalFloor": "50000", + "maintMarginRatio": "0.025", + "cum": "325.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 400000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "5", - "initialLeverage": "5", - "notionalCap": "400000", - "notionalFloor": "200000", - "maintMarginRatio": "0.1", - "cum": "10750.0" + "initialLeverage": "10", + "notionalCap": "500000", + "notionalFloor": "100000", + "maintMarginRatio": "0.05", + "cum": "2825.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "6", - "initialLeverage": "4", - "notionalCap": "500000", - "notionalFloor": "400000", - "maintMarginRatio": "0.125", - "cum": "20750.0" + "initialLeverage": "5", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.1", + "cum": "27825.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, + "minNotional": 1000000.0, + "maxNotional": 1250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "7", - "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "500000", - "maintMarginRatio": "0.25", - "cum": "83250.0" + "initialLeverage": "4", + "notionalCap": "1250000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.125", + "cum": "52825.0" } }, { "tier": 8.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 1250000.0, + "maxNotional": 2500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "2500000", + "notionalFloor": "1250000", + "maintMarginRatio": "0.25", + "cum": "209075.0" + } + }, + { + "tier": 9.0, + "currency": "USDT", + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "1", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "333250.0" + "cum": "834075.0" } } ], @@ -38792,13 +39586,13 @@ "tier": 2.0, "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 10000.0, + "maxNotional": 16000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "10000", + "notionalCap": "16000", "notionalFloor": "5000", "maintMarginRatio": "0.015", "cum": "25.0" @@ -38807,113 +39601,113 @@ { "tier": 3.0, "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 20000.0, + "minNotional": 16000.0, + "maxNotional": 80000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "20000", - "notionalFloor": "10000", + "notionalCap": "80000", + "notionalFloor": "16000", "maintMarginRatio": "0.02", - "cum": "75.0" + "cum": "105.0" } }, { "tier": 4.0, "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 40000.0, + "minNotional": 80000.0, + "maxNotional": 160000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "4", "initialLeverage": "20", - "notionalCap": "40000", - "notionalFloor": "20000", + "notionalCap": "160000", + "notionalFloor": "80000", "maintMarginRatio": "0.025", - "cum": "175.0" + "cum": "505.0" } }, { "tier": 5.0, "currency": "USDT", - "minNotional": 40000.0, - "maxNotional": 200000.0, + "minNotional": 160000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "200000", - "notionalFloor": "40000", + "notionalCap": "800000", + "notionalFloor": "160000", "maintMarginRatio": "0.05", - "cum": "1175.0" + "cum": "4505.0" } }, { "tier": 6.0, "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 400000.0, + "minNotional": 800000.0, + "maxNotional": 1600000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "400000", - "notionalFloor": "200000", + "notionalCap": "1600000", + "notionalFloor": "800000", "maintMarginRatio": "0.1", - "cum": "11175.0" + "cum": "44505.0" } }, { "tier": 7.0, "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 500000.0, + "minNotional": 1600000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "500000", - "notionalFloor": "400000", + "notionalCap": "2000000", + "notionalFloor": "1600000", "maintMarginRatio": "0.125", - "cum": "21175.0" + "cum": "84505.0" } }, { "tier": 8.0, "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 2000000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "4000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.25", - "cum": "83675.0" + "cum": "334505.0" } }, { "tier": 9.0, "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 4000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "8000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.5", - "cum": "333675.0" + "cum": "1334505.0" } } ], diff --git a/freqtrade/exchange/bybit.py b/freqtrade/exchange/bybit.py index af0071039..719c64dc3 100644 --- a/freqtrade/exchange/bybit.py +++ b/freqtrade/exchange/bybit.py @@ -238,7 +238,13 @@ class Bybit(Exchange): return orders def fetch_order(self, order_id: str, pair: str, params: Optional[Dict] = None) -> Dict: + if self.exchange_has("fetchOrder"): + # Set acknowledged to True to avoid ccxt exception + params = {"acknowledged": True} + order = super().fetch_order(order_id, pair, params) + if not order: + order = self.fetch_order_emulated(order_id, pair, {}) if ( order.get("status") == "canceled" and order.get("filled") == 0.0 diff --git a/freqtrade/exchange/hyperliquid.py b/freqtrade/exchange/hyperliquid.py index 69905c416..25c262e9f 100644 --- a/freqtrade/exchange/hyperliquid.py +++ b/freqtrade/exchange/hyperliquid.py @@ -3,8 +3,6 @@ import logging from typing import Dict -from ccxt import SIGNIFICANT_DIGITS - from freqtrade.enums import TradingMode from freqtrade.exchange import Exchange from freqtrade.exchange.exchange_types import FtHas @@ -36,10 +34,3 @@ class Hyperliquid(Exchange): config.update({"options": {"defaultType": "spot"}}) config.update(super()._ccxt_config) return config - - @property - def precision_mode_price(self) -> int: - """ - Override the default precision mode for price. - """ - return SIGNIFICANT_DIGITS diff --git a/freqtrade/exchange/okx.py b/freqtrade/exchange/okx.py index 8a781982a..a0fbb6729 100644 --- a/freqtrade/exchange/okx.py +++ b/freqtrade/exchange/okx.py @@ -13,7 +13,7 @@ from freqtrade.exceptions import ( TemporaryError, ) from freqtrade.exchange import Exchange, date_minus_candles -from freqtrade.exchange.common import retrier +from freqtrade.exchange.common import API_RETRY_COUNT, retrier from freqtrade.exchange.exchange_types import FtHas from freqtrade.misc import safe_value_fallback2 from freqtrade.util import dt_now, dt_ts @@ -208,6 +208,7 @@ class Okx(Exchange): order["type"] = "stoploss" return order + @retrier(retries=API_RETRY_COUNT) def fetch_stoploss_order(self, order_id: str, pair: str, params: Optional[Dict] = None) -> Dict: if self._config["dry_run"]: return self.fetch_dry_run_order(order_id) @@ -217,8 +218,20 @@ class Okx(Exchange): order_reg = self._api.fetch_order(order_id, pair, params=params1) self._log_exchange_response("fetch_stoploss_order", order_reg) return self._convert_stop_order(pair, order_id, order_reg) - except ccxt.OrderNotFound: + except (ccxt.OrderNotFound, ccxt.InvalidOrder): pass + except ccxt.DDoSProtection as e: + raise DDosProtection(e) from e + except (ccxt.OperationFailed, ccxt.ExchangeError) as e: + raise TemporaryError( + f"Could not get order due to {e.__class__.__name__}. Message: {e}" + ) from e + except ccxt.BaseError as e: + raise OperationalException(e) from e + + return self._fetch_stop_order_fallback(order_id, pair) + + def _fetch_stop_order_fallback(self, order_id: str, pair: str) -> Dict: params2 = {"stop": True, "ordType": "conditional"} for method in ( self._api.fetch_open_orders, @@ -231,8 +244,16 @@ class Okx(Exchange): if orders_f: order = orders_f[0] return self._convert_stop_order(pair, order_id, order) - except ccxt.BaseError: + except (ccxt.OrderNotFound, ccxt.InvalidOrder): pass + except ccxt.DDoSProtection as e: + raise DDosProtection(e) from e + except (ccxt.OperationFailed, ccxt.ExchangeError) as e: + raise TemporaryError( + f"Could not get order due to {e.__class__.__name__}. Message: {e}" + ) from e + except ccxt.BaseError as e: + raise OperationalException(e) from e raise RetryableOrderError(f"StoplossOrder not found (pair: {pair} id: {order_id}).") def get_order_id_conditional(self, order: Dict[str, Any]) -> str: diff --git a/freqtrade/freqai/base_models/BasePyTorchRegressor.py b/freqtrade/freqai/base_models/BasePyTorchRegressor.py index 9b429db23..5f53e7d07 100644 --- a/freqtrade/freqai/base_models/BasePyTorchRegressor.py +++ b/freqtrade/freqai/base_models/BasePyTorchRegressor.py @@ -86,9 +86,6 @@ class BasePyTorchRegressor(BasePyTorchModel): dk.feature_pipeline = self.define_data_pipeline(threads=dk.thread_count) dk.label_pipeline = self.define_label_pipeline(threads=dk.thread_count) - dd["train_labels"], _, _ = dk.label_pipeline.fit_transform(dd["train_labels"]) - dd["test_labels"], _, _ = dk.label_pipeline.transform(dd["test_labels"]) - (dd["train_features"], dd["train_labels"], dd["train_weights"]) = ( dk.feature_pipeline.fit_transform( dd["train_features"], dd["train_labels"], dd["train_weights"] diff --git a/freqtrade/freqai/prediction_models/PyTorchTransformerRegressor.py b/freqtrade/freqai/prediction_models/PyTorchTransformerRegressor.py index 27b7de832..2d60d68cf 100644 --- a/freqtrade/freqai/prediction_models/PyTorchTransformerRegressor.py +++ b/freqtrade/freqai/prediction_models/PyTorchTransformerRegressor.py @@ -141,7 +141,7 @@ class PyTorchTransformerRegressor(BasePyTorchRegressor): pred_df = pd.DataFrame(yb.detach().numpy(), columns=dk.label_list) pred_df, _, _ = dk.label_pipeline.inverse_transform(pred_df) - if self.freqai_info.get("DI_threshold", 0) > 0: + if self.ft_params.get("DI_threshold", 0) > 0: dk.DI_values = dk.feature_pipeline["di"].di_values else: dk.DI_values = np.zeros(outliers.shape[0]) diff --git a/freqtrade/main.py b/freqtrade/main.py index 67584c5b7..4e40c36ad 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -10,8 +10,8 @@ from typing import Any, List, Optional # check min. python version -if sys.version_info < (3, 9): # pragma: no cover - sys.exit("Freqtrade requires Python version >= 3.9") +if sys.version_info < (3, 10): # pragma: no cover + sys.exit("Freqtrade requires Python version >= 3.10") from freqtrade import __version__ from freqtrade.commands import Arguments diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index 9ecb1b75e..ba50d5b73 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -273,10 +273,6 @@ class Backtesting: def _load_protections(self, strategy: IStrategy): if self.config.get("enable_protections", False): - conf = self.config - if hasattr(strategy, "protections"): - conf = deepcopy(conf) - conf["protections"] = strategy.protections self.protections = ProtectionManager(self.config, strategy.protections) def load_bt_data(self) -> Tuple[Dict[str, DataFrame], TimeRange]: diff --git a/freqtrade/plugins/pairlist/IPairList.py b/freqtrade/plugins/pairlist/IPairList.py index 755f52b06..6a4ad32fb 100644 --- a/freqtrade/plugins/pairlist/IPairList.py +++ b/freqtrade/plugins/pairlist/IPairList.py @@ -39,6 +39,11 @@ class __OptionPairlistParameter(__PairlistParameterBase): options: List[str] +class __ListPairListParamenter(__PairlistParameterBase): + type: Literal["list"] + default: Union[List[str], None] + + class __BoolPairlistParameter(__PairlistParameterBase): type: Literal["boolean"] default: Union[bool, None] @@ -49,6 +54,7 @@ PairlistParameter = Union[ __StringPairlistParameter, __OptionPairlistParameter, __BoolPairlistParameter, + __ListPairListParamenter, ] diff --git a/freqtrade/plugins/pairlist/MarketCapPairList.py b/freqtrade/plugins/pairlist/MarketCapPairList.py index 95f0e2805..3ca31fbf2 100644 --- a/freqtrade/plugins/pairlist/MarketCapPairList.py +++ b/freqtrade/plugins/pairlist/MarketCapPairList.py @@ -35,6 +35,7 @@ class MarketCapPairList(IPairList): self._number_assets = self._pairlistconfig["number_assets"] self._max_rank = self._pairlistconfig.get("max_rank", 30) self._refresh_period = self._pairlistconfig.get("refresh_period", 86400) + self._categories = self._pairlistconfig.get("categories", []) self._marketcap_cache: TTLCache = TTLCache(maxsize=1, ttl=self._refresh_period) self._def_candletype = self._config["candle_type_def"] @@ -45,6 +46,17 @@ class MarketCapPairList(IPairList): is_demo=_coingecko_config.get("is_demo", True), ) + if self._categories: + categories = self._coingecko.get_coins_categories_list() + category_ids = [cat["category_id"] for cat in categories] + + for category in self._categories: + if category not in category_ids: + raise OperationalException( + f"Category {category} not in coingecko category list. " + f"You can choose from {category_ids}" + ) + if self._max_rank > 250: raise OperationalException("This filter only support marketcap rank up to 250.") @@ -85,6 +97,15 @@ class MarketCapPairList(IPairList): "description": "Max rank of assets", "help": "Maximum rank of assets to use from the pairlist", }, + "categories": { + "type": "list", + "default": [], + "description": "Coin Categories", + "help": ( + "The Category of the coin e.g layer-1 default [] " + "(https://www.coingecko.com/en/categories)" + ), + }, "refresh_period": { "type": "number", "default": 86400, @@ -132,15 +153,29 @@ class MarketCapPairList(IPairList): """ marketcap_list = self._marketcap_cache.get("marketcap") + default_kwargs = { + "vs_currency": "usd", + "order": "market_cap_desc", + "per_page": "250", + "page": "1", + "sparkline": "false", + "locale": "en", + } + if marketcap_list is None: - data = self._coingecko.get_coins_markets( - vs_currency="usd", - order="market_cap_desc", - per_page="250", - page="1", - sparkline="false", - locale="en", - ) + data = [] + + if not self._categories: + data = self._coingecko.get_coins_markets(**default_kwargs) + else: + for category in self._categories: + category_data = self._coingecko.get_coins_markets( + **default_kwargs, **({"category": category} if category else {}) + ) + data += category_data + + data.sort(key=lambda d: float(d.get("market_cap") or 0.0), reverse=True) + if data: marketcap_list = [row["symbol"] for row in data] self._marketcap_cache["marketcap"] = marketcap_list @@ -157,7 +192,7 @@ class MarketCapPairList(IPairList): for mc_pair in top_marketcap: test_pair = f"{mc_pair.upper()}/{pair_format}" - if test_pair in pairlist: + if test_pair in pairlist and test_pair not in filtered_pairlist: filtered_pairlist.append(test_pair) if len(filtered_pairlist) == self._number_assets: break @@ -165,4 +200,5 @@ class MarketCapPairList(IPairList): if len(filtered_pairlist) > 0: return filtered_pairlist - return pairlist + # If no pairs are found, return the original pairlist + return [] diff --git a/freqtrade/plugins/protectionmanager.py b/freqtrade/plugins/protectionmanager.py index 4f60ae0e0..1b07261d4 100644 --- a/freqtrade/plugins/protectionmanager.py +++ b/freqtrade/plugins/protectionmanager.py @@ -4,9 +4,10 @@ Protection manager class import logging from datetime import datetime, timezone -from typing import Dict, List, Optional +from typing import Any, Dict, List, Optional from freqtrade.constants import Config, LongShort +from freqtrade.exceptions import ConfigurationError from freqtrade.persistence import PairLocks from freqtrade.persistence.models import PairLock from freqtrade.plugins.protections import IProtection @@ -21,6 +22,7 @@ class ProtectionManager: self._config = config self._protection_handlers: List[IProtection] = [] + self.validate_protections(protections) for protection_handler_config in protections: protection_handler = ProtectionResolver.load_protection( protection_handler_config["method"], @@ -76,3 +78,40 @@ class ProtectionManager: pair, lock.until, lock.reason, now=now, side=lock.lock_side ) return result + + @staticmethod + def validate_protections(protections: List[Dict[str, Any]]) -> None: + """ + Validate protection setup validity + """ + + for prot in protections: + parsed_unlock_at = None + if (config_unlock_at := prot.get("unlock_at")) is not None: + try: + parsed_unlock_at = datetime.strptime(config_unlock_at, "%H:%M") + except ValueError: + raise ConfigurationError( + f"Invalid date format for unlock_at: {config_unlock_at}." + ) + + if "stop_duration" in prot and "stop_duration_candles" in prot: + raise ConfigurationError( + "Protections must specify either `stop_duration` or `stop_duration_candles`.\n" + f"Please fix the protection {prot.get('method')}." + ) + + if "lookback_period" in prot and "lookback_period_candles" in prot: + raise ConfigurationError( + "Protections must specify either `lookback_period` or " + f"`lookback_period_candles`.\n Please fix the protection {prot.get('method')}." + ) + + if parsed_unlock_at is not None and ( + "stop_duration" in prot or "stop_duration_candles" in prot + ): + raise ConfigurationError( + "Protections must specify either `unlock_at`, `stop_duration` or " + "`stop_duration_candles`.\n" + f"Please fix the protection {prot.get('method')}." + ) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index d234a680f..6cd0cef23 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -69,7 +69,6 @@ class StrategyResolver(IResolver): ("order_time_in_force", None), ("stake_currency", None), ("stake_amount", None), - ("protections", None), ("startup_candle_count", None), ("unfilledtimeout", None), ("use_exit_signal", True), diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index e4b598807..4f6484538 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -77,7 +77,6 @@ def __run_backtest_bg(btconfig: Config): lastconfig["timerange"] = btconfig["timerange"] lastconfig["timeframe"] = strat.timeframe - lastconfig["protections"] = btconfig.get("protections", []) lastconfig["enable_protections"] = btconfig.get("enable_protections") lastconfig["dry_run_wallet"] = btconfig.get("dry_run_wallet") diff --git a/freqtrade/rpc/api_server/web_ui.py b/freqtrade/rpc/api_server/web_ui.py index 6d37ec308..8f9dd7f12 100644 --- a/freqtrade/rpc/api_server/web_ui.py +++ b/freqtrade/rpc/api_server/web_ui.py @@ -31,16 +31,6 @@ async def ui_version(): } -def is_relative_to(path: Path, base: Path) -> bool: - # Helper function simulating behaviour of is_relative_to, which was only added in python 3.9 - try: - path.relative_to(base) - return True - except ValueError: - pass - return False - - @router_ui.get("/{rest_of_path:path}", include_in_schema=False) async def index_html(rest_of_path: str): """ @@ -56,7 +46,7 @@ async def index_html(rest_of_path: str): if filename.suffix == ".js": # Force text/javascript for .js files - Circumvent faulty system configuration media_type = "application/javascript" - if filename.is_file() and is_relative_to(filename, uibase): + if filename.is_file() and filename.is_relative_to(uibase): return FileResponse(str(filename), media_type=media_type) index_file = uibase / "index.html" diff --git a/ft_client/freqtrade_client/__init__.py b/ft_client/freqtrade_client/__init__.py index d43690126..31f0e4c6a 100644 --- a/ft_client/freqtrade_client/__init__.py +++ b/ft_client/freqtrade_client/__init__.py @@ -1,7 +1,7 @@ from freqtrade_client.ft_rest_client import FtRestClient -__version__ = "2024.9-dev" +__version__ = "2024.10-dev" if "dev" in __version__: from pathlib import Path diff --git a/ft_client/pyproject.toml b/ft_client/pyproject.toml index 919e524f8..a29b3d08c 100644 --- a/ft_client/pyproject.toml +++ b/ft_client/pyproject.toml @@ -13,14 +13,13 @@ authors = [ description = "Freqtrade - Client scripts" readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" license = {text = "GPLv3"} # license = "GPLv3" classifiers = [ "Environment :: Console", "Intended Audience :: Science/Research", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", diff --git a/pyproject.toml b/pyproject.toml index bd36d15c6..ad1c02928 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,14 +13,12 @@ authors = [ description = "Freqtrade - Crypto Trading Bot" readme = "README.md" -requires-python = ">=3.9" +requires-python = ">=3.10" license = {text = "GPLv3"} -# license = "GPLv3" classifiers = [ "Environment :: Console", "Intended Audience :: Science/Research", "License :: OSI Approved :: GNU General Public License v3 (GPLv3)", - "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", @@ -125,7 +123,6 @@ extend-exclude = [".env", ".venv"] target-version = "py38" [tool.ruff.lint] -# Exclude UP036 as it's causing the "exit if < 3.9" to fail. extend-select = [ "C90", # mccabe "B", # bugbear diff --git a/requirements-dev.txt b/requirements-dev.txt index 0036f6b68..984e57ffb 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,7 +7,7 @@ -r docs/requirements-docs.txt coveralls==4.0.1 -ruff==0.6.7 +ruff==0.6.8 mypy==1.11.2 pre-commit==3.8.0 pytest==8.3.3 diff --git a/requirements-freqai.txt b/requirements-freqai.txt index e71dfc129..a09eadbb3 100644 --- a/requirements-freqai.txt +++ b/requirements-freqai.txt @@ -11,5 +11,5 @@ catboost==1.2.7; 'arm' not in platform_machine matplotlib==3.9.2 lightgbm==4.5.0 xgboost==2.0.3 -tensorboard==2.17.1 +tensorboard==2.18.0 datasieve==0.1.7 diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 41afe6d58..7f60c8299 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -2,8 +2,7 @@ -r requirements.txt # Required for hyperopt -scipy==1.14.1; python_version >= "3.10" -scipy==1.13.1; python_version < "3.10" +scipy==1.14.1 scikit-learn==1.5.2 ft-scikit-optimize==0.9.2 filelock==3.16.1 diff --git a/requirements.txt b/requirements.txt index 04469b882..4233d90ec 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,10 +4,10 @@ bottleneck==1.4.0 numexpr==2.10.1 pandas-ta==0.3.14b -ccxt==4.4.6 +ccxt==4.4.9 cryptography==42.0.8; platform_machine == 'armv7l' cryptography==43.0.1; platform_machine != 'armv7l' -aiohttp==3.10.5 +aiohttp==3.10.8 SQLAlchemy==2.0.35 python-telegram-bot==21.6 # can't be hard-pinned due to telegram-bot pinning httpx with ~ @@ -22,9 +22,7 @@ technical==1.4.4 tabulate==0.9.0 pycoingecko==3.1.0 jinja2==3.1.4 -# Tables 3.10 dropped support for Python 3.9 -tables==3.9.1; python_version < "3.10" -tables==3.10.1; python_version >= "3.10" +tables==3.10.1 joblib==1.4.2 rich==13.8.1 pyarrow==17.0.0; platform_machine != 'armv7l' @@ -43,7 +41,7 @@ sdnotify==0.3.2 # API Server fastapi==0.115.0 pydantic==2.9.2 -uvicorn==0.30.6 +uvicorn==0.31.0 pyjwt==2.9.0 aiofiles==24.1.0 psutil==6.0.0 diff --git a/setup.ps1 b/setup.ps1 index 8647bea94..116ed0a2b 100644 --- a/setup.ps1 +++ b/setup.ps1 @@ -153,16 +153,13 @@ function Find-PythonExecutable { "python3.12", "python3.11", "python3.10", - "python3.9", "python3", "C:\Users\$env:USERNAME\AppData\Local\Programs\Python\Python312\python.exe", "C:\Users\$env:USERNAME\AppData\Local\Programs\Python\Python311\python.exe", "C:\Users\$env:USERNAME\AppData\Local\Programs\Python\Python310\python.exe", - "C:\Users\$env:USERNAME\AppData\Local\Programs\Python\Python39\python.exe", "C:\Python312\python.exe", "C:\Python311\python.exe", - "C:\Python310\python.exe", - "C:\Python39\python.exe" + "C:\Python310\python.exe" ) @@ -178,10 +175,10 @@ function Main { "Starting the operations..." | Out-File $LogFilePath -Append "Current directory: $(Get-Location)" | Out-File $LogFilePath -Append - # Exit on lower versions than Python 3.9 or when Python executable not found + # Exit on lower versions than Python 3.10 or when Python executable not found $PythonExecutable = Find-PythonExecutable if ($null -eq $PythonExecutable) { - Write-Log "No suitable Python executable found. Please ensure that Python 3.9 or higher is installed and available in the system PATH." -Level 'ERROR' + Write-Log "No suitable Python executable found. Please ensure that Python 3.10 or higher is installed and available in the system PATH." -Level 'ERROR' Exit 1 } diff --git a/setup.sh b/setup.sh index 18f7682d8..a9e684d62 100755 --- a/setup.sh +++ b/setup.sh @@ -25,7 +25,7 @@ function check_installed_python() { exit 2 fi - for v in 12 11 10 9 + for v in 12 11 10 do PYTHON="python3.${v}" which $PYTHON @@ -36,7 +36,7 @@ function check_installed_python() { fi done - echo "No usable python found. Please make sure to have python3.9 or newer installed." + echo "No usable python found. Please make sure to have python3.10 or newer installed." exit 1 } @@ -166,7 +166,7 @@ function install_macos() { #Gets number after decimal in python version version=$(egrep -o 3.\[0-9\]+ <<< $PYTHON | sed 's/3.//g') - if [[ $version -ge 9 ]]; then #Checks if python version >= 3.9 + if [[ $version -ge 10 ]]; then #Checks if python version >= 3.10 install_mac_newer_python_dependencies fi } @@ -277,7 +277,7 @@ function install() { install_redhat else echo "This script does not support your OS." - echo "If you have Python version 3.9 - 3.12, pip, virtualenv, ta-lib you can continue." + echo "If you have Python version 3.10 - 3.12, pip, virtualenv, ta-lib you can continue." echo "Wait 10 seconds to continue the next install steps or use ctrl+c to interrupt this shell." sleep 10 fi @@ -304,7 +304,7 @@ function help() { echo " -p,--plot Install dependencies for Plotting scripts." } -# Verify if 3.9+ is installed +# Verify if 3.10+ is installed check_installed_python case $* in diff --git a/tests/conftest_trades.py b/tests/conftest_trades.py index 7103b5169..f493a9302 100644 --- a/tests/conftest_trades.py +++ b/tests/conftest_trades.py @@ -38,7 +38,7 @@ def mock_trade_1(fee, is_short: bool): trade = Trade( pair="ETH/BTC", stake_amount=0.001, - amount=123.0, + amount=50.0, amount_requested=123.0, fee_open=fee.return_value, fee_close=fee.return_value, @@ -201,7 +201,7 @@ def mock_trade_4(fee, is_short: bool): trade = Trade( pair="ETC/BTC", stake_amount=0.001, - amount=123.0, + amount=0.0, amount_requested=124.0, fee_open=fee.return_value, fee_close=fee.return_value, diff --git a/tests/conftest_trades_usdt.py b/tests/conftest_trades_usdt.py index 1fc458279..3a547cce2 100644 --- a/tests/conftest_trades_usdt.py +++ b/tests/conftest_trades_usdt.py @@ -224,7 +224,7 @@ def mock_trade_usdt_4(fee, is_short: bool): trade = Trade( pair="NEO/USDT", stake_amount=20.0, - amount=10.0, + amount=0.0, amount_requested=10.01, fee_open=fee.return_value, fee_close=fee.return_value, diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index e3032aa9f..d71d2062e 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -3569,7 +3569,7 @@ def test_cancel_order_with_result( mocker.patch(f"{EXMS}.exchange_has", return_value=True) api_mock = MagicMock() api_mock.cancel_order = MagicMock(return_value=corder) - api_mock.fetch_order = MagicMock(return_value={}) + api_mock.fetch_order = MagicMock(return_value={"id": "1234"}) exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name) res = exchange.cancel_order_with_result("1234", "ETH/BTC", 1234) assert isinstance(res, dict) diff --git a/tests/exchange/test_okx.py b/tests/exchange/test_okx.py index 97f8a3a4c..1bf783daa 100644 --- a/tests/exchange/test_okx.py +++ b/tests/exchange/test_okx.py @@ -6,6 +6,7 @@ import pytest from freqtrade.enums import CandleType, MarginMode, TradingMode from freqtrade.exceptions import RetryableOrderError, TemporaryError +from freqtrade.exchange.common import API_RETRY_COUNT from freqtrade.exchange.exchange import timeframe_to_minutes from tests.conftest import EXMS, get_patched_exchange, log_has from tests.exchange.test_exchange import ccxt_exceptionhandlers @@ -551,6 +552,7 @@ def test__set_leverage_okx(mocker, default_conf): @pytest.mark.usefixtures("init_persistence") def test_fetch_stoploss_order_okx(default_conf, mocker): default_conf["dry_run"] = False + mocker.patch("freqtrade.exchange.common.time.sleep") api_mock = MagicMock() api_mock.fetch_order = MagicMock() @@ -569,10 +571,10 @@ def test_fetch_stoploss_order_okx(default_conf, mocker): with pytest.raises(RetryableOrderError): exchange.fetch_stoploss_order("1234", "ETH/BTC") - assert api_mock.fetch_order.call_count == 1 - assert api_mock.fetch_open_orders.call_count == 1 - assert api_mock.fetch_closed_orders.call_count == 1 - assert api_mock.fetch_canceled_orders.call_count == 1 + assert api_mock.fetch_order.call_count == API_RETRY_COUNT + 1 + assert api_mock.fetch_open_orders.call_count == API_RETRY_COUNT + 1 + assert api_mock.fetch_closed_orders.call_count == API_RETRY_COUNT + 1 + assert api_mock.fetch_canceled_orders.call_count == API_RETRY_COUNT + 1 api_mock.fetch_order.reset_mock() api_mock.fetch_open_orders.reset_mock() @@ -610,6 +612,39 @@ def test_fetch_stoploss_order_okx(default_conf, mocker): assert dro_mock.call_count == 1 +def test_fetch_stoploss_order_okx_exceptions(default_conf_usdt, mocker): + default_conf_usdt["dry_run"] = False + api_mock = MagicMock() + ccxt_exceptionhandlers( + mocker, + default_conf_usdt, + api_mock, + "okx", + "fetch_stoploss_order", + "fetch_order", + retries=API_RETRY_COUNT + 1, + order_id="12345", + pair="ETH/USDT", + ) + + # Test 2nd part of the function + api_mock.fetch_order = MagicMock(side_effect=ccxt.OrderNotFound()) + api_mock.fetch_closed_orders = MagicMock(return_value=[]) + api_mock.fetch_canceled_orders = MagicMock(return_value=[]) + + ccxt_exceptionhandlers( + mocker, + default_conf_usdt, + api_mock, + "okx", + "fetch_stoploss_order", + "fetch_open_orders", + retries=API_RETRY_COUNT + 1, + order_id="12345", + pair="ETH/USDT", + ) + + @pytest.mark.parametrize( "sl1,sl2,sl3,side", [(1501, 1499, 1501, "sell"), (1499, 1501, 1499, "buy")] ) diff --git a/tests/freqtradebot/test_freqtradebot.py b/tests/freqtradebot/test_freqtradebot.py index 691e48a13..06dbe7790 100644 --- a/tests/freqtradebot/test_freqtradebot.py +++ b/tests/freqtradebot/test_freqtradebot.py @@ -553,7 +553,7 @@ def test_enter_positions_global_pairlock( @pytest.mark.parametrize("is_short", [False, True]) def test_handle_protections(mocker, default_conf_usdt, fee, is_short): - default_conf_usdt["protections"] = [ + default_conf_usdt["_strategy_protections"] = [ {"method": "CooldownPeriod", "stop_duration": 60}, { "method": "StoplossGuard", diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index b25230791..f064247c6 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -1299,7 +1299,7 @@ def test_backtest_pricecontours_protections(default_conf, fee, mocker, testdatad # While this test IS a copy of test_backtest_pricecontours, it's needed to ensure # results do not carry-over to the next run, which is not given by using parametrize. patch_exchange(mocker) - default_conf["protections"] = [ + default_conf["_strategy_protections"] = [ { "method": "CooldownPeriod", "stop_duration": 3, @@ -1358,7 +1358,7 @@ def test_backtest_pricecontours( default_conf, mocker, testdatadir, protections, contour, expected ) -> None: if protections: - default_conf["protections"] = protections + default_conf["_strategy_protections"] = protections default_conf["enable_protections"] = True patch_exchange(mocker) diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index 37ebdc58b..1c138cc55 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -2212,7 +2212,7 @@ def test_FullTradesFilter(mocker, default_conf_usdt, fee, caplog) -> None: @pytest.mark.parametrize( - "pairlists,trade_mode,result", + "pairlists,trade_mode,result,coin_market_calls", [ ( [ @@ -2222,6 +2222,7 @@ def test_FullTradesFilter(mocker, default_conf_usdt, fee, caplog) -> None: ], "spot", ["BTC/USDT", "ETH/USDT"], + 1, ), ( [ @@ -2231,6 +2232,7 @@ def test_FullTradesFilter(mocker, default_conf_usdt, fee, caplog) -> None: ], "spot", ["BTC/USDT", "ETH/USDT", "XRP/USDT", "ADA/USDT"], + 1, ), ( [ @@ -2240,6 +2242,7 @@ def test_FullTradesFilter(mocker, default_conf_usdt, fee, caplog) -> None: ], "spot", ["BTC/USDT", "ETH/USDT", "XRP/USDT"], + 1, ), ( [ @@ -2249,6 +2252,7 @@ def test_FullTradesFilter(mocker, default_conf_usdt, fee, caplog) -> None: ], "spot", ["BTC/USDT", "ETH/USDT", "XRP/USDT"], + 1, ), ( [ @@ -2257,6 +2261,7 @@ def test_FullTradesFilter(mocker, default_conf_usdt, fee, caplog) -> None: ], "spot", ["BTC/USDT", "ETH/USDT", "XRP/USDT"], + 1, ), ( [ @@ -2265,6 +2270,7 @@ def test_FullTradesFilter(mocker, default_conf_usdt, fee, caplog) -> None: ], "spot", ["BTC/USDT", "ETH/USDT"], + 1, ), ( [ @@ -2273,6 +2279,7 @@ def test_FullTradesFilter(mocker, default_conf_usdt, fee, caplog) -> None: ], "futures", ["ETH/USDT:USDT"], + 1, ), ( [ @@ -2281,11 +2288,34 @@ def test_FullTradesFilter(mocker, default_conf_usdt, fee, caplog) -> None: ], "futures", ["ETH/USDT:USDT", "ADA/USDT:USDT"], + 1, + ), + ( + [ + # MarketCapPairList as generator - futures, 1 category + {"method": "MarketCapPairList", "number_assets": 2, "categories": ["layer-1"]} + ], + "futures", + ["ETH/USDT:USDT", "ADA/USDT:USDT"], + ["layer-1"], + ), + ( + [ + # MarketCapPairList as generator - futures, 1 category + { + "method": "MarketCapPairList", + "number_assets": 2, + "categories": ["layer-1", "protocol"], + } + ], + "futures", + ["ETH/USDT:USDT", "ADA/USDT:USDT"], + ["layer-1", "protocol"], ), ], ) def test_MarketCapPairList_filter( - mocker, default_conf_usdt, trade_mode, markets, pairlists, result + mocker, default_conf_usdt, trade_mode, markets, pairlists, result, coin_market_calls ): test_value = [ {"symbol": "btc"}, @@ -2309,8 +2339,16 @@ def test_MarketCapPairList_filter( markets=PropertyMock(return_value=markets), exchange_has=MagicMock(return_value=True), ) - mocker.patch( + "freqtrade.plugins.pairlist.MarketCapPairList.FtCoinGeckoApi.get_coins_categories_list", + return_value=[ + {"category_id": "layer-1"}, + {"category_id": "protocol"}, + {"category_id": "defi"}, + ], + ) + + gcm_mock = mocker.patch( "freqtrade.plugins.pairlist.MarketCapPairList.FtCoinGeckoApi.get_coins_markets", return_value=test_value, ) @@ -2319,6 +2357,15 @@ def test_MarketCapPairList_filter( pm = PairListManager(exchange, default_conf_usdt) pm.refresh_pairlist() + if isinstance(coin_market_calls, int): + assert gcm_mock.call_count == coin_market_calls + else: + assert gcm_mock.call_count == len(coin_market_calls) + for call in coin_market_calls: + assert any( + "category" in c.kwargs and c.kwargs["category"] == call + for c in gcm_mock.call_args_list + ) assert pm.whitelist == result @@ -2376,6 +2423,33 @@ def test_MarketCapPairList_timing(mocker, default_conf_usdt, markets, time_machi assert markets_mock.call_count == 3 +def test_MarketCapPairList_filter_special_no_pair_from_coingecko( + mocker, + default_conf_usdt, + markets, +): + default_conf_usdt["pairlists"] = [{"method": "MarketCapPairList", "number_assets": 2}] + + mocker.patch.multiple( + EXMS, + markets=PropertyMock(return_value=markets), + exchange_has=MagicMock(return_value=True), + ) + + # Simulate no pair returned from coingecko + gcm_mock = mocker.patch( + "freqtrade.plugins.pairlist.MarketCapPairList.FtCoinGeckoApi.get_coins_markets", + return_value=[], + ) + + exchange = get_patched_exchange(mocker, default_conf_usdt) + + pm = PairListManager(exchange, default_conf_usdt) + pm.refresh_pairlist() + assert gcm_mock.call_count == 1 + assert pm.whitelist == [] + + def test_MarketCapPairList_exceptions(mocker, default_conf_usdt): exchange = get_patched_exchange(mocker, default_conf_usdt) default_conf_usdt["pairlists"] = [{"method": "MarketCapPairList"}] @@ -2391,6 +2465,27 @@ def test_MarketCapPairList_exceptions(mocker, default_conf_usdt): ): PairListManager(exchange, default_conf_usdt) + # Test invalid coinmarkets list + mocker.patch( + "freqtrade.plugins.pairlist.MarketCapPairList.FtCoinGeckoApi.get_coins_categories_list", + return_value=[ + {"category_id": "layer-1"}, + {"category_id": "protocol"}, + {"category_id": "defi"}, + ], + ) + default_conf_usdt["pairlists"] = [ + { + "method": "MarketCapPairList", + "number_assets": 20, + "categories": ["layer-1", "defi", "layer250"], + } + ] + with pytest.raises( + OperationalException, match="Category layer250 not in coingecko category list." + ): + PairListManager(exchange, default_conf_usdt) + @pytest.mark.parametrize( "pairlists,expected_error,expected_warning", diff --git a/tests/plugins/test_protections.py b/tests/plugins/test_protections.py index 3fb27ce3d..bec2671eb 100644 --- a/tests/plugins/test_protections.py +++ b/tests/plugins/test_protections.py @@ -3,14 +3,17 @@ from datetime import datetime, timedelta, timezone import pytest -from freqtrade import constants from freqtrade.enums import ExitType +from freqtrade.exceptions import OperationalException from freqtrade.persistence import PairLocks, Trade from freqtrade.persistence.trade_model import Order from freqtrade.plugins.protectionmanager import ProtectionManager from tests.conftest import get_patched_freqtradebot, log_has_re +AVAILABLE_PROTECTIONS = ["CooldownPeriod", "LowProfitPairs", "MaxDrawdown", "StoplossGuard"] + + def generate_mock_trade( pair: str, fee: float, @@ -88,19 +91,76 @@ def generate_mock_trade( def test_protectionmanager(mocker, default_conf): - default_conf["protections"] = [ - {"method": protection} for protection in constants.AVAILABLE_PROTECTIONS + default_conf["_strategy_protections"] = [ + {"method": protection} for protection in AVAILABLE_PROTECTIONS ] freqtrade = get_patched_freqtradebot(mocker, default_conf) for handler in freqtrade.protections._protection_handlers: - assert handler.name in constants.AVAILABLE_PROTECTIONS + assert handler.name in AVAILABLE_PROTECTIONS if not handler.has_global_stop: assert handler.global_stop(datetime.now(timezone.utc), "*") is None if not handler.has_local_stop: assert handler.stop_per_pair("XRP/BTC", datetime.now(timezone.utc), "*") is None +@pytest.mark.parametrize( + "protconf,expected", + [ + ([], None), + ([{"method": "StoplossGuard", "lookback_period": 2000, "stop_duration_candles": 10}], None), + ([{"method": "StoplossGuard", "lookback_period_candles": 20, "stop_duration": 10}], None), + ( + [ + { + "method": "StoplossGuard", + "lookback_period_candles": 20, + "lookback_period": 2000, + "stop_duration": 10, + } + ], + r"Protections must specify either `lookback_period`.*", + ), + ( + [ + { + "method": "StoplossGuard", + "lookback_period": 20, + "stop_duration": 10, + "stop_duration_candles": 10, + } + ], + r"Protections must specify either `stop_duration`.*", + ), + ( + [ + { + "method": "StoplossGuard", + "lookback_period": 20, + "stop_duration": 10, + "unlock_at": "20:02", + } + ], + r"Protections must specify either `unlock_at`, `stop_duration` or.*", + ), + ( + [{"method": "StoplossGuard", "lookback_period_candles": 20, "unlock_at": "20:02"}], + None, + ), + ( + [{"method": "StoplossGuard", "lookback_period_candles": 20, "unlock_at": "55:102"}], + "Invalid date format for unlock_at: 55:102.", + ), + ], +) +def test_validate_protections(protconf, expected): + if expected: + with pytest.raises(OperationalException, match=expected): + ProtectionManager.validate_protections(protconf) + else: + ProtectionManager.validate_protections(protconf) + + @pytest.mark.parametrize( "timeframe,expected_lookback,expected_stop,protconf", [ @@ -196,7 +256,7 @@ def test_protections_init(default_conf, timeframe, expected_lookback, expected_s @pytest.mark.usefixtures("init_persistence") def test_stoploss_guard(mocker, default_conf, fee, caplog, is_short): # Active for both sides (long and short) - default_conf["protections"] = [ + default_conf["_strategy_protections"] = [ {"method": "StoplossGuard", "lookback_period": 60, "stop_duration": 40, "trade_limit": 3} ] freqtrade = get_patched_freqtradebot(mocker, default_conf) @@ -268,7 +328,7 @@ def test_stoploss_guard(mocker, default_conf, fee, caplog, is_short): @pytest.mark.parametrize("only_per_side", [False, True]) @pytest.mark.usefixtures("init_persistence") def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair, only_per_side): - default_conf["protections"] = [ + default_conf["_strategy_protections"] = [ { "method": "StoplossGuard", "lookback_period": 60, @@ -379,7 +439,7 @@ def test_stoploss_guard_perpair(mocker, default_conf, fee, caplog, only_per_pair @pytest.mark.usefixtures("init_persistence") def test_CooldownPeriod(mocker, default_conf, fee, caplog): - default_conf["protections"] = [ + default_conf["_strategy_protections"] = [ { "method": "CooldownPeriod", "stop_duration": 60, @@ -425,7 +485,7 @@ def test_CooldownPeriod(mocker, default_conf, fee, caplog): @pytest.mark.usefixtures("init_persistence") def test_CooldownPeriod_unlock_at(mocker, default_conf, fee, caplog, time_machine): - default_conf["protections"] = [ + default_conf["_strategy_protections"] = [ { "method": "CooldownPeriod", "unlock_at": "05:00", @@ -509,7 +569,7 @@ def test_CooldownPeriod_unlock_at(mocker, default_conf, fee, caplog, time_machin @pytest.mark.parametrize("only_per_side", [False, True]) @pytest.mark.usefixtures("init_persistence") def test_LowProfitPairs(mocker, default_conf, fee, caplog, only_per_side): - default_conf["protections"] = [ + default_conf["_strategy_protections"] = [ { "method": "LowProfitPairs", "lookback_period": 400, @@ -599,7 +659,7 @@ def test_LowProfitPairs(mocker, default_conf, fee, caplog, only_per_side): @pytest.mark.usefixtures("init_persistence") def test_MaxDrawdown(mocker, default_conf, fee, caplog): - default_conf["protections"] = [ + default_conf["_strategy_protections"] = [ { "method": "MaxDrawdown", "lookback_period": 1000, @@ -812,7 +872,7 @@ def test_MaxDrawdown(mocker, default_conf, fee, caplog): def test_protection_manager_desc( mocker, default_conf, protectionconf, desc_expected, exception_expected ): - default_conf["protections"] = [protectionconf] + default_conf["_strategy_protections"] = [protectionconf] freqtrade = get_patched_freqtradebot(mocker, default_conf) short_desc = str(freqtrade.protections.short_desc()) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 4f5ef7860..c6b4bb994 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1269,7 +1269,7 @@ def test_api_mix_tag(botclient, fee): @pytest.mark.parametrize( "is_short,current_rate,open_trade_value", - [(True, 1.098e-05, 15.0911775), (False, 1.099e-05, 15.1668225)], + [(True, 1.098e-05, 6.134625), (False, 1.099e-05, 6.165375)], ) def test_api_status( botclient, mocker, ticker, fee, markets, is_short, current_rate, open_trade_value @@ -1294,7 +1294,7 @@ def test_api_status( assert_response(rc) assert len(rc.json()) == 4 assert rc.json()[0] == { - "amount": 123.0, + "amount": 50.0, "amount_requested": 123.0, "close_date": None, "close_timestamp": None, diff --git a/tests/rpc/test_rpc_manager.py b/tests/rpc/test_rpc_manager.py index 2792fd082..67755255f 100644 --- a/tests/rpc/test_rpc_manager.py +++ b/tests/rpc/test_rpc_manager.py @@ -173,7 +173,7 @@ def test_startupmessages_telegram_enabled(mocker, default_conf) -> None: telegram_mock.reset_mock() default_conf["dry_run"] = True default_conf["whitelist"] = {"method": "VolumePairList", "config": {"number_assets": 20}} - default_conf["protections"] = [ + default_conf["_strategy_protections"] = [ {"method": "StoplossGuard", "lookback_period": 60, "trade_limit": 2, "stop_duration": 60} ] freqtradebot = get_patched_freqtradebot(mocker, default_conf) diff --git a/tests/strategy/strats/strategy_test_v3.py b/tests/strategy/strats/strategy_test_v3.py index 71404242a..007c7655e 100644 --- a/tests/strategy/strats/strategy_test_v3.py +++ b/tests/strategy/strats/strategy_test_v3.py @@ -75,15 +75,13 @@ class StrategyTestV3(IStrategy): protection_cooldown_lookback = IntParameter([0, 50], default=30) # TODO: Can this work with protection tests? (replace HyperoptableStrategy implicitly ... ) - # @property - # def protections(self): - # prot = [] - # if self.protection_enabled.value: - # prot.append({ - # "method": "CooldownPeriod", - # "stop_duration_candles": self.protection_cooldown_lookback.value - # }) - # return prot + @property + def protections(self): + prot = [] + if self.protection_enabled.value: + # Workaround to simplify tests. This will not work in real scenarios. + prot = self.config.get("_strategy_protections", {}) + return prot bot_started = False diff --git a/tests/test_configuration.py b/tests/test_configuration.py index d77fae6a8..829bf699a 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -812,65 +812,6 @@ def test_validate_whitelist(default_conf): validate_config_consistency(conf) -@pytest.mark.parametrize( - "protconf,expected", - [ - ([], None), - ([{"method": "StoplossGuard", "lookback_period": 2000, "stop_duration_candles": 10}], None), - ([{"method": "StoplossGuard", "lookback_period_candles": 20, "stop_duration": 10}], None), - ( - [ - { - "method": "StoplossGuard", - "lookback_period_candles": 20, - "lookback_period": 2000, - "stop_duration": 10, - } - ], - r"Protections must specify either `lookback_period`.*", - ), - ( - [ - { - "method": "StoplossGuard", - "lookback_period": 20, - "stop_duration": 10, - "stop_duration_candles": 10, - } - ], - r"Protections must specify either `stop_duration`.*", - ), - ( - [ - { - "method": "StoplossGuard", - "lookback_period": 20, - "stop_duration": 10, - "unlock_at": "20:02", - } - ], - r"Protections must specify either `unlock_at`, `stop_duration` or.*", - ), - ( - [{"method": "StoplossGuard", "lookback_period_candles": 20, "unlock_at": "20:02"}], - None, - ), - ( - [{"method": "StoplossGuard", "lookback_period_candles": 20, "unlock_at": "55:102"}], - "Invalid date format for unlock_at: 55:102.", - ), - ], -) -def test_validate_protections(default_conf, protconf, expected): - conf = deepcopy(default_conf) - conf["protections"] = protconf - if expected: - with pytest.raises(OperationalException, match=expected): - validate_config_consistency(conf) - else: - validate_config_consistency(conf) - - def test_validate_ask_orderbook(default_conf, caplog) -> None: conf = deepcopy(default_conf) conf["exit_pricing"]["use_order_book"] = True @@ -1533,8 +1474,8 @@ def test_process_deprecated_protections(default_conf, caplog): assert not log_has(message, caplog) config["protections"] = [] - process_temporary_deprecated_settings(config) - assert log_has(message, caplog) + with pytest.raises(ConfigurationError, match=message): + process_temporary_deprecated_settings(config) def test_flat_vars_to_nested_dict(caplog): diff --git a/tests/test_wallets.py b/tests/test_wallets.py index ef3129b88..545793151 100644 --- a/tests/test_wallets.py +++ b/tests/test_wallets.py @@ -362,7 +362,8 @@ def test_sync_wallet_dry(mocker, default_conf_usdt, fee): assert len(freqtrade.wallets._wallets) == 5 assert len(freqtrade.wallets._positions) == 0 bal = freqtrade.wallets.get_all_balances() - assert bal["NEO"].total == 10 + # NEO trade is not filled yet. + assert bal["NEO"].total == 0 assert bal["XRP"].total == 10 assert bal["LTC"].total == 2 usdt_bal = bal["USDT"] @@ -410,11 +411,11 @@ def test_sync_wallet_futures_dry(mocker, default_conf, fee): def test_check_exit_amount(mocker, default_conf, fee): freqtrade = get_patched_freqtradebot(mocker, default_conf) update_mock = mocker.patch("freqtrade.wallets.Wallets.update") - total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=123) + total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=50.0) create_mock_trades(fee, is_short=None) trade = Trade.session.scalars(select(Trade)).first() - assert trade.amount == 123 + assert trade.amount == 50.0 assert freqtrade.wallets.check_exit_amount(trade) is True assert update_mock.call_count == 0 @@ -423,7 +424,7 @@ def test_check_exit_amount(mocker, default_conf, fee): update_mock.reset_mock() # Reduce returned amount to below the trade amount - which should # trigger a wallet update and return False, triggering "order refinding" - total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=100) + total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=40) assert freqtrade.wallets.check_exit_amount(trade) is False assert update_mock.call_count == 1 assert total_mock.call_count == 2 @@ -433,12 +434,12 @@ def test_check_exit_amount_futures(mocker, default_conf, fee): default_conf["trading_mode"] = "futures" default_conf["margin_mode"] = "isolated" freqtrade = get_patched_freqtradebot(mocker, default_conf) - total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=123) + total_mock = mocker.patch("freqtrade.wallets.Wallets.get_total", return_value=50) create_mock_trades(fee, is_short=None) trade = Trade.session.scalars(select(Trade)).first() trade.trading_mode = "futures" - assert trade.amount == 123 + assert trade.amount == 50 assert freqtrade.wallets.check_exit_amount(trade) is True assert total_mock.call_count == 0