diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 16f3df9a2..5b3b9eab4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,7 +24,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [ "ubuntu-20.04", "ubuntu-22.04", "ubuntu-24.04" ] + os: [ "ubuntu-22.04", "ubuntu-24.04" ] python-version: ["3.10", "3.11", "3.12"] steps: @@ -35,6 +35,15 @@ jobs: with: python-version: ${{ matrix.python-version }} + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + python-version: ${{ matrix.python-version }} + cache-dependency-glob: "requirements**.txt" + cache-suffix: "${{ matrix.python-version }}" + prune-cache: false + - name: Cache_dependencies uses: actions/cache@v4 id: cache @@ -42,12 +51,6 @@ jobs: path: ~/dependencies/ key: ${{ runner.os }}-dependencies - - name: pip cache (linux) - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: pip-${{ matrix.python-version }}-ubuntu - - name: TA binary *nix if: steps.cache.outputs.cache-hit != 'true' run: | @@ -55,13 +58,13 @@ jobs: - name: Installation - *nix run: | - python -m pip install --upgrade pip wheel + uv pip install --upgrade wheel export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_INCLUDE_PATH=${HOME}/dependencies/include - pip install -r requirements-dev.txt - pip install -e ft_client/ - pip install -e . + uv pip install -r requirements-dev.txt + uv pip install -e ft_client/ + uv pip install -e . - name: Check for version alignment run: | @@ -91,6 +94,11 @@ jobs: run: | python build_helpers/extract_config_json_schema.py + - name: Run command docs partials extract + # This should be kept before the repository check to ensure that the docs are up-to-date + run: | + python build_helpers/create_command_partials.py + - name: Check for repository changes run: | if [ -n "$(git status --porcelain)" ]; then @@ -156,6 +164,15 @@ jobs: python-version: ${{ matrix.python-version }} check-latest: true + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + python-version: ${{ matrix.python-version }} + cache-dependency-glob: "requirements**.txt" + cache-suffix: "${{ matrix.python-version }}" + prune-cache: false + - name: Cache_dependencies uses: actions/cache@v4 id: cache @@ -163,12 +180,6 @@ jobs: path: ~/dependencies/ key: ${{ matrix.os }}-dependencies - - name: pip cache (macOS) - uses: actions/cache@v4 - with: - path: ~/Library/Caches/pip - key: pip-${{ matrix.os }}-${{ matrix.python-version }} - - name: TA binary *nix if: steps.cache.outputs.cache-hit != 'true' run: | @@ -200,13 +211,13 @@ jobs: - name: Installation (python) run: | - python -m pip install --upgrade pip wheel + uv pip install wheel export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_INCLUDE_PATH=${HOME}/dependencies/include - pip install -r requirements-dev.txt - pip install -e ft_client/ - pip install -e . + uv pip install -r requirements-dev.txt + uv pip install -e ft_client/ + uv pip install -e . - name: Tests run: | @@ -277,27 +288,24 @@ jobs: python-version: ${{ matrix.python-version }} - name: Install uv - uses: astral-sh/setup-uv@v4 + uses: astral-sh/setup-uv@v5 with: enable-cache: true + python-version: ${{ matrix.python-version }} cache-dependency-glob: "requirements**.txt" cache-suffix: "${{ matrix.python-version }}" prune-cache: false - name: Installation run: | - uv venv - .venv\Scripts\activate - # persist the venv path for future steps - "$(pwd)/.venv/Scripts" >> $env:GITHUB_PATH - function uvpipFunction { uv pip $args } Set-Alias -name pip -value uvpipFunction + ./build_helpers/install_windows.ps1 - name: Tests run: | - pytest --random-order + pytest --random-order --durations 20 -n auto - name: Check for repository changes run: | @@ -418,6 +426,15 @@ jobs: with: python-version: "3.12" + - name: Install uv + uses: astral-sh/setup-uv@v5 + with: + enable-cache: true + python-version: "3.12" + cache-dependency-glob: "requirements**.txt" + cache-suffix: "3.12" + prune-cache: false + - name: Cache_dependencies uses: actions/cache@v4 id: cache @@ -425,11 +442,6 @@ jobs: path: ~/dependencies/ key: ${{ runner.os }}-dependencies - - name: pip cache (linux) - uses: actions/cache@v4 - with: - path: ~/.cache/pip - key: pip-3.12-ubuntu - name: TA binary *nix if: steps.cache.outputs.cache-hit != 'true' @@ -438,13 +450,13 @@ jobs: - name: Installation - *nix run: | - python -m pip install --upgrade pip wheel + uv pip install --upgrade wheel export LD_LIBRARY_PATH=${HOME}/dependencies/lib:$LD_LIBRARY_PATH export TA_LIBRARY_PATH=${HOME}/dependencies/lib export TA_INCLUDE_PATH=${HOME}/dependencies/include - pip install -r requirements-dev.txt - pip install -e ft_client/ - pip install -e . + uv pip install -r requirements-dev.txt + uv pip install -e ft_client/ + uv pip install -e . - name: Tests incl. ccxt compatibility tests env: @@ -548,7 +560,7 @@ jobs: merge-multiple: true - name: Publish to PyPI (Test) - uses: pypa/gh-action-pypi-publish@v1.12.2 + uses: pypa/gh-action-pypi-publish@v1.12.4 with: repository-url: https://test.pypi.org/legacy/ @@ -575,7 +587,7 @@ jobs: merge-multiple: true - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@v1.12.2 + uses: pypa/gh-action-pypi-publish@v1.12.4 deploy-docker: diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 745cc391f..2c284eb6f 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -9,7 +9,7 @@ repos: # stages: [push] - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v1.13.0" + rev: "v1.15.0" hooks: - id: mypy exclude: build_helpers @@ -19,11 +19,11 @@ repos: - types-requests==2.32.0.20241016 - types-tabulate==0.9.0.20241207 - types-python-dateutil==2.9.0.20241206 - - SQLAlchemy==2.0.36 + - SQLAlchemy==2.0.38 # stages: [push] - repo: https://github.com/pycqa/isort - rev: "5.13.2" + rev: "6.0.0" hooks: - id: isort name: isort (python) @@ -31,7 +31,7 @@ repos: - repo: https://github.com/charliermarsh/ruff-pre-commit # Ruff version. - rev: 'v0.8.2' + rev: 'v0.9.6' hooks: - id: ruff - id: ruff-format @@ -62,7 +62,7 @@ repos: - id: strip-exif - repo: https://github.com/codespell-project/codespell - rev: v2.3.0 + rev: v2.4.1 hooks: - id: codespell additional_dependencies: diff --git a/Dockerfile b/Dockerfile index 76249919b..8398492ce 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.12.7-slim-bookworm as base +FROM python:3.12.8-slim-bookworm as base # Setup env ENV LANG C.UTF-8 @@ -11,7 +11,7 @@ ENV FT_APP_ENV="docker" # Prepare environment RUN mkdir /freqtrade \ && apt-get update \ - && apt-get -y install sudo libatlas3-base curl sqlite3 libhdf5-serial-dev libgomp1 \ + && apt-get -y install sudo libatlas3-base curl sqlite3 libgomp1 \ && apt-get clean \ && useradd -u 1000 -G sudo -U -m -s /bin/bash ftuser \ && chown ftuser:ftuser /freqtrade \ diff --git a/README.md b/README.md index 5a4330bb9..4a0b8121b 100644 --- a/README.md +++ b/README.md @@ -32,10 +32,11 @@ Please read the [exchange specific notes](docs/exchanges.md) to learn about even - [X] [BingX](https://bingx.com/invite/0EM9RX) - [X] [Bybit](https://bybit.com/) - [X] [Gate.io](https://www.gate.io/ref/6266643) -- [X] [HTX](https://www.htx.com/) (Former Huobi) +- [X] [HTX](https://www.htx.com/) - [X] [Hyperliquid](https://hyperliquid.xyz/) (A decentralized exchange, or DEX) - [X] [Kraken](https://kraken.com/) -- [X] [OKX](https://okx.com/) (Former OKEX) +- [X] [OKX](https://okx.com/) +- [X] [MyOKX](https://okx.com/) (OKX EEA) - [ ] [potentially many others](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_ ### Supported Futures Exchanges (experimental) @@ -89,13 +90,13 @@ For further (native) installation methods, please refer to the [Installation doc ``` usage: freqtrade [-h] [-V] - {trade,create-userdir,new-config,show-config,new-strategy,download-data,convert-data,convert-trade-data,trades-to-ohlcv,list-data,backtesting,backtesting-show,backtesting-analysis,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-markets,list-pairs,list-strategies,list-freqaimodels,list-timeframes,show-trades,test-pairlist,convert-db,install-ui,plot-dataframe,plot-profit,webserver,strategy-updater,lookahead-analysis,recursive-analysis} + {trade,create-userdir,new-config,show-config,new-strategy,download-data,convert-data,convert-trade-data,trades-to-ohlcv,list-data,backtesting,backtesting-show,backtesting-analysis,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-markets,list-pairs,list-strategies,list-hyperoptloss,list-freqaimodels,list-timeframes,show-trades,test-pairlist,convert-db,install-ui,plot-dataframe,plot-profit,webserver,strategy-updater,lookahead-analysis,recursive-analysis} ... Free, open source crypto trading bot positional arguments: - {trade,create-userdir,new-config,show-config,new-strategy,download-data,convert-data,convert-trade-data,trades-to-ohlcv,list-data,backtesting,backtesting-show,backtesting-analysis,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-markets,list-pairs,list-strategies,list-freqaimodels,list-timeframes,show-trades,test-pairlist,convert-db,install-ui,plot-dataframe,plot-profit,webserver,strategy-updater,lookahead-analysis,recursive-analysis} + {trade,create-userdir,new-config,show-config,new-strategy,download-data,convert-data,convert-trade-data,trades-to-ohlcv,list-data,backtesting,backtesting-show,backtesting-analysis,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-markets,list-pairs,list-strategies,list-hyperoptloss,list-freqaimodels,list-timeframes,show-trades,test-pairlist,convert-db,install-ui,plot-dataframe,plot-profit,webserver,strategy-updater,lookahead-analysis,recursive-analysis} trade Trade module. create-userdir Create user-data directory. new-config Create new config @@ -119,6 +120,7 @@ positional arguments: list-markets Print markets on exchange. list-pairs Print pairs on exchange. list-strategies Print available strategies. + list-hyperoptloss Print available hyperopt loss functions. list-freqaimodels Print available freqAI models. list-timeframes Print available timeframes for the exchange. show-trades Show trades. @@ -135,7 +137,6 @@ positional arguments: options: -h, --help show this help message and exit -V, --version show program's version number and exit - ``` ### Telegram RPC commands diff --git a/build_helpers/create_command_partials.py b/build_helpers/create_command_partials.py new file mode 100644 index 000000000..7127950ed --- /dev/null +++ b/build_helpers/create_command_partials.py @@ -0,0 +1,53 @@ +import subprocess +from pathlib import Path + + +subcommands = [ + "trade", + "create-userdir", + "new-config", + "show-config", + "new-strategy", + "download-data", + "convert-data", + "convert-trade-data", + "trades-to-ohlcv", + "list-data", + "backtesting", + "backtesting-show", + "backtesting-analysis", + "edge", + "hyperopt", + "hyperopt-list", + "hyperopt-show", + "list-exchanges", + "list-markets", + "list-pairs", + "list-strategies", + "list-hyperoptloss", + "list-freqaimodels", + "list-timeframes", + "show-trades", + "test-pairlist", + "convert-db", + "install-ui", + "plot-dataframe", + "plot-profit", + "webserver", + "strategy-updater", + "lookahead-analysis", + "recursive-analysis", +] + +result = subprocess.run(["freqtrade", "--help"], capture_output=True, text=True) + +with Path("docs/commands/main.md").open("w") as f: + f.write(f"```\n{result.stdout}\n```\n") + + +for command in subcommands: + print(f"Running for {command}") + result = subprocess.run(["freqtrade", command, "--help"], capture_output=True, text=True) + + with Path(f"docs/commands/{command}.md").open("w") as f: + f.write(f"```\n{result.stdout}\n```\n") diff --git a/build_helpers/install_windows.ps1 b/build_helpers/install_windows.ps1 index 228a2ca23..919d8b7da 100644 --- a/build_helpers/install_windows.ps1 +++ b/build_helpers/install_windows.ps1 @@ -4,7 +4,7 @@ python -m pip install --upgrade pip python -c "import sys; print(f'{sys.version_info.major}.{sys.version_info.minor}')" pip install -U wheel "numpy<2" -pip install --no-build --find-links=build_helpers\ ta-lib +pip install --only-binary ta-lib --find-links=build_helpers\ ta-lib pip install -r requirements-dev.txt pip install -e . diff --git a/build_helpers/pyarrow-18.1.0-cp311-cp311-linux_armv7l.whl b/build_helpers/pyarrow-19.0.0-cp311-cp311-linux_armv7l.whl similarity index 61% rename from build_helpers/pyarrow-18.1.0-cp311-cp311-linux_armv7l.whl rename to build_helpers/pyarrow-19.0.0-cp311-cp311-linux_armv7l.whl index d40603a5f..dbd6ba991 100644 Binary files a/build_helpers/pyarrow-18.1.0-cp311-cp311-linux_armv7l.whl and b/build_helpers/pyarrow-19.0.0-cp311-cp311-linux_armv7l.whl differ diff --git a/build_helpers/schema.json b/build_helpers/schema.json index 73c06ba46..15764df90 100644 --- a/build_helpers/schema.json +++ b/build_helpers/schema.json @@ -13,6 +13,10 @@ "description": "The timeframe to use (e.g `1m`, `5m`, `15m`, `30m`, `1h` ...). \nUsually specified in the strategy and missing in the configuration.", "type": "string" }, + "proxy_coin": { + "description": "Proxy coin - must be used for specific futures modes (e.g. BNFCR)", + "type": "string" + }, "stake_currency": { "description": "Currency used for staking.", "type": "string" @@ -601,7 +605,11 @@ "type": "string" }, "chat_id": { - "description": "Telegram chat ID", + "description": "Telegram chat or group ID", + "type": "string" + }, + "topic_id": { + "description": "Telegram topic ID - only applicable for group chats", "type": "string" }, "allow_custom_messages": { @@ -971,7 +979,7 @@ "type": "string" } }, - "x": { + "verbosity": { "description": "Logging verbosity level.", "type": "string", "enum": [ @@ -1047,7 +1055,6 @@ "enum": [ "json", "jsongz", - "hdf5", "feather", "parquet" ], @@ -1059,7 +1066,6 @@ "enum": [ "json", "jsongz", - "hdf5", "feather", "parquet" ], diff --git a/build_helpers/ta_lib-0.4.34-cp311-cp311-win_amd64.whl b/build_helpers/ta_lib-0.4.38-cp310-cp310-win_amd64.whl similarity index 67% rename from build_helpers/ta_lib-0.4.34-cp311-cp311-win_amd64.whl rename to build_helpers/ta_lib-0.4.38-cp310-cp310-win_amd64.whl index 3513df4ba..00610d687 100644 Binary files a/build_helpers/ta_lib-0.4.34-cp311-cp311-win_amd64.whl and b/build_helpers/ta_lib-0.4.38-cp310-cp310-win_amd64.whl differ diff --git a/build_helpers/ta_lib-0.4.34-cp311-cp311-linux_armv7l.whl b/build_helpers/ta_lib-0.4.38-cp311-cp311-linux_armv7l.whl similarity index 50% rename from build_helpers/ta_lib-0.4.34-cp311-cp311-linux_armv7l.whl rename to build_helpers/ta_lib-0.4.38-cp311-cp311-linux_armv7l.whl index 581361925..a255164d0 100644 Binary files a/build_helpers/ta_lib-0.4.34-cp311-cp311-linux_armv7l.whl and b/build_helpers/ta_lib-0.4.38-cp311-cp311-linux_armv7l.whl differ diff --git a/build_helpers/ta_lib-0.4.34-cp310-cp310-win_amd64.whl b/build_helpers/ta_lib-0.4.38-cp311-cp311-win_amd64.whl similarity index 67% rename from build_helpers/ta_lib-0.4.34-cp310-cp310-win_amd64.whl rename to build_helpers/ta_lib-0.4.38-cp311-cp311-win_amd64.whl index 32bb10e4f..7237604ba 100644 Binary files a/build_helpers/ta_lib-0.4.34-cp310-cp310-win_amd64.whl and b/build_helpers/ta_lib-0.4.38-cp311-cp311-win_amd64.whl differ diff --git a/build_helpers/ta_lib-0.4.34-cp312-cp312-win_amd64.whl b/build_helpers/ta_lib-0.4.38-cp312-cp312-win_amd64.whl similarity index 67% rename from build_helpers/ta_lib-0.4.34-cp312-cp312-win_amd64.whl rename to build_helpers/ta_lib-0.4.38-cp312-cp312-win_amd64.whl index 212668e58..fe37c8468 100644 Binary files a/build_helpers/ta_lib-0.4.34-cp312-cp312-win_amd64.whl and b/build_helpers/ta_lib-0.4.38-cp312-cp312-win_amd64.whl differ diff --git a/docker/Dockerfile.armhf b/docker/Dockerfile.armhf index 1a6d52ad4..ccc039a9d 100644 --- a/docker/Dockerfile.armhf +++ b/docker/Dockerfile.armhf @@ -1,4 +1,4 @@ -FROM python:3.11.10-slim-bookworm as base +FROM python:3.11.11-slim-bookworm as base # Setup env ENV LANG C.UTF-8 @@ -11,7 +11,7 @@ ENV FT_APP_ENV="docker" # Prepare environment RUN mkdir /freqtrade \ && apt-get update \ - && apt-get -y install sudo libatlas3-base libopenblas-dev curl sqlite3 libhdf5-dev libutf8proc-dev libsnappy-dev \ + && apt-get -y install sudo libatlas3-base libopenblas-dev curl sqlite3 libutf8proc-dev libsnappy-dev \ && apt-get clean \ && useradd -u 1000 -G sudo -U -m ftuser \ && chown ftuser:ftuser /freqtrade \ diff --git a/docs/advanced-orderflow.md b/docs/advanced-orderflow.md index 9769b8e92..4561b35a3 100644 --- a/docs/advanced-orderflow.md +++ b/docs/advanced-orderflow.md @@ -70,8 +70,8 @@ dataframe["delta"] # Difference between ask and bid volume. dataframe["min_delta"] # Minimum delta within the candle dataframe["max_delta"] # Maximum delta within the candle dataframe["total_trades"] # Total number of trades -dataframe["stacked_imbalances_bid"] # Price level of stacked bid imbalance -dataframe["stacked_imbalances_ask"] # Price level of stacked ask imbalance +dataframe["stacked_imbalances_bid"] # List of price levels of stacked bid imbalance range beginnings +dataframe["stacked_imbalances_ask"] # List of price levels of stacked ask imbalance range beginnings ``` You can access these columns in your strategy code for further analysis. Here's an example: diff --git a/docs/backtesting.md b/docs/backtesting.md index 710c4fd03..133f288f2 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -7,111 +7,7 @@ To learn how to get data for the pairs and exchange you're interested in, head o ## Backtesting command reference -``` -usage: freqtrade backtesting [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] [-s NAME] - [--strategy-path PATH] - [--recursive-strategy-search] - [--freqaimodel NAME] [--freqaimodel-path PATH] - [-i TIMEFRAME] [--timerange TIMERANGE] - [--data-format-ohlcv {json,jsongz,hdf5,feather,parquet}] - [--max-open-trades INT] - [--stake-amount STAKE_AMOUNT] [--fee FLOAT] - [-p PAIRS [PAIRS ...]] [--eps] - [--enable-protections] - [--dry-run-wallet DRY_RUN_WALLET] - [--timeframe-detail TIMEFRAME_DETAIL] - [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] - [--export {none,trades,signals}] - [--export-filename PATH] - [--breakdown {day,week,month} [{day,week,month} ...]] - [--cache {none,day,week,month}] - [--freqai-backtest-live-models] - -options: - -h, --help show this help message and exit - -i TIMEFRAME, --timeframe TIMEFRAME - Specify timeframe (`1m`, `5m`, `30m`, `1h`, `1d`). - --timerange TIMERANGE - Specify what timerange of data to use. - --data-format-ohlcv {json,jsongz,hdf5,feather,parquet} - Storage format for downloaded candle (OHLCV) data. - (default: `feather`). - --max-open-trades INT - Override the value of the `max_open_trades` - configuration setting. - --stake-amount STAKE_AMOUNT - Override the value of the `stake_amount` configuration - setting. - --fee FLOAT Specify fee ratio. Will be applied twice (on trade - entry and exit). - -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] - Limit command to these pairs. Pairs are space- - separated. - --eps, --enable-position-stacking - Allow buying the same pair multiple times (position - stacking). - --enable-protections, --enableprotections - Enable protections for backtesting.Will slow - backtesting down by a considerable amount, but will - include configured protections - --dry-run-wallet DRY_RUN_WALLET, --starting-balance DRY_RUN_WALLET - Starting balance, used for backtesting / hyperopt and - dry-runs. - --timeframe-detail TIMEFRAME_DETAIL - Specify detail timeframe for backtesting (`1m`, `5m`, - `30m`, `1h`, `1d`). - --strategy-list STRATEGY_LIST [STRATEGY_LIST ...] - Provide a space-separated list of strategies to - backtest. Please note that timeframe needs to be set - either in config or via command line. When using this - together with `--export trades`, the strategy-name is - injected into the filename (so `backtest-data.json` - becomes `backtest-data-SampleStrategy.json` - --export {none,trades,signals} - Export backtest results (default: trades). - --export-filename PATH, --backtest-filename PATH - Use this filename for backtest results.Requires - `--export` to be set as well. Example: `--export-filen - ame=user_data/backtest_results/backtest_today.json` - --breakdown {day,week,month} [{day,week,month} ...] - Show backtesting breakdown per [day, week, month]. - --cache {none,day,week,month} - Load a cached backtest result no older than specified - age (default: day). - --freqai-backtest-live-models - Run backtest with ready models. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE, --log-file FILE - Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH, --data-dir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -Strategy arguments: - -s NAME, --strategy NAME - Specify strategy class name which will be used by the - bot. - --strategy-path PATH Specify additional strategy lookup path. - --recursive-strategy-search - Recursively search for a strategy in the strategies - folder. - --freqaimodel NAME Specify a custom freqaimodels. - --freqaimodel-path PATH - Specify additional lookup path for freqaimodels. - -``` +--8<-- "commands/backtesting.md" ## Test your strategy with Backtesting @@ -612,7 +508,12 @@ To utilize this, you can append `--timeframe-detail 5m` to your regular backtest freqtrade backtesting --strategy AwesomeStrategy --timeframe 1h --timeframe-detail 5m ``` -This will load 1h data as well as 5m data for the timeframe. The strategy will be analyzed with the 1h timeframe, and Entry orders will only be placed at the main timeframe, however Order fills and exit signals will be evaluated at the 5m candle, simulating intra-candle movements. +This will load 1h data (the main timeframe) as well as 5m data (detail timeframe) for the selected timerange. +The strategy will be analyzed with the 1h timeframe. +Candles where activity may take place (there's an active signal, the pair is in a trade) are evaluated at the 5m timeframe. +This will allow for a more accurate simulation of intra-candle movements - and can lead to different results, especially on higher timeframes. + +Entries will generally still happen at the main candle's open, however freed trade slots may be freed earlier (if the exit signal is triggered on the 5m candle), which can then be used for a new trade of a different pair. All callback functions (`custom_exit()`, `custom_stoploss()`, ... ) will be running for each 5m candle once the trade is opened (so 12 times in the above example of 1h timeframe, and 5m detailed timeframe). @@ -624,6 +525,27 @@ Also, data must be available / downloaded already. !!! Tip You can use this function as the last part of strategy development, to ensure your strategy is not exploiting one of the [backtesting assumptions](#assumptions-made-by-backtesting). Strategies that perform similarly well with this mode have a good chance to perform well in dry/live modes too (although only forward-testing (dry-mode) can really confirm a strategy). +??? Sample "Extreme Difference Example" + Using `--timeframe-detail` on an extreme example (all below pairs have the 10:00 candle with an entry signal) may lead to the following backtesting Trade sequence with 1 max_open_trades: + + | Pair | Entry Time | Exit Time | Duration | + |------|------------|-----------| -------- | + | BTC/USDT | 2024-01-01 10:00:00 | 2021-01-01 10:05:00 | 5m | + | ETH/USDT | 2024-01-01 10:05:00 | 2021-01-01 10:15:00 | 10m | + | XRP/USDT | 2024-01-01 10:15:00 | 2021-01-01 10:30:00 | 15m | + | SOL/USDT | 2024-01-01 10:15:00 | 2021-01-01 11:05:00 | 50m | + | BTC/USDT | 2024-01-01 11:05:00 | 2021-01-01 12:00:00 | 55m | + + Without timeframe-detail, this would look like: + + | Pair | Entry Time | Exit Time | Duration | + |------|------------|-----------| -------- | + | BTC/USDT | 2024-01-01 10:00:00 | 2021-01-01 11:00:00 | 1h | + | BTC/USDT | 2024-01-01 11:00:00 | 2021-01-01 12:00:00 | 1h | + + The difference is significant, as without detail data, only the first `max_open_trades` signals per candle are evaluated, and the trade slots are only freed at the end of the candle, allowing for a new trade to be opened at the next candle. + + ## Backtesting multiple strategies To compare multiple strategies, a list of Strategies can be provided to backtesting. diff --git a/docs/bot-usage.md b/docs/bot-usage.md index 8ebc82552..593a36500 100644 --- a/docs/bot-usage.md +++ b/docs/bot-usage.md @@ -10,101 +10,11 @@ This page explains the different parameters of the bot and how to run it. ## Bot commands -``` -usage: freqtrade [-h] [-V] - {trade,create-userdir,new-config,show-config,new-strategy,download-data,convert-data,convert-trade-data,trades-to-ohlcv,list-data,backtesting,backtesting-show,backtesting-analysis,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-markets,list-pairs,list-strategies,list-freqaimodels,list-timeframes,show-trades,test-pairlist,convert-db,install-ui,plot-dataframe,plot-profit,webserver,strategy-updater,lookahead-analysis,recursive-analysis} - ... - -Free, open source crypto trading bot - -positional arguments: - {trade,create-userdir,new-config,show-config,new-strategy,download-data,convert-data,convert-trade-data,trades-to-ohlcv,list-data,backtesting,backtesting-show,backtesting-analysis,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-markets,list-pairs,list-strategies,list-freqaimodels,list-timeframes,show-trades,test-pairlist,convert-db,install-ui,plot-dataframe,plot-profit,webserver,strategy-updater,lookahead-analysis,recursive-analysis} - trade Trade module. - create-userdir Create user-data directory. - new-config Create new config - show-config Show resolved config - new-strategy Create new strategy - download-data Download backtesting data. - convert-data Convert candle (OHLCV) data from one format to - another. - convert-trade-data Convert trade data from one format to another. - trades-to-ohlcv Convert trade data to OHLCV data. - list-data List downloaded data. - backtesting Backtesting module. - backtesting-show Show past Backtest results - backtesting-analysis - Backtest Analysis module. - edge Edge module. - hyperopt Hyperopt module. - hyperopt-list List Hyperopt results - hyperopt-show Show details of Hyperopt results - list-exchanges Print available exchanges. - list-markets Print markets on exchange. - list-pairs Print pairs on exchange. - list-strategies Print available strategies. - list-freqaimodels Print available freqAI models. - list-timeframes Print available timeframes for the exchange. - show-trades Show trades. - test-pairlist Test your pairlist configuration. - convert-db Migrate database to different system - install-ui Install FreqUI - plot-dataframe Plot candles with indicators. - plot-profit Generate plot showing profits. - webserver Webserver module. - strategy-updater updates outdated strategy files to the current version - lookahead-analysis Check for potential look ahead bias. - recursive-analysis Check for potential recursive formula issue. - -options: - -h, --help show this help message and exit - -V, --version show program's version number and exit - -``` +--8<-- "commands/main.md" ### Bot trading commands -``` -usage: freqtrade trade [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] - [--userdir PATH] [-s NAME] [--strategy-path PATH] - [--db-url PATH] [--sd-notify] [--dry-run] - [--dry-run-wallet DRY_RUN_WALLET] - -optional arguments: - -h, --help show this help message and exit - --db-url PATH Override trades database URL, this is useful in custom - deployments (default: `sqlite:///tradesv3.sqlite` for - Live Run mode, `sqlite:///tradesv3.dryrun.sqlite` for - Dry Run). - --sd-notify Notify systemd service manager. - --dry-run Enforce dry-run for trading (removes Exchange secrets - and simulates trades). - --dry-run-wallet DRY_RUN_WALLET, --starting-balance DRY_RUN_WALLET - Starting balance, used for backtesting / hyperopt and - dry-runs. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -Strategy arguments: - -s NAME, --strategy NAME - Specify strategy class name which will be used by the - bot. - --strategy-path PATH Specify additional strategy lookup path. - -``` +--8<-- "commands/trade.md" ### How to specify which configuration file be used? diff --git a/docs/commands/backtesting-analysis.md b/docs/commands/backtesting-analysis.md new file mode 100644 index 000000000..50707d03a --- /dev/null +++ b/docs/commands/backtesting-analysis.md @@ -0,0 +1,66 @@ +``` +usage: freqtrade backtesting-analysis [-h] [-v] [--no-color] [--logfile FILE] + [-V] [-c PATH] [-d PATH] + [--userdir PATH] + [--export-filename PATH] + [--analysis-groups {0,1,2,3,4,5} [{0,1,2,3,4,5} ...]] + [--enter-reason-list ENTER_REASON_LIST [ENTER_REASON_LIST ...]] + [--exit-reason-list EXIT_REASON_LIST [EXIT_REASON_LIST ...]] + [--indicator-list INDICATOR_LIST [INDICATOR_LIST ...]] + [--entry-only] [--exit-only] + [--timerange TIMERANGE] + [--rejected-signals] [--analysis-to-csv] + [--analysis-csv-path ANALYSIS_CSV_PATH] + +options: + -h, --help show this help message and exit + --export-filename PATH, --backtest-filename PATH + Use this filename for backtest results.Requires + `--export` to be set as well. Example: `--export-filen + ame=user_data/backtest_results/backtest_today.json` + --analysis-groups {0,1,2,3,4,5} [{0,1,2,3,4,5} ...] + grouping output - 0: simple wins/losses by enter tag, + 1: by enter_tag, 2: by enter_tag and exit_tag, 3: by + pair and enter_tag, 4: by pair, enter_ and exit_tag + (this can get quite large), 5: by exit_tag + --enter-reason-list ENTER_REASON_LIST [ENTER_REASON_LIST ...] + Space separated list of entry signals to analyse. + Default: all. e.g. 'entry_tag_a entry_tag_b' + --exit-reason-list EXIT_REASON_LIST [EXIT_REASON_LIST ...] + Space separated list of exit signals to analyse. + Default: all. e.g. 'exit_tag_a roi stop_loss + trailing_stop_loss' + --indicator-list INDICATOR_LIST [INDICATOR_LIST ...] + Space separated list of indicators to analyse. e.g. + 'close rsi bb_lowerband profit_abs' + --entry-only Only analyze entry signals. + --exit-only Only analyze exit signals. + --timerange TIMERANGE + Specify what timerange of data to use. + --rejected-signals Analyse rejected signals + --analysis-to-csv Save selected analysis tables to individual CSVs + --analysis-csv-path ANALYSIS_CSV_PATH + Specify a path to save the analysis CSVs if + --analysis-to-csv is enabled. Default: + user_data/basktesting_results/ + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/backtesting-show.md b/docs/commands/backtesting-show.md new file mode 100644 index 000000000..1c7889851 --- /dev/null +++ b/docs/commands/backtesting-show.md @@ -0,0 +1,36 @@ +``` +usage: freqtrade backtesting-show [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] + [--export-filename PATH] [--show-pair-list] + [--breakdown {day,week,month} [{day,week,month} ...]] + +options: + -h, --help show this help message and exit + --export-filename PATH, --backtest-filename PATH + Use this filename for backtest results.Requires + `--export` to be set as well. Example: `--export-filen + ame=user_data/backtest_results/backtest_today.json` + --show-pair-list Show backtesting pairlist sorted by profit. + --breakdown {day,week,month} [{day,week,month} ...] + Show backtesting breakdown per [day, week, month]. + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/backtesting.md b/docs/commands/backtesting.md new file mode 100644 index 000000000..160bda7cc --- /dev/null +++ b/docs/commands/backtesting.md @@ -0,0 +1,107 @@ +``` +usage: freqtrade backtesting [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] [-s NAME] + [--strategy-path PATH] + [--recursive-strategy-search] + [--freqaimodel NAME] [--freqaimodel-path PATH] + [-i TIMEFRAME] [--timerange TIMERANGE] + [--data-format-ohlcv {json,jsongz,feather,parquet}] + [--max-open-trades INT] + [--stake-amount STAKE_AMOUNT] [--fee FLOAT] + [-p PAIRS [PAIRS ...]] [--eps] + [--enable-protections] + [--dry-run-wallet DRY_RUN_WALLET] + [--timeframe-detail TIMEFRAME_DETAIL] + [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] + [--export {none,trades,signals}] + [--export-filename PATH] + [--breakdown {day,week,month} [{day,week,month} ...]] + [--cache {none,day,week,month}] + [--freqai-backtest-live-models] + +options: + -h, --help show this help message and exit + -i TIMEFRAME, --timeframe TIMEFRAME + Specify timeframe (`1m`, `5m`, `30m`, `1h`, `1d`). + --timerange TIMERANGE + Specify what timerange of data to use. + --data-format-ohlcv {json,jsongz,feather,parquet} + Storage format for downloaded candle (OHLCV) data. + (default: `feather`). + --max-open-trades INT + Override the value of the `max_open_trades` + configuration setting. + --stake-amount STAKE_AMOUNT + Override the value of the `stake_amount` configuration + setting. + --fee FLOAT Specify fee ratio. Will be applied twice (on trade + entry and exit). + -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] + Limit command to these pairs. Pairs are space- + separated. + --eps, --enable-position-stacking + Allow buying the same pair multiple times (position + stacking). + --enable-protections, --enableprotections + Enable protections for backtesting.Will slow + backtesting down by a considerable amount, but will + include configured protections + --dry-run-wallet DRY_RUN_WALLET, --starting-balance DRY_RUN_WALLET + Starting balance, used for backtesting / hyperopt and + dry-runs. + --timeframe-detail TIMEFRAME_DETAIL + Specify detail timeframe for backtesting (`1m`, `5m`, + `30m`, `1h`, `1d`). + --strategy-list STRATEGY_LIST [STRATEGY_LIST ...] + Provide a space-separated list of strategies to + backtest. Please note that timeframe needs to be set + either in config or via command line. When using this + together with `--export trades`, the strategy-name is + injected into the filename (so `backtest-data.json` + becomes `backtest-data-SampleStrategy.json` + --export {none,trades,signals} + Export backtest results (default: trades). + --export-filename PATH, --backtest-filename PATH + Use this filename for backtest results.Requires + `--export` to be set as well. Example: `--export-filen + ame=user_data/backtest_results/backtest_today.json` + --breakdown {day,week,month} [{day,week,month} ...] + Show backtesting breakdown per [day, week, month]. + --cache {none,day,week,month} + Load a cached backtest result no older than specified + age (default: day). + --freqai-backtest-live-models + Run backtest with ready models. + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name which will be used by the + bot. + --strategy-path PATH Specify additional strategy lookup path. + --recursive-strategy-search + Recursively search for a strategy in the strategies + folder. + --freqaimodel NAME Specify a custom freqaimodels. + --freqaimodel-path PATH + Specify additional lookup path for freqaimodels. + +``` diff --git a/docs/commands/convert-data.md b/docs/commands/convert-data.md new file mode 100644 index 000000000..3bd0fa45b --- /dev/null +++ b/docs/commands/convert-data.md @@ -0,0 +1,52 @@ +``` +usage: freqtrade convert-data [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] + [-p PAIRS [PAIRS ...]] --format-from + {json,jsongz,feather,parquet} --format-to + {json,jsongz,feather,parquet} [--erase] + [--exchange EXCHANGE] + [-t TIMEFRAMES [TIMEFRAMES ...]] + [--trading-mode {spot,margin,futures}] + [--candle-types {spot,futures,mark,index,premiumIndex,funding_rate} [{spot,futures,mark,index,premiumIndex,funding_rate} ...]] + +options: + -h, --help show this help message and exit + -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] + Limit command to these pairs. Pairs are space- + separated. + --format-from {json,jsongz,feather,parquet} + Source format for data conversion. + --format-to {json,jsongz,feather,parquet} + Destination format for data conversion. + --erase Clean all existing data for the selected + exchange/pairs/timeframes. + --exchange EXCHANGE Exchange name. Only valid if no config is provided. + -t TIMEFRAMES [TIMEFRAMES ...], --timeframes TIMEFRAMES [TIMEFRAMES ...] + Specify which tickers to download. Space-separated + list. Default: `1m 5m`. + --trading-mode {spot,margin,futures}, --tradingmode {spot,margin,futures} + Select Trading mode + --candle-types {spot,futures,mark,index,premiumIndex,funding_rate} [{spot,futures,mark,index,premiumIndex,funding_rate} ...] + Select candle type to convert. Defaults to all + available types. + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/convert-db.md b/docs/commands/convert-db.md new file mode 100644 index 000000000..fcaab3192 --- /dev/null +++ b/docs/commands/convert-db.md @@ -0,0 +1,12 @@ +``` +usage: freqtrade convert-db [-h] [--db-url PATH] [--db-url-from PATH] + +options: + -h, --help show this help message and exit + --db-url PATH Override trades database URL, this is useful in custom + deployments (default: `sqlite:///tradesv3.sqlite` for + Live Run mode, `sqlite:///tradesv3.dryrun.sqlite` for + Dry Run). + --db-url-from PATH Source db url to use when migrating a database. + +``` diff --git a/docs/commands/convert-trade-data.md b/docs/commands/convert-trade-data.md new file mode 100644 index 000000000..25843a8da --- /dev/null +++ b/docs/commands/convert-trade-data.md @@ -0,0 +1,41 @@ +``` +usage: freqtrade convert-trade-data [-h] [-v] [--no-color] [--logfile FILE] + [-V] [-c PATH] [-d PATH] [--userdir PATH] + [-p PAIRS [PAIRS ...]] --format-from + {json,jsongz,feather,parquet,kraken_csv} + --format-to {json,jsongz,feather,parquet} + [--erase] [--exchange EXCHANGE] + +options: + -h, --help show this help message and exit + -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] + Limit command to these pairs. Pairs are space- + separated. + --format-from {json,jsongz,feather,parquet,kraken_csv} + Source format for data conversion. + --format-to {json,jsongz,feather,parquet} + Destination format for data conversion. + --erase Clean all existing data for the selected + exchange/pairs/timeframes. + --exchange EXCHANGE Exchange name. Only valid if no config is provided. + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/create-userdir.md b/docs/commands/create-userdir.md new file mode 100644 index 000000000..9d52e2cf4 --- /dev/null +++ b/docs/commands/create-userdir.md @@ -0,0 +1,10 @@ +``` +usage: freqtrade create-userdir [-h] [--userdir PATH] [--reset] + +options: + -h, --help show this help message and exit + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + --reset Reset sample files to their original state. + +``` diff --git a/docs/commands/download-data.md b/docs/commands/download-data.md new file mode 100644 index 000000000..e70ae9a23 --- /dev/null +++ b/docs/commands/download-data.md @@ -0,0 +1,70 @@ +``` +usage: freqtrade download-data [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] + [-p PAIRS [PAIRS ...]] [--pairs-file FILE] + [--days INT] [--new-pairs-days INT] + [--include-inactive-pairs] + [--timerange TIMERANGE] [--dl-trades] + [--convert] [--exchange EXCHANGE] + [-t TIMEFRAMES [TIMEFRAMES ...]] [--erase] + [--data-format-ohlcv {json,jsongz,feather,parquet}] + [--data-format-trades {json,jsongz,feather,parquet}] + [--trading-mode {spot,margin,futures}] + [--prepend] + +options: + -h, --help show this help message and exit + -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] + Limit command to these pairs. Pairs are space- + separated. + --pairs-file FILE File containing a list of pairs. Takes precedence over + --pairs or pairs configured in the configuration. + --days INT Download data for given number of days. + --new-pairs-days INT Download data of new pairs for given number of days. + Default: `None`. + --include-inactive-pairs + Also download data from inactive pairs. + --timerange TIMERANGE + Specify what timerange of data to use. + --dl-trades Download trades instead of OHLCV data. + --convert Convert downloaded trades to OHLCV data. Only + applicable in combination with `--dl-trades`. Will be + automatic for exchanges which don't have historic + OHLCV (e.g. Kraken). If not provided, use `trades-to- + ohlcv` to convert trades data to OHLCV data. + --exchange EXCHANGE Exchange name. Only valid if no config is provided. + -t TIMEFRAMES [TIMEFRAMES ...], --timeframes TIMEFRAMES [TIMEFRAMES ...] + Specify which tickers to download. Space-separated + list. Default: `1m 5m`. + --erase Clean all existing data for the selected + exchange/pairs/timeframes. + --data-format-ohlcv {json,jsongz,feather,parquet} + Storage format for downloaded candle (OHLCV) data. + (default: `feather`). + --data-format-trades {json,jsongz,feather,parquet} + Storage format for downloaded trades data. (default: + `feather`). + --trading-mode {spot,margin,futures}, --tradingmode {spot,margin,futures} + Select Trading mode + --prepend Allow data prepending. (Data-appending is disabled) + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/edge.md b/docs/commands/edge.md new file mode 100644 index 000000000..06f883230 --- /dev/null +++ b/docs/commands/edge.md @@ -0,0 +1,69 @@ +``` +usage: freqtrade edge [-h] [-v] [--no-color] [--logfile FILE] [-V] [-c PATH] + [-d PATH] [--userdir PATH] [-s NAME] + [--strategy-path PATH] [--recursive-strategy-search] + [--freqaimodel NAME] [--freqaimodel-path PATH] + [-i TIMEFRAME] [--timerange TIMERANGE] + [--data-format-ohlcv {json,jsongz,feather,parquet}] + [--max-open-trades INT] [--stake-amount STAKE_AMOUNT] + [--fee FLOAT] [-p PAIRS [PAIRS ...]] + [--stoplosses STOPLOSS_RANGE] + +options: + -h, --help show this help message and exit + -i TIMEFRAME, --timeframe TIMEFRAME + Specify timeframe (`1m`, `5m`, `30m`, `1h`, `1d`). + --timerange TIMERANGE + Specify what timerange of data to use. + --data-format-ohlcv {json,jsongz,feather,parquet} + Storage format for downloaded candle (OHLCV) data. + (default: `feather`). + --max-open-trades INT + Override the value of the `max_open_trades` + configuration setting. + --stake-amount STAKE_AMOUNT + Override the value of the `stake_amount` configuration + setting. + --fee FLOAT Specify fee ratio. Will be applied twice (on trade + entry and exit). + -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] + Limit command to these pairs. Pairs are space- + separated. + --stoplosses STOPLOSS_RANGE + Defines a range of stoploss values against which edge + will assess the strategy. The format is "min,max,step" + (without any space). Example: + `--stoplosses=-0.01,-0.1,-0.001` + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name which will be used by the + bot. + --strategy-path PATH Specify additional strategy lookup path. + --recursive-strategy-search + Recursively search for a strategy in the strategies + folder. + --freqaimodel NAME Specify a custom freqaimodels. + --freqaimodel-path PATH + Specify additional lookup path for freqaimodels. + +``` diff --git a/docs/commands/hyperopt-list.md b/docs/commands/hyperopt-list.md new file mode 100644 index 000000000..5e8398f51 --- /dev/null +++ b/docs/commands/hyperopt-list.md @@ -0,0 +1,62 @@ +``` +usage: freqtrade hyperopt-list [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] [--best] + [--profitable] [--min-trades INT] + [--max-trades INT] [--min-avg-time FLOAT] + [--max-avg-time FLOAT] [--min-avg-profit FLOAT] + [--max-avg-profit FLOAT] + [--min-total-profit FLOAT] + [--max-total-profit FLOAT] + [--min-objective FLOAT] [--max-objective FLOAT] + [--print-json] [--no-details] + [--hyperopt-filename FILENAME] + [--export-csv FILE] + +options: + -h, --help show this help message and exit + --best Select only best epochs. + --profitable Select only profitable epochs. + --min-trades INT Select epochs with more than INT trades. + --max-trades INT Select epochs with less than INT trades. + --min-avg-time FLOAT Select epochs above average time. + --max-avg-time FLOAT Select epochs below average time. + --min-avg-profit FLOAT + Select epochs above average profit. + --max-avg-profit FLOAT + Select epochs below average profit. + --min-total-profit FLOAT + Select epochs above total profit. + --max-total-profit FLOAT + Select epochs below total profit. + --min-objective FLOAT + Select epochs above objective. + --max-objective FLOAT + Select epochs below objective. + --print-json Print output in JSON format. + --no-details Do not print best epoch details. + --hyperopt-filename FILENAME + Hyperopt result filename.Example: `--hyperopt- + filename=hyperopt_results_2020-09-27_16-20-48.pickle` + --export-csv FILE Export to CSV-File. This will disable table print. + Example: --export-csv hyperopt.csv + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/hyperopt-show.md b/docs/commands/hyperopt-show.md new file mode 100644 index 000000000..9c7c378ab --- /dev/null +++ b/docs/commands/hyperopt-show.md @@ -0,0 +1,43 @@ +``` +usage: freqtrade hyperopt-show [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] [--best] + [--profitable] [-n INT] [--print-json] + [--hyperopt-filename FILENAME] [--no-header] + [--disable-param-export] + [--breakdown {day,week,month} [{day,week,month} ...]] + +options: + -h, --help show this help message and exit + --best Select only best epochs. + --profitable Select only profitable epochs. + -n INT, --index INT Specify the index of the epoch to print details for. + --print-json Print output in JSON format. + --hyperopt-filename FILENAME + Hyperopt result filename.Example: `--hyperopt- + filename=hyperopt_results_2020-09-27_16-20-48.pickle` + --no-header Do not print epoch details header. + --disable-param-export + Disable automatic hyperopt parameter export. + --breakdown {day,week,month} [{day,week,month} ...] + Show backtesting breakdown per [day, week, month]. + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/hyperopt.md b/docs/commands/hyperopt.md new file mode 100644 index 000000000..2fc522d0f --- /dev/null +++ b/docs/commands/hyperopt.md @@ -0,0 +1,121 @@ +``` +usage: freqtrade hyperopt [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] [-s NAME] + [--strategy-path PATH] [--recursive-strategy-search] + [--freqaimodel NAME] [--freqaimodel-path PATH] + [-i TIMEFRAME] [--timerange TIMERANGE] + [--data-format-ohlcv {json,jsongz,feather,parquet}] + [--max-open-trades INT] + [--stake-amount STAKE_AMOUNT] [--fee FLOAT] + [-p PAIRS [PAIRS ...]] [--hyperopt-path PATH] + [--eps] [--enable-protections] + [--dry-run-wallet DRY_RUN_WALLET] + [--timeframe-detail TIMEFRAME_DETAIL] [-e INT] + [--spaces {all,buy,sell,roi,stoploss,trailing,protection,trades,default} [{all,buy,sell,roi,stoploss,trailing,protection,trades,default} ...]] + [--print-all] [--print-json] [-j JOBS] + [--random-state INT] [--min-trades INT] + [--hyperopt-loss NAME] [--disable-param-export] + [--ignore-missing-spaces] [--analyze-per-epoch] + +options: + -h, --help show this help message and exit + -i TIMEFRAME, --timeframe TIMEFRAME + Specify timeframe (`1m`, `5m`, `30m`, `1h`, `1d`). + --timerange TIMERANGE + Specify what timerange of data to use. + --data-format-ohlcv {json,jsongz,feather,parquet} + Storage format for downloaded candle (OHLCV) data. + (default: `feather`). + --max-open-trades INT + Override the value of the `max_open_trades` + configuration setting. + --stake-amount STAKE_AMOUNT + Override the value of the `stake_amount` configuration + setting. + --fee FLOAT Specify fee ratio. Will be applied twice (on trade + entry and exit). + -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] + Limit command to these pairs. Pairs are space- + separated. + --hyperopt-path PATH Specify additional lookup path for Hyperopt Loss + functions. + --eps, --enable-position-stacking + Allow buying the same pair multiple times (position + stacking). + --enable-protections, --enableprotections + Enable protections for backtesting.Will slow + backtesting down by a considerable amount, but will + include configured protections + --dry-run-wallet DRY_RUN_WALLET, --starting-balance DRY_RUN_WALLET + Starting balance, used for backtesting / hyperopt and + dry-runs. + --timeframe-detail TIMEFRAME_DETAIL + Specify detail timeframe for backtesting (`1m`, `5m`, + `30m`, `1h`, `1d`). + -e INT, --epochs INT Specify number of epochs (default: 100). + --spaces {all,buy,sell,roi,stoploss,trailing,protection,trades,default} [{all,buy,sell,roi,stoploss,trailing,protection,trades,default} ...] + Specify which parameters to hyperopt. Space-separated + list. + --print-all Print all results, not only the best ones. + --print-json Print output in JSON format. + -j JOBS, --job-workers JOBS + The number of concurrently running jobs for + hyperoptimization (hyperopt worker processes). If -1 + (default), all CPUs are used, for -2, all CPUs but one + are used, etc. If 1 is given, no parallel computing + code is used at all. + --random-state INT Set random state to some positive integer for + reproducible hyperopt results. + --min-trades INT Set minimal desired number of trades for evaluations + in the hyperopt optimization path (default: 1). + --hyperopt-loss NAME, --hyperoptloss NAME + Specify the class name of the hyperopt loss function + class (IHyperOptLoss). Different functions can + generate completely different results, since the + target for optimization is different. Built-in + Hyperopt-loss-functions are: + ShortTradeDurHyperOptLoss, OnlyProfitHyperOptLoss, + SharpeHyperOptLoss, SharpeHyperOptLossDaily, + SortinoHyperOptLoss, SortinoHyperOptLossDaily, + CalmarHyperOptLoss, MaxDrawDownHyperOptLoss, + MaxDrawDownRelativeHyperOptLoss, + ProfitDrawDownHyperOptLoss, MultiMetricHyperOptLoss + --disable-param-export + Disable automatic hyperopt parameter export. + --ignore-missing-spaces, --ignore-unparameterized-spaces + Suppress errors for any requested Hyperopt spaces that + do not contain any parameters. + --analyze-per-epoch Run populate_indicators once per epoch. + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name which will be used by the + bot. + --strategy-path PATH Specify additional strategy lookup path. + --recursive-strategy-search + Recursively search for a strategy in the strategies + folder. + --freqaimodel NAME Specify a custom freqaimodels. + --freqaimodel-path PATH + Specify additional lookup path for freqaimodels. + +``` diff --git a/docs/commands/install-ui.md b/docs/commands/install-ui.md new file mode 100644 index 000000000..57e8fbda2 --- /dev/null +++ b/docs/commands/install-ui.md @@ -0,0 +1,11 @@ +``` +usage: freqtrade install-ui [-h] [--erase] [--ui-version UI_VERSION] + +options: + -h, --help show this help message and exit + --erase Clean UI folder, don't download new version. + --ui-version UI_VERSION + Specify a specific version of FreqUI to install. Not + specifying this installs the latest version. + +``` diff --git a/docs/commands/list-data.md b/docs/commands/list-data.md new file mode 100644 index 000000000..c6e8cc94e --- /dev/null +++ b/docs/commands/list-data.md @@ -0,0 +1,48 @@ +``` +usage: freqtrade list-data [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] + [--exchange EXCHANGE] + [--data-format-ohlcv {json,jsongz,feather,parquet}] + [--data-format-trades {json,jsongz,feather,parquet}] + [--trades] [-p PAIRS [PAIRS ...]] + [--trading-mode {spot,margin,futures}] + [--show-timerange] + +options: + -h, --help show this help message and exit + --exchange EXCHANGE Exchange name. Only valid if no config is provided. + --data-format-ohlcv {json,jsongz,feather,parquet} + Storage format for downloaded candle (OHLCV) data. + (default: `feather`). + --data-format-trades {json,jsongz,feather,parquet} + Storage format for downloaded trades data. (default: + `feather`). + --trades Work on trades data instead of OHLCV data. + -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] + Limit command to these pairs. Pairs are space- + separated. + --trading-mode {spot,margin,futures}, --tradingmode {spot,margin,futures} + Select Trading mode + --show-timerange Show timerange available for available data. (May take + a while to calculate). + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/list-exchanges.md b/docs/commands/list-exchanges.md new file mode 100644 index 000000000..c95ae3ebd --- /dev/null +++ b/docs/commands/list-exchanges.md @@ -0,0 +1,29 @@ +``` +usage: freqtrade list-exchanges [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] [-1] [-a] + +options: + -h, --help show this help message and exit + -1, --one-column Print output in one column. + -a, --all Print all exchanges known to the ccxt library. + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/list-freqaimodels.md b/docs/commands/list-freqaimodels.md new file mode 100644 index 000000000..3e66880ab --- /dev/null +++ b/docs/commands/list-freqaimodels.md @@ -0,0 +1,31 @@ +``` +usage: freqtrade list-freqaimodels [-h] [-v] [--no-color] [--logfile FILE] + [-V] [-c PATH] [-d PATH] [--userdir PATH] + [--freqaimodel-path PATH] [-1] + +options: + -h, --help show this help message and exit + --freqaimodel-path PATH + Specify additional lookup path for freqaimodels. + -1, --one-column Print output in one column. + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/list-hyperoptloss.md b/docs/commands/list-hyperoptloss.md new file mode 100644 index 000000000..1a1604b0f --- /dev/null +++ b/docs/commands/list-hyperoptloss.md @@ -0,0 +1,31 @@ +``` +usage: freqtrade list-hyperoptloss [-h] [-v] [--no-color] [--logfile FILE] + [-V] [-c PATH] [-d PATH] [--userdir PATH] + [--hyperopt-path PATH] [-1] + +options: + -h, --help show this help message and exit + --hyperopt-path PATH Specify additional lookup path for Hyperopt Loss + functions. + -1, --one-column Print output in one column. + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/list-markets.md b/docs/commands/list-markets.md new file mode 100644 index 000000000..d33b9c5d1 --- /dev/null +++ b/docs/commands/list-markets.md @@ -0,0 +1,46 @@ +``` +usage: freqtrade list-markets [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] + [--exchange EXCHANGE] [--print-list] + [--print-json] [-1] [--print-csv] + [--base BASE_CURRENCY [BASE_CURRENCY ...]] + [--quote QUOTE_CURRENCY [QUOTE_CURRENCY ...]] + [-a] [--trading-mode {spot,margin,futures}] + +options: + -h, --help show this help message and exit + --exchange EXCHANGE Exchange name. Only valid if no config is provided. + --print-list Print list of pairs or market symbols. By default data + is printed in the tabular format. + --print-json Print list of pairs or market symbols in JSON format. + -1, --one-column Print output in one column. + --print-csv Print exchange pair or market data in the csv format. + --base BASE_CURRENCY [BASE_CURRENCY ...] + Specify base currency(-ies). Space-separated list. + --quote QUOTE_CURRENCY [QUOTE_CURRENCY ...] + Specify quote currency(-ies). Space-separated list. + -a, --all Print all pairs or market symbols. By default only + active ones are shown. + --trading-mode {spot,margin,futures}, --tradingmode {spot,margin,futures} + Select Trading mode + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/list-pairs.md b/docs/commands/list-pairs.md new file mode 100644 index 000000000..3d8e677ee --- /dev/null +++ b/docs/commands/list-pairs.md @@ -0,0 +1,46 @@ +``` +usage: freqtrade list-pairs [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] + [--exchange EXCHANGE] [--print-list] + [--print-json] [-1] [--print-csv] + [--base BASE_CURRENCY [BASE_CURRENCY ...]] + [--quote QUOTE_CURRENCY [QUOTE_CURRENCY ...]] [-a] + [--trading-mode {spot,margin,futures}] + +options: + -h, --help show this help message and exit + --exchange EXCHANGE Exchange name. Only valid if no config is provided. + --print-list Print list of pairs or market symbols. By default data + is printed in the tabular format. + --print-json Print list of pairs or market symbols in JSON format. + -1, --one-column Print output in one column. + --print-csv Print exchange pair or market data in the csv format. + --base BASE_CURRENCY [BASE_CURRENCY ...] + Specify base currency(-ies). Space-separated list. + --quote QUOTE_CURRENCY [QUOTE_CURRENCY ...] + Specify quote currency(-ies). Space-separated list. + -a, --all Print all pairs or market symbols. By default only + active ones are shown. + --trading-mode {spot,margin,futures}, --tradingmode {spot,margin,futures} + Select Trading mode + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/list-strategies.md b/docs/commands/list-strategies.md new file mode 100644 index 000000000..af5771860 --- /dev/null +++ b/docs/commands/list-strategies.md @@ -0,0 +1,34 @@ +``` +usage: freqtrade list-strategies [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] + [--strategy-path PATH] [-1] + [--recursive-strategy-search] + +options: + -h, --help show this help message and exit + --strategy-path PATH Specify additional strategy lookup path. + -1, --one-column Print output in one column. + --recursive-strategy-search + Recursively search for a strategy in the strategies + folder. + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/list-timeframes.md b/docs/commands/list-timeframes.md new file mode 100644 index 000000000..8dc10a789 --- /dev/null +++ b/docs/commands/list-timeframes.md @@ -0,0 +1,30 @@ +``` +usage: freqtrade list-timeframes [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] + [--exchange EXCHANGE] [-1] + +options: + -h, --help show this help message and exit + --exchange EXCHANGE Exchange name. Only valid if no config is provided. + -1, --one-column Print output in one column. + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/lookahead-analysis.md b/docs/commands/lookahead-analysis.md new file mode 100644 index 000000000..bb7ef5d8e --- /dev/null +++ b/docs/commands/lookahead-analysis.md @@ -0,0 +1,108 @@ +``` +usage: freqtrade lookahead-analysis [-h] [-v] [--no-color] [--logfile FILE] + [-V] [-c PATH] [-d PATH] [--userdir PATH] + [-s NAME] [--strategy-path PATH] + [--recursive-strategy-search] + [--freqaimodel NAME] + [--freqaimodel-path PATH] [-i TIMEFRAME] + [--timerange TIMERANGE] + [--data-format-ohlcv {json,jsongz,feather,parquet}] + [--max-open-trades INT] + [--stake-amount STAKE_AMOUNT] + [--fee FLOAT] [-p PAIRS [PAIRS ...]] + [--enable-protections] + [--dry-run-wallet DRY_RUN_WALLET] + [--timeframe-detail TIMEFRAME_DETAIL] + [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] + [--export {none,trades,signals}] + [--export-filename PATH] + [--freqai-backtest-live-models] + [--minimum-trade-amount INT] + [--targeted-trade-amount INT] + [--lookahead-analysis-exportfilename LOOKAHEAD_ANALYSIS_EXPORTFILENAME] + +options: + -h, --help show this help message and exit + -i TIMEFRAME, --timeframe TIMEFRAME + Specify timeframe (`1m`, `5m`, `30m`, `1h`, `1d`). + --timerange TIMERANGE + Specify what timerange of data to use. + --data-format-ohlcv {json,jsongz,feather,parquet} + Storage format for downloaded candle (OHLCV) data. + (default: `feather`). + --max-open-trades INT + Override the value of the `max_open_trades` + configuration setting. + --stake-amount STAKE_AMOUNT + Override the value of the `stake_amount` configuration + setting. + --fee FLOAT Specify fee ratio. Will be applied twice (on trade + entry and exit). + -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] + Limit command to these pairs. Pairs are space- + separated. + --enable-protections, --enableprotections + Enable protections for backtesting.Will slow + backtesting down by a considerable amount, but will + include configured protections + --dry-run-wallet DRY_RUN_WALLET, --starting-balance DRY_RUN_WALLET + Starting balance, used for backtesting / hyperopt and + dry-runs. + --timeframe-detail TIMEFRAME_DETAIL + Specify detail timeframe for backtesting (`1m`, `5m`, + `30m`, `1h`, `1d`). + --strategy-list STRATEGY_LIST [STRATEGY_LIST ...] + Provide a space-separated list of strategies to + backtest. Please note that timeframe needs to be set + either in config or via command line. When using this + together with `--export trades`, the strategy-name is + injected into the filename (so `backtest-data.json` + becomes `backtest-data-SampleStrategy.json` + --export {none,trades,signals} + Export backtest results (default: trades). + --export-filename PATH, --backtest-filename PATH + Use this filename for backtest results.Requires + `--export` to be set as well. Example: `--export-filen + ame=user_data/backtest_results/backtest_today.json` + --freqai-backtest-live-models + Run backtest with ready models. + --minimum-trade-amount INT + Minimum trade amount for lookahead-analysis + --targeted-trade-amount INT + Targeted trade amount for lookahead analysis + --lookahead-analysis-exportfilename LOOKAHEAD_ANALYSIS_EXPORTFILENAME + Use this csv-filename to store lookahead-analysis- + results + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name which will be used by the + bot. + --strategy-path PATH Specify additional strategy lookup path. + --recursive-strategy-search + Recursively search for a strategy in the strategies + folder. + --freqaimodel NAME Specify a custom freqaimodels. + --freqaimodel-path PATH + Specify additional lookup path for freqaimodels. + +``` diff --git a/docs/commands/main.md b/docs/commands/main.md new file mode 100644 index 000000000..c84f4854e --- /dev/null +++ b/docs/commands/main.md @@ -0,0 +1,51 @@ +``` +usage: freqtrade [-h] [-V] + {trade,create-userdir,new-config,show-config,new-strategy,download-data,convert-data,convert-trade-data,trades-to-ohlcv,list-data,backtesting,backtesting-show,backtesting-analysis,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-markets,list-pairs,list-strategies,list-hyperoptloss,list-freqaimodels,list-timeframes,show-trades,test-pairlist,convert-db,install-ui,plot-dataframe,plot-profit,webserver,strategy-updater,lookahead-analysis,recursive-analysis} + ... + +Free, open source crypto trading bot + +positional arguments: + {trade,create-userdir,new-config,show-config,new-strategy,download-data,convert-data,convert-trade-data,trades-to-ohlcv,list-data,backtesting,backtesting-show,backtesting-analysis,edge,hyperopt,hyperopt-list,hyperopt-show,list-exchanges,list-markets,list-pairs,list-strategies,list-hyperoptloss,list-freqaimodels,list-timeframes,show-trades,test-pairlist,convert-db,install-ui,plot-dataframe,plot-profit,webserver,strategy-updater,lookahead-analysis,recursive-analysis} + trade Trade module. + create-userdir Create user-data directory. + new-config Create new config + show-config Show resolved config + new-strategy Create new strategy + download-data Download backtesting data. + convert-data Convert candle (OHLCV) data from one format to + another. + convert-trade-data Convert trade data from one format to another. + trades-to-ohlcv Convert trade data to OHLCV data. + list-data List downloaded data. + backtesting Backtesting module. + backtesting-show Show past Backtest results + backtesting-analysis + Backtest Analysis module. + edge Edge module. + hyperopt Hyperopt module. + hyperopt-list List Hyperopt results + hyperopt-show Show details of Hyperopt results + list-exchanges Print available exchanges. + list-markets Print markets on exchange. + list-pairs Print pairs on exchange. + list-strategies Print available strategies. + list-hyperoptloss Print available hyperopt loss functions. + list-freqaimodels Print available freqAI models. + list-timeframes Print available timeframes for the exchange. + show-trades Show trades. + test-pairlist Test your pairlist configuration. + convert-db Migrate database to different system + install-ui Install FreqUI + plot-dataframe Plot candles with indicators. + plot-profit Generate plot showing profits. + webserver Webserver module. + strategy-updater updates outdated strategy files to the current version + lookahead-analysis Check for potential look ahead bias. + recursive-analysis Check for potential recursive formula issue. + +options: + -h, --help show this help message and exit + -V, --version show program's version number and exit + +``` diff --git a/docs/commands/new-config.md b/docs/commands/new-config.md new file mode 100644 index 000000000..ec604321b --- /dev/null +++ b/docs/commands/new-config.md @@ -0,0 +1,12 @@ +``` +usage: freqtrade new-config [-h] [-c PATH] + +options: + -h, --help show this help message and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + +``` diff --git a/docs/commands/new-strategy.md b/docs/commands/new-strategy.md new file mode 100644 index 000000000..bf4bbf44f --- /dev/null +++ b/docs/commands/new-strategy.md @@ -0,0 +1,19 @@ +``` +usage: freqtrade new-strategy [-h] [--userdir PATH] [-s NAME] + [--strategy-path PATH] + [--template {full,minimal,advanced}] + +options: + -h, --help show this help message and exit + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + -s NAME, --strategy NAME + Specify strategy class name which will be used by the + bot. + --strategy-path PATH Specify additional strategy lookup path. + --template {full,minimal,advanced} + Use a template which is either `minimal`, `full` + (containing multiple sample indicators) or `advanced`. + Default: `full`. + +``` diff --git a/docs/commands/plot-dataframe.md b/docs/commands/plot-dataframe.md new file mode 100644 index 000000000..81ecc0da7 --- /dev/null +++ b/docs/commands/plot-dataframe.md @@ -0,0 +1,82 @@ +``` +usage: freqtrade plot-dataframe [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] [-s NAME] + [--strategy-path PATH] + [--recursive-strategy-search] + [--freqaimodel NAME] [--freqaimodel-path PATH] + [-p PAIRS [PAIRS ...]] + [--indicators1 INDICATORS1 [INDICATORS1 ...]] + [--indicators2 INDICATORS2 [INDICATORS2 ...]] + [--plot-limit INT] [--db-url PATH] + [--trade-source {DB,file}] + [--export {none,trades,signals}] + [--export-filename PATH] + [--timerange TIMERANGE] [-i TIMEFRAME] + [--no-trades] + +options: + -h, --help show this help message and exit + -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] + Limit command to these pairs. Pairs are space- + separated. + --indicators1 INDICATORS1 [INDICATORS1 ...] + Set indicators from your strategy you want in the + first row of the graph. Space-separated list. Example: + `ema3 ema5`. Default: `['sma', 'ema3', 'ema5']`. + --indicators2 INDICATORS2 [INDICATORS2 ...] + Set indicators from your strategy you want in the + third row of the graph. Space-separated list. Example: + `fastd fastk`. Default: `['macd', 'macdsignal']`. + --plot-limit INT Specify tick limit for plotting. Notice: too high + values cause huge files. Default: 750. + --db-url PATH Override trades database URL, this is useful in custom + deployments (default: `sqlite:///tradesv3.sqlite` for + Live Run mode, `sqlite:///tradesv3.dryrun.sqlite` for + Dry Run). + --trade-source {DB,file} + Specify the source for trades (Can be DB or file + (backtest file)) Default: file + --export {none,trades,signals} + Export backtest results (default: trades). + --export-filename PATH, --backtest-filename PATH + Use this filename for backtest results.Requires + `--export` to be set as well. Example: `--export-filen + ame=user_data/backtest_results/backtest_today.json` + --timerange TIMERANGE + Specify what timerange of data to use. + -i TIMEFRAME, --timeframe TIMEFRAME + Specify timeframe (`1m`, `5m`, `30m`, `1h`, `1d`). + --no-trades Skip using trades from backtesting file and DB. + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name which will be used by the + bot. + --strategy-path PATH Specify additional strategy lookup path. + --recursive-strategy-search + Recursively search for a strategy in the strategies + folder. + --freqaimodel NAME Specify a custom freqaimodels. + --freqaimodel-path PATH + Specify additional lookup path for freqaimodels. + +``` diff --git a/docs/commands/plot-profit.md b/docs/commands/plot-profit.md new file mode 100644 index 000000000..207697a93 --- /dev/null +++ b/docs/commands/plot-profit.md @@ -0,0 +1,68 @@ +``` +usage: freqtrade plot-profit [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] [-s NAME] + [--strategy-path PATH] + [--recursive-strategy-search] + [--freqaimodel NAME] [--freqaimodel-path PATH] + [-p PAIRS [PAIRS ...]] [--timerange TIMERANGE] + [--export {none,trades,signals}] + [--export-filename PATH] [--db-url PATH] + [--trade-source {DB,file}] [-i TIMEFRAME] + [--auto-open] + +options: + -h, --help show this help message and exit + -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] + Limit command to these pairs. Pairs are space- + separated. + --timerange TIMERANGE + Specify what timerange of data to use. + --export {none,trades,signals} + Export backtest results (default: trades). + --export-filename PATH, --backtest-filename PATH + Use this filename for backtest results.Requires + `--export` to be set as well. Example: `--export-filen + ame=user_data/backtest_results/backtest_today.json` + --db-url PATH Override trades database URL, this is useful in custom + deployments (default: `sqlite:///tradesv3.sqlite` for + Live Run mode, `sqlite:///tradesv3.dryrun.sqlite` for + Dry Run). + --trade-source {DB,file} + Specify the source for trades (Can be DB or file + (backtest file)) Default: file + -i TIMEFRAME, --timeframe TIMEFRAME + Specify timeframe (`1m`, `5m`, `30m`, `1h`, `1d`). + --auto-open Automatically open generated plot. + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name which will be used by the + bot. + --strategy-path PATH Specify additional strategy lookup path. + --recursive-strategy-search + Recursively search for a strategy in the strategies + folder. + --freqaimodel NAME Specify a custom freqaimodels. + --freqaimodel-path PATH + Specify additional lookup path for freqaimodels. + +``` diff --git a/docs/commands/recursive-analysis.md b/docs/commands/recursive-analysis.md new file mode 100644 index 000000000..b39e8fb35 --- /dev/null +++ b/docs/commands/recursive-analysis.md @@ -0,0 +1,60 @@ +``` +usage: freqtrade recursive-analysis [-h] [-v] [--no-color] [--logfile FILE] + [-V] [-c PATH] [-d PATH] [--userdir PATH] + [-s NAME] [--strategy-path PATH] + [--recursive-strategy-search] + [--freqaimodel NAME] + [--freqaimodel-path PATH] [-i TIMEFRAME] + [--timerange TIMERANGE] + [--data-format-ohlcv {json,jsongz,feather,parquet}] + [-p PAIRS [PAIRS ...]] + [--startup-candle STARTUP_CANDLE [STARTUP_CANDLE ...]] + +options: + -h, --help show this help message and exit + -i TIMEFRAME, --timeframe TIMEFRAME + Specify timeframe (`1m`, `5m`, `30m`, `1h`, `1d`). + --timerange TIMERANGE + Specify what timerange of data to use. + --data-format-ohlcv {json,jsongz,feather,parquet} + Storage format for downloaded candle (OHLCV) data. + (default: `feather`). + -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] + Limit command to these pairs. Pairs are space- + separated. + --startup-candle STARTUP_CANDLE [STARTUP_CANDLE ...] + Specify startup candles to be checked (`199`, `499`, + `999`, `1999`). + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name which will be used by the + bot. + --strategy-path PATH Specify additional strategy lookup path. + --recursive-strategy-search + Recursively search for a strategy in the strategies + folder. + --freqaimodel NAME Specify a custom freqaimodels. + --freqaimodel-path PATH + Specify additional lookup path for freqaimodels. + +``` diff --git a/docs/commands/show-config.md b/docs/commands/show-config.md new file mode 100644 index 000000000..7ebf6fd84 --- /dev/null +++ b/docs/commands/show-config.md @@ -0,0 +1,16 @@ +``` +usage: freqtrade show-config [-h] [--userdir PATH] [-c PATH] + [--show-sensitive] + +options: + -h, --help show this help message and exit + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + --show-sensitive Show secrets in the output. + +``` diff --git a/docs/commands/show-trades.md b/docs/commands/show-trades.md new file mode 100644 index 000000000..316d47448 --- /dev/null +++ b/docs/commands/show-trades.md @@ -0,0 +1,37 @@ +``` +usage: freqtrade show-trades [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] + [--db-url PATH] + [--trade-ids TRADE_IDS [TRADE_IDS ...]] + [--print-json] + +options: + -h, --help show this help message and exit + --db-url PATH Override trades database URL, this is useful in custom + deployments (default: `sqlite:///tradesv3.sqlite` for + Live Run mode, `sqlite:///tradesv3.dryrun.sqlite` for + Dry Run). + --trade-ids TRADE_IDS [TRADE_IDS ...] + Specify the list of trade ids. + --print-json Print output in JSON format. + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/strategy-updater.md b/docs/commands/strategy-updater.md new file mode 100644 index 000000000..ba40322ec --- /dev/null +++ b/docs/commands/strategy-updater.md @@ -0,0 +1,41 @@ +``` +usage: freqtrade strategy-updater [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] + [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] + [--strategy-path PATH] + [--recursive-strategy-search] + +options: + -h, --help show this help message and exit + --strategy-list STRATEGY_LIST [STRATEGY_LIST ...] + Provide a space-separated list of strategies to + backtest. Please note that timeframe needs to be set + either in config or via command line. When using this + together with `--export trades`, the strategy-name is + injected into the filename (so `backtest-data.json` + becomes `backtest-data-SampleStrategy.json` + --strategy-path PATH Specify additional strategy lookup path. + --recursive-strategy-search + Recursively search for a strategy in the strategies + folder. + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/test-pairlist.md b/docs/commands/test-pairlist.md new file mode 100644 index 000000000..51a278b52 --- /dev/null +++ b/docs/commands/test-pairlist.md @@ -0,0 +1,22 @@ +``` +usage: freqtrade test-pairlist [-h] [--userdir PATH] [-v] [-c PATH] + [--quote QUOTE_CURRENCY [QUOTE_CURRENCY ...]] + [-1] [--print-json] [--exchange EXCHANGE] + +options: + -h, --help show this help message and exit + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + --quote QUOTE_CURRENCY [QUOTE_CURRENCY ...] + Specify quote currency(-ies). Space-separated list. + -1, --one-column Print output in one column. + --print-json Print list of pairs or market symbols in JSON format. + --exchange EXCHANGE Exchange name. Only valid if no config is provided. + +``` diff --git a/docs/commands/trade.md b/docs/commands/trade.md new file mode 100644 index 000000000..b689902c2 --- /dev/null +++ b/docs/commands/trade.md @@ -0,0 +1,55 @@ +``` +usage: freqtrade trade [-h] [-v] [--no-color] [--logfile FILE] [-V] [-c PATH] + [-d PATH] [--userdir PATH] [-s NAME] + [--strategy-path PATH] [--recursive-strategy-search] + [--freqaimodel NAME] [--freqaimodel-path PATH] + [--db-url PATH] [--sd-notify] [--dry-run] + [--dry-run-wallet DRY_RUN_WALLET] [--fee FLOAT] + +options: + -h, --help show this help message and exit + --db-url PATH Override trades database URL, this is useful in custom + deployments (default: `sqlite:///tradesv3.sqlite` for + Live Run mode, `sqlite:///tradesv3.dryrun.sqlite` for + Dry Run). + --sd-notify Notify systemd service manager. + --dry-run Enforce dry-run for trading (removes Exchange secrets + and simulates trades). + --dry-run-wallet DRY_RUN_WALLET, --starting-balance DRY_RUN_WALLET + Starting balance, used for backtesting / hyperopt and + dry-runs. + --fee FLOAT Specify fee ratio. Will be applied twice (on trade + entry and exit). + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +Strategy arguments: + -s NAME, --strategy NAME + Specify strategy class name which will be used by the + bot. + --strategy-path PATH Specify additional strategy lookup path. + --recursive-strategy-search + Recursively search for a strategy in the strategies + folder. + --freqaimodel NAME Specify a custom freqaimodels. + --freqaimodel-path PATH + Specify additional lookup path for freqaimodels. + +``` diff --git a/docs/commands/trades-to-ohlcv.md b/docs/commands/trades-to-ohlcv.md new file mode 100644 index 000000000..c2d42a5cc --- /dev/null +++ b/docs/commands/trades-to-ohlcv.md @@ -0,0 +1,48 @@ +``` +usage: freqtrade trades-to-ohlcv [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] + [-p PAIRS [PAIRS ...]] + [-t TIMEFRAMES [TIMEFRAMES ...]] + [--exchange EXCHANGE] + [--data-format-ohlcv {json,jsongz,feather,parquet}] + [--data-format-trades {json,jsongz,feather,parquet}] + [--trading-mode {spot,margin,futures}] + +options: + -h, --help show this help message and exit + -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] + Limit command to these pairs. Pairs are space- + separated. + -t TIMEFRAMES [TIMEFRAMES ...], --timeframes TIMEFRAMES [TIMEFRAMES ...] + Specify which tickers to download. Space-separated + list. Default: `1m 5m`. + --exchange EXCHANGE Exchange name. Only valid if no config is provided. + --data-format-ohlcv {json,jsongz,feather,parquet} + Storage format for downloaded candle (OHLCV) data. + (default: `feather`). + --data-format-trades {json,jsongz,feather,parquet} + Storage format for downloaded trades data. (default: + `feather`). + --trading-mode {spot,margin,futures}, --tradingmode {spot,margin,futures} + Select Trading mode + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/commands/webserver.md b/docs/commands/webserver.md new file mode 100644 index 000000000..a99b83e97 --- /dev/null +++ b/docs/commands/webserver.md @@ -0,0 +1,27 @@ +``` +usage: freqtrade webserver [-h] [-v] [--no-color] [--logfile FILE] [-V] + [-c PATH] [-d PATH] [--userdir PATH] + +options: + -h, --help show this help message and exit + +Common arguments: + -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). + --no-color Disable colorization of hyperopt results. May be + useful if you are redirecting output to a file. + --logfile FILE, --log-file FILE + Log to the file specified. Special values are: + 'syslog', 'journald'. See the documentation for more + details. + -V, --version show program's version number and exit + -c PATH, --config PATH + Specify configuration file (default: + `userdir/config.json` or `config.json` whichever + exists). Multiple --config options may be used. Can be + set to `-` to read config from stdin. + -d PATH, --datadir PATH, --data-dir PATH + Path to directory with historical backtesting data. + --userdir PATH, --user-data-dir PATH + Path to userdata directory. + +``` diff --git a/docs/data-download.md b/docs/data-download.md index 559211c2f..c2b104b1b 100644 --- a/docs/data-download.md +++ b/docs/data-download.md @@ -16,76 +16,7 @@ You can use a relative timerange (`--days 20`) or an absolute starting point (`- ### Usage -``` -usage: freqtrade download-data [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] - [-p PAIRS [PAIRS ...]] [--pairs-file FILE] - [--days INT] [--new-pairs-days INT] - [--include-inactive-pairs] - [--timerange TIMERANGE] [--dl-trades] - [--convert] [--exchange EXCHANGE] - [-t TIMEFRAMES [TIMEFRAMES ...]] [--erase] - [--data-format-ohlcv {json,jsongz,hdf5,feather,parquet}] - [--data-format-trades {json,jsongz,hdf5,feather,parquet}] - [--trading-mode {spot,margin,futures}] - [--prepend] - -options: - -h, --help show this help message and exit - -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] - Limit command to these pairs. Pairs are space- - separated. - --pairs-file FILE File containing a list of pairs. Takes precedence over - --pairs or pairs configured in the configuration. - --days INT Download data for given number of days. - --new-pairs-days INT Download data of new pairs for given number of days. - Default: `None`. - --include-inactive-pairs - Also download data from inactive pairs. - --timerange TIMERANGE - Specify what timerange of data to use. - --dl-trades Download trades instead of OHLCV data. The bot will - resample trades to the desired timeframe as specified - as --timeframes/-t. - --convert Convert downloaded trades to OHLCV data. Only - applicable in combination with `--dl-trades`. Will be - automatic for exchanges which don't have historic - OHLCV (e.g. Kraken). If not provided, use `trades-to- - ohlcv` to convert trades data to OHLCV data. - --exchange EXCHANGE Exchange name. Only valid if no config is provided. - -t TIMEFRAMES [TIMEFRAMES ...], --timeframes TIMEFRAMES [TIMEFRAMES ...] - Specify which tickers to download. Space-separated - list. Default: `1m 5m`. - --erase Clean all existing data for the selected - exchange/pairs/timeframes. - --data-format-ohlcv {json,jsongz,hdf5,feather,parquet} - Storage format for downloaded candle (OHLCV) data. - (default: `feather`). - --data-format-trades {json,jsongz,hdf5,feather,parquet} - Storage format for downloaded trades data. (default: - `feather`). - --trading-mode {spot,margin,futures}, --tradingmode {spot,margin,futures} - Select Trading mode - --prepend Allow data prepending. (Data-appending is disabled) - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE, --log-file FILE - Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH, --data-dir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -``` +--8<-- "commands/download-data.md" !!! Tip "Downloading all data for one quote currency" Often, you'll want to download data for all pairs of a specific quote-currency. In such cases, you can use the following shorthand: @@ -162,7 +93,6 @@ Freqtrade currently supports the following data-formats: * `feather` - a dataformat based on Apache Arrow * `json` - plain "text" json files * `jsongz` - a gzip-zipped version of json files -* `hdf5` - a high performance datastore * `parquet` - columnar datastore (OHLCV only) By default, both OHLCV data and trades data are stored in the `feather` format. @@ -172,8 +102,8 @@ To persist this change, you should also add the following snippet to your config ``` jsonc // ... - "dataformat_ohlcv": "hdf5", - "dataformat_trades": "hdf5", + "dataformat_ohlcv": "feather", + "dataformat_trades": "feather", // ... ``` @@ -211,7 +141,6 @@ time freqtrade list-data --show-timerange --data-format-ohlcv | `feather` | 72Mb | 3.5s | | `json` | 149Mb | 25.6s | | `jsongz` | 39Mb | 27s | -| `hdf5` | 145Mb | 3.9s | | `parquet` | 83Mb | 3.8s | Size has been taken from the BTC/USDT 1m spot combination for the timerange specified above. @@ -249,55 +178,7 @@ Mixing different stake-currencies is allowed for this file, since it's only used ## Sub-command convert data -``` -usage: freqtrade convert-data [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] - [-p PAIRS [PAIRS ...]] --format-from - {json,jsongz,hdf5,feather,parquet} --format-to - {json,jsongz,hdf5,feather,parquet} [--erase] - [--exchange EXCHANGE] - [-t TIMEFRAMES [TIMEFRAMES ...]] - [--trading-mode {spot,margin,futures}] - [--candle-types {spot,futures,mark,index,premiumIndex,funding_rate} [{spot,futures,mark,index,premiumIndex,funding_rate} ...]] - -options: - -h, --help show this help message and exit - -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] - Limit command to these pairs. Pairs are space- - separated. - --format-from {json,jsongz,hdf5,feather,parquet} - Source format for data conversion. - --format-to {json,jsongz,hdf5,feather,parquet} - Destination format for data conversion. - --erase Clean all existing data for the selected - exchange/pairs/timeframes. - --exchange EXCHANGE Exchange name. Only valid if no config is provided. - -t TIMEFRAMES [TIMEFRAMES ...], --timeframes TIMEFRAMES [TIMEFRAMES ...] - Specify which tickers to download. Space-separated - list. Default: `1m 5m`. - --trading-mode {spot,margin,futures}, --tradingmode {spot,margin,futures} - Select Trading mode - --candle-types {spot,futures,mark,index,premiumIndex,funding_rate} [{spot,futures,mark,index,premiumIndex,funding_rate} ...] - Select candle type to convert. Defaults to all - available types. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE, --log-file FILE - Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH, --data-dir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. -``` +--8<-- "commands/convert-data.md" ### Example converting data @@ -310,46 +191,7 @@ freqtrade convert-data --format-from json --format-to jsongz --datadir ~/.freqtr ## Sub-command convert trade data -``` -usage: freqtrade convert-trade-data [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] - [-p PAIRS [PAIRS ...]] --format-from - {json,jsongz,hdf5,feather,parquet} - --format-to - {json,jsongz,hdf5,feather,parquet} - [--erase] [--exchange EXCHANGE] - -options: - -h, --help show this help message and exit - -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] - Limit command to these pairs. Pairs are space- - separated. - --format-from {json,jsongz,hdf5,feather,parquet} - Source format for data conversion. - --format-to {json,jsongz,hdf5,feather,parquet} - Destination format for data conversion. - --erase Clean all existing data for the selected - exchange/pairs/timeframes. - --exchange EXCHANGE Exchange name. Only valid if no config is provided. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE, --log-file FILE - Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH, --data-dir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -``` +--8<-- "commands/convert-trade-data.md" ### Example converting trades @@ -365,49 +207,7 @@ freqtrade convert-trade-data --format-from jsongz --format-to json --datadir ~/. When you need to use `--dl-trades` (kraken only) to download data, conversion of trades data to ohlcv data is the last step. This command will allow you to repeat this last step for additional timeframes without re-downloading the data. -``` -usage: freqtrade trades-to-ohlcv [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] - [-p PAIRS [PAIRS ...]] - [-t TIMEFRAMES [TIMEFRAMES ...]] - [--exchange EXCHANGE] - [--data-format-ohlcv {json,jsongz,hdf5,feather,parquet}] - [--data-format-trades {json,jsongz,hdf5,feather}] - -options: - -h, --help show this help message and exit - -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] - Limit command to these pairs. Pairs are space- - separated. - -t TIMEFRAMES [TIMEFRAMES ...], --timeframes TIMEFRAMES [TIMEFRAMES ...] - Specify which tickers to download. Space-separated - list. Default: `1m 5m`. - --exchange EXCHANGE Exchange name. Only valid if no config is provided. - --data-format-ohlcv {json,jsongz,hdf5,feather,parquet} - Storage format for downloaded candle (OHLCV) data. - (default: `feather`). - --data-format-trades {json,jsongz,hdf5,feather} - Storage format for downloaded trades data. (default: - `feather`). - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE, --log-file FILE - Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH, --data-dir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -``` +--8<-- "commands/trades-to-ohlcv.md" ### Example trade-to-ohlcv conversion @@ -419,51 +219,7 @@ freqtrade trades-to-ohlcv --exchange kraken -t 5m 1h 1d --pairs BTC/EUR ETH/EUR You can get a list of downloaded data using the `list-data` sub-command. -``` -usage: freqtrade list-data [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] - [--userdir PATH] [--exchange EXCHANGE] - [--data-format-ohlcv {json,jsongz,hdf5,feather,parquet}] - [--data-format-trades {json,jsongz,hdf5,feather,parquet}] - [--trades] [-p PAIRS [PAIRS ...]] - [--trading-mode {spot,margin,futures}] - [--show-timerange] - -options: - -h, --help show this help message and exit - --exchange EXCHANGE Exchange name. Only valid if no config is provided. - --data-format-ohlcv {json,jsongz,hdf5,feather,parquet} - Storage format for downloaded candle (OHLCV) data. - (default: `feather`). - --data-format-trades {json,jsongz,hdf5,feather,parquet} - Storage format for downloaded trades data. (default: - `feather`). - --trades Work on trades data instead of OHLCV data. - -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] - Limit command to these pairs. Pairs are space- - separated. - --trading-mode {spot,margin,futures}, --tradingmode {spot,margin,futures} - Select Trading mode - --show-timerange Show timerange available for available data. (May take - a while to calculate). - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE, --log-file FILE - Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH, --data-dir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -``` +--8<-- "commands/list-data.md" ### Example list-data diff --git a/docs/deprecated.md b/docs/deprecated.md index 5357acc62..729db4915 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -81,4 +81,10 @@ version 2023.3 saw the removal of `populate_any_indicators` in favor of split me ## 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. +Setting protections from the configuration via `"protections": [],` has been removed in 2024.10, after having raised deprecation warnings for over 3 years. + +## hdf5 data storage + +Using hdf5 as data storage has been deprecated in 2024.12 and was removed in 2025.1. We recommend switching to the feather data format. + +Please use the [`convert-data` subcommand](data-download.md#sub-command-convert-data) to convert your existing data to one of the supported formats before updating. diff --git a/docs/edge.md b/docs/edge.md index fe33f141b..e48fe592a 100644 --- a/docs/edge.md +++ b/docs/edge.md @@ -215,64 +215,7 @@ Let's say the stake currency is **ETH** and there is $10$ **ETH** on the wallet. ## Edge command reference -``` -usage: freqtrade edge [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] - [--userdir PATH] [-s NAME] [--strategy-path PATH] - [-i TIMEFRAME] [--timerange TIMERANGE] - [--data-format-ohlcv {json,jsongz,hdf5}] - [--max-open-trades INT] [--stake-amount STAKE_AMOUNT] - [--fee FLOAT] [-p PAIRS [PAIRS ...]] - [--stoplosses STOPLOSS_RANGE] - -optional arguments: - -h, --help show this help message and exit - -i TIMEFRAME, --timeframe TIMEFRAME - Specify timeframe (`1m`, `5m`, `30m`, `1h`, `1d`). - --timerange TIMERANGE - Specify what timerange of data to use. - --data-format-ohlcv {json,jsongz,hdf5} - Storage format for downloaded candle (OHLCV) data. - (default: `None`). - --max-open-trades INT - Override the value of the `max_open_trades` - configuration setting. - --stake-amount STAKE_AMOUNT - Override the value of the `stake_amount` configuration - setting. - --fee FLOAT Specify fee ratio. Will be applied twice (on trade - entry and exit). - -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] - Limit command to these pairs. Pairs are space- - separated. - --stoplosses STOPLOSS_RANGE - Defines a range of stoploss values against which edge - will assess the strategy. The format is "min,max,step" - (without any space). Example: - `--stoplosses=-0.01,-0.1,-0.001` - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -Strategy arguments: - -s NAME, --strategy NAME - Specify strategy class name which will be used by the - bot. - --strategy-path PATH Specify additional strategy lookup path. - -``` +--8<-- "commands/edge.md" ## Configurations diff --git a/docs/exchanges.md b/docs/exchanges.md index 34a2e4d14..953fb5f8c 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -118,7 +118,7 @@ When trading on Binance Futures market, orderbook must be used because there is }, ``` -#### Binance futures settings +#### Binance isolated futures settings Users will also have to have the futures-setting "Position Mode" set to "One-way Mode", and "Asset Mode" set to "Single-Asset Mode". These settings will be checked on startup, and freqtrade will show an error if this setting is wrong. @@ -127,6 +127,27 @@ These settings will be checked on startup, and freqtrade will show an error if t Freqtrade will not attempt to change these settings. +#### Binance BNFCR futures + +BNFCR mode are a special type of futures mode on Binance to work around regulatory issues in Europe. +To use BNFCR futures, you will have to have the following combination of settings: + +``` jsonc +{ + // ... + "trading_mode": "futures", + "margin_mode": "cross", + "proxy_coin": "BNFCR", + "stake_currency": "USDT" // or "USDC" + // ... +} +``` + +The `stake_currency` setting defines the markets the bot will be operating in. This choice is really arbitrary. + +On the exchange, you'll have to use "Multi-asset Mode" - and "Position Mode set to "One-way Mode". +Freqtrade will check these settings on startup, but won't attempt to change them. + ## Bingx BingX supports [time_in_force](configuration.md#understand-order_time_in_force) with settings "GTC" (good till cancelled), "IOC" (immediate-or-cancel) and "PO" (Post only) settings. @@ -189,7 +210,7 @@ freqtrade download-data --exchange kraken --dl-trades -p BTC/EUR BCH/EUR It will also take a long time, as freqtrade will need to download every single trade that happened on the exchange for the pair / timerange combination, therefore please be patient. !!! Warning "rateLimit tuning" - Please pay attention that rateLimit configuration entry holds delay in milliseconds between requests, NOT requests\sec rate. + Please pay attention that rateLimit configuration entry holds delay in milliseconds between requests, NOT requests/sec rate. So, in order to mitigate Kraken API "Rate limit exceeded" exception, this configuration should be increased, NOT decreased. ## Kucoin @@ -217,12 +238,12 @@ Kucoin supports [time_in_force](configuration.md#understand-order_time_in_force) For Kucoin, it is suggested to add `"KCS/"` to your blacklist to avoid issues, unless you are willing to maintain enough extra `KCS` on the account or unless you're willing to disable using `KCS` for fees. Kucoin accounts may use `KCS` for fees, and if a trade happens to be on `KCS`, further trades may consume this position and make the initial `KCS` trade unsellable as the expected amount is not there anymore. -## HTX (formerly Huobi) +## HTX !!! Tip "Stoploss on Exchange" HTX supports `stoploss_on_exchange` and uses `stop-limit` orders. It provides great advantages, so we recommend to benefit from it by enabling stoploss on exchange. -## OKX (former OKEX) +## OKX OKX requires a passphrase for each api key, you will therefore need to add this key into the configuration so your exchange section looks as follows: @@ -236,6 +257,9 @@ OKX requires a passphrase for each api key, you will therefore need to add this } ``` +If you've registered with OKX on the host my.okx.com (OKX EAA)- you will need to use `"myokx"` as the exchange name. +Using the wrong exchange will result in the error "OKX Error 50119: API key doesn't exist" - as the 2 are separate entities. + !!! Warning OKX only provides 100 candles per api call. Therefore, the strategy will only have a pretty low amount of data available in backtesting mode. diff --git a/docs/faq.md b/docs/faq.md index a41e641f6..8d19b5db0 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -46,10 +46,12 @@ Make sure you set the `initial_state` config option to `"running"` in your confi ### I have waited 5 minutes, why hasn't the bot made any trades yet? -* Depending on the buy strategy, the amount of whitelisted coins, the -situation of the market etc, it can take up to hours to find a good entry +* Depending on the entry strategy, the amount of whitelisted coins, the +situation of the market etc, it can take up to hours or days to find a good entry position for a trade. Be patient! +* Backtesting will tell you roughly how many trades to expect - but that won't guarantee that they'll be distributed evenly across time - so you could have 20 trades on one day, and 0 for the rest of the week. + * It may be because of a configuration error. It's best to check the logs, they usually tell you if the bot is simply not getting buy signals (only heartbeat messages), or if there is something wrong (errors / exceptions in the log). ### I have made 12 trades already, why is my total profit negative? @@ -133,6 +135,10 @@ This message is a warning that the candles had a price jump of > 30%. This might be a sign that the pair stopped trading, and some token exchange took place (e.g. COCOS in 2021 - where price jumped from 0.0000154 to 0.01621). This message is often accompanied by ["Missing data fillup"](#im-getting-missing-data-fillup-messages-in-the-log) - as trading on such pairs is often stopped for some time. +### I want to reset the bot's database + +To reset the bot's database, you can either delete the database (by default `tradesv3.sqlite` or `tradesv3.dryrun.sqlite`), or use a different database url via `--db-url` (e.g. `sqlite:///mynewdatabase.sqlite`). + ### I'm getting "Outdated history for pair xxx" in the log The bot is trying to tell you that it got an outdated last candle (not the last complete candle). diff --git a/docs/freqai.md b/docs/freqai.md index a81c9a81b..cda8efe13 100644 --- a/docs/freqai.md +++ b/docs/freqai.md @@ -18,7 +18,7 @@ Features include: * **Extensibility** - The generalized and robust architecture allows for incorporating any [machine learning library/method](freqai-configuration.md#using-different-prediction-models) available in Python. Eight examples are currently available, including classifiers, regressors, and a convolutional neural network * **Smart outlier removal** - Remove outliers from training and prediction data sets using a variety of [outlier detection techniques](freqai-feature-engineering.md#outlier-detection) * **Crash resilience** - Store trained models to disk to make reloading from a crash fast and easy, and [purge obsolete files](freqai-running.md#purging-old-model-data) for sustained dry/live runs -* **Automatic data normalization** - [Normalize the data](freqai-feature-engineering.md#feature-normalization) in a smart and statistically safe way +* **Automatic data normalization** - [Normalize the data](freqai-feature-engineering.md#building-the-data-pipeline) in a smart and statistically safe way * **Automatic data download** - Compute timeranges for data downloads and update historic data (in live deployments) * **Cleaning of incoming data** - Handle NaNs safely before training and model inferencing * **Dimensionality reduction** - Reduce the size of the training data via [Principal Component Analysis](freqai-feature-engineering.md#data-dimensionality-reduction-with-principal-component-analysis) @@ -76,14 +76,14 @@ pip install -r requirements-freqai.txt ### Usage with docker -If you are using docker, a dedicated tag with FreqAI dependencies is available as `:freqai`. As such - you can replace the image line in your docker compose file with `image: freqtradeorg/freqtrade:develop_freqai`. This image contains the regular FreqAI dependencies. Similar to native installs, Catboost will not be available on ARM based devices. If you would like to use PyTorch or Reinforcement learning, you should use the torch or RL tags, `image: freqtradeorg/freqtrade:develop_freqaitorch`, `image: freqtradeorg/freqtrade:develop_freqairl`. +If you are using docker, a dedicated tag with FreqAI dependencies is available as `:freqai`. As such - you can replace the image line in your docker compose file with `image: freqtradeorg/freqtrade:stable_freqai`. This image contains the regular FreqAI dependencies. Similar to native installs, Catboost will not be available on ARM based devices. If you would like to use PyTorch or Reinforcement learning, you should use the torch or RL tags, `image: freqtradeorg/freqtrade:stable_freqaitorch`, `image: freqtradeorg/freqtrade:stable_freqairl`. !!! note "docker-compose-freqai.yml" We do provide an explicit docker-compose file for this in `docker/docker-compose-freqai.yml` - which can be used via `docker compose -f docker/docker-compose-freqai.yml run ...` - or can be copied to replace the original docker file. This docker-compose file also contains a (disabled) section to enable GPU resources within docker containers. This obviously assumes the system has GPU resources available. ### FreqAI position in open-source machine learning landscape -Forecasting chaotic time-series based systems, such as equity/cryptocurrency markets, requires a broad set of tools geared toward testing a wide range of hypotheses. Fortunately, a recent maturation of robust machine learning libraries (e.g. `scikit-learn`) has opened up a wide range of research possibilities. Scientists from a diverse range of fields can now easily prototype their studies on an abundance of established machine learning algorithms. Similarly, these user-friendly libraries enable "citzen scientists" to use their basic Python skills for data exploration. However, leveraging these machine learning libraries on historical and live chaotic data sources can be logistically difficult and expensive. Additionally, robust data collection, storage, and handling presents a disparate challenge. [`FreqAI`](#freqai) aims to provide a generalized and extensible open-sourced framework geared toward live deployments of adaptive modeling for market forecasting. The `FreqAI` framework is effectively a sandbox for the rich world of open-source machine learning libraries. Inside the `FreqAI` sandbox, users find they can combine a wide variety of third-party libraries to test creative hypotheses on a free live 24/7 chaotic data source - cryptocurrency exchange data. +Forecasting chaotic time-series based systems, such as equity/cryptocurrency markets, requires a broad set of tools geared toward testing a wide range of hypotheses. Fortunately, a recent maturation of robust machine learning libraries (e.g. `scikit-learn`) has opened up a wide range of research possibilities. Scientists from a diverse range of fields can now easily prototype their studies on an abundance of established machine learning algorithms. Similarly, these user-friendly libraries enable "citizen scientists" to use their basic Python skills for data exploration. However, leveraging these machine learning libraries on historical and live chaotic data sources can be logistically difficult and expensive. Additionally, robust data collection, storage, and handling presents a disparate challenge. [`FreqAI`](#freqai) aims to provide a generalized and extensible open-sourced framework geared toward live deployments of adaptive modeling for market forecasting. The `FreqAI` framework is effectively a sandbox for the rich world of open-source machine learning libraries. Inside the `FreqAI` sandbox, users find they can combine a wide variety of third-party libraries to test creative hypotheses on a free live 24/7 chaotic data source - cryptocurrency exchange data. ### Citing FreqAI diff --git a/docs/hyperopt.md b/docs/hyperopt.md index f36b1c6b3..51a6a5187 100644 --- a/docs/hyperopt.md +++ b/docs/hyperopt.md @@ -36,127 +36,7 @@ pip install -r requirements-hyperopt.txt ## Hyperopt command reference -``` -usage: freqtrade hyperopt [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] - [--userdir PATH] [-s NAME] [--strategy-path PATH] - [--recursive-strategy-search] [--freqaimodel NAME] - [--freqaimodel-path PATH] [-i TIMEFRAME] - [--timerange TIMERANGE] - [--data-format-ohlcv {json,jsongz,hdf5,feather,parquet}] - [--max-open-trades INT] - [--stake-amount STAKE_AMOUNT] [--fee FLOAT] - [-p PAIRS [PAIRS ...]] [--hyperopt-path PATH] - [--eps] [--enable-protections] - [--dry-run-wallet DRY_RUN_WALLET] - [--timeframe-detail TIMEFRAME_DETAIL] [-e INT] - [--spaces {all,buy,sell,roi,stoploss,trailing,protection,trades,default} [{all,buy,sell,roi,stoploss,trailing,protection,trades,default} ...]] - [--print-all] [--no-color] [--print-json] [-j JOBS] - [--random-state INT] [--min-trades INT] - [--hyperopt-loss NAME] [--disable-param-export] - [--ignore-missing-spaces] [--analyze-per-epoch] - -options: - -h, --help show this help message and exit - -i TIMEFRAME, --timeframe TIMEFRAME - Specify timeframe (`1m`, `5m`, `30m`, `1h`, `1d`). - --timerange TIMERANGE - Specify what timerange of data to use. - --data-format-ohlcv {json,jsongz,hdf5,feather,parquet} - Storage format for downloaded candle (OHLCV) data. - (default: `feather`). - --max-open-trades INT - Override the value of the `max_open_trades` - configuration setting. - --stake-amount STAKE_AMOUNT - Override the value of the `stake_amount` configuration - setting. - --fee FLOAT Specify fee ratio. Will be applied twice (on trade - entry and exit). - -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] - Limit command to these pairs. Pairs are space- - separated. - --hyperopt-path PATH Specify additional lookup path for Hyperopt Loss - functions. - --eps, --enable-position-stacking - Allow buying the same pair multiple times (position - stacking). - --enable-protections, --enableprotections - Enable protections for backtesting.Will slow - backtesting down by a considerable amount, but will - include configured protections - --dry-run-wallet DRY_RUN_WALLET, --starting-balance DRY_RUN_WALLET - Starting balance, used for backtesting / hyperopt and - dry-runs. - --timeframe-detail TIMEFRAME_DETAIL - Specify detail timeframe for backtesting (`1m`, `5m`, - `30m`, `1h`, `1d`). - -e INT, --epochs INT Specify number of epochs (default: 100). - --spaces {all,buy,sell,roi,stoploss,trailing,protection,trades,default} [{all,buy,sell,roi,stoploss,trailing,protection,trades,default} ...] - Specify which parameters to hyperopt. Space-separated - list. - --print-all Print all results, not only the best ones. - --no-color Disable colorization of hyperopt results. May be - useful if you are redirecting output to a file. - --print-json Print output in JSON format. - -j JOBS, --job-workers JOBS - The number of concurrently running jobs for - hyperoptimization (hyperopt worker processes). If -1 - (default), all CPUs are used, for -2, all CPUs but one - are used, etc. If 1 is given, no parallel computing - code is used at all. - --random-state INT Set random state to some positive integer for - reproducible hyperopt results. - --min-trades INT Set minimal desired number of trades for evaluations - in the hyperopt optimization path (default: 1). - --hyperopt-loss NAME, --hyperoptloss NAME - Specify the class name of the hyperopt loss function - class (IHyperOptLoss). Different functions can - generate completely different results, since the - target for optimization is different. Built-in - Hyperopt-loss-functions are: - ShortTradeDurHyperOptLoss, OnlyProfitHyperOptLoss, - SharpeHyperOptLoss, SharpeHyperOptLossDaily, - SortinoHyperOptLoss, SortinoHyperOptLossDaily, - CalmarHyperOptLoss, MaxDrawDownHyperOptLoss, - MaxDrawDownRelativeHyperOptLoss, - ProfitDrawDownHyperOptLoss - --disable-param-export - Disable automatic hyperopt parameter export. - --ignore-missing-spaces, --ignore-unparameterized-spaces - Suppress errors for any requested Hyperopt spaces that - do not contain any parameters. - --analyze-per-epoch Run populate_indicators once per epoch. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE, --log-file FILE - Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH, --data-dir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -Strategy arguments: - -s NAME, --strategy NAME - Specify strategy class name which will be used by the - bot. - --strategy-path PATH Specify additional strategy lookup path. - --recursive-strategy-search - Recursively search for a strategy in the strategies - folder. - --freqaimodel NAME Specify a custom freqaimodels. - --freqaimodel-path PATH - Specify additional lookup path for freqaimodels. - -``` +--8<-- "commands/hyperopt.md" ### Hyperopt checklist diff --git a/docs/index.md b/docs/index.md index 45871acb9..79c60072d 100644 --- a/docs/index.md +++ b/docs/index.md @@ -44,10 +44,11 @@ Please read the [exchange specific notes](exchanges.md) to learn about eventual, - [X] [Bitmart](https://bitmart.com/) - [X] [Bybit](https://bybit.com/) - [X] [Gate.io](https://www.gate.io/ref/6266643) -- [X] [HTX](https://www.htx.com/) (Former Huobi) +- [X] [HTX](https://www.htx.com/) - [X] [Hyperliquid](https://hyperliquid.xyz/) (A decentralized exchange, or DEX) - [X] [Kraken](https://kraken.com/) -- [X] [OKX](https://okx.com/) (Former OKEX) +- [X] [OKX](https://okx.com/) +- [X] [MyOKX](https://okx.com/) (OKX EEA) - [ ] [potentially many others through ccxt](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_ ### Supported Futures Exchanges (experimental) diff --git a/docs/leverage.md b/docs/leverage.md index 2fbd13145..d1517e2d3 100644 --- a/docs/leverage.md +++ b/docs/leverage.md @@ -82,7 +82,7 @@ Each market(trading pair), keeps collateral in a separate account "margin_mode": "isolated" ``` -#### Cross margin mode (currently unavailable) +#### Cross margin mode One account is used to share collateral between markets (trading pairs). Margin is taken from total account balance to avoid liquidation when needed. diff --git a/docs/lookahead-analysis.md b/docs/lookahead-analysis.md index 1cdf9aaf0..a64479fe6 100644 --- a/docs/lookahead-analysis.md +++ b/docs/lookahead-analysis.md @@ -29,39 +29,7 @@ Those are set to avoid users accidentally generating false positives. ## Lookahead-analysis command reference -``` -usage: freqtrade lookahead-analysis [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] [-s NAME] - [--strategy-path PATH] - [--recursive-strategy-search] - [--freqaimodel NAME] - [--freqaimodel-path PATH] [-i TIMEFRAME] - [--timerange TIMERANGE] - [--data-format-ohlcv {json,jsongz,hdf5,feather,parquet}] - [--max-open-trades INT] - [--stake-amount STAKE_AMOUNT] - [--fee FLOAT] [-p PAIRS [PAIRS ...]] - [--dry-run-wallet DRY_RUN_WALLET] - [--timeframe-detail TIMEFRAME_DETAIL] - [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] - [--export {none,trades,signals}] - [--export-filename PATH] - [--breakdown {day,week,month} [{day,week,month} ...]] - [--cache {none,day,week,month}] - [--freqai-backtest-live-models] - [--minimum-trade-amount INT] - [--targeted-trade-amount INT] - [--lookahead-analysis-exportfilename LOOKAHEAD_ANALYSIS_EXPORTFILENAME] - -options: - --minimum-trade-amount INT - Minimum trade amount for lookahead-analysis - --targeted-trade-amount INT - Targeted trade amount for lookahead analysis - --lookahead-analysis-exportfilename LOOKAHEAD_ANALYSIS_EXPORTFILENAME - Use this csv-filename to store lookahead-analysis- - results -``` +--8<-- "commands/lookahead-analysis.md" !!! Note "" The above Output was reduced to options `lookahead-analysis` adds on top of regular backtesting commands. diff --git a/docs/plotting.md b/docs/plotting.md index f0a52415c..ae480e78f 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -30,76 +30,7 @@ The `freqtrade plot-dataframe` subcommand shows an interactive graph with three Possible arguments: -``` -usage: freqtrade plot-dataframe [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] [-s NAME] - [--strategy-path PATH] [-p PAIRS [PAIRS ...]] - [--indicators1 INDICATORS1 [INDICATORS1 ...]] - [--indicators2 INDICATORS2 [INDICATORS2 ...]] - [--plot-limit INT] [--db-url PATH] - [--trade-source {DB,file}] [--export EXPORT] - [--export-filename PATH] - [--timerange TIMERANGE] [-i TIMEFRAME] - [--no-trades] - -optional arguments: - -h, --help show this help message and exit - -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] - Limit command to these pairs. Pairs are space- - separated. - --indicators1 INDICATORS1 [INDICATORS1 ...] - Set indicators from your strategy you want in the - first row of the graph. Space-separated list. Example: - `ema3 ema5`. Default: `['sma', 'ema3', 'ema5']`. - --indicators2 INDICATORS2 [INDICATORS2 ...] - Set indicators from your strategy you want in the - third row of the graph. Space-separated list. Example: - `fastd fastk`. Default: `['macd', 'macdsignal']`. - --plot-limit INT Specify tick limit for plotting. Notice: too high - values cause huge files. Default: 750. - --db-url PATH Override trades database URL, this is useful in custom - deployments (default: `sqlite:///tradesv3.sqlite` for - Live Run mode, `sqlite:///tradesv3.dryrun.sqlite` for - Dry Run). - --trade-source {DB,file} - Specify the source for trades (Can be DB or file - (backtest file)) Default: file - --export EXPORT Export backtest results, argument are: trades. - Example: `--export=trades` - --export-filename PATH - Save backtest results to the file with this filename. - Requires `--export` to be set as well. Example: - `--export-filename=user_data/backtest_results/backtest - _today.json` - --timerange TIMERANGE - Specify what timerange of data to use. - -i TIMEFRAME, --timeframe TIMEFRAME - Specify timeframe (`1m`, `5m`, `30m`, `1h`, `1d`). - --no-trades Skip using trades from backtesting file and DB. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -Strategy arguments: - -s NAME, --strategy NAME - Specify strategy class name which will be used by the - bot. - --strategy-path PATH Specify additional strategy lookup path. - -``` +--8<-- "commands/plot-dataframe.md" Example: @@ -306,62 +237,7 @@ The forth graph can help you analyze trade parallelism, showing how often max_op Possible options for the `freqtrade plot-profit` subcommand: -``` -usage: freqtrade plot-profit [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] [-s NAME] - [--strategy-path PATH] [-p PAIRS [PAIRS ...]] - [--timerange TIMERANGE] [--export EXPORT] - [--export-filename PATH] [--db-url PATH] - [--trade-source {DB,file}] [-i TIMEFRAME] - -optional arguments: - -h, --help show this help message and exit - -p PAIRS [PAIRS ...], --pairs PAIRS [PAIRS ...] - Limit command to these pairs. Pairs are space- - separated. - --timerange TIMERANGE - Specify what timerange of data to use. - --export EXPORT Export backtest results, argument are: trades. - Example: `--export=trades` - --export-filename PATH, --backtest-filename PATH - Use backtest results from this filename. - Requires `--export` to be set as well. Example: - `--export-filename=user_data/backtest_results/backtest - _today.json` - --db-url PATH Override trades database URL, this is useful in custom - deployments (default: `sqlite:///tradesv3.sqlite` for - Live Run mode, `sqlite:///tradesv3.dryrun.sqlite` for - Dry Run). - --trade-source {DB,file} - Specify the source for trades (Can be DB or file - (backtest file)) Default: file - -i TIMEFRAME, --timeframe TIMEFRAME - Specify timeframe (`1m`, `5m`, `30m`, `1h`, `1d`). - --auto-open Automatically open generated plot. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -Strategy arguments: - -s NAME, --strategy NAME - Specify strategy class name which will be used by the - bot. - --strategy-path PATH Specify additional strategy lookup path. - -``` +--8<-- "commands/plot-profit.md" The `-p/--pairs` argument, can be used to limit the pairs that are considered for this calculation. diff --git a/docs/recursive-analysis.md b/docs/recursive-analysis.md index 07ef2121c..033b6605c 100644 --- a/docs/recursive-analysis.md +++ b/docs/recursive-analysis.md @@ -26,56 +26,7 @@ In addition to the recursive formula check, this command also carries out a simp ## Recursive-analysis command reference -``` -usage: freqtrade recursive-analysis [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] [-s NAME] - [--strategy-path PATH] - [--recursive-strategy-search] - [--freqaimodel NAME] - [--freqaimodel-path PATH] [-i TIMEFRAME] - [--timerange TIMERANGE] - [--data-format-ohlcv {json,jsongz,hdf5,feather,parquet}] - [-p PAIR] - [--freqai-backtest-live-models] - [--startup-candle STARTUP_CANDLES [STARTUP_CANDLES ...]] - -optional arguments: - -h, --help show this help message and exit - -i TIMEFRAME, --timeframe TIMEFRAME - Specify timeframe (`1m`, `5m`, `30m`, `1h`, `1d`). - --data-format-ohlcv {json,jsongz,hdf5,feather,parquet} - Storage format for downloaded candle (OHLCV) data. - (default: `feather`). - -p PAIR, --pairs PAIR - Limit command to this pair. - --startup-candle STARTUP_CANDLE [STARTUP_CANDLE ...] - Provide a space-separated list of startup_candle_count to - be checked. Default : `199 399 499 999 1999`. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -Strategy arguments: - -s NAME, --strategy NAME - Specify strategy class name which will be used by the - bot. - --strategy-path PATH Specify additional strategy lookup path. - --timerange TIMERANGE - Specify what timerange of data to use. -``` +--8<-- "commands/recursive-analysis.md" ### Why are odd-numbered default startup candles used? diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index f342680e2..b71ff4682 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.48 +mkdocs-material==9.6.3 mdx_truly_sane_lists==1.3 -pymdown-extensions==10.12 -jinja2==3.1.4 +pymdown-extensions==10.14.3 +jinja2==3.1.5 mike==2.1.3 diff --git a/docs/rest-api.md b/docs/rest-api.md index 8c927d5d7..b958c0927 100644 --- a/docs/rest-api.md +++ b/docs/rest-api.md @@ -88,8 +88,9 @@ Make sure that the following 2 lines are available in your docker-compose file: ### Consuming the API -You can consume the API by using `freqtrade-client` (also available as `scripts/rest_client.py`). -This command can be installed independent of the bot by using `pip install freqtrade-client`. +We advise consuming the API by using the supported `freqtrade-client` package (also available as `scripts/rest_client.py`). + +This command can be installed independent of any running freqtrade bot by using `pip install freqtrade-client`. This module is designed to be lightweight, and only depends on the `requests` and `python-rapidjson` modules, skipping all heavy dependencies freqtrade otherwise needs. @@ -144,57 +145,6 @@ This method will work for all arguments - check the "show" command for a list of For a full list of available commands, please refer to the list below. -### Available endpoints - -| Command | Description | -|----------|-------------| -| `ping` | Simple command testing the API Readiness - requires no authentication. -| `start` | Starts the trader. -| `stop` | Stops the trader. -| `stopbuy` | Stops the trader from opening new trades. Gracefully closes open trades according to their rules. -| `reload_config` | Reloads the configuration file. -| `trades` | List last trades. Limited to 500 trades per call. -| `trade/` | Get specific trade. -| `trades/` | DELETE - Remove trade from the database. Tries to close open orders. Requires manual handling of this trade on the exchange. -| `trades//open-order` | DELETE - Cancel open order for this trade. -| `trades//reload` | GET - Reload a trade from the Exchange. Only works in live, and can potentially help recover a trade that was manually sold on the exchange. -| `show_config` | Shows part of the current configuration with relevant settings to operation. -| `logs` | Shows last log messages. -| `status` | Lists all open trades. -| `count` | Displays number of trades used and available. -| `entries [pair]` | Shows profit statistics for each enter tags for given pair (or all pairs if pair isn't given). Pair is optional. -| `exits [pair]` | Shows profit statistics for each exit reasons for given pair (or all pairs if pair isn't given). Pair is optional. -| `mix_tags [pair]` | Shows profit statistics for each combinations of enter tag + exit reasons for given pair (or all pairs if pair isn't given). Pair is optional. -| `locks` | Displays currently locked pairs. -| `delete_lock ` | Deletes (disables) the lock by id. -| `locks add , , [side], [reason]` | Locks a pair until "until". (Until will be rounded up to the nearest timeframe). -| `profit` | Display a summary of your profit/loss from close trades and some stats about your performance. -| `forceexit [order_type] [amount]` | Instantly exits the given trade (ignoring `minimum_roi`), using the given order type ("market" or "limit", uses your config setting if not specified), and the chosen amount (full sell if not specified). -| `forceexit all` | Instantly exits all open trades (Ignoring `minimum_roi`). -| `forceenter [rate]` | Instantly enters the given pair. Rate is optional. (`force_entry_enable` must be set to True) -| `forceenter [rate]` | Instantly longs or shorts the given pair. Rate is optional. (`force_entry_enable` must be set to True) -| `performance` | Show performance of each finished trade grouped by pair. -| `balance` | Show account balance per currency. -| `daily ` | Shows profit or loss per day, over the last n days (n defaults to 7). -| `weekly ` | Shows profit or loss per week, over the last n days (n defaults to 4). -| `monthly ` | Shows profit or loss per month, over the last n days (n defaults to 3). -| `stats` | Display a summary of profit / loss reasons as well as average holding times. -| `whitelist` | Show the current whitelist. -| `blacklist [pair]` | Show the current blacklist, or adds a pair to the blacklist. -| `edge` | Show validated pairs by Edge if it is enabled. -| `pair_candles` | Returns dataframe for a pair / timeframe combination while the bot is running. **Alpha** -| `pair_history` | Returns an analyzed dataframe for a given timerange, analyzed by a given strategy. **Alpha** -| `plot_config` | Get plot config from the strategy (or nothing if not configured). **Alpha** -| `strategies` | List strategies in strategy directory. **Alpha** -| `strategy ` | Get specific Strategy content. **Alpha** -| `available_pairs` | List available backtest data. **Alpha** -| `version` | Show version. -| `sysinfo` | Show information about the system load. -| `health` | Show bot health (last bot loop). - -!!! Warning "Alpha status" - Endpoints labeled with *Alpha status* above may change at any time without notice. - Possible commands can be listed from the rest-client script using the `help` command. ``` bash @@ -266,6 +216,14 @@ forceexit health Provides a quick health check of the running bot. +lock_add + Manually lock a specific pair + + :param pair: Pair to lock + :param until: Lock until this date (format "2024-03-30 16:00:00Z") + :param side: Side to lock (long, short, *) + :param reason: Reason for the lock + locks Return current locks @@ -353,6 +311,62 @@ whitelist ``` +### Available endpoints + +If you wish to call the REST API manually via another route, e.g. directly via `curl`, the table below shows the relevant URL endpoints and parameters. +All endpoints in the below table need to be prefixed with the base URL of the API, e.g. `http://127.0.0.1:8080/api/v1/` - so the command becomes `http://127.0.0.1:8080/api/v1/`. + +| Endpoint | Method | Description / Parameters | +|-----------|--------|--------------------------| +| `/ping` | GET | Simple command testing the API Readiness - requires no authentication. +| `/start` | POST | Starts the trader. +| `/stop` | POST | Stops the trader. +| `/stopbuy` | POST | Stops the trader from opening new trades. Gracefully closes open trades according to their rules. +| `/reload_config` | POST | Reloads the configuration file. +| `/trades` | GET | List last trades. Limited to 500 trades per call. +| `/trade/` | GET | Get specific trade.
*Params:*
- `tradeid` (`int`) +| `/trades/` | DELETE | Remove trade from the database. Tries to close open orders. Requires manual handling of this trade on the exchange.
*Params:*
- `tradeid` (`int`) +| `/trades//open-order` | DELETE | Cancel open order for this trade.
*Params:*
- `tradeid` (`int`) +| `/trades//reload` | POST | Reload a trade from the Exchange. Only works in live, and can potentially help recover a trade that was manually sold on the exchange.
*Params:*
- `tradeid` (`int`) +| `/show_config` | GET | Shows part of the current configuration with relevant settings to operation. +| `/logs` | GET | Shows last log messages. +| `/status` | GET | Lists all open trades. +| `/count` | GET | Displays number of trades used and available. +| `/entries` | GET | Shows profit statistics for each enter tags for given pair (or all pairs if pair isn't given). Pair is optional.
*Params:*
- `pair` (`str`) +| `/exits` | GET | Shows profit statistics for each exit reasons for given pair (or all pairs if pair isn't given). Pair is optional.
*Params:*
- `pair` (`str`) +| `/mix_tags` | GET | Shows profit statistics for each combinations of enter tag + exit reasons for given pair (or all pairs if pair isn't given). Pair is optional.
*Params:*
- `pair` (`str`) +| `/locks` | GET | Displays currently locked pairs. +| `/locks` | POST | Locks a pair until "until". (Until will be rounded up to the nearest timeframe). Side is optional and is either `long` or `short` (default is `long`). Reason is optional.
*Params:*
- `` (`str`)
- `` (`datetime`)
- `[side]` (`str`)
- `[reason]` (`str`) +| `/locks/` | DELETE | Deletes (disables) the lock by id.
*Params:*
- `lockid` (`int`) +| `/profit` | GET | Display a summary of your profit/loss from close trades and some stats about your performance. +| `/forceexit` | POST | Instantly exits the given trade (ignoring `minimum_roi`), using the given order type ("market" or "limit", uses your config setting if not specified), and the chosen amount (full sell if not specified). If `all` is supplied as the `tradeid`, then all currently open trades will be forced to exit.
*Params:*
- `` (`int` or `str`)
- `` (`str`)
- `[amount]` (`float`) +| `/forceenter` | POST | Instantly enters the given pair. Side is optional and is either `long` or `short` (default is `long`). Rate is optional. (`force_entry_enable` must be set to True)
*Params:*
- `` (`str`)
- `` (`str`)
- `[rate]` (`float`) +| `/performance` | GET | Show performance of each finished trade grouped by pair. +| `/balance` | GET | Show account balance per currency. +| `/daily` | GET | Shows profit or loss per day, over the last n days (n defaults to 7).
*Params:*
- `` (`int`) +| `/weekly` | GET | Shows profit or loss per week, over the last n days (n defaults to 4).
*Params:*
- `` (`int`) +| `/monthly` | GET | Shows profit or loss per month, over the last n days (n defaults to 3).
*Params:*
- `` (`int`) +| `/stats` | GET | Display a summary of profit / loss reasons as well as average holding times. +| `/whitelist` | GET | Show the current whitelist. +| `/blacklist` | GET | Show the current blacklist. +| `/blacklist` | POST | Adds the specified pair to the blacklist.
*Params:*
- `pair` (`str`) +| `/blacklist` | DELETE | Deletes the specified list of pairs from the blacklist.
*Params:*
- `[pair,pair]` (`list[str]`) +| `/edge` | GET | Show validated pairs by Edge if it is enabled. +| `/pair_candles` | GET | Returns dataframe for a pair / timeframe combination while the bot is running. **Alpha** +| `/pair_candles` | POST | Returns dataframe for a pair / timeframe combination while the bot is running, filtered by a provided list of columns to return. **Alpha**
*Params:*
- `` (`list[str]`) +| `/pair_history` | GET | Returns an analyzed dataframe for a given timerange, analyzed by a given strategy. **Alpha** +| `/pair_history` | POST | Returns an analyzed dataframe for a given timerange, analyzed by a given strategy, filtered by a provided list of columns to return. **Alpha**
*Params:*
- `` (`list[str]`) +| `/plot_config` | GET | Get plot config from the strategy (or nothing if not configured). **Alpha** +| `/strategies` | GET | List strategies in strategy directory. **Alpha** +| `/strategy/` | GET | Get specific Strategy content by strategy class name. **Alpha**
*Params:*
- `` (`str`) +| `/available_pairs` | GET | List available backtest data. **Alpha** +| `/version` | GET | Show version. +| `/sysinfo` | GET | Show information about the system load. +| `/health` | GET | Show bot health (last bot loop). + +!!! Warning "Alpha status" + Endpoints labeled with *Alpha status* above may change at any time without notice. + ### Message WebSocket The API Server includes a websocket endpoint for subscribing to RPC messages from the freqtrade Bot. diff --git a/docs/stoploss.md b/docs/stoploss.md index 7442484de..69c69626f 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -30,8 +30,8 @@ The Order-type will be ignored if only one mode is available. |----------|-------------| | Binance | limit | | Binance Futures | market, limit | -| Bingx | market, limit | -| HTX (former Huobi) | limit | +| Bingx | market, limit | +| HTX | limit | | kraken | market, limit | | Gate | limit | | Okx | limit | diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index 14e39a447..64f7987f6 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -758,7 +758,7 @@ For performance reasons, it's disabled by default and freqtrade will show a warn Additional orders also result in additional fees and those orders don't count towards `max_open_trades`. -This callback is **not** called when there is an open order (either buy or sell) waiting for execution. +This callback is also called when there is an open order (either buy or sell) waiting for execution - and will cancel the existing open order to place a new order if the amount, price or direction is different. `adjust_trade_position()` is called very frequently for the duration of a trade, so you must keep your implementation as performant as possible. @@ -767,11 +767,18 @@ Adjustment orders can be assigned with a tag by returning a 2 element Tuple, wit Modifications to leverage are not possible, and the stake-amount returned is assumed to be before applying leverage. +The combined stake currently allocated to the position is held in `trade.stake_amount`. Therefore `trade.stake_amount` will always be updated on every additional entry and partial exit made through `adjust_trade_position()`. + !!! Danger "Loose Logic" On dry and live run, this function will be called every `throttle_process_secs` (default to 5s). If you have a loose logic, for example your logic for extra entry is only to check RSI of last candle is below 30, then when such condition fulfilled, your bot will do extra re-entry every 5 secs until either it run out of money, it hit the `max_position_adjustment` limit, or a new candle with RSI more than 30 arrived. Same thing also can happen with partial exit. So be sure to have a strict logic and/or check for the last filled order. +!!! Warning "Performance with many position adjustments" + Position adjustments can be a good approach to increase a strategy's output - but it can also have drawbacks if using this feature extensively. + Each of the orders will be attached to the trade object for the duration of the trade - hence increasing memory usage. + Trades with long duration and 10s or even 100ds of position adjustments are therefore not recommended, and should be closed at regular intervals to not affect performance. + !!! Warning "Backtesting" During backtesting this callback is called for each candle in `timeframe` or `timeframe_detail`, so run-time performance will be affected. This can also cause deviating results between live and backtesting, since backtesting can adjust the trade only once per candle, whereas live could adjust the trade multiple times per candle. @@ -808,11 +815,6 @@ Back to the example above, since current rate is 200, the current USDT value of While `/stopentry` command stops the bot from entering new trades, the position adjustment feature will continue buying new orders on existing trades. -!!! Warning "Performance with many position adjustments" - Position adjustments can be a good approach to increase a strategy's output - but it can also have drawbacks if using this feature extensively. - Each of the orders will be attached to the trade object for the duration of the trade - hence increasing memory usage. - Trades with long duration and 10s or even 100ds of position adjustments are therefore not recommended, and should be closed at regular intervals to not affect performance. - ``` python # Default imports @@ -934,7 +936,10 @@ class DigDeeperStrategy(IStrategy): ## Adjust Entry Price -The `adjust_entry_price()` callback may be used by strategy developer to refresh/replace limit orders upon arrival of new candles. +The `adjust_entry_price()` callback may be used by strategy developer to refresh/replace limit orders upon arrival of new candles. +This callback is called once every iteration unless the order has been (re)placed within the current candle - limiting the maximum (re)placement of each order to once per candle. +This also means that the first call will be at the start of the next candle after the initial order was placed. + Be aware that `custom_entry_price()` is still the one dictating initial entry limit order price target at the time of entry trigger. Orders can be cancelled out of this callback by returning `None`. @@ -945,7 +950,8 @@ Returning any other price will cancel the existing order, and replace it with a The trade open-date (`trade.open_date_utc`) will remain at the time of the very first order placed. Please make sure to be aware of this - and eventually adjust your logic in other callbacks to account for this, and use the date of the first filled order instead. -If the cancellation of the original order fails, then the order will not be replaced - though the order will most likely have been canceled on exchange. Having this happen on initial entries will result in the deletion of the order, while on position adjustment orders, it'll result in the trade size remaining as is. +If the cancellation of the original order fails, then the order will not be replaced - though the order will most likely have been canceled on exchange. Having this happen on initial entries will result in the deletion of the order, while on position adjustment orders, it'll result in the trade size remaining as is. +If the order has been partially filled, the order will not be replaced. You can however use [`adjust_trade_position()`](#adjust-trade-position) to adjust the trade size to the full, expected position size, should this be necessary / desired. !!! Warning "Regular timeout" Entry `unfilledtimeout` mechanism (as well as `check_entry_timeout()`) takes precedence over this. diff --git a/docs/strategy-customization.md b/docs/strategy-customization.md index f360c411d..11b71caa9 100644 --- a/docs/strategy-customization.md +++ b/docs/strategy-customization.md @@ -545,7 +545,7 @@ def informative_pairs(self): ] ``` -A full sample can be found [in the DataProvider section](#complete-data-provider-sample). +A full sample can be found [in the DataProvider section](#complete-dataprovider-sample). !!! Warning As these pairs will be refreshed as part of the regular whitelist refresh, it's best to keep this list short. @@ -576,7 +576,7 @@ To easily define informative pairs, use the `@informative` decorator. All decora and do not have access to data from other informative pairs. However, all informative dataframes for each pair are merged and passed to main `populate_indicators()` method. !!! Note - Do not use the `@informative` decorator if you need to use data from one informative pair when generating another informative pair. Instead, define informative pairs manually as described [in the DataProvider section](#complete-data-provider-sample). + Do not use the `@informative` decorator if you need to use data from one informative pair when generating another informative pair. Instead, define informative pairs manually as described [in the DataProvider section](#complete-dataprovider-sample). When hyperopting, use of the hyperoptable parameter `.value` attribute is not supported. Please use the `.range` attribute. See [optimizing an indicator parameter](hyperopt.md#optimizing-an-indicator-parameter) for more information. @@ -710,7 +710,7 @@ Options: - Merge the dataframe without lookahead bias - Forward-fill (optional) -For a full sample, please refer to the [complete data provider example](#complete-data-provider-sample) below. +For a full sample, please refer to the [complete data provider example](#complete-dataprovider-sample) below. All columns of the informative dataframe will be available on the returning dataframe in a renamed fashion: diff --git a/docs/strategy_analysis_example.md b/docs/strategy_analysis_example.md index a1c74162e..814531c2d 100644 --- a/docs/strategy_analysis_example.md +++ b/docs/strategy_analysis_example.md @@ -152,7 +152,7 @@ print(stats["strategy"][strategy]["pairlist"]) # Get market change (average change of all pairs from start to end of the backtest period) print(stats["strategy"][strategy]["market_change"]) # Maximum drawdown () -print(stats["strategy"][strategy]["max_drawdown"]) +print(stats["strategy"][strategy]["max_drawdown_abs"]) # Maximum drawdown start and end print(stats["strategy"][strategy]["drawdown_start"]) print(stats["strategy"][strategy]["drawdown_end"]) diff --git a/docs/stylesheets/ft.extra.css b/docs/stylesheets/ft.extra.css index 18985baf0..930463ca8 100644 --- a/docs/stylesheets/ft.extra.css +++ b/docs/stylesheets/ft.extra.css @@ -15,3 +15,7 @@ .md-version__list { font-weight: 500 !important; } + +#available-endpoints ~ .md-typeset__scrollwrap .md-typeset__table th:first-of-type { + width: 35% !important; +} diff --git a/docs/telegram-usage.md b/docs/telegram-usage.md index 3fc76d58f..946686b1c 100644 --- a/docs/telegram-usage.md +++ b/docs/telegram-usage.md @@ -45,15 +45,22 @@ Get your "Id", you will use it for the config parameter `chat_id`. #### Use Group id -You can use bots in telegram groups by just adding them to the group. You can find the group id by first adding a [RawDataBot](https://telegram.me/rawdatabot) to your group. The Group id is shown as id in the `"chat"` section, which the RawDataBot will send to you: +To get the group ID, you can add the bot to the group, start freqtrade, and issue a `/tg_info` command. +This will return the group id to you, without having to use some random bot. +While "chat_id" is still required, it doesn't need to be set to this particular group id for this command. + +The response will also contain the "topic_id" if necessary - both in a format ready to copy/paste into your configuration. ``` json -"chat":{ - "id":-1001332619709 + { + "enabled": true, + "token": "********", + "chat_id": "-1001332619709", + "topic_id": "122" } ``` -For the Freqtrade configuration, you can then use the full value (including `-` if it's there) as string: +For the Freqtrade configuration, you can then use the full value (including `-` ) as string: ```json "chat_id": "-1001332619709" @@ -62,6 +69,18 @@ For the Freqtrade configuration, you can then use the full value (including `-` !!! Warning "Using telegram groups" When using telegram groups, you're giving every member of the telegram group access to your freqtrade bot and to all commands possible via telegram. Please make sure that you can trust everyone in the telegram group to avoid unpleasant surprises. +##### Group Topic ID + +To use a specific topic in a group, you can use the `topic_id` parameter in the configuration. This will allow you to use the bot in a specific topic in a group. +Without this, the bot will always respond to the general channel in the group if topics are enabled for a group chat. + +```json + "chat_id": "-1001332619709", + "topic_id": "3" +``` + +Similar to the group-id - you can use `/tg_info` from the topic/thread to get the correct topic-id. + ## Control telegram noise Freqtrade provides means to control the verbosity of your telegram bot. @@ -341,6 +360,8 @@ Return the performance of each crypto-currency the bot has sold. > 5. `STORJ/BTC 0.0009 BTC (27.24%) (1)` > ... +The relative performance is calculated against the total investment in the currency, aggregating all filled entries for the currency. + ### /balance Return the balance of all crypto-currency your have on the exchange. diff --git a/docs/utils.md b/docs/utils.md index dde6006ba..335785539 100644 --- a/docs/utils.md +++ b/docs/utils.md @@ -6,17 +6,9 @@ Besides the Live-Trade and Dry-Run run modes, the `backtesting`, `edge` and `hyp Creates the directory structure to hold your files for freqtrade. Will also create strategy and hyperopt examples for you to get started. -Can be used multiple times - using `--reset` will reset the sample strategy and hyperopt files to their default state. +Can be used multiple times - using `--reset` will reset the sample strategy and hyperopt files to their default state. -``` -usage: freqtrade create-userdir [-h] [--userdir PATH] [--reset] - -optional arguments: - -h, --help show this help message and exit - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - --reset Reset sample files to their original state. -``` +--8<-- "commands/create-userdir.md" !!! Warning Using `--reset` may result in loss of data, since this will overwrite all sample files without asking again. @@ -38,15 +30,7 @@ optional arguments: Creates a new configuration file, asking some questions which are important selections for a configuration. -``` -usage: freqtrade new-config [-h] [-c PATH] - -optional arguments: - -h, --help show this help message and exit - -c PATH, --config PATH - Specify configuration file (default: `config.json`). Multiple --config options may be used. Can be set to `-` - to read config from stdin. -``` +--8<-- "commands/new-config.md" !!! Warning Only vital questions are asked. Freqtrade offers a lot more configuration possibilities, which are listed in the [Configuration documentation](configuration.md#configuration-parameters) @@ -73,21 +57,7 @@ Especially useful with [split configuration files](configuration.md#multiple-con ![Show config output](assets/show-config-output.png) -``` -usage: freqtrade show-config [-h] [--userdir PATH] [-c PATH] - [--show-sensitive] - -options: - -h, --help show this help message and exit - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - --show-sensitive Show secrets in the output. -``` +--8<-- "commands/show-config.md" ``` output Your combined configuration is: @@ -120,23 +90,7 @@ The file will be named inline with your class name, and will not overwrite exist Results will be located in `user_data/strategies/.py`. -``` output -usage: freqtrade new-strategy [-h] [--userdir PATH] [-s NAME] - [--template {full,minimal,advanced}] - -optional arguments: - -h, --help show this help message and exit - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -s NAME, --strategy NAME - Specify strategy class name which will be used by the - bot. - --template {full,minimal,advanced} - Use a template which is either `minimal`, `full` - (containing multiple sample indicators) or `advanced`. - Default: `full`. - -``` +--8<-- "commands/new-strategy.md" ### Sample usage of new-strategy @@ -162,38 +116,7 @@ Use the `list-strategies` subcommand to see all strategies in one particular dir This subcommand is useful for finding problems in your environment with loading strategies: modules with strategies that contain errors and failed to load are printed in red (LOAD FAILED), while strategies with duplicate names are printed in yellow (DUPLICATE NAME). -``` -usage: freqtrade list-strategies [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] - [--strategy-path PATH] [-1] [--no-color] - [--recursive-strategy-search] - -optional arguments: - -h, --help show this help message and exit - --strategy-path PATH Specify additional strategy lookup path. - -1, --one-column Print output in one column. - --no-color Disable colorization of hyperopt results. May be - useful if you are redirecting output to a file. - --recursive-strategy-search - Recursively search for a strategy in the strategies - folder. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. -``` +--8<-- "commands/list-strategies.md" !!! Warning Using these commands will try to load all python files from a directory. This can be a security risk if untrusted files reside in this directory, since all module-level code is executed. @@ -224,36 +147,7 @@ It provides a quick list of all available loss functions in your environment. This subcommand can be useful for finding problems in your environment with loading loss functions: modules with Hyperopt-Loss functions that contain errors and failed to load are printed in red (LOAD FAILED), while hyperopt-Loss functions with duplicate names are printed in yellow (DUPLICATE NAME). -``` -usage: freqtrade list-hyperoptloss [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] - [--hyperopt-path PATH] [-1] [--no-color] - -options: - -h, --help show this help message and exit - --hyperopt-path PATH Specify additional lookup path for Hyperopt Loss - functions. - -1, --one-column Print output in one column. - --no-color Disable colorization of hyperopt results. May be - useful if you are redirecting output to a file. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE, --log-file FILE - Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH, --data-dir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. -``` +--8<-- "commands/list-hyperoptloss.md" ## List freqAI models @@ -261,49 +155,13 @@ Use the `list-freqaimodels` subcommand to see all freqAI models available. This subcommand is useful for finding problems in your environment with loading freqAI models: modules with models that contain errors and failed to load are printed in red (LOAD FAILED), while models with duplicate names are printed in yellow (DUPLICATE NAME). -``` -usage: freqtrade list-freqaimodels [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] - [--freqaimodel-path PATH] [-1] [--no-color] - -optional arguments: - -h, --help show this help message and exit - --freqaimodel-path PATH - Specify additional lookup path for freqaimodels. - -1, --one-column Print output in one column. - --no-color Disable colorization of hyperopt results. May be - useful if you are redirecting output to a file. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH, --data-dir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -``` +--8<-- "commands/list-freqaimodels.md" ## List Exchanges Use the `list-exchanges` subcommand to see the exchanges available for the bot. -``` -usage: freqtrade list-exchanges [-h] [-1] [-a] - -optional arguments: - -h, --help show this help message and exit - -1, --one-column Print output in one column. - -a, --all Print all exchanges known to the ccxt library. -``` +--8<-- "commands/list-exchanges.md" Example: see exchanges available for the bot: @@ -352,35 +210,7 @@ okx True Official spot, isolated futures Use the `list-timeframes` subcommand to see the list of timeframes available for the exchange. -``` -usage: freqtrade list-timeframes [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] - [--exchange EXCHANGE] [-1] - -options: - -h, --help show this help message and exit - --exchange EXCHANGE Exchange name. Only valid if no config is provided. - -1, --one-column Print output in one column. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE, --log-file FILE - Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH, --data-dir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - - -``` +--8<-- "commands/list-timeframes.md" * Example: see the timeframes for the 'binance' exchange, set in the configuration file: @@ -408,54 +238,7 @@ You can print info about any pair/market with these subcommands - and you can fi These subcommands have same usage and same set of available options: -``` -usage: freqtrade list-markets [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] [--exchange EXCHANGE] - [--print-list] [--print-json] [-1] [--print-csv] - [--base BASE_CURRENCY [BASE_CURRENCY ...]] - [--quote QUOTE_CURRENCY [QUOTE_CURRENCY ...]] - [-a] [--trading-mode {spot,margin,futures}] -usage: freqtrade list-pairs [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] [--exchange EXCHANGE] - [--print-list] [--print-json] [-1] [--print-csv] - [--base BASE_CURRENCY [BASE_CURRENCY ...]] - [--quote QUOTE_CURRENCY [QUOTE_CURRENCY ...]] [-a] - [--trading-mode {spot,margin,futures}] -options: - -h, --help show this help message and exit - --exchange EXCHANGE Exchange name. Only valid if no config is provided. - --print-list Print list of pairs or market symbols. By default data - is printed in the tabular format. - --print-json Print list of pairs or market symbols in JSON format. - -1, --one-column Print output in one column. - --print-csv Print exchange pair or market data in the csv format. - --base BASE_CURRENCY [BASE_CURRENCY ...] - Specify base currency(-ies). Space-separated list. - --quote QUOTE_CURRENCY [QUOTE_CURRENCY ...] - Specify quote currency(-ies). Space-separated list. - -a, --all Print all pairs or market symbols. By default only - active ones are shown. - --trading-mode {spot,margin,futures}, --tradingmode {spot,margin,futures} - Select Trading mode - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE, --log-file FILE - Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH, --data-dir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -``` +--8<-- "commands/list-pairs.md" By default, only active pairs/markets are shown. Active pairs/markets are those that can currently be traded on the exchange. You can use the `-a`/`-all` option to see the list of all pairs/markets, including the inactive ones. @@ -493,28 +276,7 @@ Use the `test-pairlist` subcommand to test the configuration of [dynamic pairlis Requires a configuration with specified `pairlists` attribute. Can be used to generate static pairlists to be used during backtesting / hyperopt. -``` -usage: freqtrade test-pairlist [-h] [--userdir PATH] [-v] [-c PATH] - [--quote QUOTE_CURRENCY [QUOTE_CURRENCY ...]] - [-1] [--print-json] [--exchange EXCHANGE] - -options: - -h, --help show this help message and exit - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - --quote QUOTE_CURRENCY [QUOTE_CURRENCY ...] - Specify quote currency(-ies). Space-separated list. - -1, --one-column Print output in one column. - --print-json Print list of pairs or market symbols in JSON format. - --exchange EXCHANGE Exchange name. Only valid if no config is provided. - -``` +--8<-- "commands/test-pairlist.md" ### Examples @@ -530,17 +292,7 @@ freqtrade test-pairlist --config config.json --quote USDT BTC Please refer to the [corresponding documentation](advanced-setup.md#use-a-different-database-system) to learn about requirements for different database systems. -``` -usage: freqtrade convert-db [-h] [--db-url PATH] [--db-url-from PATH] - -optional arguments: - -h, --help show this help message and exit - --db-url PATH Override trades database URL, this is useful in custom - deployments (default: `sqlite:///tradesv3.sqlite` for - Live Run mode, `sqlite:///tradesv3.dryrun.sqlite` for - Dry Run). - --db-url-from PATH Source db url to use when migrating a database. -``` +--8<-- "commands/convert-db.md" !!! Warning Please ensure to only use this on an empty target database. Freqtrade will perform a regular migration, but may fail if entries already existed. @@ -556,30 +308,7 @@ Freqtrade will start the webserver and allow FreqUI to start and control backtes This has the advantage that data will not be reloaded between backtesting runs (as long as timeframe and timerange remain identical). FreqUI will also show the backtesting results. -``` -usage: freqtrade webserver [-h] [-v] [--logfile FILE] [-V] [-c PATH] [-d PATH] - [--userdir PATH] - -optional arguments: - -h, --help show this help message and exit - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -``` +--8<-- "commands/webserver.md" ### Webserver mode - docker @@ -609,37 +338,7 @@ Adding `--show-pair-list` outputs a sorted pair list you can easily copy/paste i ??? Warning "Strategy overfitting" Only using winning pairs can lead to an overfitted strategy, which will not work well on future data. Make sure to extensively test your strategy in dry-run before risking real money. -``` -usage: freqtrade backtesting-show [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] - [--export-filename PATH] [--show-pair-list] - -optional arguments: - -h, --help show this help message and exit - --export-filename PATH - Save backtest results to the file with this filename. - Requires `--export` to be set as well. Example: - `--export-filename=user_data/backtest_results/backtest - _today.json` - --show-pair-list Show backtesting pairlist sorted by profit. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -``` +--8<-- "commands/backtesting-show.md" ## Detailed backtest analysis @@ -647,134 +346,13 @@ Advanced backtest result analysis. More details in the [Backtesting analysis](advanced-backtesting.md#analyze-the-buyentry-and-sellexit-tags) Section. -``` -usage: freqtrade backtesting-analysis [-h] [-v] [--logfile FILE] [-V] - [-c PATH] [-d PATH] [--userdir PATH] - [--export-filename PATH] - [--analysis-groups {0,1,2,3,4} [{0,1,2,3,4} ...]] - [--enter-reason-list ENTER_REASON_LIST [ENTER_REASON_LIST ...]] - [--exit-reason-list EXIT_REASON_LIST [EXIT_REASON_LIST ...]] - [--indicator-list INDICATOR_LIST [INDICATOR_LIST ...]] - [--timerange YYYYMMDD-[YYYYMMDD]] - [--rejected] - [--analysis-to-csv] - [--analysis-csv-path PATH] - -optional arguments: - -h, --help show this help message and exit - --export-filename PATH, --backtest-filename PATH - Use this filename for backtest results.Requires - `--export` to be set as well. Example: `--export-filen - ame=user_data/backtest_results/backtest_today.json` - --analysis-groups {0,1,2,3,4} [{0,1,2,3,4} ...] - grouping output - 0: simple wins/losses by enter tag, - 1: by enter_tag, 2: by enter_tag and exit_tag, 3: by - pair and enter_tag, 4: by pair, enter_ and exit_tag - (this can get quite large) - --enter-reason-list ENTER_REASON_LIST [ENTER_REASON_LIST ...] - Space separated list of entry signals to analyse. - Default: all. e.g. 'entry_tag_a entry_tag_b' - --exit-reason-list EXIT_REASON_LIST [EXIT_REASON_LIST ...] - Space separated list of exit signals to analyse. - Default: all. e.g. - 'exit_tag_a roi stop_loss trailing_stop_loss' - --indicator-list INDICATOR_LIST [INDICATOR_LIST ...] - Space separated list of indicators to analyse. e.g. - 'close rsi bb_lowerband profit_abs' - --timerange YYYYMMDD-[YYYYMMDD] - Timerange to filter trades for analysis, - start inclusive, end exclusive. e.g. - 20220101-20220201 - --rejected - Print out rejected trades table - --analysis-to-csv - Write out tables to individual CSVs, by default to - 'user_data/backtest_results' unless '--analysis-csv-path' is given. - --analysis-csv-path [PATH] - Optional path where individual CSVs will be written. If not used, - CSVs will be written to 'user_data/backtest_results'. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -``` +--8<-- "commands/backtesting-analysis.md" ## List Hyperopt results You can list the hyperoptimization epochs the Hyperopt module evaluated previously with the `hyperopt-list` sub-command. -``` -usage: freqtrade hyperopt-list [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] [--best] - [--profitable] [--min-trades INT] - [--max-trades INT] [--min-avg-time FLOAT] - [--max-avg-time FLOAT] [--min-avg-profit FLOAT] - [--max-avg-profit FLOAT] - [--min-total-profit FLOAT] - [--max-total-profit FLOAT] - [--min-objective FLOAT] [--max-objective FLOAT] - [--no-color] [--print-json] [--no-details] - [--hyperopt-filename PATH] [--export-csv FILE] - -optional arguments: - -h, --help show this help message and exit - --best Select only best epochs. - --profitable Select only profitable epochs. - --min-trades INT Select epochs with more than INT trades. - --max-trades INT Select epochs with less than INT trades. - --min-avg-time FLOAT Select epochs above average time. - --max-avg-time FLOAT Select epochs below average time. - --min-avg-profit FLOAT - Select epochs above average profit. - --max-avg-profit FLOAT - Select epochs below average profit. - --min-total-profit FLOAT - Select epochs above total profit. - --max-total-profit FLOAT - Select epochs below total profit. - --min-objective FLOAT - Select epochs above objective. - --max-objective FLOAT - Select epochs below objective. - --no-color Disable colorization of hyperopt results. May be - useful if you are redirecting output to a file. - --print-json Print output in JSON format. - --no-details Do not print best epoch details. - --hyperopt-filename FILENAME - Hyperopt result filename.Example: `--hyperopt- - filename=hyperopt_results_2020-09-27_16-20-48.pickle` - --export-csv FILE Export to CSV-File. This will disable table print. - Example: --export-csv hyperopt.csv - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. -``` +--8<-- "commands/hyperopt-list.md" !!! Note `hyperopt-list` will automatically use the latest available hyperopt results file. @@ -796,46 +374,7 @@ freqtrade hyperopt-list --profitable --no-details You can show the details of any hyperoptimization epoch previously evaluated by the Hyperopt module with the `hyperopt-show` subcommand. -``` -usage: freqtrade hyperopt-show [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] [--best] - [--profitable] [-n INT] [--print-json] - [--hyperopt-filename FILENAME] [--no-header] - [--disable-param-export] - [--breakdown {day,week,month} [{day,week,month} ...]] - -optional arguments: - -h, --help show this help message and exit - --best Select only best epochs. - --profitable Select only profitable epochs. - -n INT, --index INT Specify the index of the epoch to print details for. - --print-json Print output in JSON format. - --hyperopt-filename FILENAME - Hyperopt result filename.Example: `--hyperopt- - filename=hyperopt_results_2020-09-27_16-20-48.pickle` - --no-header Do not print epoch details header. - --disable-param-export - Disable automatic hyperopt parameter export. - --breakdown {day,week,month} [{day,week,month} ...] - Show backtesting breakdown per [day, week, month]. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -``` +--8<-- "commands/hyperopt-show.md" !!! Note `hyperopt-show` will automatically use the latest available hyperopt results file. @@ -859,38 +398,7 @@ freqtrade hyperopt-show --best -n -1 --print-json --no-header Print selected (or all) trades from database to screen. -``` -usage: freqtrade show-trades [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] [--db-url PATH] - [--trade-ids TRADE_IDS [TRADE_IDS ...]] - [--print-json] - -optional arguments: - -h, --help show this help message and exit - --db-url PATH Override trades database URL, this is useful in custom - deployments (default: `sqlite:///tradesv3.sqlite` for - Live Run mode, `sqlite:///tradesv3.dryrun.sqlite` for - Dry Run). - --trade-ids TRADE_IDS [TRADE_IDS ...] - Specify the list of trade ids. - --print-json Print output in JSON format. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. -``` +--8<-- "commands/show-trades.md" ### Examples @@ -910,32 +418,4 @@ Your original strategy will remain available in the `user_data/strategies_orig_u Strategy updater will work on a "best effort" approach. Please do your due diligence and verify the results of the conversion. We also recommend to run a python formatter (e.g. `black`) to format results in a sane manner. -``` -usage: freqtrade strategy-updater [-h] [-v] [--logfile FILE] [-V] [-c PATH] - [-d PATH] [--userdir PATH] - [--strategy-list STRATEGY_LIST [STRATEGY_LIST ...]] - -options: - -h, --help show this help message and exit - --strategy-list STRATEGY_LIST [STRATEGY_LIST ...] - Provide a space-separated list of strategies to - be converted. - -Common arguments: - -v, --verbose Verbose mode (-vv for more, -vvv to get all messages). - --logfile FILE, --log-file FILE - Log to the file specified. Special values are: - 'syslog', 'journald'. See the documentation for more - details. - -V, --version show program's version number and exit - -c PATH, --config PATH - Specify configuration file (default: - `userdir/config.json` or `config.json` whichever - exists). Multiple --config options may be used. Can be - set to `-` to read config from stdin. - -d PATH, --datadir PATH, --data-dir PATH - Path to directory with historical backtesting data. - --userdir PATH, --user-data-dir PATH - Path to userdata directory. - -``` +--8<-- "commands/strategy-updater.md" diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 8688aa917..e93de1dbc 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,6 +1,6 @@ """Freqtrade bot""" -__version__ = "2024.12-dev" +__version__ = "2025.2-dev" if "dev" in __version__: from pathlib import Path diff --git a/freqtrade/commands/analyze_commands.py b/freqtrade/commands/analyze_commands.py index 7966ae6e6..d76abd1ef 100644 --- a/freqtrade/commands/analyze_commands.py +++ b/freqtrade/commands/analyze_commands.py @@ -1,63 +1,23 @@ import logging -from pathlib import Path from typing import Any from freqtrade.enums import RunMode -from freqtrade.exceptions import ConfigurationError, OperationalException logger = logging.getLogger(__name__) -def setup_analyze_configuration(args: dict[str, Any], method: RunMode) -> dict[str, Any]: - """ - Prepare the configuration for the entry/exit reason analysis module - :param args: Cli args from Arguments() - :param method: Bot running mode - :return: Configuration - """ - from freqtrade.configuration import setup_utils_configuration - - config = setup_utils_configuration(args, method) - - no_unlimited_runmodes = { - RunMode.BACKTEST: "backtesting", - } - if method in no_unlimited_runmodes.keys(): - from freqtrade.data.btanalysis import get_latest_backtest_filename - - if "exportfilename" in config: - if config["exportfilename"].is_dir(): - btfile = Path(get_latest_backtest_filename(config["exportfilename"])) - signals_file = f"{config['exportfilename']}/{btfile.stem}_signals.pkl" - else: - if config["exportfilename"].exists(): - btfile = Path(config["exportfilename"]) - signals_file = f"{btfile.parent}/{btfile.stem}_signals.pkl" - else: - raise ConfigurationError(f"{config['exportfilename']} does not exist.") - else: - raise ConfigurationError("exportfilename not in config.") - - if not Path(signals_file).exists(): - raise OperationalException( - f"Cannot find latest backtest signals file: {signals_file}." - "Run backtesting with `--export signals`." - ) - - return config - - def start_analysis_entries_exits(args: dict[str, Any]) -> None: """ Start analysis script :param args: Cli args from Arguments() :return: None """ + from freqtrade.configuration import setup_utils_configuration from freqtrade.data.entryexitanalysis import process_entry_exit_reasons # Initialize configuration - config = setup_analyze_configuration(args, RunMode.BACKTEST) + config = setup_utils_configuration(args, RunMode.BACKTEST) logger.info("Starting freqtrade in analysis mode") diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index a56185471..d0bad4567 100755 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -11,7 +11,15 @@ from freqtrade.commands.cli_options import AVAILABLE_CLI_OPTIONS from freqtrade.constants import DEFAULT_CONFIG -ARGS_COMMON = ["verbosity", "logfile", "version", "config", "datadir", "user_data_dir"] +ARGS_COMMON = [ + "verbosity", + "print_colorized", + "logfile", + "version", + "config", + "datadir", + "user_data_dir", +] ARGS_STRATEGY = [ "strategy", @@ -58,7 +66,6 @@ ARGS_HYPEROPT = ARGS_COMMON_OPTIMIZE + [ "epochs", "spaces", "print_all", - "print_colorized", "print_json", "hyperopt_jobs", "hyperopt_random_state", @@ -74,13 +81,12 @@ ARGS_EDGE = ARGS_COMMON_OPTIMIZE + ["stoploss_range"] ARGS_LIST_STRATEGIES = [ "strategy_path", "print_one_column", - "print_colorized", "recursive_strategy_search", ] -ARGS_LIST_FREQAIMODELS = ["freqaimodel_path", "print_one_column", "print_colorized"] +ARGS_LIST_FREQAIMODELS = ["freqaimodel_path", "print_one_column"] -ARGS_LIST_HYPEROPTS = ["hyperopt_path", "print_one_column", "print_colorized"] +ARGS_LIST_HYPEROPTS = ["hyperopt_path", "print_one_column"] ARGS_BACKTEST_SHOW = ["exportfilename", "backtest_show_pair_list", "backtest_breakdown"] @@ -202,7 +208,6 @@ ARGS_HYPEROPT_LIST = [ "hyperopt_list_max_total_profit", "hyperopt_list_min_objective", "hyperopt_list_max_objective", - "print_colorized", "print_json", "hyperopt_list_no_details", "hyperoptexportfilename", @@ -342,7 +347,7 @@ class Arguments: self.parser = ArgumentParser( prog="freqtrade", description="Free, open source crypto trading bot" ) - self._build_args(optionlist=["version"], parser=self.parser) + self._build_args(optionlist=["version_main"], parser=self.parser) from freqtrade.commands import ( start_analysis_entries_exits, diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 6e90a521f..16f5d1c4d 100755 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -4,7 +4,7 @@ Definition of cli arguments used in arguments.py from argparse import SUPPRESS, ArgumentTypeError -from freqtrade import __version__, constants +from freqtrade import constants from freqtrade.constants import HYPEROPT_LOSS_BUILTIN from freqtrade.enums import CandleType @@ -48,7 +48,6 @@ AVAILABLE_CLI_OPTIONS = { "--verbose", help="Verbose mode (-vv for more, -vvv to get all messages).", action="count", - default=0, ), "logfile": Arg( "--logfile", @@ -60,8 +59,15 @@ AVAILABLE_CLI_OPTIONS = { "version": Arg( "-V", "--version", - action="version", - version=f"%(prog)s {__version__}", + help="show program's version number and exit", + action="store_true", + ), + "version_main": Arg( + # Copy of version - used to have -V available with and without subcommand. + "-V", + "--version", + help="show program's version number and exit", + action="store_true", ), "config": Arg( "-c", @@ -335,7 +341,7 @@ AVAILABLE_CLI_OPTIONS = { help="Specify the class name of the hyperopt loss function class (IHyperOptLoss). " "Different functions can generate completely different results, " "since the target for optimization is different. Built-in Hyperopt-loss-functions are: " - f'{", ".join(HYPEROPT_LOSS_BUILTIN)}', + f"{', '.join(HYPEROPT_LOSS_BUILTIN)}", metavar="NAME", ), "hyperoptexportfilename": Arg( @@ -664,8 +670,7 @@ AVAILABLE_CLI_OPTIONS = { "--ignore-missing-spaces", "--ignore-unparameterized-spaces", help=( - "Suppress errors for any requested Hyperopt spaces " - "that do not contain any parameters." + "Suppress errors for any requested Hyperopt spaces that do not contain any parameters." ), action="store_true", ), diff --git a/freqtrade/commands/data_commands.py b/freqtrade/commands/data_commands.py index c529e8dc7..756c5a287 100644 --- a/freqtrade/commands/data_commands.py +++ b/freqtrade/commands/data_commands.py @@ -15,8 +15,7 @@ logger = logging.getLogger(__name__) def _check_data_config_download_sanity(config: Config) -> None: if "days" in config and "timerange" in config: raise ConfigurationError( - "--days and --timerange are mutually exclusive. " - "You can only specify one or the other." + "--days and --timerange are mutually exclusive. You can only specify one or the other." ) if "pairs" not in config: diff --git a/freqtrade/configuration/config_schema.py b/freqtrade/configuration/config_schema.py index d31069e7b..91e5c2d1b 100644 --- a/freqtrade/configuration/config_schema.py +++ b/freqtrade/configuration/config_schema.py @@ -40,6 +40,10 @@ CONF_SCHEMA = { ), "type": "string", }, + "proxy_coin": { + "description": "Proxy coin - must be used for specific futures modes (e.g. BNFCR)", + "type": "string", + }, "stake_currency": { "description": "Currency used for staking.", "type": "string", @@ -460,7 +464,11 @@ CONF_SCHEMA = { }, "token": {"description": "Telegram bot token.", "type": "string"}, "chat_id": { - "description": "Telegram chat ID", + "description": "Telegram chat or group ID", + "type": "string", + }, + "topic_id": { + "description": "Telegram topic ID - only applicable for group chats", "type": "string", }, "allow_custom_messages": { @@ -645,7 +653,7 @@ CONF_SCHEMA = { "type": "array", "items": {"type": "string"}, }, - "x": { + "verbosity": { "description": "Logging verbosity level.", "type": "string", "enum": ["error", "info"], diff --git a/freqtrade/configuration/configuration.py b/freqtrade/configuration/configuration.py index 796296a7b..3fc30c375 100644 --- a/freqtrade/configuration/configuration.py +++ b/freqtrade/configuration/configuration.py @@ -26,7 +26,7 @@ from freqtrade.enums import ( ) from freqtrade.exceptions import OperationalException from freqtrade.loggers import setup_logging -from freqtrade.misc import deep_merge_dicts, parse_db_uri_for_logging +from freqtrade.misc import deep_merge_dicts, parse_db_uri_for_logging, safe_value_fallback logger = logging.getLogger(__name__) @@ -130,11 +130,20 @@ class Configuration: the -v/--verbose, --logfile options """ # Log level - config.update({"verbosity": self.args.get("verbosity", 0)}) + if "verbosity" not in config or self.args.get("verbosity") is not None: + config.update( + {"verbosity": safe_value_fallback(self.args, "verbosity", default_value=0)} + ) if "logfile" in self.args and self.args["logfile"]: config.update({"logfile": self.args["logfile"]}) + if "print_colorized" in self.args and not self.args["print_colorized"]: + logger.info("Parameter --no-color detected ...") + config.update({"print_colorized": False}) + else: + config.update({"print_colorized": True}) + setup_logging(config) def _process_trading_options(self, config: Config) -> None: @@ -326,12 +335,6 @@ class Configuration: ] self._args_to_config_loop(config, configurations) - if "print_colorized" in self.args and not self.args["print_colorized"]: - logger.info("Parameter --no-color detected ...") - config.update({"print_colorized": False}) - else: - config.update({"print_colorized": True}) - configurations = [ ("print_json", "Parameter --print-json detected ..."), ("export_csv", "Parameter --export-csv detected: {}"), diff --git a/freqtrade/configuration/timerange.py b/freqtrade/configuration/timerange.py index 8afee82d1..30860cd63 100644 --- a/freqtrade/configuration/timerange.py +++ b/freqtrade/configuration/timerange.py @@ -10,6 +10,7 @@ from typing_extensions import Self from freqtrade.constants import DATETIME_PRINT_FORMAT from freqtrade.exceptions import ConfigurationError +from freqtrade.util import dt_from_ts logger = logging.getLogger(__name__) @@ -37,13 +38,13 @@ class TimeRange: @property def startdt(self) -> datetime | None: if self.startts: - return datetime.fromtimestamp(self.startts, tz=timezone.utc) + return dt_from_ts(self.startts) return None @property def stopdt(self) -> datetime | None: if self.stopts: - return datetime.fromtimestamp(self.stopts, tz=timezone.utc) + return dt_from_ts(self.stopts) return None @property diff --git a/freqtrade/constants.py b/freqtrade/constants.py index 266db9ae5..2d3b4a5e8 100644 --- a/freqtrade/constants.py +++ b/freqtrade/constants.py @@ -58,7 +58,7 @@ AVAILABLE_PAIRLISTS = [ "SpreadFilter", "VolatilityFilter", ] -AVAILABLE_DATAHANDLERS = ["json", "jsongz", "hdf5", "feather", "parquet"] +AVAILABLE_DATAHANDLERS = ["json", "jsongz", "feather", "parquet"] BACKTEST_BREAKDOWNS = ["day", "week", "month"] BACKTEST_CACHE_AGE = ["none", "day", "week", "month"] BACKTEST_CACHE_DEFAULT = "day" diff --git a/freqtrade/data/btanalysis.py b/freqtrade/data/btanalysis.py index a9d8648d2..7b0109fa9 100644 --- a/freqtrade/data/btanalysis.py +++ b/freqtrade/data/btanalysis.py @@ -3,8 +3,10 @@ Helpers when analyzing backtest data """ import logging +import zipfile from copy import copy from datetime import datetime, timezone +from io import BytesIO, StringIO from pathlib import Path from typing import Any, Literal @@ -165,8 +167,16 @@ def load_backtest_stats(filename: Path | str) -> BacktestResultType: if not filename.is_file(): raise ValueError(f"File {filename} does not exist.") logger.info(f"Loading backtest result from {filename}") - with filename.open() as file: - data = json_load(file) + + if filename.suffix == ".zip": + data = json_load( + StringIO( + load_file_from_zip(filename, filename.with_suffix(".json").name).decode("utf-8") + ) + ) + else: + with filename.open() as file: + data = json_load(file) # Legacy list format does not contain metadata. if isinstance(data, dict): @@ -194,8 +204,10 @@ def load_and_merge_backtest_result(strategy_name: str, filename: Path, results: def _get_backtest_files(dirname: Path) -> list[Path]: - # Weird glob expression here avoids including .meta.json files. - return list(reversed(sorted(dirname.glob("backtest-result-*-[0-9][0-9].json")))) + # Get both json and zip files separately and combine the results + json_files = dirname.glob("backtest-result-*-[0-9][0-9]*.json") + zip_files = dirname.glob("backtest-result-*-[0-9][0-9]*.zip") + return list(reversed(sorted(list(json_files) + list(zip_files)))) def _extract_backtest_result(filename: Path) -> list[BacktestHistoryEntryType]: @@ -267,7 +279,11 @@ def get_backtest_market_change(filename: Path, include_ts: bool = True) -> pd.Da """ Read backtest market change file. """ - df = pd.read_feather(filename) + if filename.suffix == ".zip": + data = load_file_from_zip(filename, f"{filename.stem}_market_change.feather") + df = pd.read_feather(BytesIO(data)) + else: + df = pd.read_feather(filename) if include_ts: df.loc[:, "__date_ts"] = df.loc[:, "date"].astype(np.int64) // 1000 // 1000 return df @@ -388,6 +404,93 @@ def load_backtest_data(filename: Path | str, strategy: str | None = None) -> pd. return df +def load_file_from_zip(zip_path: Path, filename: str) -> bytes: + """ + Load a file from a zip file + :param zip_path: Path to the zip file + :param filename: Name of the file to load + :return: Bytes of the file + :raises: ValueError if loading goes wrong. + """ + try: + with zipfile.ZipFile(zip_path) as zipf: + try: + with zipf.open(filename) as file: + return file.read() + except KeyError: + logger.error(f"File {filename} not found in zip: {zip_path}") + raise ValueError(f"File {filename} not found in zip: {zip_path}") from None + except FileNotFoundError: + raise ValueError(f"Zip file {zip_path} not found.") + except zipfile.BadZipFile: + logger.error(f"Bad zip file: {zip_path}.") + raise ValueError(f"Bad zip file: {zip_path}.") from None + + +def load_backtest_analysis_data(backtest_dir: Path, name: str): + """ + Load backtest analysis data either from a pickle file or from within a zip file + :param backtest_dir: Directory containing backtest results + :param name: Name of the analysis data to load (signals, rejected, exited) + :return: Analysis data + """ + import joblib + + if backtest_dir.is_dir(): + lbf = Path(get_latest_backtest_filename(backtest_dir)) + zip_path = backtest_dir / lbf + else: + zip_path = backtest_dir + + if zip_path.suffix == ".zip": + # Load from zip file + analysis_name = f"{zip_path.stem}_{name}.pkl" + data = load_file_from_zip(zip_path, analysis_name) + if not data: + return None + loaded_data = joblib.load(BytesIO(data)) + + logger.info(f"Loaded {name} candles from zip: {str(zip_path)}:{analysis_name}") + return loaded_data + + else: + # Load from separate pickle file + if backtest_dir.is_dir(): + scpf = Path(backtest_dir, f"{zip_path.stem}_{name}.pkl") + else: + scpf = Path(backtest_dir.parent / f"{backtest_dir.stem}_{name}.pkl") + + try: + with scpf.open("rb") as scp: + loaded_data = joblib.load(scp) + logger.info(f"Loaded {name} candles: {str(scpf)}") + return loaded_data + except Exception: + logger.exception(f"Cannot load {name} data from pickled results.") + return None + + +def load_rejected_signals(backtest_dir: Path): + """ + Load rejected signals from backtest directory + """ + return load_backtest_analysis_data(backtest_dir, "rejected") + + +def load_signal_candles(backtest_dir: Path): + """ + Load signal candles from backtest directory + """ + return load_backtest_analysis_data(backtest_dir, "signals") + + +def load_exit_signal_candles(backtest_dir: Path) -> dict[str, dict[str, pd.DataFrame]]: + """ + Load exit signal candles from backtest directory + """ + return load_backtest_analysis_data(backtest_dir, "exited") + + def analyze_trade_parallelism(results: pd.DataFrame, timeframe: str) -> pd.DataFrame: """ Find overlapping trades by expanding each trade once per period it was open diff --git a/freqtrade/data/converter/orderflow.py b/freqtrade/data/converter/orderflow.py index a51394ce9..4b4923cfb 100644 --- a/freqtrade/data/converter/orderflow.py +++ b/freqtrade/data/converter/orderflow.py @@ -164,12 +164,12 @@ def populate_dataframe_with_trades( dataframe.at[index, "imbalances"] = imbalances.to_dict(orient="index") stacked_imbalance_range = config_orderflow["stacked_imbalance_range"] - dataframe.at[index, "stacked_imbalances_bid"] = stacked_imbalance_bid( - imbalances, stacked_imbalance_range=stacked_imbalance_range + dataframe.at[index, "stacked_imbalances_bid"] = stacked_imbalance( + imbalances, label="bid", stacked_imbalance_range=stacked_imbalance_range ) - dataframe.at[index, "stacked_imbalances_ask"] = stacked_imbalance_ask( - imbalances, stacked_imbalance_range=stacked_imbalance_range + dataframe.at[index, "stacked_imbalances_ask"] = stacked_imbalance( + imbalances, label="ask", stacked_imbalance_range=stacked_imbalance_range ) bid = np.where( @@ -256,34 +256,24 @@ def trades_orderflow_to_imbalances(df: pd.DataFrame, imbalance_ratio: int, imbal return dataframe -def stacked_imbalance( - df: pd.DataFrame, label: str, stacked_imbalance_range: int, should_reverse: bool -): +def stacked_imbalance(df: pd.DataFrame, label: str, stacked_imbalance_range: int): """ y * (y.groupby((y != y.shift()).cumsum()).cumcount() + 1) https://stackoverflow.com/questions/27626542/counting-consecutive-positive-values-in-python-pandas-array """ imbalance = df[f"{label}_imbalance"] int_series = pd.Series(np.where(imbalance, 1, 0)) - stacked = int_series * ( - int_series.groupby((int_series != int_series.shift()).cumsum()).cumcount() + 1 - ) + # Group consecutive True values and get their counts + groups = (int_series != int_series.shift()).cumsum() + counts = int_series.groupby(groups).cumsum() - max_stacked_imbalance_idx = stacked.index[stacked >= stacked_imbalance_range] - stacked_imbalance_price = np.nan - if not max_stacked_imbalance_idx.empty: - idx = ( - max_stacked_imbalance_idx[0] - if not should_reverse - else np.flipud(max_stacked_imbalance_idx)[0] - ) - stacked_imbalance_price = imbalance.index[idx] - return stacked_imbalance_price + # Find indices where count meets or exceeds the range requirement + valid_indices = counts[counts >= stacked_imbalance_range].index - -def stacked_imbalance_ask(df: pd.DataFrame, stacked_imbalance_range: int): - return stacked_imbalance(df, "ask", stacked_imbalance_range, should_reverse=True) - - -def stacked_imbalance_bid(df: pd.DataFrame, stacked_imbalance_range: int): - return stacked_imbalance(df, "bid", stacked_imbalance_range, should_reverse=False) + stacked_imbalance_prices = [] + if not valid_indices.empty: + # Get all prices from valid indices from beginning of the range + stacked_imbalance_prices = [ + imbalance.index.values[idx - (stacked_imbalance_range - 1)] for idx in valid_indices + ] + return stacked_imbalance_prices diff --git a/freqtrade/data/dataprovider.py b/freqtrade/data/dataprovider.py index 7e4fc36d4..b979faff6 100644 --- a/freqtrade/data/dataprovider.py +++ b/freqtrade/data/dataprovider.py @@ -79,7 +79,7 @@ class DataProvider: def _set_dataframe_max_date(self, limit_date: datetime): """ - Limit infomrative dataframe to max specified index. + Limit informative dataframe to max specified index. Only relevant in backtesting. :param limit_date: "current date" """ diff --git a/freqtrade/data/entryexitanalysis.py b/freqtrade/data/entryexitanalysis.py index b4b5b966a..0c49b775c 100644 --- a/freqtrade/data/entryexitanalysis.py +++ b/freqtrade/data/entryexitanalysis.py @@ -1,56 +1,25 @@ import logging from pathlib import Path -import joblib import pandas as pd from freqtrade.configuration import TimeRange from freqtrade.constants import Config from freqtrade.data.btanalysis import ( BT_DATA_COLUMNS, - get_latest_backtest_filename, load_backtest_data, load_backtest_stats, + load_exit_signal_candles, + load_rejected_signals, + load_signal_candles, ) -from freqtrade.exceptions import OperationalException +from freqtrade.exceptions import ConfigurationError, OperationalException from freqtrade.util import print_df_rich_table logger = logging.getLogger(__name__) -def _load_backtest_analysis_data(backtest_dir: Path, name: str): - if backtest_dir.is_dir(): - scpf = Path( - backtest_dir, - Path(get_latest_backtest_filename(backtest_dir)).stem + "_" + name + ".pkl", - ) - else: - scpf = Path(backtest_dir.parent / f"{backtest_dir.stem}_{name}.pkl") - - try: - with scpf.open("rb") as scp: - loaded_data = joblib.load(scp) - logger.info(f"Loaded {name} candles: {str(scpf)}") - except Exception as e: - logger.error(f"Cannot load {name} data from pickled results: ", e) - return None - - return loaded_data - - -def _load_rejected_signals(backtest_dir: Path): - return _load_backtest_analysis_data(backtest_dir, "rejected") - - -def _load_signal_candles(backtest_dir: Path): - return _load_backtest_analysis_data(backtest_dir, "signals") - - -def _load_exit_signal_candles(backtest_dir: Path) -> dict[str, dict[str, pd.DataFrame]]: - return _load_backtest_analysis_data(backtest_dir, "exited") - - def _process_candles_and_indicators( pairlist, strategy_name, trades, signal_candles, date_col: str = "open_date" ): @@ -374,19 +343,21 @@ def process_entry_exit_reasons(config: Config): timerange = TimeRange.parse_timerange( None if config.get("timerange") is None else str(config.get("timerange")) ) - - backtest_stats = load_backtest_stats(config["exportfilename"]) + try: + backtest_stats = load_backtest_stats(config["exportfilename"]) + except ValueError as e: + raise ConfigurationError(e) from e for strategy_name, results in backtest_stats["strategy"].items(): trades = load_backtest_data(config["exportfilename"], strategy_name) if trades is not None and not trades.empty: - signal_candles = _load_signal_candles(config["exportfilename"]) - exit_signals = _load_exit_signal_candles(config["exportfilename"]) + signal_candles = load_signal_candles(config["exportfilename"]) + exit_signals = load_exit_signal_candles(config["exportfilename"]) rej_df = None if do_rejected: - rejected_signals_dict = _load_rejected_signals(config["exportfilename"]) + rejected_signals_dict = load_rejected_signals(config["exportfilename"]) rej_df = prepare_results( rejected_signals_dict, strategy_name, diff --git a/freqtrade/data/history/datahandlers/hdf5datahandler.py b/freqtrade/data/history/datahandlers/hdf5datahandler.py deleted file mode 100644 index 58ae65849..000000000 --- a/freqtrade/data/history/datahandlers/hdf5datahandler.py +++ /dev/null @@ -1,181 +0,0 @@ -import logging - -import numpy as np -import pandas as pd - -from freqtrade.configuration import TimeRange -from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS -from freqtrade.enums import CandleType, TradingMode - -from .idatahandler import IDataHandler - - -logger = logging.getLogger(__name__) - - -class HDF5DataHandler(IDataHandler): - _columns = DEFAULT_DATAFRAME_COLUMNS - - def ohlcv_store( - self, pair: str, timeframe: str, data: pd.DataFrame, candle_type: CandleType - ) -> None: - """ - Store data in hdf5 file. - :param pair: Pair - used to generate filename - :param timeframe: Timeframe - used to generate filename - :param data: Dataframe containing OHLCV data - :param candle_type: Any of the enum CandleType (must match trading mode!) - :return: None - """ - key = self._pair_ohlcv_key(pair, timeframe) - _data = data.copy() - - filename = self._pair_data_filename(self._datadir, pair, timeframe, candle_type) - self.create_dir_if_needed(filename) - - _data.loc[:, self._columns].to_hdf( - filename, - key=key, - mode="a", - complevel=9, - complib="blosc", - format="table", - data_columns=["date"], - ) - - def _ohlcv_load( - self, pair: str, timeframe: str, timerange: TimeRange | None, candle_type: CandleType - ) -> pd.DataFrame: - """ - Internal method used to load data for one pair from disk. - Implements the loading and conversion to a Pandas dataframe. - Timerange trimming and dataframe validation happens outside of this method. - :param pair: Pair to load data - :param timeframe: Timeframe (e.g. "5m") - :param timerange: Limit data to be loaded to this timerange. - Optionally implemented by subclasses to avoid loading - all data where possible. - :param candle_type: Any of the enum CandleType (must match trading mode!) - :return: DataFrame with ohlcv data, or empty DataFrame - """ - key = self._pair_ohlcv_key(pair, timeframe) - filename = self._pair_data_filename(self._datadir, pair, timeframe, candle_type=candle_type) - - if not filename.exists(): - # Fallback mode for 1M files - filename = self._pair_data_filename( - self._datadir, pair, timeframe, candle_type=candle_type, no_timeframe_modify=True - ) - if not filename.exists(): - return pd.DataFrame(columns=self._columns) - try: - where = [] - if timerange: - if timerange.starttype == "date": - where.append(f"date >= Timestamp({timerange.startts * 1e9})") - if timerange.stoptype == "date": - where.append(f"date <= Timestamp({timerange.stopts * 1e9})") - - pairdata = pd.read_hdf(filename, key=key, mode="r", where=where) - - if list(pairdata.columns) != self._columns: - raise ValueError("Wrong dataframe format") - pairdata = pairdata.astype( - dtype={ - "open": "float", - "high": "float", - "low": "float", - "close": "float", - "volume": "float", - } - ) - pairdata = pairdata.reset_index(drop=True) - return pairdata - except ValueError: - raise - except Exception as e: - logger.exception( - f"Error loading data from {filename}. Exception: {e}. Returning empty dataframe." - ) - return pd.DataFrame(columns=self._columns) - - def ohlcv_append( - self, pair: str, timeframe: str, data: pd.DataFrame, candle_type: CandleType - ) -> None: - """ - Append data to existing data structures - :param pair: Pair - :param timeframe: Timeframe this ohlcv data is for - :param data: Data to append. - :param candle_type: Any of the enum CandleType (must match trading mode!) - """ - raise NotImplementedError() - - def _trades_store(self, pair: str, data: pd.DataFrame, trading_mode: TradingMode) -> None: - """ - Store trades data (list of Dicts) to file - :param pair: Pair - used for filename - :param data: Dataframe containing trades - column sequence as in DEFAULT_TRADES_COLUMNS - :param trading_mode: Trading mode to use (used to determine the filename) - """ - key = self._pair_trades_key(pair) - - data.to_hdf( - self._pair_trades_filename(self._datadir, pair, trading_mode), - key=key, - mode="a", - complevel=9, - complib="blosc", - format="table", - data_columns=["timestamp"], - ) - - def trades_append(self, pair: str, data: pd.DataFrame): - """ - Append data to existing files - :param pair: Pair - used for filename - :param data: Dataframe containing trades - column sequence as in DEFAULT_TRADES_COLUMNS - """ - raise NotImplementedError() - - def _trades_load( - self, pair: str, trading_mode: TradingMode, timerange: TimeRange | None = None - ) -> pd.DataFrame: - """ - Load a pair from h5 file. - :param pair: Load trades for this pair - :param trading_mode: Trading mode to use (used to determine the filename) - :param timerange: Timerange to load trades for - currently not implemented - :return: Dataframe containing trades - """ - key = self._pair_trades_key(pair) - filename = self._pair_trades_filename(self._datadir, pair, trading_mode) - - if not filename.exists(): - return pd.DataFrame(columns=DEFAULT_TRADES_COLUMNS) - where = [] - if timerange: - if timerange.starttype == "date": - where.append(f"timestamp >= {timerange.startts * 1e3}") - if timerange.stoptype == "date": - where.append(f"timestamp < {timerange.stopts * 1e3}") - - trades: pd.DataFrame = pd.read_hdf(filename, key=key, mode="r", where=where) - trades[["id", "type"]] = trades[["id", "type"]].replace({np.nan: None}) - return trades - - @classmethod - def _get_file_extension(cls): - return "h5" - - @classmethod - def _pair_ohlcv_key(cls, pair: str, timeframe: str) -> str: - # Escape futures pairs to avoid warnings - pair_esc = pair.replace(":", "_") - return f"{pair_esc}/ohlcv/tf_{timeframe}" - - @classmethod - def _pair_trades_key(cls, pair: str) -> str: - return f"{pair}/trades" diff --git a/freqtrade/data/history/datahandlers/idatahandler.py b/freqtrade/data/history/datahandlers/idatahandler.py index 330620134..e368c1d41 100644 --- a/freqtrade/data/history/datahandlers/idatahandler.py +++ b/freqtrade/data/history/datahandlers/idatahandler.py @@ -23,6 +23,7 @@ from freqtrade.data.converter import ( trim_dataframe, ) from freqtrade.enums import CandleType, TradingMode +from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_seconds @@ -549,9 +550,13 @@ def get_datahandlerclass(datatype: str) -> type[IDataHandler]: return JsonGzDataHandler elif datatype == "hdf5": - from .hdf5datahandler import HDF5DataHandler + raise OperationalException( + "DEPRECATED: The hdf5 dataformat is deprecated and has been removed in 2025.1. " + "Please downgrade to 2024.12 and use the convert-data command to convert your data " + "to a supported format." + "We recommend using the feather format, as it is faster and is more space-efficient." + ) - return HDF5DataHandler elif datatype == "feather": from .featherdatahandler import FeatherDataHandler diff --git a/freqtrade/data/history/history_utils.py b/freqtrade/data/history/history_utils.py index eb93f3ad5..806fd49da 100644 --- a/freqtrade/data/history/history_utils.py +++ b/freqtrade/data/history/history_utils.py @@ -18,7 +18,7 @@ from freqtrade.enums import CandleType, TradingMode from freqtrade.exceptions import OperationalException from freqtrade.exchange import Exchange from freqtrade.plugins.pairlist.pairlist_helpers import dynamic_expand_pairlist -from freqtrade.util import dt_now, dt_ts, format_ms_time +from freqtrade.util import dt_now, dt_ts, format_ms_time, format_ms_time_det from freqtrade.util.migrations import migrate_data from freqtrade.util.progress_tracker import CustomProgress, retrieve_progress_tracker @@ -259,8 +259,8 @@ def _download_pair_history( logger.info( f'Download history data for "{pair}", {timeframe}, ' f"{candle_type} and store in {datadir}. " - f'From {format_ms_time(since_ms) if since_ms else "start"} to ' - f'{format_ms_time(until_ms) if until_ms else "now"}' + f"From {format_ms_time(since_ms) if since_ms else 'start'} to " + f"{format_ms_time(until_ms) if until_ms else 'now'}" ) logger.debug( @@ -431,11 +431,11 @@ def _download_trades_history( # DEFAULT_TRADES_COLUMNS: 0 -> timestamp # DEFAULT_TRADES_COLUMNS: 1 -> id - if not trades.empty and since > 0 and since < trades.iloc[0]["timestamp"]: + if not trades.empty and since > 0 and (since + 1000) < trades.iloc[0]["timestamp"]: # since is before the first trade raise ValueError( - f"Start {format_ms_time(since)} earlier than " - f"available data ({trades.iloc[0]['date']:{DATETIME_PRINT_FORMAT}}). " + f"Start {format_ms_time_det(since)} earlier than " + f"available data ({format_ms_time_det(trades.iloc[0]['timestamp'])}). " f"Please use `--erase` if you'd like to redownload {pair}." ) @@ -443,7 +443,7 @@ def _download_trades_history( if not trades.empty and since < trades.iloc[-1]["timestamp"]: # Reset since to the last available point # - 5 seconds (to ensure we're getting all trades) - since = trades.iloc[-1]["timestamp"] - (5 * 1000) + since = int(trades.iloc[-1]["timestamp"] - (5 * 1000)) logger.info( f"Using last trade date -5s - Downloading trades for {pair} " f"since: {format_ms_time(since)}." @@ -462,7 +462,6 @@ def _download_trades_history( ) logger.info(f"Current Amount of trades: {len(trades)}") - # Default since_ms to 30 days if nothing is given new_trades = exchange.get_historic_trades( pair=pair, since=since, @@ -679,11 +678,17 @@ def download_data( ) else: if not exchange.get_option("ohlcv_has_history", True): - raise OperationalException( - f"Historic klines not available for {exchange.name}. " - "Please use `--dl-trades` instead for this exchange " - "(will unfortunately take a long time)." - ) + if not exchange.get_option("trades_has_history", True): + raise OperationalException( + f"Historic data not available for {exchange.name}. " + f"{exchange.name} does not support downloading trades or ohlcv data." + ) + else: + raise OperationalException( + f"Historic klines not available for {exchange.name}. " + "Please use `--dl-trades` instead for this exchange " + "(will unfortunately take a long time)." + ) migrate_data(config, exchange) pairs_not_available = refresh_backtest_ohlcv_data( exchange, diff --git a/freqtrade/exchange/__init__.py b/freqtrade/exchange/__init__.py index 0d1176b35..01f9d93ed 100644 --- a/freqtrade/exchange/__init__.py +++ b/freqtrade/exchange/__init__.py @@ -10,7 +10,6 @@ from freqtrade.exchange.bitmart import Bitmart from freqtrade.exchange.bitpanda import Bitpanda from freqtrade.exchange.bitvavo import Bitvavo from freqtrade.exchange.bybit import Bybit -from freqtrade.exchange.coinbasepro import Coinbasepro from freqtrade.exchange.cryptocom import Cryptocom from freqtrade.exchange.exchange_utils import ( ROUND_DOWN, diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 74a37b4f6..e9c44a41e 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -29,7 +29,6 @@ class Binance(Exchange): "stop_price_prop": "stopPrice", "stoploss_order_types": {"limit": "stop_loss_limit"}, "order_time_in_force": ["GTC", "FOK", "IOC", "PO"], - "ohlcv_candle_limit": 1000, "trades_pagination": "id", "trades_pagination_arg": "fromId", "trades_has_history": True, @@ -37,6 +36,7 @@ class Binance(Exchange): "ws_enabled": True, } _ft_has_futures: FtHas = { + "funding_fee_candle_limit": 1000, "stoploss_order_types": {"limit": "stop", "market": "stop_market"}, "order_time_in_force": ["GTC", "FOK", "IOC"], "tickers_have_price": False, @@ -48,15 +48,29 @@ class Binance(Exchange): PriceType.MARK: "MARK_PRICE", }, "ws_enabled": False, + "proxy_coin_mapping": { + "BNFCR": "USDC", + "BFUSD": "USDT", + }, } _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [ # TradingMode.SPOT always supported and not required in this list # (TradingMode.MARGIN, MarginMode.CROSS), - # (TradingMode.FUTURES, MarginMode.CROSS), - (TradingMode.FUTURES, MarginMode.ISOLATED) + (TradingMode.FUTURES, MarginMode.CROSS), + (TradingMode.FUTURES, MarginMode.ISOLATED), ] + def get_proxy_coin(self) -> str: + """ + Get the proxy coin for the given coin + Falls back to the stake currency if no proxy coin is found + :return: Proxy coin or stake currency + """ + if self.margin_mode == MarginMode.CROSS: + return self._config.get("proxy_coin", self._config["stake_currency"]) + return self._config["stake_currency"] + def get_tickers( self, symbols: list[str] | None = None, @@ -126,9 +140,10 @@ class Binance(Exchange): :param candle_type: Any of the enum CandleType (must match trading mode!) """ if is_new_pair: - x = self.loop.run_until_complete( - self._async_get_candle_history(pair, timeframe, candle_type, 0) - ) + with self._loop_lock: + x = self.loop.run_until_complete( + self._async_get_candle_history(pair, timeframe, candle_type, 0) + ) if x and x[3] and x[3][0] and x[3][0][0] > since_ms: # Set starting date to first available candle. since_ms = x[3][0][0] @@ -187,16 +202,17 @@ class Binance(Exchange): """ Fastly fetch OHLCV data by leveraging https://data.binance.vision. """ - df = self.loop.run_until_complete( - download_archive_ohlcv( - candle_type=candle_type, - pair=pair, - timeframe=timeframe, - since_ms=since_ms, - until_ms=until_ms, - markets=self.markets, + with self._loop_lock: + df = self.loop.run_until_complete( + download_archive_ohlcv( + candle_type=candle_type, + pair=pair, + timeframe=timeframe, + since_ms=since_ms, + until_ms=until_ms, + markets=self.markets, + ) ) - ) # download the remaining data from rest API if df.empty: @@ -349,7 +365,7 @@ class Binance(Exchange): return {} async def _async_get_trade_history_id_startup( - self, pair: str, since: int | None + self, pair: str, since: int ) -> tuple[list[list], str]: """ override for initial call diff --git a/freqtrade/exchange/binance_leverage_tiers.json b/freqtrade/exchange/binance_leverage_tiers.json index 4bd8a7490..0181243f9 100644 --- a/freqtrade/exchange/binance_leverage_tiers.json +++ b/freqtrade/exchange/binance_leverage_tiers.json @@ -7,10 +7,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -24,10 +24,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -41,10 +41,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -58,10 +58,10 @@ "minNotional": 30000.0, "maxNotional": 60000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "60000", "notionalFloor": "30000", "maintMarginRatio": "0.025", @@ -75,10 +75,10 @@ "minNotional": 60000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "300000", "notionalFloor": "60000", "maintMarginRatio": "0.05", @@ -141,13 +141,13 @@ "symbol": "1000000MOG/USDT:USDT", "currency": "USDT", "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "3000000", + "notionalCap": "2000000", "notionalFloor": "1500000", "maintMarginRatio": "0.5", "cum": "500475.0" @@ -610,10 +610,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -627,10 +627,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -644,10 +644,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -661,10 +661,10 @@ "minNotional": 30000.0, "maxNotional": 60000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "60000", "notionalFloor": "30000", "maintMarginRatio": "0.025", @@ -678,10 +678,10 @@ "minNotional": 60000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "300000", "notionalFloor": "60000", "maintMarginRatio": "0.05", @@ -744,13 +744,13 @@ "symbol": "1000CHEEMS/USDT:USDT", "currency": "USDT", "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "3000000", + "notionalCap": "2000000", "notionalFloor": "1500000", "maintMarginRatio": "0.5", "cum": "500475.0" @@ -1351,10 +1351,10 @@ "minNotional": 0.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -1368,10 +1368,10 @@ "minNotional": 10000.0, "maxNotional": 20000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.015", @@ -1385,10 +1385,10 @@ "minNotional": 20000.0, "maxNotional": 100000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "100000", "notionalFloor": "20000", "maintMarginRatio": "0.02", @@ -1402,10 +1402,10 @@ "minNotional": 100000.0, "maxNotional": 200000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "200000", "notionalFloor": "100000", "maintMarginRatio": "0.025", @@ -1419,10 +1419,10 @@ "minNotional": 200000.0, "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "1000000", "notionalFloor": "200000", "maintMarginRatio": "0.05", @@ -1468,13 +1468,13 @@ "symbol": "1000RATS/USDT:USDT", "currency": "USDT", "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "5000000", + "notionalCap": "3000000", "notionalFloor": "2500000", "maintMarginRatio": "0.25", "cum": "418150.0" @@ -1484,17 +1484,17 @@ "tier": 9.0, "symbol": "1000RATS/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 3000000.0, + "maxNotional": 3500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "3500000", + "notionalFloor": "3000000", "maintMarginRatio": "0.5", - "cum": "1668150.0" + "cum": "1168150.0" } } ], @@ -1606,13 +1606,13 @@ "symbol": "1000SATS/USDT:USDT", "currency": "USDT", "minNotional": 6000000.0, - "maxNotional": 7500000.0, + "maxNotional": 7000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "7500000", + "notionalCap": "7000000", "notionalFloor": "6000000", "maintMarginRatio": "0.125", "cum": "316850.0" @@ -1622,34 +1622,34 @@ "tier": 8.0, "symbol": "1000SATS/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 15000000.0, + "minNotional": 7000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "15000000", - "notionalFloor": "7500000", + "notionalCap": "8000000", + "notionalFloor": "7000000", "maintMarginRatio": "0.25", - "cum": "1254350.0" + "cum": "1191850.0" } }, { "tier": 9.0, "symbol": "1000SATS/USDT:USDT", "currency": "USDT", - "minNotional": 15000000.0, - "maxNotional": 30000000.0, + "minNotional": 8000000.0, + "maxNotional": 9000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "15000000", + "notionalCap": "9000000", + "notionalFloor": "8000000", "maintMarginRatio": "0.5", - "cum": "5004350.0" + "cum": "3191850.0" } } ], @@ -1814,13 +1814,13 @@ "symbol": "1000SHIB/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 10000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.0065, "maxLeverage": 75.0, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", + "notionalCap": "25000", "notionalFloor": "0", "maintMarginRatio": "0.0065", "cum": "0.0" @@ -1830,153 +1830,153 @@ "tier": 2.0, "symbol": "1000SHIB/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.0075, + "minNotional": 25000.0, + "maxNotional": 150000.0, + "maintenanceMarginRate": 0.01, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "25000", - "notionalFloor": "10000", - "maintMarginRatio": "0.0075", - "cum": "10.0" + "notionalCap": "150000", + "notionalFloor": "25000", + "maintMarginRatio": "0.01", + "cum": "87.5" } }, { "tier": 3.0, "symbol": "1000SHIB/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 150000.0, - "maintenanceMarginRate": 0.01, + "minNotional": 150000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.015, "maxLeverage": 40.0, "info": { "bracket": "3", "initialLeverage": "40", - "notionalCap": "150000", - "notionalFloor": "25000", - "maintMarginRatio": "0.01", - "cum": "72.5" + "notionalCap": "200000", + "notionalFloor": "150000", + "maintMarginRatio": "0.015", + "cum": "837.5" } }, { "tier": 4.0, "symbol": "1000SHIB/USDT:USDT", "currency": "USDT", - "minNotional": 150000.0, - "maxNotional": 500000.0, + "minNotional": 200000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "4", "initialLeverage": "25", - "notionalCap": "500000", - "notionalFloor": "150000", + "notionalCap": "800000", + "notionalFloor": "200000", "maintMarginRatio": "0.02", - "cum": "1572.5" + "cum": "1837.5" } }, { "tier": 5.0, "symbol": "1000SHIB/USDT:USDT", "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 800000.0, + "maxNotional": 1600000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "5", "initialLeverage": "20", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "1600000", + "notionalFloor": "800000", "maintMarginRatio": "0.025", - "cum": "4072.5" + "cum": "5837.5" } }, { "tier": 6.0, "symbol": "1000SHIB/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 5000000.0, + "minNotional": 1600000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "6", "initialLeverage": "10", - "notionalCap": "5000000", - "notionalFloor": "1000000", + "notionalCap": "8000000", + "notionalFloor": "1600000", "maintMarginRatio": "0.05", - "cum": "29072.5" + "cum": "45837.5" } }, { "tier": 7.0, "symbol": "1000SHIB/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 8000000.0, + "maxNotional": 16000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "7", "initialLeverage": "5", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "16000000", + "notionalFloor": "8000000", "maintMarginRatio": "0.1", - "cum": "279072.5" + "cum": "445837.5" } }, { "tier": 8.0, "symbol": "1000SHIB/USDT:USDT", "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 12500000.0, + "minNotional": 16000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "8", "initialLeverage": "4", - "notionalCap": "12500000", - "notionalFloor": "10000000", + "notionalCap": "20000000", + "notionalFloor": "16000000", "maintMarginRatio": "0.125", - "cum": "529072.5" + "cum": "845837.5" } }, { "tier": 9.0, "symbol": "1000SHIB/USDT:USDT", "currency": "USDT", - "minNotional": 12500000.0, - "maxNotional": 25000000.0, + "minNotional": 20000000.0, + "maxNotional": 40000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "9", "initialLeverage": "2", - "notionalCap": "25000000", - "notionalFloor": "12500000", + "notionalCap": "40000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.25", - "cum": "2091572.5" + "cum": "3345837.5" } }, { "tier": 10.0, "symbol": "1000SHIB/USDT:USDT", "currency": "USDT", - "minNotional": 25000000.0, - "maxNotional": 50000000.0, + "minNotional": 40000000.0, + "maxNotional": 80000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "10", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "25000000", + "notionalCap": "80000000", + "notionalFloor": "40000000", "maintMarginRatio": "0.5", - "cum": "8341572.5" + "cum": "13345837.5" } } ], @@ -1988,10 +1988,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -2005,10 +2005,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -2022,10 +2022,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -2039,10 +2039,10 @@ "minNotional": 30000.0, "maxNotional": 60000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "60000", "notionalFloor": "30000", "maintMarginRatio": "0.025", @@ -2056,10 +2056,10 @@ "minNotional": 60000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "300000", "notionalFloor": "60000", "maintMarginRatio": "0.05", @@ -2105,13 +2105,13 @@ "symbol": "1000WHY/USDT:USDT", "currency": "USDT", "minNotional": 750000.0, - "maxNotional": 1500000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "1500000", + "notionalCap": "1000000", "notionalFloor": "750000", "maintMarginRatio": "0.25", "cum": "125475.0" @@ -2121,17 +2121,17 @@ "tier": 9.0, "symbol": "1000WHY/USDT:USDT", "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "minNotional": 1000000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "3000000", - "notionalFloor": "1500000", + "notionalCap": "1500000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", - "cum": "500475.0" + "cum": "375475.0" } } ], @@ -2143,10 +2143,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -2160,10 +2160,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -2177,10 +2177,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -2194,10 +2194,10 @@ "minNotional": 30000.0, "maxNotional": 60000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "60000", "notionalFloor": "30000", "maintMarginRatio": "0.025", @@ -2211,10 +2211,10 @@ "minNotional": 60000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "300000", "notionalFloor": "60000", "maintMarginRatio": "0.05", @@ -2243,13 +2243,13 @@ "symbol": "1000X/USDT:USDT", "currency": "USDT", "minNotional": 600000.0, - "maxNotional": 750000.0, + "maxNotional": 700000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "750000", + "notionalCap": "700000", "notionalFloor": "600000", "maintMarginRatio": "0.125", "cum": "31725.0" @@ -2259,34 +2259,34 @@ "tier": 8.0, "symbol": "1000X/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1500000.0, + "minNotional": 700000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "1500000", - "notionalFloor": "750000", + "notionalCap": "800000", + "notionalFloor": "700000", "maintMarginRatio": "0.25", - "cum": "125475.0" + "cum": "119225.0" } }, { "tier": 9.0, "symbol": "1000X/USDT:USDT", "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "minNotional": 800000.0, + "maxNotional": 900000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "3000000", - "notionalFloor": "1500000", + "notionalCap": "900000", + "notionalFloor": "800000", "maintMarginRatio": "0.5", - "cum": "500475.0" + "cum": "319225.0" } } ], @@ -3056,10 +3056,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.015", @@ -3073,10 +3073,10 @@ "minNotional": 5000.0, "maxNotional": 20000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "20000", "notionalFloor": "5000", "maintMarginRatio": "0.02", @@ -3090,10 +3090,10 @@ "minNotional": 20000.0, "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "3", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "25000", "notionalFloor": "20000", "maintMarginRatio": "0.025", @@ -3107,10 +3107,10 @@ "minNotional": 25000.0, "maxNotional": 200000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "4", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "200000", "notionalFloor": "25000", "maintMarginRatio": "0.05", @@ -3173,13 +3173,13 @@ "symbol": "ACH/USDT:USDT", "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "2000000", + "notionalCap": "1500000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "333250.0" @@ -3311,13 +3311,13 @@ "symbol": "ACT/USDT:USDT", "currency": "USDT", "minNotional": 10000000.0, - "maxNotional": 20000000.0, + "maxNotional": 12000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "20000000", + "notionalCap": "12000000", "notionalFloor": "10000000", "maintMarginRatio": "0.25", "cum": "1672450.0" @@ -3327,17 +3327,17 @@ "tier": 9.0, "symbol": "ACT/USDT:USDT", "currency": "USDT", - "minNotional": 20000000.0, - "maxNotional": 40000000.0, + "minNotional": 12000000.0, + "maxNotional": 14000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "40000000", - "notionalFloor": "20000000", + "notionalCap": "14000000", + "notionalFloor": "12000000", "maintMarginRatio": "0.5", - "cum": "6672450.0" + "cum": "4672450.0" } } ], @@ -3831,10 +3831,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -3848,10 +3848,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -3865,10 +3865,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -3882,10 +3882,10 @@ "minNotional": 30000.0, "maxNotional": 60000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "60000", "notionalFloor": "30000", "maintMarginRatio": "0.025", @@ -3899,10 +3899,10 @@ "minNotional": 60000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "300000", "notionalFloor": "60000", "maintMarginRatio": "0.05", @@ -3965,13 +3965,13 @@ "symbol": "AERO/USDT:USDT", "currency": "USDT", "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "3000000", + "notionalCap": "2000000", "notionalFloor": "1500000", "maintMarginRatio": "0.5", "cum": "500475.0" @@ -4243,15 +4243,15 @@ "symbol": "AGLD/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "25", + "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.01", "cum": "0.0" } }, @@ -4259,16 +4259,16 @@ "tier": 2.0, "symbol": "AGLD/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.015, "maxLeverage": 20.0, "info": { "bracket": "2", "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "5000", - "maintMarginRatio": "0.025", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", "cum": "50.0" } }, @@ -4276,51 +4276,51 @@ "tier": 3.0, "symbol": "AGLD/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 200000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 20000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "10", - "notionalCap": "200000", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "675.0" + "initialLeverage": "15", + "notionalCap": "100000", + "notionalFloor": "20000", + "maintMarginRatio": "0.02", + "cum": "150.0" } }, { "tier": 4.0, "symbol": "AGLD/USDT:USDT", "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 100000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "5", - "notionalCap": "500000", - "notionalFloor": "200000", - "maintMarginRatio": "0.1", - "cum": "10675.0" + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "100000", + "maintMarginRatio": "0.025", + "cum": "650.0" } }, { "tier": 5.0, "symbol": "AGLD/USDT:USDT", "currency": "USDT", - "minNotional": 500000.0, + "minNotional": 200000.0, "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "4", + "initialLeverage": "8", "notionalCap": "1000000", - "notionalFloor": "500000", - "maintMarginRatio": "0.125", - "cum": "23175.0" + "notionalFloor": "200000", + "maintMarginRatio": "0.05", + "cum": "5650.0" } }, { @@ -4328,33 +4328,67 @@ "symbol": "AGLD/USDT:USDT", "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": "148175.0" + "maintMarginRatio": "0.1", + "cum": "55650.0" } }, { "tier": 7.0, "symbol": "AGLD/USDT:USDT", "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, + "symbol": "AGLD/USDT:USDT", + "currency": "USDT", + "minNotional": 2500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "3000000", + "notionalFloor": "2500000", + "maintMarginRatio": "0.25", + "cum": "418150.0" + } + }, + { + "tier": 9.0, + "symbol": "AGLD/USDT:USDT", + "currency": "USDT", "minNotional": 3000000.0, - "maxNotional": 5000000.0, + "maxNotional": 3500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "7", + "bracket": "9", "initialLeverage": "1", - "notionalCap": "5000000", + "notionalCap": "3500000", "notionalFloor": "3000000", "maintMarginRatio": "0.5", - "cum": "898175.0" + "cum": "1168150.0" } } ], @@ -4496,6 +4530,316 @@ } } ], + "AI16Z/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "AI16Z/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "AI16Z/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 16000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "16000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "AI16Z/USDT:USDT", + "currency": "USDT", + "minNotional": 16000.0, + "maxNotional": 80000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 15.0, + "info": { + "bracket": "3", + "initialLeverage": "15", + "notionalCap": "80000", + "notionalFloor": "16000", + "maintMarginRatio": "0.02", + "cum": "105.0" + } + }, + { + "tier": 4.0, + "symbol": "AI16Z/USDT:USDT", + "currency": "USDT", + "minNotional": 80000.0, + "maxNotional": 160000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "160000", + "notionalFloor": "80000", + "maintMarginRatio": "0.025", + "cum": "505.0" + } + }, + { + "tier": 5.0, + "symbol": "AI16Z/USDT:USDT", + "currency": "USDT", + "minNotional": 160000.0, + "maxNotional": 800000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, + "info": { + "bracket": "5", + "initialLeverage": "8", + "notionalCap": "800000", + "notionalFloor": "160000", + "maintMarginRatio": "0.05", + "cum": "4505.0" + } + }, + { + "tier": 6.0, + "symbol": "AI16Z/USDT:USDT", + "currency": "USDT", + "minNotional": 800000.0, + "maxNotional": 1600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "1600000", + "notionalFloor": "800000", + "maintMarginRatio": "0.1", + "cum": "44505.0" + } + }, + { + "tier": 7.0, + "symbol": "AI16Z/USDT:USDT", + "currency": "USDT", + "minNotional": 1600000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "2000000", + "notionalFloor": "1600000", + "maintMarginRatio": "0.125", + "cum": "84505.0" + } + }, + { + "tier": 8.0, + "symbol": "AI16Z/USDT:USDT", + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "4000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.25", + "cum": "334505.0" + } + }, + { + "tier": 9.0, + "symbol": "AI16Z/USDT:USDT", + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 4500000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "4500000", + "notionalFloor": "4000000", + "maintMarginRatio": "0.5", + "cum": "1334505.0" + } + } + ], + "AIXBT/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "AIXBT/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 10.0, + "info": { + "bracket": "1", + "initialLeverage": "10", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "AIXBT/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 16000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 9.0, + "info": { + "bracket": "2", + "initialLeverage": "9", + "notionalCap": "16000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "AIXBT/USDT:USDT", + "currency": "USDT", + "minNotional": 16000.0, + "maxNotional": 80000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 8.0, + "info": { + "bracket": "3", + "initialLeverage": "8", + "notionalCap": "80000", + "notionalFloor": "16000", + "maintMarginRatio": "0.02", + "cum": "105.0" + } + }, + { + "tier": 4.0, + "symbol": "AIXBT/USDT:USDT", + "currency": "USDT", + "minNotional": 80000.0, + "maxNotional": 160000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 7.0, + "info": { + "bracket": "4", + "initialLeverage": "7", + "notionalCap": "160000", + "notionalFloor": "80000", + "maintMarginRatio": "0.025", + "cum": "505.0" + } + }, + { + "tier": 5.0, + "symbol": "AIXBT/USDT:USDT", + "currency": "USDT", + "minNotional": 160000.0, + "maxNotional": 800000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 6.0, + "info": { + "bracket": "5", + "initialLeverage": "6", + "notionalCap": "800000", + "notionalFloor": "160000", + "maintMarginRatio": "0.05", + "cum": "4505.0" + } + }, + { + "tier": 6.0, + "symbol": "AIXBT/USDT:USDT", + "currency": "USDT", + "minNotional": 800000.0, + "maxNotional": 1600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "1600000", + "notionalFloor": "800000", + "maintMarginRatio": "0.1", + "cum": "44505.0" + } + }, + { + "tier": 7.0, + "symbol": "AIXBT/USDT:USDT", + "currency": "USDT", + "minNotional": 1600000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "2000000", + "notionalFloor": "1600000", + "maintMarginRatio": "0.125", + "cum": "84505.0" + } + }, + { + "tier": 8.0, + "symbol": "AIXBT/USDT:USDT", + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "4000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.25", + "cum": "334505.0" + } + }, + { + "tier": 9.0, + "symbol": "AIXBT/USDT:USDT", + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 4100000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "4100000", + "notionalFloor": "4000000", + "maintMarginRatio": "0.5", + "cum": "1334505.0" + } + } + ], "AKT/USDT:USDT": [ { "tier": 1.0, @@ -4651,6 +4995,161 @@ } } ], + "ALCH/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "ALCH/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "ALCH/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "ALCH/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 15.0, + "info": { + "bracket": "3", + "initialLeverage": "15", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "ALCH/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "ALCH/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, + "info": { + "bracket": "5", + "initialLeverage": "8", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "ALCH/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "ALCH/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "ALCH/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "ALCH/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], "ALGO/USDT:USDT": [ { "tier": 1.0, @@ -4952,10 +5451,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 20.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "20", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -4969,10 +5468,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 15.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "15", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -4986,10 +5485,10 @@ "minNotional": 10000.0, "maxNotional": 20000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 10.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "10", "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -5003,10 +5502,10 @@ "minNotional": 20000.0, "maxNotional": 40000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 8.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "8", "notionalCap": "40000", "notionalFloor": "20000", "maintMarginRatio": "0.025", @@ -5020,10 +5519,10 @@ "minNotional": 40000.0, "maxNotional": 200000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 6.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "6", "notionalCap": "200000", "notionalFloor": "40000", "maintMarginRatio": "0.05", @@ -5052,13 +5551,13 @@ "symbol": "ALPACA/USDT:USDT", "currency": "USDT", "minNotional": 400000.0, - "maxNotional": 500000.0, + "maxNotional": 450000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "500000", + "notionalCap": "450000", "notionalFloor": "400000", "maintMarginRatio": "0.125", "cum": "21175.0" @@ -5068,34 +5567,34 @@ "tier": 8.0, "symbol": "ALPACA/USDT:USDT", "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 450000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "500000", + "notionalFloor": "450000", "maintMarginRatio": "0.25", - "cum": "83675.0" + "cum": "77425.0" } }, { "tier": 9.0, "symbol": "ALPACA/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 500000.0, + "maxNotional": 550000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "550000", + "notionalFloor": "500000", "maintMarginRatio": "0.5", - "cum": "333675.0" + "cum": "202425.0" } } ], @@ -5400,10 +5899,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "1", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.02", @@ -5417,10 +5916,10 @@ "minNotional": 5000.0, "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 15.0, + "maxLeverage": 8.0, "info": { "bracket": "2", - "initialLeverage": "15", + "initialLeverage": "8", "notionalCap": "25000", "notionalFloor": "5000", "maintMarginRatio": "0.025", @@ -5434,10 +5933,10 @@ "minNotional": 25000.0, "maxNotional": 200000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 6.0, "info": { "bracket": "3", - "initialLeverage": "10", + "initialLeverage": "6", "notionalCap": "200000", "notionalFloor": "25000", "maintMarginRatio": "0.05", @@ -5500,19 +5999,174 @@ "symbol": "AMB/USDT:USDT", "currency": "USDT", "minNotional": 3000000.0, - "maxNotional": 5000000.0, + "maxNotional": 3500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "5000000", + "notionalCap": "3500000", "notionalFloor": "3000000", "maintMarginRatio": "0.5", "cum": "898150.0" } } ], + "ANIME/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "ANIME/USDT:USDT", + "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, + "symbol": "ANIME/USDT:USDT", + "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, + "symbol": "ANIME/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "ANIME/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "ANIME/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "ANIME/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "ANIME/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "ANIME/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "ANIME/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], "ANKR/USDT:USDT": [ { "tier": 1.0, @@ -5725,13 +6379,13 @@ "symbol": "APE/USDT:USDT", "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 5000000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "6", "initialLeverage": "10", - "notionalCap": "5000000", + "notionalCap": "3000000", "notionalFloor": "1000000", "maintMarginRatio": "0.05", "cum": "28337.5" @@ -5741,68 +6395,68 @@ "tier": 7.0, "symbol": "APE/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 3000000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "7", "initialLeverage": "5", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "4000000", + "notionalFloor": "3000000", "maintMarginRatio": "0.1", - "cum": "278337.5" + "cum": "178337.5" } }, { "tier": 8.0, "symbol": "APE/USDT:USDT", "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 12500000.0, + "minNotional": 4000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "8", "initialLeverage": "4", - "notionalCap": "12500000", - "notionalFloor": "10000000", + "notionalCap": "5000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.125", - "cum": "528337.5" + "cum": "278337.5" } }, { "tier": 9.0, "symbol": "APE/USDT:USDT", "currency": "USDT", - "minNotional": 12500000.0, - "maxNotional": 25000000.0, + "minNotional": 5000000.0, + "maxNotional": 6000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "9", "initialLeverage": "2", - "notionalCap": "25000000", - "notionalFloor": "12500000", + "notionalCap": "6000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.25", - "cum": "2090837.5" + "cum": "903337.5" } }, { "tier": 10.0, "symbol": "APE/USDT:USDT", "currency": "USDT", - "minNotional": 25000000.0, - "maxNotional": 50000000.0, + "minNotional": 6000000.0, + "maxNotional": 7000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "10", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "25000000", + "notionalCap": "7000000", + "notionalFloor": "6000000", "maintMarginRatio": "0.5", - "cum": "8340837.5" + "cum": "2403337.5" } } ], @@ -6400,10 +7054,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.006, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.006", @@ -6417,10 +7071,10 @@ "minNotional": 5000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "50000", "notionalFloor": "5000", "maintMarginRatio": "0.01", @@ -6434,10 +7088,10 @@ "minNotional": 50000.0, "maxNotional": 100000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 40.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "40", + "initialLeverage": "15", "notionalCap": "100000", "notionalFloor": "50000", "maintMarginRatio": "0.015", @@ -6451,10 +7105,10 @@ "minNotional": 100000.0, "maxNotional": 500000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "25", + "initialLeverage": "10", "notionalCap": "500000", "notionalFloor": "100000", "maintMarginRatio": "0.02", @@ -6468,10 +7122,10 @@ "minNotional": 500000.0, "maxNotional": 1000000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "20", + "initialLeverage": "8", "notionalCap": "1000000", "notionalFloor": "500000", "maintMarginRatio": "0.025", @@ -6485,10 +7139,10 @@ "minNotional": 1000000.0, "maxNotional": 5000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 6.0, "info": { "bracket": "6", - "initialLeverage": "10", + "initialLeverage": "6", "notionalCap": "5000000", "notionalFloor": "1000000", "maintMarginRatio": "0.05", @@ -6500,13 +7154,13 @@ "symbol": "ARB/USDT:USDT", "currency": "USDT", "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "maxNotional": 6000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "7", "initialLeverage": "5", - "notionalCap": "10000000", + "notionalCap": "6000000", "notionalFloor": "5000000", "maintMarginRatio": "0.1", "cum": "278270.0" @@ -6516,51 +7170,172 @@ "tier": 8.0, "symbol": "ARB/USDT:USDT", "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 12500000.0, + "minNotional": 6000000.0, + "maxNotional": 7000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "8", "initialLeverage": "4", - "notionalCap": "12500000", - "notionalFloor": "10000000", + "notionalCap": "7000000", + "notionalFloor": "6000000", "maintMarginRatio": "0.125", - "cum": "528270.0" + "cum": "428270.0" } }, { "tier": 9.0, "symbol": "ARB/USDT:USDT", "currency": "USDT", - "minNotional": 12500000.0, - "maxNotional": 25000000.0, + "minNotional": 7000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "9", "initialLeverage": "2", - "notionalCap": "25000000", - "notionalFloor": "12500000", + "notionalCap": "8000000", + "notionalFloor": "7000000", "maintMarginRatio": "0.25", - "cum": "2090770.0" + "cum": "1303270.0" } }, { "tier": 10.0, "symbol": "ARB/USDT:USDT", "currency": "USDT", - "minNotional": 25000000.0, - "maxNotional": 50000000.0, + "minNotional": 8000000.0, + "maxNotional": 9000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "10", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "25000000", + "notionalCap": "9000000", + "notionalFloor": "8000000", "maintMarginRatio": "0.5", - "cum": "8340770.0" + "cum": "3303270.0" + } + } + ], + "ARC/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "ARC/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.02", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "ARC/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "ARC/USDT:USDT", + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "650.0" + } + }, + { + "tier": 4.0, + "symbol": "ARC/USDT:USDT", + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10650.0" + } + }, + { + "tier": 5.0, + "symbol": "ARC/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "23150.0" + } + }, + { + "tier": 6.0, + "symbol": "ARC/USDT:USDT", + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.25", + "cum": "148150.0" + } + }, + { + "tier": 7.0, + "symbol": "ARC/USDT:USDT", + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.5", + "cum": "648150.0" } } ], @@ -6572,10 +7347,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 10.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "10", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -6589,10 +7364,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 9.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "9", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -6606,10 +7381,10 @@ "minNotional": 10000.0, "maxNotional": 20000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 8.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "8", "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -6623,10 +7398,10 @@ "minNotional": 20000.0, "maxNotional": 40000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 7.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "7", "notionalCap": "40000", "notionalFloor": "20000", "maintMarginRatio": "0.025", @@ -6640,10 +7415,10 @@ "minNotional": 40000.0, "maxNotional": 200000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 6.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "6", "notionalCap": "200000", "notionalFloor": "40000", "maintMarginRatio": "0.05", @@ -6706,13 +7481,13 @@ "symbol": "ARK/USDT:USDT", "currency": "USDT", "minNotional": 3000000.0, - "maxNotional": 5000000.0, + "maxNotional": 3500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "5000000", + "notionalCap": "3500000", "notionalFloor": "3000000", "maintMarginRatio": "0.5", "cum": "898675.0" @@ -7547,6 +8322,282 @@ } } ], + "AVA/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "AVA/USDT:USDT", + "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, + "symbol": "AVA/USDT:USDT", + "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, + "symbol": "AVA/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "AVA/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "AVA/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "AVA/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "AVA/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "AVA/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "AVA/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], + "AVAAI/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "AVAAI/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.02", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "AVAAI/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "AVAAI/USDT:USDT", + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "650.0" + } + }, + { + "tier": 4.0, + "symbol": "AVAAI/USDT:USDT", + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10650.0" + } + }, + { + "tier": 5.0, + "symbol": "AVAAI/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "1000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "23150.0" + } + }, + { + "tier": 6.0, + "symbol": "AVAAI/USDT:USDT", + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.25", + "cum": "148150.0" + } + }, + { + "tier": 7.0, + "symbol": "AVAAI/USDT:USDT", + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.5", + "cum": "648150.0" + } + } + ], "AVAX/USDC:USDC": [ { "tier": 1.0, @@ -8468,10 +9519,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 25.0, + "maxLeverage": 10.0, "info": { "bracket": "1", - "initialLeverage": "25", + "initialLeverage": "10", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -8485,10 +9536,10 @@ "minNotional": 5000.0, "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 8.0, "info": { "bracket": "2", - "initialLeverage": "20", + "initialLeverage": "8", "notionalCap": "25000", "notionalFloor": "5000", "maintMarginRatio": "0.025", @@ -8502,10 +9553,10 @@ "minNotional": 25000.0, "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 6.0, "info": { "bracket": "3", - "initialLeverage": "10", + "initialLeverage": "6", "notionalCap": "100000", "notionalFloor": "25000", "maintMarginRatio": "0.05", @@ -8551,13 +9602,13 @@ "symbol": "BAL/USDT:USDT", "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 5000000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "5000000", + "notionalCap": "1500000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386950.0" @@ -8572,10 +9623,10 @@ "minNotional": 0.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 10.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "10", "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -8589,10 +9640,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 9.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "9", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.015", @@ -8606,10 +9657,10 @@ "minNotional": 30000.0, "maxNotional": 150000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 8.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "8", "notionalCap": "150000", "notionalFloor": "30000", "maintMarginRatio": "0.02", @@ -8623,10 +9674,10 @@ "minNotional": 150000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 7.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "7", "notionalCap": "300000", "notionalFloor": "150000", "maintMarginRatio": "0.025", @@ -8640,10 +9691,10 @@ "minNotional": 300000.0, "maxNotional": 1500000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 6.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "6", "notionalCap": "1500000", "notionalFloor": "300000", "maintMarginRatio": "0.05", @@ -8672,13 +9723,13 @@ "symbol": "BAN/USDT:USDT", "currency": "USDT", "minNotional": 3000000.0, - "maxNotional": 3750000.0, + "maxNotional": 3500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "3750000", + "notionalCap": "3500000", "notionalFloor": "3000000", "maintMarginRatio": "0.125", "cum": "158450.0" @@ -8688,34 +9739,34 @@ "tier": 8.0, "symbol": "BAN/USDT:USDT", "currency": "USDT", - "minNotional": 3750000.0, - "maxNotional": 7500000.0, + "minNotional": 3500000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "7500000", - "notionalFloor": "3750000", + "notionalCap": "4000000", + "notionalFloor": "3500000", "maintMarginRatio": "0.25", - "cum": "627200.0" + "cum": "595950.0" } }, { "tier": 9.0, "symbol": "BAN/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 15000000.0, + "minNotional": 4000000.0, + "maxNotional": 4100000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "15000000", - "notionalFloor": "7500000", + "notionalCap": "4100000", + "notionalFloor": "4000000", "maintMarginRatio": "0.5", - "cum": "2502200.0" + "cum": "1595950.0" } } ], @@ -9466,13 +10517,13 @@ "symbol": "BCH/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.005, "maxLeverage": 75.0, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "5000", + "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.005", "cum": "0.0" @@ -9482,153 +10533,153 @@ "tier": 2.0, "symbol": "BCH/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.0065, + "minNotional": 10000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.01, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "10000", - "notionalFloor": "5000", - "maintMarginRatio": "0.0065", - "cum": "7.5" + "notionalCap": "100000", + "notionalFloor": "10000", + "maintMarginRatio": "0.01", + "cum": "50.0" } }, { "tier": 3.0, "symbol": "BCH/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.01, + "minNotional": 100000.0, + "maxNotional": 160000.0, + "maintenanceMarginRate": 0.015, "maxLeverage": 40.0, "info": { "bracket": "3", "initialLeverage": "40", - "notionalCap": "100000", - "notionalFloor": "10000", - "maintMarginRatio": "0.01", - "cum": "42.5" + "notionalCap": "160000", + "notionalFloor": "100000", + "maintMarginRatio": "0.015", + "cum": "550.0" } }, { "tier": 4.0, "symbol": "BCH/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 500000.0, + "minNotional": 160000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "4", "initialLeverage": "25", - "notionalCap": "500000", - "notionalFloor": "100000", + "notionalCap": "800000", + "notionalFloor": "160000", "maintMarginRatio": "0.02", - "cum": "1042.5" + "cum": "1350.0" } }, { "tier": 5.0, "symbol": "BCH/USDT:USDT", "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 800000.0, + "maxNotional": 1600000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "5", "initialLeverage": "20", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "1600000", + "notionalFloor": "800000", "maintMarginRatio": "0.025", - "cum": "3542.5" + "cum": "5350.0" } }, { "tier": 6.0, "symbol": "BCH/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 5000000.0, + "minNotional": 1600000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "6", "initialLeverage": "10", - "notionalCap": "5000000", - "notionalFloor": "1000000", + "notionalCap": "8000000", + "notionalFloor": "1600000", "maintMarginRatio": "0.05", - "cum": "28542.5" + "cum": "45350.0" } }, { "tier": 7.0, "symbol": "BCH/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 8000000.0, + "maxNotional": 16000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "7", "initialLeverage": "5", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "16000000", + "notionalFloor": "8000000", "maintMarginRatio": "0.1", - "cum": "278542.5" + "cum": "445350.0" } }, { "tier": 8.0, "symbol": "BCH/USDT:USDT", "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 15000000.0, + "minNotional": 16000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "8", "initialLeverage": "4", - "notionalCap": "15000000", - "notionalFloor": "10000000", + "notionalCap": "20000000", + "notionalFloor": "16000000", "maintMarginRatio": "0.125", - "cum": "528542.5" + "cum": "845350.0" } }, { "tier": 9.0, "symbol": "BCH/USDT:USDT", "currency": "USDT", - "minNotional": 15000000.0, - "maxNotional": 25000000.0, + "minNotional": 20000000.0, + "maxNotional": 40000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "9", "initialLeverage": "2", - "notionalCap": "25000000", - "notionalFloor": "15000000", + "notionalCap": "40000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.25", - "cum": "2403542.5" + "cum": "3345350.0" } }, { "tier": 10.0, "symbol": "BCH/USDT:USDT", "currency": "USDT", - "minNotional": 25000000.0, - "maxNotional": 50000000.0, + "minNotional": 40000000.0, + "maxNotional": 80000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "10", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "25000000", + "notionalCap": "80000000", + "notionalFloor": "40000000", "maintMarginRatio": "0.5", - "cum": "8653542.5" + "cum": "13345350.0" } } ], @@ -10218,6 +11269,161 @@ } } ], + "BIO/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "BIO/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "BIO/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "BIO/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 15.0, + "info": { + "bracket": "3", + "initialLeverage": "15", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "BIO/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "BIO/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, + "info": { + "bracket": "5", + "initialLeverage": "8", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "BIO/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "BIO/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "BIO/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "BIO/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], "BLUR/USDT:USDT": [ { "tier": 1.0, @@ -11502,10 +12708,10 @@ "minNotional": 0.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -11519,10 +12725,10 @@ "minNotional": 10000.0, "maxNotional": 20000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.015", @@ -11536,10 +12742,10 @@ "minNotional": 20000.0, "maxNotional": 100000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "100000", "notionalFloor": "20000", "maintMarginRatio": "0.02", @@ -11553,10 +12759,10 @@ "minNotional": 100000.0, "maxNotional": 200000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "200000", "notionalFloor": "100000", "maintMarginRatio": "0.025", @@ -11570,10 +12776,10 @@ "minNotional": 200000.0, "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "1000000", "notionalFloor": "200000", "maintMarginRatio": "0.05", @@ -11585,13 +12791,13 @@ "symbol": "BRETT/USDT:USDT", "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "2000000", + "notionalCap": "1500000", "notionalFloor": "1000000", "maintMarginRatio": "0.1", "cum": "55650.0" @@ -11601,51 +12807,51 @@ "tier": 7.0, "symbol": "BRETT/USDT:USDT", "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 2500000.0, + "minNotional": 1500000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "2500000", - "notionalFloor": "2000000", + "notionalCap": "2000000", + "notionalFloor": "1500000", "maintMarginRatio": "0.125", - "cum": "105650.0" + "cum": "93150.0" } }, { "tier": 8.0, "symbol": "BRETT/USDT:USDT", "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "2500000", + "notionalFloor": "2000000", "maintMarginRatio": "0.25", - "cum": "418150.0" + "cum": "343150.0" } }, { "tier": 9.0, "symbol": "BRETT/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 2500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "3000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "1668150.0" + "cum": "968150.0" } } ], @@ -12303,144 +13509,6 @@ } } ], - "BTC/USDT:USDT-241227": [ - { - "tier": 1.0, - "symbol": "BTC/USDT:USDT-241227", - "currency": "USDT", - "minNotional": 0.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50.0, - "info": { - "bracket": "1", - "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2.0, - "symbol": "BTC/USDT:USDT-241227", - "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 375000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, - "info": { - "bracket": "2", - "initialLeverage": "25", - "notionalCap": "375000", - "notionalFloor": "50000", - "maintMarginRatio": "0.02", - "cum": "500.0" - } - }, - { - "tier": 3.0, - "symbol": "BTC/USDT:USDT-241227", - "currency": "USDT", - "minNotional": 375000.0, - "maxNotional": 2000000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, - "info": { - "bracket": "3", - "initialLeverage": "10", - "notionalCap": "2000000", - "notionalFloor": "375000", - "maintMarginRatio": "0.05", - "cum": "11750.0" - } - }, - { - "tier": 4.0, - "symbol": "BTC/USDT:USDT-241227", - "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 4000000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, - "info": { - "bracket": "4", - "initialLeverage": "5", - "notionalCap": "4000000", - "notionalFloor": "2000000", - "maintMarginRatio": "0.1", - "cum": "111750.0" - } - }, - { - "tier": 5.0, - "symbol": "BTC/USDT:USDT-241227", - "currency": "USDT", - "minNotional": 4000000.0, - "maxNotional": 10000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": "5", - "initialLeverage": "4", - "notionalCap": "10000000", - "notionalFloor": "4000000", - "maintMarginRatio": "0.125", - "cum": "211750.0" - } - }, - { - "tier": 6.0, - "symbol": "BTC/USDT:USDT-241227", - "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 20000000.0, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3.0, - "info": { - "bracket": "6", - "initialLeverage": "3", - "notionalCap": "20000000", - "notionalFloor": "10000000", - "maintMarginRatio": "0.15", - "cum": "461750.0" - } - }, - { - "tier": 7.0, - "symbol": "BTC/USDT:USDT-241227", - "currency": "USDT", - "minNotional": 20000000.0, - "maxNotional": 40000000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": "7", - "initialLeverage": "2", - "notionalCap": "40000000", - "notionalFloor": "20000000", - "maintMarginRatio": "0.25", - "cum": "2461750.0" - } - }, - { - "tier": 8.0, - "symbol": "BTC/USDT:USDT-241227", - "currency": "USDT", - "minNotional": 40000000.0, - "maxNotional": 120000000.0, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1.0, - "info": { - "bracket": "8", - "initialLeverage": "1", - "notionalCap": "120000000", - "notionalFloor": "40000000", - "maintMarginRatio": "0.5", - "cum": "12461750.0" - } - } - ], "BTC/USDT:USDT-250328": [ { "tier": 1.0, @@ -12579,6 +13647,144 @@ } } ], + "BTC/USDT:USDT-250627": [ + { + "tier": 1.0, + "symbol": "BTC/USDT:USDT-250627", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "50000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "BTC/USDT:USDT-250627", + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 375000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "2", + "initialLeverage": "25", + "notionalCap": "375000", + "notionalFloor": "50000", + "maintMarginRatio": "0.02", + "cum": "500.0" + } + }, + { + "tier": 3.0, + "symbol": "BTC/USDT:USDT-250627", + "currency": "USDT", + "minNotional": 375000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "2000000", + "notionalFloor": "375000", + "maintMarginRatio": "0.05", + "cum": "11750.0" + } + }, + { + "tier": 4.0, + "symbol": "BTC/USDT:USDT-250627", + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "4000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.1", + "cum": "111750.0" + } + }, + { + "tier": 5.0, + "symbol": "BTC/USDT:USDT-250627", + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "10000000", + "notionalFloor": "4000000", + "maintMarginRatio": "0.125", + "cum": "211750.0" + } + }, + { + "tier": 6.0, + "symbol": "BTC/USDT:USDT-250627", + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "6", + "initialLeverage": "3", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.15", + "cum": "461750.0" + } + }, + { + "tier": 7.0, + "symbol": "BTC/USDT:USDT-250627", + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 40000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "40000000", + "notionalFloor": "20000000", + "maintMarginRatio": "0.25", + "cum": "2461750.0" + } + }, + { + "tier": 8.0, + "symbol": "BTC/USDT:USDT-250627", + "currency": "USDT", + "minNotional": 40000000.0, + "maxNotional": 120000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "8", + "initialLeverage": "1", + "notionalCap": "120000000", + "notionalFloor": "40000000", + "maintMarginRatio": "0.5", + "cum": "12461750.0" + } + } + ], "BTCDOM/USDT:USDT": [ { "tier": 1.0, @@ -12914,13 +14120,13 @@ "symbol": "CAKE/USDT:USDT", "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" @@ -12930,136 +14136,136 @@ "tier": 2.0, "symbol": "CAKE/USDT:USDT", "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, "symbol": "CAKE/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 50000.0, + "minNotional": 20000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "50000", - "notionalFloor": "10000", + "notionalCap": "100000", + "notionalFloor": "20000", "maintMarginRatio": "0.02", - "cum": "75.0" + "cum": "150.0" } }, { "tier": 4.0, "symbol": "CAKE/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, + "minNotional": 100000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "4", "initialLeverage": "20", - "notionalCap": "100000", - "notionalFloor": "50000", + "notionalCap": "200000", + "notionalFloor": "100000", "maintMarginRatio": "0.025", - "cum": "325.0" + "cum": "650.0" } }, { "tier": 5.0, "symbol": "CAKE/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 500000.0, + "minNotional": 200000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "500000", - "notionalFloor": "100000", + "notionalCap": "1000000", + "notionalFloor": "200000", "maintMarginRatio": "0.05", - "cum": "2825.0" + "cum": "5650.0" } }, { "tier": 6.0, "symbol": "CAKE/USDT:USDT", "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "2000000", + "notionalFloor": "1000000", "maintMarginRatio": "0.1", - "cum": "27825.0" + "cum": "55650.0" } }, { "tier": 7.0, "symbol": "CAKE/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 1250000.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "1250000", - "notionalFloor": "1000000", + "notionalCap": "2500000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", - "cum": "52825.0" + "cum": "105650.0" } }, { "tier": 8.0, "symbol": "CAKE/USDT:USDT", "currency": "USDT", - "minNotional": 1250000.0, - "maxNotional": 2500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "2500000", - "notionalFloor": "1250000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.25", - "cum": "209075.0" + "cum": "418150.0" } }, { "tier": 9.0, "symbol": "CAKE/USDT:USDT", "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", - "cum": "834075.0" + "cum": "1668150.0" } } ], @@ -13723,13 +14929,13 @@ "symbol": "CFX/USDT:USDT", "currency": "USDT", "minNotional": 600000.0, - "maxNotional": 3000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "3000000", + "notionalCap": "2000000", "notionalFloor": "600000", "maintMarginRatio": "0.05", "cum": "17350.0" @@ -13739,68 +14945,223 @@ "tier": 6.0, "symbol": "CFX/USDT:USDT", "currency": "USDT", - "minNotional": 3000000.0, - "maxNotional": 6000000.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "6000000", - "notionalFloor": "3000000", + "notionalCap": "2500000", + "notionalFloor": "2000000", "maintMarginRatio": "0.1", - "cum": "167350.0" + "cum": "117350.0" } }, { "tier": 7.0, "symbol": "CFX/USDT:USDT", "currency": "USDT", - "minNotional": 6000000.0, - "maxNotional": 7500000.0, + "minNotional": 2500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "7500000", - "notionalFloor": "6000000", + "notionalCap": "3000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.125", - "cum": "317350.0" + "cum": "179850.0" } }, { "tier": 8.0, "symbol": "CFX/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 18000000.0, + "minNotional": 3000000.0, + "maxNotional": 3500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "18000000", - "notionalFloor": "7500000", + "notionalCap": "3500000", + "notionalFloor": "3000000", "maintMarginRatio": "0.25", - "cum": "1254850.0" + "cum": "554850.0" } }, { "tier": 9.0, "symbol": "CFX/USDT:USDT", "currency": "USDT", - "minNotional": 18000000.0, - "maxNotional": 30000000.0, + "minNotional": 3500000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "18000000", + "notionalCap": "4000000", + "notionalFloor": "3500000", "maintMarginRatio": "0.5", - "cum": "5754850.0" + "cum": "1429850.0" + } + } + ], + "CGPT/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "CGPT/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "CGPT/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "CGPT/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 15.0, + "info": { + "bracket": "3", + "initialLeverage": "15", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "CGPT/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "CGPT/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, + "info": { + "bracket": "5", + "initialLeverage": "8", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "CGPT/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "CGPT/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "CGPT/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "CGPT/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" } } ], @@ -13967,10 +15328,10 @@ "minNotional": 0.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -13984,10 +15345,10 @@ "minNotional": 10000.0, "maxNotional": 20000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.015", @@ -14001,10 +15362,10 @@ "minNotional": 20000.0, "maxNotional": 100000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "100000", "notionalFloor": "20000", "maintMarginRatio": "0.02", @@ -14018,10 +15379,10 @@ "minNotional": 100000.0, "maxNotional": 200000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "200000", "notionalFloor": "100000", "maintMarginRatio": "0.025", @@ -14035,10 +15396,10 @@ "minNotional": 200000.0, "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "1000000", "notionalFloor": "200000", "maintMarginRatio": "0.05", @@ -14101,13 +15462,13 @@ "symbol": "CHILLGUY/USDT:USDT", "currency": "USDT", "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "maxNotional": 6000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "10000000", + "notionalCap": "6000000", "notionalFloor": "5000000", "maintMarginRatio": "0.5", "cum": "1668150.0" @@ -14855,6 +16216,161 @@ } } ], + "COOKIE/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "COOKIE/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "COOKIE/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "COOKIE/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 15.0, + "info": { + "bracket": "3", + "initialLeverage": "15", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "COOKIE/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "COOKIE/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, + "info": { + "bracket": "5", + "initialLeverage": "8", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "COOKIE/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "COOKIE/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "COOKIE/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "COOKIE/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], "COS/USDT:USDT": [ { "tier": 1.0, @@ -16150,6 +17666,161 @@ } } ], + "D/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "D/USDT:USDT", + "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, + "symbol": "D/USDT:USDT", + "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, + "symbol": "D/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "D/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "D/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "D/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "D/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "D/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "D/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], "DAR/USDT:USDT": [ { "tier": 1.0, @@ -16158,10 +17829,10 @@ "minNotional": 0.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 50.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "50", "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -16175,10 +17846,10 @@ "minNotional": 10000.0, "maxNotional": 20000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 25.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.015", @@ -16192,10 +17863,10 @@ "minNotional": 20000.0, "maxNotional": 100000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 20.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "20", "notionalCap": "100000", "notionalFloor": "20000", "maintMarginRatio": "0.02", @@ -16209,10 +17880,10 @@ "minNotional": 100000.0, "maxNotional": 200000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 15.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "15", "notionalCap": "200000", "notionalFloor": "100000", "maintMarginRatio": "0.025", @@ -16241,13 +17912,13 @@ "symbol": "DAR/USDT:USDT", "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "2000000", + "notionalCap": "1500000", "notionalFloor": "1000000", "maintMarginRatio": "0.1", "cum": "55650.0" @@ -16257,51 +17928,51 @@ "tier": 7.0, "symbol": "DAR/USDT:USDT", "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 2500000.0, + "minNotional": 1500000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "2500000", - "notionalFloor": "2000000", + "notionalCap": "2000000", + "notionalFloor": "1500000", "maintMarginRatio": "0.125", - "cum": "105650.0" + "cum": "93150.0" } }, { "tier": 8.0, "symbol": "DAR/USDT:USDT", "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 6000000.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "6000000", - "notionalFloor": "2500000", + "notionalCap": "2500000", + "notionalFloor": "2000000", "maintMarginRatio": "0.25", - "cum": "418150.0" + "cum": "343150.0" } }, { "tier": 9.0, "symbol": "DAR/USDT:USDT", "currency": "USDT", - "minNotional": 6000000.0, - "maxNotional": 10000000.0, + "minNotional": 2500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "10000000", - "notionalFloor": "6000000", + "notionalCap": "3000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "1918150.0" + "cum": "968150.0" } } ], @@ -16572,10 +18243,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -16589,10 +18260,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -16606,10 +18277,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -16623,10 +18294,10 @@ "minNotional": 30000.0, "maxNotional": 60000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "60000", "notionalFloor": "30000", "maintMarginRatio": "0.025", @@ -16640,10 +18311,10 @@ "minNotional": 60000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "300000", "notionalFloor": "60000", "maintMarginRatio": "0.05", @@ -16706,6 +18377,161 @@ "symbol": "DEGEN/USDT:USDT", "currency": "USDT", "minNotional": 1500000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], + "DEGO/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "DEGO/USDT:USDT", + "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, + "symbol": "DEGO/USDT:USDT", + "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, + "symbol": "DEGO/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "DEGO/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "DEGO/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "DEGO/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "DEGO/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "DEGO/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "DEGO/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, "maxNotional": 3000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, @@ -16840,6 +18666,316 @@ } } ], + "DEXE/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "DEXE/USDT:USDT", + "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, + "symbol": "DEXE/USDT:USDT", + "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, + "symbol": "DEXE/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "DEXE/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "DEXE/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "DEXE/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "DEXE/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "DEXE/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "DEXE/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], + "DF/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "DF/USDT:USDT", + "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, + "symbol": "DF/USDT:USDT", + "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, + "symbol": "DF/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "DF/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "DF/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "DF/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "DF/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "DF/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "DF/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], "DGB/USDT:USDT": [ { "tier": 1.0, @@ -17638,13 +19774,13 @@ "symbol": "DOGS/USDT:USDT", "currency": "USDT", "minNotional": 600000.0, - "maxNotional": 3000000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "3000000", + "notionalCap": "1500000", "notionalFloor": "600000", "maintMarginRatio": "0.05", "cum": "16850.0" @@ -17654,68 +19790,68 @@ "tier": 6.0, "symbol": "DOGS/USDT:USDT", "currency": "USDT", - "minNotional": 3000000.0, - "maxNotional": 6000000.0, + "minNotional": 1500000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "6000000", - "notionalFloor": "3000000", + "notionalCap": "2000000", + "notionalFloor": "1500000", "maintMarginRatio": "0.1", - "cum": "166850.0" + "cum": "91850.0" } }, { "tier": 7.0, "symbol": "DOGS/USDT:USDT", "currency": "USDT", - "minNotional": 6000000.0, - "maxNotional": 7500000.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "7500000", - "notionalFloor": "6000000", + "notionalCap": "2500000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", - "cum": "316850.0" + "cum": "141850.0" } }, { "tier": 8.0, "symbol": "DOGS/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 15000000.0, + "minNotional": 2500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "15000000", - "notionalFloor": "7500000", + "notionalCap": "3000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.25", - "cum": "1254350.0" + "cum": "454350.0" } }, { "tier": 9.0, "symbol": "DOGS/USDT:USDT", "currency": "USDT", - "minNotional": 15000000.0, - "maxNotional": 30000000.0, + "minNotional": 3000000.0, + "maxNotional": 3500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "15000000", + "notionalCap": "3500000", + "notionalFloor": "3000000", "maintMarginRatio": "0.5", - "cum": "5004350.0" + "cum": "1204350.0" } } ], @@ -19362,13 +21498,13 @@ "symbol": "ENS/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", - "notionalCap": "5000", + "initialLeverage": "25", + "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" @@ -19378,136 +21514,136 @@ "tier": 2.0, "symbol": "ENS/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 16000.0, + "minNotional": 10000.0, + "maxNotional": 40000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", - "notionalCap": "16000", - "notionalFloor": "5000", + "initialLeverage": "20", + "notionalCap": "40000", + "notionalFloor": "10000", "maintMarginRatio": "0.015", - "cum": "25.0" + "cum": "50.0" } }, { "tier": 3.0, "symbol": "ENS/USDT:USDT", "currency": "USDT", - "minNotional": 16000.0, - "maxNotional": 80000.0, + "minNotional": 40000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", - "notionalCap": "80000", - "notionalFloor": "16000", + "initialLeverage": "15", + "notionalCap": "200000", + "notionalFloor": "40000", "maintMarginRatio": "0.02", - "cum": "105.0" + "cum": "250.0" } }, { "tier": 4.0, "symbol": "ENS/USDT:USDT", "currency": "USDT", - "minNotional": 80000.0, - "maxNotional": 160000.0, + "minNotional": 200000.0, + "maxNotional": 400000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", - "notionalCap": "160000", - "notionalFloor": "80000", + "initialLeverage": "10", + "notionalCap": "400000", + "notionalFloor": "200000", "maintMarginRatio": "0.025", - "cum": "505.0" + "cum": "1250.0" } }, { "tier": 5.0, "symbol": "ENS/USDT:USDT", "currency": "USDT", - "minNotional": 160000.0, - "maxNotional": 800000.0, + "minNotional": 400000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", - "notionalCap": "800000", - "notionalFloor": "160000", + "initialLeverage": "8", + "notionalCap": "2000000", + "notionalFloor": "400000", "maintMarginRatio": "0.05", - "cum": "4505.0" + "cum": "11250.0" } }, { "tier": 6.0, "symbol": "ENS/USDT:USDT", "currency": "USDT", - "minNotional": 800000.0, - "maxNotional": 1600000.0, + "minNotional": 2000000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "1600000", - "notionalFloor": "800000", + "notionalCap": "4000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.1", - "cum": "44505.0" + "cum": "111250.0" } }, { "tier": 7.0, "symbol": "ENS/USDT:USDT", "currency": "USDT", - "minNotional": 1600000.0, - "maxNotional": 2000000.0, + "minNotional": 4000000.0, + "maxNotional": 4500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "2000000", - "notionalFloor": "1600000", + "notionalCap": "4500000", + "notionalFloor": "4000000", "maintMarginRatio": "0.125", - "cum": "84505.0" + "cum": "211250.0" } }, { "tier": 8.0, "symbol": "ENS/USDT:USDT", "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 4000000.0, + "minNotional": 4500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "4000000", - "notionalFloor": "2000000", + "notionalCap": "5000000", + "notionalFloor": "4500000", "maintMarginRatio": "0.25", - "cum": "334505.0" + "cum": "773750.0" } }, { "tier": 9.0, "symbol": "ENS/USDT:USDT", "currency": "USDT", - "minNotional": 4000000.0, - "maxNotional": 8000000.0, + "minNotional": 5000000.0, + "maxNotional": 5500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "8000000", - "notionalFloor": "4000000", + "notionalCap": "5500000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", - "cum": "1334505.0" + "cum": "2023750.0" } } ], @@ -20422,144 +22558,6 @@ } } ], - "ETH/USDT:USDT-241227": [ - { - "tier": 1.0, - "symbol": "ETH/USDT:USDT-241227", - "currency": "USDT", - "minNotional": 0.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 50.0, - "info": { - "bracket": "1", - "initialLeverage": "50", - "notionalCap": "50000", - "notionalFloor": "0", - "maintMarginRatio": "0.01", - "cum": "0.0" - } - }, - { - "tier": 2.0, - "symbol": "ETH/USDT:USDT-241227", - "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 375000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, - "info": { - "bracket": "2", - "initialLeverage": "25", - "notionalCap": "375000", - "notionalFloor": "50000", - "maintMarginRatio": "0.02", - "cum": "500.0" - } - }, - { - "tier": 3.0, - "symbol": "ETH/USDT:USDT-241227", - "currency": "USDT", - "minNotional": 375000.0, - "maxNotional": 2000000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, - "info": { - "bracket": "3", - "initialLeverage": "10", - "notionalCap": "2000000", - "notionalFloor": "375000", - "maintMarginRatio": "0.05", - "cum": "11750.0" - } - }, - { - "tier": 4.0, - "symbol": "ETH/USDT:USDT-241227", - "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 4000000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, - "info": { - "bracket": "4", - "initialLeverage": "5", - "notionalCap": "4000000", - "notionalFloor": "2000000", - "maintMarginRatio": "0.1", - "cum": "111750.0" - } - }, - { - "tier": 5.0, - "symbol": "ETH/USDT:USDT-241227", - "currency": "USDT", - "minNotional": 4000000.0, - "maxNotional": 10000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": "5", - "initialLeverage": "4", - "notionalCap": "10000000", - "notionalFloor": "4000000", - "maintMarginRatio": "0.125", - "cum": "211750.0" - } - }, - { - "tier": 6.0, - "symbol": "ETH/USDT:USDT-241227", - "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 20000000.0, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3.0, - "info": { - "bracket": "6", - "initialLeverage": "3", - "notionalCap": "20000000", - "notionalFloor": "10000000", - "maintMarginRatio": "0.15", - "cum": "461750.0" - } - }, - { - "tier": 7.0, - "symbol": "ETH/USDT:USDT-241227", - "currency": "USDT", - "minNotional": 20000000.0, - "maxNotional": 40000000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": "7", - "initialLeverage": "2", - "notionalCap": "40000000", - "notionalFloor": "20000000", - "maintMarginRatio": "0.25", - "cum": "2461750.0" - } - }, - { - "tier": 8.0, - "symbol": "ETH/USDT:USDT-241227", - "currency": "USDT", - "minNotional": 40000000.0, - "maxNotional": 120000000.0, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1.0, - "info": { - "bracket": "8", - "initialLeverage": "1", - "notionalCap": "120000000", - "notionalFloor": "40000000", - "maintMarginRatio": "0.5", - "cum": "12461750.0" - } - } - ], "ETH/USDT:USDT-250328": [ { "tier": 1.0, @@ -20698,6 +22696,144 @@ } } ], + "ETH/USDT:USDT-250627": [ + { + "tier": 1.0, + "symbol": "ETH/USDT:USDT-250627", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "50000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "ETH/USDT:USDT-250627", + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 375000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "2", + "initialLeverage": "25", + "notionalCap": "375000", + "notionalFloor": "50000", + "maintMarginRatio": "0.02", + "cum": "500.0" + } + }, + { + "tier": 3.0, + "symbol": "ETH/USDT:USDT-250627", + "currency": "USDT", + "minNotional": 375000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "2000000", + "notionalFloor": "375000", + "maintMarginRatio": "0.05", + "cum": "11750.0" + } + }, + { + "tier": 4.0, + "symbol": "ETH/USDT:USDT-250627", + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "4000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.1", + "cum": "111750.0" + } + }, + { + "tier": 5.0, + "symbol": "ETH/USDT:USDT-250627", + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "10000000", + "notionalFloor": "4000000", + "maintMarginRatio": "0.125", + "cum": "211750.0" + } + }, + { + "tier": 6.0, + "symbol": "ETH/USDT:USDT-250627", + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, + "maintenanceMarginRate": 0.15, + "maxLeverage": 3.0, + "info": { + "bracket": "6", + "initialLeverage": "3", + "notionalCap": "20000000", + "notionalFloor": "10000000", + "maintMarginRatio": "0.15", + "cum": "461750.0" + } + }, + { + "tier": 7.0, + "symbol": "ETH/USDT:USDT-250627", + "currency": "USDT", + "minNotional": 20000000.0, + "maxNotional": 40000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "7", + "initialLeverage": "2", + "notionalCap": "40000000", + "notionalFloor": "20000000", + "maintMarginRatio": "0.25", + "cum": "2461750.0" + } + }, + { + "tier": 8.0, + "symbol": "ETH/USDT:USDT-250627", + "currency": "USDT", + "minNotional": 40000000.0, + "maxNotional": 120000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "8", + "initialLeverage": "1", + "notionalCap": "120000000", + "notionalFloor": "40000000", + "maintMarginRatio": "0.5", + "cum": "12461750.0" + } + } + ], "ETHFI/USDC:USDC": [ { "tier": 1.0, @@ -20999,10 +23135,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -21016,10 +23152,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -21033,10 +23169,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -21050,10 +23186,10 @@ "minNotional": 30000.0, "maxNotional": 60000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "60000", "notionalFloor": "30000", "maintMarginRatio": "0.025", @@ -21067,10 +23203,10 @@ "minNotional": 60000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "300000", "notionalFloor": "60000", "maintMarginRatio": "0.05", @@ -21133,19 +23269,174 @@ "symbol": "ETHW/USDT:USDT", "currency": "USDT", "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "3000000", + "notionalCap": "2000000", "notionalFloor": "1500000", "maintMarginRatio": "0.5", "cum": "500475.0" } } ], + "FARTCOIN/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "FARTCOIN/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "10000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "FARTCOIN/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "symbol": "FARTCOIN/USDT:USDT", + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 15.0, + "info": { + "bracket": "3", + "initialLeverage": "15", + "notionalCap": "100000", + "notionalFloor": "20000", + "maintMarginRatio": "0.02", + "cum": "150.0" + } + }, + { + "tier": 4.0, + "symbol": "FARTCOIN/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "100000", + "maintMarginRatio": "0.025", + "cum": "650.0" + } + }, + { + "tier": 5.0, + "symbol": "FARTCOIN/USDT:USDT", + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, + "info": { + "bracket": "5", + "initialLeverage": "8", + "notionalCap": "1000000", + "notionalFloor": "200000", + "maintMarginRatio": "0.05", + "cum": "5650.0" + } + }, + { + "tier": 6.0, + "symbol": "FARTCOIN/USDT:USDT", + "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, + "symbol": "FARTCOIN/USDT:USDT", + "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, + "symbol": "FARTCOIN/USDT:USDT", + "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, + "symbol": "FARTCOIN/USDT:USDT", + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 6000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "6000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.5", + "cum": "1668150.0" + } + } + ], "FET/USDT:USDT": [ { "tier": 1.0, @@ -22394,10 +24685,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.006, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "1", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.006", @@ -22411,10 +24702,10 @@ "minNotional": 5000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 15.0, + "maxLeverage": 9.0, "info": { "bracket": "2", - "initialLeverage": "15", + "initialLeverage": "9", "notionalCap": "50000", "notionalFloor": "5000", "maintMarginRatio": "0.01", @@ -22428,10 +24719,10 @@ "minNotional": 50000.0, "maxNotional": 80000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 12.0, + "maxLeverage": 8.0, "info": { "bracket": "3", - "initialLeverage": "12", + "initialLeverage": "8", "notionalCap": "80000", "notionalFloor": "50000", "maintMarginRatio": "0.015", @@ -22445,10 +24736,10 @@ "minNotional": 80000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 10.0, + "maxLeverage": 7.0, "info": { "bracket": "4", - "initialLeverage": "10", + "initialLeverage": "7", "notionalCap": "300000", "notionalFloor": "80000", "maintMarginRatio": "0.02", @@ -22462,10 +24753,10 @@ "minNotional": 300000.0, "maxNotional": 600000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 8.0, + "maxLeverage": 6.0, "info": { "bracket": "5", - "initialLeverage": "8", + "initialLeverage": "6", "notionalCap": "600000", "notionalFloor": "300000", "maintMarginRatio": "0.025", @@ -22479,10 +24770,10 @@ "minNotional": 600000.0, "maxNotional": 3000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 6.0, + "maxLeverage": 5.0, "info": { "bracket": "6", - "initialLeverage": "6", + "initialLeverage": "5", "notionalCap": "3000000", "notionalFloor": "600000", "maintMarginRatio": "0.05", @@ -22496,10 +24787,10 @@ "minNotional": 3000000.0, "maxNotional": 6000000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maxLeverage": 4.0, "info": { "bracket": "7", - "initialLeverage": "5", + "initialLeverage": "4", "notionalCap": "6000000", "notionalFloor": "3000000", "maintMarginRatio": "0.1", @@ -22513,10 +24804,10 @@ "minNotional": 6000000.0, "maxNotional": 7000000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maxLeverage": 3.0, "info": { "bracket": "8", - "initialLeverage": "4", + "initialLeverage": "3", "notionalCap": "7000000", "notionalFloor": "6000000", "maintMarginRatio": "0.125", @@ -22528,13 +24819,13 @@ "symbol": "FTM/USDT:USDT", "currency": "USDT", "minNotional": 7000000.0, - "maxNotional": 8000000.0, + "maxNotional": 7200000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "9", "initialLeverage": "2", - "notionalCap": "8000000", + "notionalCap": "7200000", "notionalFloor": "7000000", "maintMarginRatio": "0.25", "cum": "1192170.0" @@ -22544,17 +24835,17 @@ "tier": 10.0, "symbol": "FTM/USDT:USDT", "currency": "USDT", - "minNotional": 8000000.0, - "maxNotional": 10000000.0, + "minNotional": 7200000.0, + "maxNotional": 7500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "10", "initialLeverage": "1", - "notionalCap": "10000000", - "notionalFloor": "8000000", + "notionalCap": "7500000", + "notionalFloor": "7200000", "maintMarginRatio": "0.5", - "cum": "3192170.0" + "cum": "2992170.0" } } ], @@ -23067,10 +25358,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -23084,10 +25375,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -23101,10 +25392,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -23118,10 +25409,10 @@ "minNotional": 30000.0, "maxNotional": 60000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "60000", "notionalFloor": "30000", "maintMarginRatio": "0.025", @@ -23135,10 +25426,10 @@ "minNotional": 60000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "300000", "notionalFloor": "60000", "maintMarginRatio": "0.05", @@ -23861,10 +26152,10 @@ "minNotional": 0.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -23878,10 +26169,10 @@ "minNotional": 10000.0, "maxNotional": 80000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "80000", "notionalFloor": "10000", "maintMarginRatio": "0.015", @@ -23895,10 +26186,10 @@ "minNotional": 80000.0, "maxNotional": 400000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "400000", "notionalFloor": "80000", "maintMarginRatio": "0.02", @@ -23912,10 +26203,10 @@ "minNotional": 400000.0, "maxNotional": 800000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "800000", "notionalFloor": "400000", "maintMarginRatio": "0.025", @@ -23929,10 +26220,10 @@ "minNotional": 800000.0, "maxNotional": 4000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "4000000", "notionalFloor": "800000", "maintMarginRatio": "0.05", @@ -23995,13 +26286,13 @@ "symbol": "GOAT/USDT:USDT", "currency": "USDT", "minNotional": 20000000.0, - "maxNotional": 40000000.0, + "maxNotional": 21000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "40000000", + "notionalCap": "21000000", "notionalFloor": "20000000", "maintMarginRatio": "0.5", "cum": "6672450.0" @@ -24016,10 +26307,10 @@ "minNotional": 0.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -24033,10 +26324,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.015", @@ -24050,10 +26341,10 @@ "minNotional": 30000.0, "maxNotional": 150000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "150000", "notionalFloor": "30000", "maintMarginRatio": "0.02", @@ -24067,10 +26358,10 @@ "minNotional": 150000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "300000", "notionalFloor": "150000", "maintMarginRatio": "0.025", @@ -24084,10 +26375,10 @@ "minNotional": 300000.0, "maxNotional": 1500000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "1500000", "notionalFloor": "300000", "maintMarginRatio": "0.05", @@ -24116,13 +26407,13 @@ "symbol": "GRASS/USDT:USDT", "currency": "USDT", "minNotional": 3000000.0, - "maxNotional": 3750000.0, + "maxNotional": 3500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "3750000", + "notionalCap": "3500000", "notionalFloor": "3000000", "maintMarginRatio": "0.125", "cum": "158450.0" @@ -24132,34 +26423,189 @@ "tier": 8.0, "symbol": "GRASS/USDT:USDT", "currency": "USDT", - "minNotional": 3750000.0, - "maxNotional": 7500000.0, + "minNotional": 3500000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "7500000", - "notionalFloor": "3750000", + "notionalCap": "4000000", + "notionalFloor": "3500000", "maintMarginRatio": "0.25", - "cum": "627200.0" + "cum": "595950.0" } }, { "tier": 9.0, "symbol": "GRASS/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 15000000.0, + "minNotional": 4000000.0, + "maxNotional": 4500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "15000000", - "notionalFloor": "7500000", + "notionalCap": "4500000", + "notionalFloor": "4000000", "maintMarginRatio": "0.5", - "cum": "2502200.0" + "cum": "1595950.0" + } + } + ], + "GRIFFAIN/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "GRIFFAIN/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "GRIFFAIN/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "GRIFFAIN/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 15.0, + "info": { + "bracket": "3", + "initialLeverage": "15", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "GRIFFAIN/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "GRIFFAIN/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, + "info": { + "bracket": "5", + "initialLeverage": "8", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "GRIFFAIN/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "GRIFFAIN/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "GRIFFAIN/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "GRIFFAIN/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" } } ], @@ -24462,13 +26908,13 @@ "symbol": "HBAR/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 40000.0, + "maxNotional": 80000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "40000", + "notionalCap": "80000", "notionalFloor": "10000", "maintMarginRatio": "0.015", "cum": "50.0" @@ -24478,119 +26924,119 @@ "tier": 3.0, "symbol": "HBAR/USDT:USDT", "currency": "USDT", - "minNotional": 40000.0, - "maxNotional": 200000.0, + "minNotional": 80000.0, + "maxNotional": 400000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "200000", - "notionalFloor": "40000", + "notionalCap": "400000", + "notionalFloor": "80000", "maintMarginRatio": "0.02", - "cum": "250.0" + "cum": "450.0" } }, { "tier": 4.0, "symbol": "HBAR/USDT:USDT", "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 400000.0, + "minNotional": 400000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "4", "initialLeverage": "20", - "notionalCap": "400000", - "notionalFloor": "200000", + "notionalCap": "800000", + "notionalFloor": "400000", "maintMarginRatio": "0.025", - "cum": "1250.0" + "cum": "2450.0" } }, { "tier": 5.0, "symbol": "HBAR/USDT:USDT", "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 2000000.0, + "minNotional": 800000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "2000000", - "notionalFloor": "400000", + "notionalCap": "4000000", + "notionalFloor": "800000", "maintMarginRatio": "0.05", - "cum": "11250.0" + "cum": "22450.0" } }, { "tier": 6.0, "symbol": "HBAR/USDT:USDT", "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 4000000.0, + "minNotional": 4000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "4000000", - "notionalFloor": "2000000", + "notionalCap": "8000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.1", - "cum": "111250.0" + "cum": "222450.0" } }, { "tier": 7.0, "symbol": "HBAR/USDT:USDT", "currency": "USDT", - "minNotional": 4000000.0, - "maxNotional": 5000000.0, + "minNotional": 8000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "4000000", + "notionalCap": "10000000", + "notionalFloor": "8000000", "maintMarginRatio": "0.125", - "cum": "211250.0" + "cum": "422450.0" } }, { "tier": 8.0, "symbol": "HBAR/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.25", - "cum": "836250.0" + "cum": "1672450.0" } }, { "tier": 9.0, "symbol": "HBAR/USDT:USDT", "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 20000000.0, + "minNotional": 20000000.0, + "maxNotional": 40000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "notionalCap": "40000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.5", - "cum": "3336250.0" + "cum": "6672450.0" } } ], @@ -24965,10 +27411,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -24982,10 +27428,10 @@ "minNotional": 5000.0, "maxNotional": 16000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "16000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -24999,10 +27445,10 @@ "minNotional": 16000.0, "maxNotional": 80000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "80000", "notionalFloor": "16000", "maintMarginRatio": "0.02", @@ -25016,10 +27462,10 @@ "minNotional": 80000.0, "maxNotional": 160000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "160000", "notionalFloor": "80000", "maintMarginRatio": "0.025", @@ -25033,10 +27479,10 @@ "minNotional": 160000.0, "maxNotional": 800000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "800000", "notionalFloor": "160000", "maintMarginRatio": "0.05", @@ -25082,13 +27528,13 @@ "symbol": "HIPPO/USDT:USDT", "currency": "USDT", "minNotional": 2000000.0, - "maxNotional": 4000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "4000000", + "notionalCap": "2500000", "notionalFloor": "2000000", "maintMarginRatio": "0.25", "cum": "334505.0" @@ -25098,17 +27544,172 @@ "tier": 9.0, "symbol": "HIPPO/USDT:USDT", "currency": "USDT", - "minNotional": 4000000.0, - "maxNotional": 8000000.0, + "minNotional": 2500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "8000000", - "notionalFloor": "4000000", + "notionalCap": "3000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "1334505.0" + "cum": "959505.0" + } + } + ], + "HIVE/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "HIVE/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 10.0, + "info": { + "bracket": "1", + "initialLeverage": "10", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "HIVE/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 9.0, + "info": { + "bracket": "2", + "initialLeverage": "9", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "HIVE/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 8.0, + "info": { + "bracket": "3", + "initialLeverage": "8", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "HIVE/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 7.0, + "info": { + "bracket": "4", + "initialLeverage": "7", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "HIVE/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 6.0, + "info": { + "bracket": "5", + "initialLeverage": "6", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "HIVE/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "HIVE/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "HIVE/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "HIVE/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 1600000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "1600000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" } } ], @@ -26774,15 +29375,15 @@ "symbol": "IOTA/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "25", + "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.01", "cum": "0.0" } }, @@ -26790,17 +29391,17 @@ "tier": 2.0, "symbol": "IOTA/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, + "minNotional": 10000.0, "maxNotional": 20000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "20", "notionalCap": "20000", - "notionalFloor": "5000", - "maintMarginRatio": "0.02", - "cum": "25.0" + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" } }, { @@ -26808,33 +29409,33 @@ "symbol": "IOTA/USDT:USDT", "currency": "USDT", "minNotional": 20000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "20", - "notionalCap": "25000", + "initialLeverage": "15", + "notionalCap": "100000", "notionalFloor": "20000", - "maintMarginRatio": "0.025", - "cum": "125.0" + "maintMarginRatio": "0.02", + "cum": "150.0" } }, { "tier": 4.0, "symbol": "IOTA/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 100000.0, "maxNotional": 200000.0, - "maintenanceMarginRate": 0.05, + "maintenanceMarginRate": 0.025, "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", "notionalCap": "200000", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "750.0" + "notionalFloor": "100000", + "maintMarginRatio": "0.025", + "cum": "650.0" } }, { @@ -26842,67 +29443,84 @@ "symbol": "IOTA/USDT:USDT", "currency": "USDT", "minNotional": 200000.0, - "maxNotional": 400000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "5", - "notionalCap": "400000", + "initialLeverage": "8", + "notionalCap": "1000000", "notionalFloor": "200000", - "maintMarginRatio": "0.1", - "cum": "10750.0" + "maintMarginRatio": "0.05", + "cum": "5650.0" } }, { "tier": 6.0, "symbol": "IOTA/USDT:USDT", "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 1000000.0, + "maxNotional": 2000000.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": "2000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.1", + "cum": "55650.0" } }, { "tier": 7.0, "symbol": "IOTA/USDT:USDT", "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.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": "2500000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.125", + "cum": "105650.0" } }, { "tier": 8.0, "symbol": "IOTA/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 2500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "3000000", + "notionalFloor": "2500000", + "maintMarginRatio": "0.25", + "cum": "418150.0" + } + }, + { + "tier": 9.0, + "symbol": "IOTA/USDT:USDT", + "currency": "USDT", + "minNotional": 3000000.0, + "maxNotional": 3500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "1", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "3500000", + "notionalFloor": "3000000", "maintMarginRatio": "0.5", - "cum": "333250.0" + "cum": "1168150.0" } } ], @@ -27052,10 +29670,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 10.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "10", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.015", @@ -27069,10 +29687,10 @@ "minNotional": 5000.0, "maxNotional": 25000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 9.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "9", "notionalCap": "25000", "notionalFloor": "5000", "maintMarginRatio": "0.02", @@ -27086,10 +29704,10 @@ "minNotional": 25000.0, "maxNotional": 80000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 8.0, "info": { "bracket": "3", - "initialLeverage": "20", + "initialLeverage": "8", "notionalCap": "80000", "notionalFloor": "25000", "maintMarginRatio": "0.025", @@ -27103,10 +29721,10 @@ "minNotional": 80000.0, "maxNotional": 800000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 6.0, "info": { "bracket": "4", - "initialLeverage": "10", + "initialLeverage": "6", "notionalCap": "800000", "notionalFloor": "80000", "maintMarginRatio": "0.05", @@ -27169,13 +29787,13 @@ "symbol": "JASMY/USDT:USDT", "currency": "USDT", "minNotional": 4000000.0, - "maxNotional": 8000000.0, + "maxNotional": 4500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "8000000", + "notionalCap": "4500000", "notionalFloor": "4000000", "maintMarginRatio": "0.5", "cum": "1332150.0" @@ -27810,10 +30428,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -27827,10 +30445,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -27844,10 +30462,10 @@ "minNotional": 10000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "50000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -27861,10 +30479,10 @@ "minNotional": 50000.0, "maxNotional": 100000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "100000", "notionalFloor": "50000", "maintMarginRatio": "0.025", @@ -27878,10 +30496,10 @@ "minNotional": 100000.0, "maxNotional": 500000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "500000", "notionalFloor": "100000", "maintMarginRatio": "0.05", @@ -27944,13 +30562,13 @@ "symbol": "KAS/USDT:USDT", "currency": "USDT", "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "5000000", + "notionalCap": "3000000", "notionalFloor": "2500000", "maintMarginRatio": "0.5", "cum": "834075.0" @@ -28492,6 +31110,161 @@ } } ], + "KMNO/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "KMNO/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "KMNO/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "KMNO/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 15.0, + "info": { + "bracket": "3", + "initialLeverage": "15", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "KMNO/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "KMNO/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, + "info": { + "bracket": "5", + "initialLeverage": "8", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "KMNO/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "KMNO/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "KMNO/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1000000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "KMNO/USDT:USDT", + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "1500000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.5", + "cum": "375475.0" + } + } + ], "KNC/USDT:USDT": [ { "tier": 1.0, @@ -28638,10 +31411,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -28655,10 +31428,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -28672,10 +31445,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -28689,10 +31462,10 @@ "minNotional": 30000.0, "maxNotional": 60000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "60000", "notionalFloor": "30000", "maintMarginRatio": "0.025", @@ -28706,10 +31479,10 @@ "minNotional": 60000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "300000", "notionalFloor": "60000", "maintMarginRatio": "0.05", @@ -28772,13 +31545,13 @@ "symbol": "KOMA/USDT:USDT", "currency": "USDT", "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "3000000", + "notionalCap": "2000000", "notionalFloor": "1500000", "maintMarginRatio": "0.5", "cum": "500475.0" @@ -29224,10 +31997,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "1", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.02", @@ -29241,10 +32014,10 @@ "minNotional": 5000.0, "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 15.0, + "maxLeverage": 8.0, "info": { "bracket": "2", - "initialLeverage": "15", + "initialLeverage": "8", "notionalCap": "25000", "notionalFloor": "5000", "maintMarginRatio": "0.025", @@ -29258,10 +32031,10 @@ "minNotional": 25000.0, "maxNotional": 900000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 6.0, "info": { "bracket": "3", - "initialLeverage": "10", + "initialLeverage": "6", "notionalCap": "900000", "notionalFloor": "25000", "maintMarginRatio": "0.05", @@ -29324,13 +32097,13 @@ "symbol": "LINA/USDT:USDT", "currency": "USDT", "minNotional": 5000000.0, - "maxNotional": 7000000.0, + "maxNotional": 5500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "7000000", + "notionalCap": "5500000", "notionalFloor": "5000000", "maintMarginRatio": "0.5", "cum": "1730650.0" @@ -29549,13 +32322,13 @@ "symbol": "LINK/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 80000.0, + "maxNotional": 160000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 40.0, "info": { "bracket": "3", "initialLeverage": "40", - "notionalCap": "80000", + "notionalCap": "160000", "notionalFloor": "50000", "maintMarginRatio": "0.015", "cum": "300.0" @@ -29565,75 +32338,75 @@ "tier": 4.0, "symbol": "LINK/USDT:USDT", "currency": "USDT", - "minNotional": 80000.0, - "maxNotional": 400000.0, + "minNotional": 160000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "4", "initialLeverage": "25", - "notionalCap": "400000", - "notionalFloor": "80000", + "notionalCap": "800000", + "notionalFloor": "160000", "maintMarginRatio": "0.02", - "cum": "700.0" + "cum": "1100.0" } }, { "tier": 5.0, "symbol": "LINK/USDT:USDT", "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 800000.0, + "minNotional": 800000.0, + "maxNotional": 1600000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "5", "initialLeverage": "20", - "notionalCap": "800000", - "notionalFloor": "400000", + "notionalCap": "1600000", + "notionalFloor": "800000", "maintMarginRatio": "0.025", - "cum": "2700.0" + "cum": "5100.0" } }, { "tier": 6.0, "symbol": "LINK/USDT:USDT", "currency": "USDT", - "minNotional": 800000.0, - "maxNotional": 4000000.0, + "minNotional": 1600000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "6", "initialLeverage": "10", - "notionalCap": "4000000", - "notionalFloor": "800000", + "notionalCap": "8000000", + "notionalFloor": "1600000", "maintMarginRatio": "0.05", - "cum": "22700.0" + "cum": "45100.0" } }, { "tier": 7.0, "symbol": "LINK/USDT:USDT", "currency": "USDT", - "minNotional": 4000000.0, - "maxNotional": 8000000.0, + "minNotional": 8000000.0, + "maxNotional": 16000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "7", "initialLeverage": "5", - "notionalCap": "8000000", - "notionalFloor": "4000000", + "notionalCap": "16000000", + "notionalFloor": "8000000", "maintMarginRatio": "0.1", - "cum": "222700.0" + "cum": "445100.0" } }, { "tier": 8.0, "symbol": "LINK/USDT:USDT", "currency": "USDT", - "minNotional": 8000000.0, + "minNotional": 16000000.0, "maxNotional": 20000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, @@ -29641,9 +32414,9 @@ "bracket": "8", "initialLeverage": "4", "notionalCap": "20000000", - "notionalFloor": "8000000", + "notionalFloor": "16000000", "maintMarginRatio": "0.125", - "cum": "422700.0" + "cum": "845100.0" } }, { @@ -29651,33 +32424,33 @@ "symbol": "LINK/USDT:USDT", "currency": "USDT", "minNotional": 20000000.0, - "maxNotional": 30000000.0, + "maxNotional": 40000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "9", "initialLeverage": "2", - "notionalCap": "30000000", + "notionalCap": "40000000", "notionalFloor": "20000000", "maintMarginRatio": "0.25", - "cum": "2922700.0" + "cum": "3345100.0" } }, { "tier": 10.0, "symbol": "LINK/USDT:USDT", "currency": "USDT", - "minNotional": 30000000.0, - "maxNotional": 50000000.0, + "minNotional": 40000000.0, + "maxNotional": 80000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "10", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "30000000", + "notionalCap": "80000000", + "notionalFloor": "40000000", "maintMarginRatio": "0.5", - "cum": "10422700.0" + "cum": "13345100.0" } } ], @@ -29827,10 +32600,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 10.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "10", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.015", @@ -29844,10 +32617,10 @@ "minNotional": 5000.0, "maxNotional": 20000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 8.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "8", "notionalCap": "20000", "notionalFloor": "5000", "maintMarginRatio": "0.02", @@ -29861,10 +32634,10 @@ "minNotional": 20000.0, "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 6.0, "info": { "bracket": "3", - "initialLeverage": "20", + "initialLeverage": "6", "notionalCap": "25000", "notionalFloor": "20000", "maintMarginRatio": "0.025", @@ -29878,10 +32651,10 @@ "minNotional": 25000.0, "maxNotional": 200000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 5.0, "info": { "bracket": "4", - "initialLeverage": "10", + "initialLeverage": "5", "notionalCap": "200000", "notionalFloor": "25000", "maintMarginRatio": "0.05", @@ -29895,10 +32668,10 @@ "minNotional": 200000.0, "maxNotional": 400000.0, "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maxLeverage": 4.0, "info": { "bracket": "5", - "initialLeverage": "5", + "initialLeverage": "4", "notionalCap": "400000", "notionalFloor": "200000", "maintMarginRatio": "0.1", @@ -29912,10 +32685,10 @@ "minNotional": 400000.0, "maxNotional": 500000.0, "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maxLeverage": 3.0, "info": { "bracket": "6", - "initialLeverage": "4", + "initialLeverage": "3", "notionalCap": "500000", "notionalFloor": "400000", "maintMarginRatio": "0.125", @@ -29927,13 +32700,13 @@ "symbol": "LIT/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 1000000.0, + "maxNotional": 520000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "1000000", + "notionalCap": "520000", "notionalFloor": "500000", "maintMarginRatio": "0.25", "cum": "83250.0" @@ -29943,17 +32716,17 @@ "tier": 8.0, "symbol": "LIT/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 520000.0, + "maxNotional": 550000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "550000", + "notionalFloor": "520000", "maintMarginRatio": "0.5", - "cum": "333250.0" + "cum": "213250.0" } } ], @@ -30223,14 +32996,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" } }, @@ -30239,84 +33012,135 @@ "symbol": "LPT/USDT:USDT", "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 16000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": "2", - "initialLeverage": "20", - "notionalCap": "25000", + "initialLeverage": "50", + "notionalCap": "16000", "notionalFloor": "5000", - "maintMarginRatio": "0.025", - "cum": "50.0" + "maintMarginRatio": "0.015", + "cum": "25.0" } }, { "tier": 3.0, "symbol": "LPT/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 16000.0, + "maxNotional": 80000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "3", - "initialLeverage": "10", - "notionalCap": "100000", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "675.0" + "initialLeverage": "25", + "notionalCap": "80000", + "notionalFloor": "16000", + "maintMarginRatio": "0.02", + "cum": "105.0" } }, { "tier": 4.0, "symbol": "LPT/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 80000.0, + "maxNotional": 160000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "4", - "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "100000", - "maintMarginRatio": "0.1", - "cum": "5675.0" + "initialLeverage": "20", + "notionalCap": "160000", + "notionalFloor": "80000", + "maintMarginRatio": "0.025", + "cum": "505.0" } }, { "tier": 5.0, "symbol": "LPT/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 2.0, + "minNotional": 160000.0, + "maxNotional": 800000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "5", - "initialLeverage": "2", - "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "11925.0" + "initialLeverage": "10", + "notionalCap": "800000", + "notionalFloor": "160000", + "maintMarginRatio": "0.05", + "cum": "4505.0" } }, { "tier": 6.0, "symbol": "LPT/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 3000000.0, + "minNotional": 800000.0, + "maxNotional": 1600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "1600000", + "notionalFloor": "800000", + "maintMarginRatio": "0.1", + "cum": "44505.0" + } + }, + { + "tier": 7.0, + "symbol": "LPT/USDT:USDT", + "currency": "USDT", + "minNotional": 1600000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "2000000", + "notionalFloor": "1600000", + "maintMarginRatio": "0.125", + "cum": "84505.0" + } + }, + { + "tier": 8.0, + "symbol": "LPT/USDT:USDT", + "currency": "USDT", + "minNotional": 2000000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "4000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.25", + "cum": "334505.0" + } + }, + { + "tier": 9.0, + "symbol": "LPT/USDT:USDT", + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "6", + "bracket": "9", "initialLeverage": "1", - "notionalCap": "3000000", - "notionalFloor": "1000000", + "notionalCap": "8000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.5", - "cum": "386925.0" + "cum": "1334505.0" } } ], @@ -30878,13 +33702,13 @@ "symbol": "LTC/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 100000.0, + "maxNotional": 160000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 40.0, "info": { "bracket": "3", "initialLeverage": "40", - "notionalCap": "100000", + "notionalCap": "160000", "notionalFloor": "50000", "maintMarginRatio": "0.015", "cum": "300.0" @@ -30894,75 +33718,75 @@ "tier": 4.0, "symbol": "LTC/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 750000.0, + "minNotional": 160000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "4", "initialLeverage": "25", - "notionalCap": "750000", - "notionalFloor": "100000", + "notionalCap": "800000", + "notionalFloor": "160000", "maintMarginRatio": "0.02", - "cum": "800.0" + "cum": "1100.0" } }, { "tier": 5.0, "symbol": "LTC/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1000000.0, + "minNotional": 800000.0, + "maxNotional": 1600000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "5", "initialLeverage": "20", - "notionalCap": "1000000", - "notionalFloor": "750000", + "notionalCap": "1600000", + "notionalFloor": "800000", "maintMarginRatio": "0.025", - "cum": "4550.0" + "cum": "5100.0" } }, { "tier": 6.0, "symbol": "LTC/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 5000000.0, + "minNotional": 1600000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "6", "initialLeverage": "10", - "notionalCap": "5000000", - "notionalFloor": "1000000", + "notionalCap": "8000000", + "notionalFloor": "1600000", "maintMarginRatio": "0.05", - "cum": "29550.0" + "cum": "45100.0" } }, { "tier": 7.0, "symbol": "LTC/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 8000000.0, + "maxNotional": 16000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "7", "initialLeverage": "5", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "16000000", + "notionalFloor": "8000000", "maintMarginRatio": "0.1", - "cum": "279550.0" + "cum": "445100.0" } }, { "tier": 8.0, "symbol": "LTC/USDT:USDT", "currency": "USDT", - "minNotional": 10000000.0, + "minNotional": 16000000.0, "maxNotional": 20000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, @@ -30970,9 +33794,9 @@ "bracket": "8", "initialLeverage": "4", "notionalCap": "20000000", - "notionalFloor": "10000000", + "notionalFloor": "16000000", "maintMarginRatio": "0.125", - "cum": "529550.0" + "cum": "845100.0" } }, { @@ -30980,33 +33804,188 @@ "symbol": "LTC/USDT:USDT", "currency": "USDT", "minNotional": 20000000.0, - "maxNotional": 30000000.0, + "maxNotional": 40000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "9", "initialLeverage": "2", - "notionalCap": "30000000", + "notionalCap": "40000000", "notionalFloor": "20000000", "maintMarginRatio": "0.25", - "cum": "3029550.0" + "cum": "3345100.0" } }, { "tier": 10.0, "symbol": "LTC/USDT:USDT", "currency": "USDT", - "minNotional": 30000000.0, - "maxNotional": 50000000.0, + "minNotional": 40000000.0, + "maxNotional": 80000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "10", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "30000000", + "notionalCap": "80000000", + "notionalFloor": "40000000", "maintMarginRatio": "0.5", - "cum": "10529550.0" + "cum": "13345100.0" + } + } + ], + "LUMIA/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "LUMIA/USDT:USDT", + "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, + "symbol": "LUMIA/USDT:USDT", + "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, + "symbol": "LUMIA/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "LUMIA/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "LUMIA/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "LUMIA/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "LUMIA/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "LUMIA/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "LUMIA/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" } } ], @@ -32309,13 +35288,13 @@ "symbol": "ME/USDT:USDT", "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" @@ -32325,136 +35304,257 @@ "tier": 2.0, "symbol": "ME/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, + "minNotional": 10000.0, + "maxNotional": 30000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "10000", - "notionalFloor": "5000", + "notionalCap": "30000", + "notionalFloor": "10000", "maintMarginRatio": "0.015", - "cum": "25.0" + "cum": "50.0" } }, { "tier": 3.0, "symbol": "ME/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 30000.0, + "minNotional": 30000.0, + "maxNotional": 150000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "30000", - "notionalFloor": "10000", + "notionalCap": "150000", + "notionalFloor": "30000", "maintMarginRatio": "0.02", - "cum": "75.0" + "cum": "200.0" } }, { "tier": 4.0, "symbol": "ME/USDT:USDT", "currency": "USDT", - "minNotional": 30000.0, - "maxNotional": 60000.0, + "minNotional": 150000.0, + "maxNotional": 300000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "4", "initialLeverage": "20", - "notionalCap": "60000", - "notionalFloor": "30000", + "notionalCap": "300000", + "notionalFloor": "150000", "maintMarginRatio": "0.025", - "cum": "225.0" + "cum": "950.0" } }, { "tier": 5.0, "symbol": "ME/USDT:USDT", "currency": "USDT", - "minNotional": 60000.0, - "maxNotional": 300000.0, + "minNotional": 300000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "300000", - "notionalFloor": "60000", + "notionalCap": "1500000", + "notionalFloor": "300000", "maintMarginRatio": "0.05", - "cum": "1725.0" + "cum": "8450.0" } }, { "tier": 6.0, "symbol": "ME/USDT:USDT", "currency": "USDT", - "minNotional": 300000.0, - "maxNotional": 600000.0, + "minNotional": 1500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "600000", - "notionalFloor": "300000", + "notionalCap": "3000000", + "notionalFloor": "1500000", "maintMarginRatio": "0.1", - "cum": "16725.0" + "cum": "83450.0" } }, { "tier": 7.0, "symbol": "ME/USDT:USDT", "currency": "USDT", - "minNotional": 600000.0, - "maxNotional": 750000.0, + "minNotional": 3000000.0, + "maxNotional": 3750000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "750000", - "notionalFloor": "600000", + "notionalCap": "3750000", + "notionalFloor": "3000000", "maintMarginRatio": "0.125", - "cum": "31725.0" + "cum": "158450.0" } }, { "tier": 8.0, "symbol": "ME/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1500000.0, + "minNotional": 3750000.0, + "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "1500000", - "notionalFloor": "750000", + "notionalCap": "7500000", + "notionalFloor": "3750000", "maintMarginRatio": "0.25", - "cum": "125475.0" + "cum": "627200.0" } }, { "tier": 9.0, "symbol": "ME/USDT:USDT", "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "minNotional": 7500000.0, + "maxNotional": 15000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", + "notionalCap": "15000000", + "notionalFloor": "7500000", + "maintMarginRatio": "0.5", + "cum": "2502200.0" + } + } + ], + "MELANIA/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "MELANIA/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.02", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "MELANIA/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "MELANIA/USDT:USDT", + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "650.0" + } + }, + { + "tier": 4.0, + "symbol": "MELANIA/USDT:USDT", + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10650.0" + } + }, + { + "tier": 5.0, + "symbol": "MELANIA/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "23150.0" + } + }, + { + "tier": 6.0, + "symbol": "MELANIA/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "116900.0" + } + }, + { + "tier": 7.0, + "symbol": "MELANIA/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", "notionalCap": "3000000", "notionalFloor": "1500000", "maintMarginRatio": "0.5", - "cum": "500475.0" + "cum": "491900.0" } } ], @@ -32808,13 +35908,13 @@ "symbol": "MEW/USDT:USDT", "currency": "USDT", "minNotional": 600000.0, - "maxNotional": 3000000.0, + "maxNotional": 900000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "3000000", + "notionalCap": "900000", "notionalFloor": "600000", "maintMarginRatio": "0.05", "cum": "16850.0" @@ -32824,68 +35924,68 @@ "tier": 6.0, "symbol": "MEW/USDT:USDT", "currency": "USDT", - "minNotional": 3000000.0, - "maxNotional": 6000000.0, + "minNotional": 900000.0, + "maxNotional": 1200000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "6000000", - "notionalFloor": "3000000", + "notionalCap": "1200000", + "notionalFloor": "900000", "maintMarginRatio": "0.1", - "cum": "166850.0" + "cum": "61850.0" } }, { "tier": 7.0, "symbol": "MEW/USDT:USDT", "currency": "USDT", - "minNotional": 6000000.0, - "maxNotional": 7500000.0, + "minNotional": 1200000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "7500000", - "notionalFloor": "6000000", + "notionalCap": "1500000", + "notionalFloor": "1200000", "maintMarginRatio": "0.125", - "cum": "316850.0" + "cum": "91850.0" } }, { "tier": 8.0, "symbol": "MEW/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 15000000.0, + "minNotional": 1500000.0, + "maxNotional": 1800000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "15000000", - "notionalFloor": "7500000", + "notionalCap": "1800000", + "notionalFloor": "1500000", "maintMarginRatio": "0.25", - "cum": "1254350.0" + "cum": "279350.0" } }, { "tier": 9.0, "symbol": "MEW/USDT:USDT", "currency": "USDT", - "minNotional": 15000000.0, - "maxNotional": 30000000.0, + "minNotional": 1800000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "15000000", + "notionalCap": "2000000", + "notionalFloor": "1800000", "maintMarginRatio": "0.5", - "cum": "5004350.0" + "cum": "729350.0" } } ], @@ -33182,6 +36282,161 @@ } } ], + "MOCA/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "MOCA/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "10000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "MOCA/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "symbol": "MOCA/USDT:USDT", + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 15.0, + "info": { + "bracket": "3", + "initialLeverage": "15", + "notionalCap": "100000", + "notionalFloor": "20000", + "maintMarginRatio": "0.02", + "cum": "150.0" + } + }, + { + "tier": 4.0, + "symbol": "MOCA/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "100000", + "maintMarginRatio": "0.025", + "cum": "650.0" + } + }, + { + "tier": 5.0, + "symbol": "MOCA/USDT:USDT", + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, + "info": { + "bracket": "5", + "initialLeverage": "8", + "notionalCap": "1000000", + "notionalFloor": "200000", + "maintMarginRatio": "0.05", + "cum": "5650.0" + } + }, + { + "tier": 6.0, + "symbol": "MOCA/USDT:USDT", + "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, + "symbol": "MOCA/USDT:USDT", + "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, + "symbol": "MOCA/USDT:USDT", + "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, + "symbol": "MOCA/USDT:USDT", + "currency": "USDT", + "minNotional": 5000000.0, + "maxNotional": 5100000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "5100000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.5", + "cum": "1668150.0" + } + } + ], "MOODENG/USDT:USDT": [ { "tier": 1.0, @@ -33190,10 +36445,10 @@ "minNotional": 0.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -33207,10 +36462,10 @@ "minNotional": 10000.0, "maxNotional": 80000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "80000", "notionalFloor": "10000", "maintMarginRatio": "0.015", @@ -33224,10 +36479,10 @@ "minNotional": 80000.0, "maxNotional": 400000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "400000", "notionalFloor": "80000", "maintMarginRatio": "0.02", @@ -33241,10 +36496,10 @@ "minNotional": 400000.0, "maxNotional": 800000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "800000", "notionalFloor": "400000", "maintMarginRatio": "0.025", @@ -33258,10 +36513,10 @@ "minNotional": 800000.0, "maxNotional": 4000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "4000000", "notionalFloor": "800000", "maintMarginRatio": "0.05", @@ -33324,13 +36579,13 @@ "symbol": "MOODENG/USDT:USDT", "currency": "USDT", "minNotional": 20000000.0, - "maxNotional": 40000000.0, + "maxNotional": 21000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "40000000", + "notionalCap": "21000000", "notionalFloor": "20000000", "maintMarginRatio": "0.5", "cum": "6672450.0" @@ -33345,10 +36600,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -33362,10 +36617,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -33379,10 +36634,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -33396,10 +36651,10 @@ "minNotional": 30000.0, "maxNotional": 60000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "60000", "notionalFloor": "30000", "maintMarginRatio": "0.025", @@ -33413,10 +36668,10 @@ "minNotional": 60000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "300000", "notionalFloor": "60000", "maintMarginRatio": "0.05", @@ -33479,13 +36734,13 @@ "symbol": "MORPHO/USDT:USDT", "currency": "USDT", "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "3000000", + "notionalCap": "2000000", "notionalFloor": "1500000", "maintMarginRatio": "0.5", "cum": "500475.0" @@ -33498,13 +36753,13 @@ "symbol": "MOVE/USDT:USDT", "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" @@ -33514,136 +36769,136 @@ "tier": 2.0, "symbol": "MOVE/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, + "minNotional": 10000.0, + "maxNotional": 30000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "10000", - "notionalFloor": "5000", + "notionalCap": "30000", + "notionalFloor": "10000", "maintMarginRatio": "0.015", - "cum": "25.0" + "cum": "50.0" } }, { "tier": 3.0, "symbol": "MOVE/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 30000.0, + "minNotional": 30000.0, + "maxNotional": 150000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "30000", - "notionalFloor": "10000", + "notionalCap": "150000", + "notionalFloor": "30000", "maintMarginRatio": "0.02", - "cum": "75.0" + "cum": "200.0" } }, { "tier": 4.0, "symbol": "MOVE/USDT:USDT", "currency": "USDT", - "minNotional": 30000.0, - "maxNotional": 60000.0, + "minNotional": 150000.0, + "maxNotional": 300000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "4", "initialLeverage": "20", - "notionalCap": "60000", - "notionalFloor": "30000", + "notionalCap": "300000", + "notionalFloor": "150000", "maintMarginRatio": "0.025", - "cum": "225.0" + "cum": "950.0" } }, { "tier": 5.0, "symbol": "MOVE/USDT:USDT", "currency": "USDT", - "minNotional": 60000.0, - "maxNotional": 300000.0, + "minNotional": 300000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "300000", - "notionalFloor": "60000", + "notionalCap": "1500000", + "notionalFloor": "300000", "maintMarginRatio": "0.05", - "cum": "1725.0" + "cum": "8450.0" } }, { "tier": 6.0, "symbol": "MOVE/USDT:USDT", "currency": "USDT", - "minNotional": 300000.0, - "maxNotional": 600000.0, + "minNotional": 1500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "600000", - "notionalFloor": "300000", + "notionalCap": "3000000", + "notionalFloor": "1500000", "maintMarginRatio": "0.1", - "cum": "16725.0" + "cum": "83450.0" } }, { "tier": 7.0, "symbol": "MOVE/USDT:USDT", "currency": "USDT", - "minNotional": 600000.0, - "maxNotional": 750000.0, + "minNotional": 3000000.0, + "maxNotional": 3750000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "750000", - "notionalFloor": "600000", + "notionalCap": "3750000", + "notionalFloor": "3000000", "maintMarginRatio": "0.125", - "cum": "31725.0" + "cum": "158450.0" } }, { "tier": 8.0, "symbol": "MOVE/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1500000.0, + "minNotional": 3750000.0, + "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "1500000", - "notionalFloor": "750000", + "notionalCap": "7500000", + "notionalFloor": "3750000", "maintMarginRatio": "0.25", - "cum": "125475.0" + "cum": "627200.0" } }, { "tier": 9.0, "symbol": "MOVE/USDT:USDT", "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "minNotional": 7500000.0, + "maxNotional": 15000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "3000000", - "notionalFloor": "1500000", + "notionalCap": "15000000", + "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "500475.0" + "cum": "2502200.0" } } ], @@ -33897,10 +37152,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -33914,10 +37169,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -33931,10 +37186,10 @@ "minNotional": 10000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "50000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -33948,10 +37203,10 @@ "minNotional": 50000.0, "maxNotional": 100000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "100000", "notionalFloor": "50000", "maintMarginRatio": "0.025", @@ -33965,10 +37220,10 @@ "minNotional": 100000.0, "maxNotional": 500000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "500000", "notionalFloor": "100000", "maintMarginRatio": "0.05", @@ -33980,13 +37235,13 @@ "symbol": "MYRO/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 1000000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "1000000", + "notionalCap": "800000", "notionalFloor": "500000", "maintMarginRatio": "0.1", "cum": "27825.0" @@ -33996,51 +37251,51 @@ "tier": 7.0, "symbol": "MYRO/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 1250000.0, + "minNotional": 800000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "1250000", - "notionalFloor": "1000000", + "notionalCap": "1000000", + "notionalFloor": "800000", "maintMarginRatio": "0.125", - "cum": "52825.0" + "cum": "47825.0" } }, { "tier": 8.0, "symbol": "MYRO/USDT:USDT", "currency": "USDT", - "minNotional": 1250000.0, - "maxNotional": 2500000.0, + "minNotional": 1000000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "2500000", - "notionalFloor": "1250000", + "notionalCap": "1500000", + "notionalFloor": "1000000", "maintMarginRatio": "0.25", - "cum": "209075.0" + "cum": "172825.0" } }, { "tier": 9.0, "symbol": "MYRO/USDT:USDT", "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 1500000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "2000000", + "notionalFloor": "1500000", "maintMarginRatio": "0.5", - "cum": "834075.0" + "cum": "547825.0" } } ], @@ -34307,13 +37562,13 @@ "symbol": "NEAR/USDT:USDT", "currency": "USDT", "minNotional": 25000000.0, - "maxNotional": 50000000.0, + "maxNotional": 30000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "50000000", + "notionalCap": "30000000", "notionalFloor": "25000000", "maintMarginRatio": "0.25", "cum": "4181100.0" @@ -34323,17 +37578,17 @@ "tier": 9.0, "symbol": "NEAR/USDT:USDT", "currency": "USDT", - "minNotional": 50000000.0, - "maxNotional": 100000000.0, + "minNotional": 30000000.0, + "maxNotional": 35000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "100000000", - "notionalFloor": "50000000", + "notionalCap": "35000000", + "notionalFloor": "30000000", "maintMarginRatio": "0.5", - "cum": "16681100.0" + "cum": "11681100.0" } } ], @@ -34500,10 +37755,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 15.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "15", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -34517,10 +37772,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 12.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "12", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -34534,10 +37789,10 @@ "minNotional": 10000.0, "maxNotional": 20000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 10.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "10", "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -34551,10 +37806,10 @@ "minNotional": 20000.0, "maxNotional": 40000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 8.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "8", "notionalCap": "40000", "notionalFloor": "20000", "maintMarginRatio": "0.025", @@ -34568,10 +37823,10 @@ "minNotional": 40000.0, "maxNotional": 200000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 6.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "6", "notionalCap": "200000", "notionalFloor": "40000", "maintMarginRatio": "0.05", @@ -34617,13 +37872,13 @@ "symbol": "NEIROETH/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 1000000.0, + "maxNotional": 700000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "1000000", + "notionalCap": "700000", "notionalFloor": "500000", "maintMarginRatio": "0.25", "cum": "83675.0" @@ -34633,17 +37888,17 @@ "tier": 9.0, "symbol": "NEIROETH/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 700000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalCap": "1000000", + "notionalFloor": "700000", "maintMarginRatio": "0.5", - "cum": "333675.0" + "cum": "258675.0" } } ], @@ -35360,13 +38615,13 @@ "symbol": "NOT/USDT:USDT", "currency": "USDT", "minNotional": 600000.0, - "maxNotional": 3000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "3000000", + "notionalCap": "2000000", "notionalFloor": "600000", "maintMarginRatio": "0.05", "cum": "16850.0" @@ -35376,68 +38631,68 @@ "tier": 6.0, "symbol": "NOT/USDT:USDT", "currency": "USDT", - "minNotional": 3000000.0, - "maxNotional": 6000000.0, + "minNotional": 2000000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "6000000", - "notionalFloor": "3000000", + "notionalCap": "3000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.1", - "cum": "166850.0" + "cum": "116850.0" } }, { "tier": 7.0, "symbol": "NOT/USDT:USDT", "currency": "USDT", - "minNotional": 6000000.0, - "maxNotional": 7500000.0, + "minNotional": 3000000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "7500000", - "notionalFloor": "6000000", + "notionalCap": "4000000", + "notionalFloor": "3000000", "maintMarginRatio": "0.125", - "cum": "316850.0" + "cum": "191850.0" } }, { "tier": 8.0, "symbol": "NOT/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 15000000.0, + "minNotional": 4000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "15000000", - "notionalFloor": "7500000", + "notionalCap": "5000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.25", - "cum": "1254350.0" + "cum": "691850.0" } }, { "tier": 9.0, "symbol": "NOT/USDT:USDT", "currency": "USDT", - "minNotional": 15000000.0, - "maxNotional": 30000000.0, + "minNotional": 5000000.0, + "maxNotional": 6000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "15000000", + "notionalCap": "6000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", - "cum": "5004350.0" + "cum": "1941850.0" } } ], @@ -35967,10 +39222,10 @@ "minNotional": 0.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 10.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "10", "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -35984,10 +39239,10 @@ "minNotional": 10000.0, "maxNotional": 20000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 9.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "9", "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.015", @@ -36001,10 +39256,10 @@ "minNotional": 20000.0, "maxNotional": 100000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 8.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "8", "notionalCap": "100000", "notionalFloor": "20000", "maintMarginRatio": "0.02", @@ -36018,10 +39273,10 @@ "minNotional": 100000.0, "maxNotional": 200000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 7.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "7", "notionalCap": "200000", "notionalFloor": "100000", "maintMarginRatio": "0.025", @@ -36035,10 +39290,10 @@ "minNotional": 200000.0, "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 6.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "6", "notionalCap": "1000000", "notionalFloor": "200000", "maintMarginRatio": "0.05", @@ -36101,13 +39356,13 @@ "symbol": "OM/USDT:USDT", "currency": "USDT", "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "maxNotional": 5100000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "10000000", + "notionalCap": "5100000", "notionalFloor": "5000000", "maintMarginRatio": "0.5", "cum": "1668150.0" @@ -36396,13 +39651,13 @@ "symbol": "ONDO/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 40000.0, + "maxNotional": 80000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "40000", + "notionalCap": "80000", "notionalFloor": "10000", "maintMarginRatio": "0.015", "cum": "50.0" @@ -36412,119 +39667,119 @@ "tier": 3.0, "symbol": "ONDO/USDT:USDT", "currency": "USDT", - "minNotional": 40000.0, - "maxNotional": 200000.0, + "minNotional": 80000.0, + "maxNotional": 400000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "200000", - "notionalFloor": "40000", + "notionalCap": "400000", + "notionalFloor": "80000", "maintMarginRatio": "0.02", - "cum": "250.0" + "cum": "450.0" } }, { "tier": 4.0, "symbol": "ONDO/USDT:USDT", "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 400000.0, + "minNotional": 400000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "4", "initialLeverage": "20", - "notionalCap": "400000", - "notionalFloor": "200000", + "notionalCap": "800000", + "notionalFloor": "400000", "maintMarginRatio": "0.025", - "cum": "1250.0" + "cum": "2450.0" } }, { "tier": 5.0, "symbol": "ONDO/USDT:USDT", "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 2000000.0, + "minNotional": 800000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "2000000", - "notionalFloor": "400000", + "notionalCap": "4000000", + "notionalFloor": "800000", "maintMarginRatio": "0.05", - "cum": "11250.0" + "cum": "22450.0" } }, { "tier": 6.0, "symbol": "ONDO/USDT:USDT", "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 4000000.0, + "minNotional": 4000000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "4000000", - "notionalFloor": "2000000", + "notionalCap": "8000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.1", - "cum": "111250.0" + "cum": "222450.0" } }, { "tier": 7.0, "symbol": "ONDO/USDT:USDT", "currency": "USDT", - "minNotional": 4000000.0, - "maxNotional": 5000000.0, + "minNotional": 8000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "4000000", + "notionalCap": "10000000", + "notionalFloor": "8000000", "maintMarginRatio": "0.125", - "cum": "211250.0" + "cum": "422450.0" } }, { "tier": 8.0, "symbol": "ONDO/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.25", - "cum": "836250.0" + "cum": "1672450.0" } }, { "tier": 9.0, "symbol": "ONDO/USDT:USDT", "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 20000000.0, + "minNotional": 20000000.0, + "maxNotional": 40000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "notionalCap": "40000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.5", - "cum": "3336250.0" + "cum": "6672450.0" } } ], @@ -36535,14 +39790,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" } }, @@ -36551,15 +39806,15 @@ "symbol": "ONE/USDT:USDT", "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" } }, @@ -36567,102 +39822,119 @@ "tier": 3.0, "symbol": "ONE/USDT:USDT", "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, "symbol": "ONE/USDT:USDT", "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, "symbol": "ONE/USDT:USDT", "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, "symbol": "ONE/USDT:USDT", "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, "symbol": "ONE/USDT:USDT", "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, "symbol": "ONE/USDT:USDT", "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, + "symbol": "ONE/USDT:USDT", + "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" } } ], @@ -36795,10 +40067,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.015", @@ -36812,10 +40084,10 @@ "minNotional": 5000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 15.0, "info": { "bracket": "2", - "initialLeverage": "20", + "initialLeverage": "15", "notionalCap": "50000", "notionalFloor": "5000", "maintMarginRatio": "0.025", @@ -36844,13 +40116,13 @@ "symbol": "ONT/USDT:USDT", "currency": "USDT", "minNotional": 600000.0, - "maxNotional": 1600000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "1600000", + "notionalCap": "1000000", "notionalFloor": "600000", "maintMarginRatio": "0.1", "cum": "31300.0" @@ -36860,51 +40132,51 @@ "tier": 5.0, "symbol": "ONT/USDT:USDT", "currency": "USDT", - "minNotional": 1600000.0, - "maxNotional": 2000000.0, + "minNotional": 1000000.0, + "maxNotional": 1200000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "2000000", - "notionalFloor": "1600000", + "notionalCap": "1200000", + "notionalFloor": "1000000", "maintMarginRatio": "0.125", - "cum": "71300.0" + "cum": "56300.0" } }, { "tier": 6.0, "symbol": "ONT/USDT:USDT", "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 6000000.0, + "minNotional": 1200000.0, + "maxNotional": 1300000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "6000000", - "notionalFloor": "2000000", + "notionalCap": "1300000", + "notionalFloor": "1200000", "maintMarginRatio": "0.25", - "cum": "321300.0" + "cum": "206300.0" } }, { "tier": 7.0, "symbol": "ONT/USDT:USDT", "currency": "USDT", - "minNotional": 6000000.0, - "maxNotional": 10000000.0, + "minNotional": 1300000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "10000000", - "notionalFloor": "6000000", + "notionalCap": "1500000", + "notionalFloor": "1300000", "maintMarginRatio": "0.5", - "cum": "1821300.0" + "cum": "531300.0" } } ], @@ -38010,6 +41282,161 @@ } } ], + "PENGU/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "PENGU/USDT:USDT", + "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, + "symbol": "PENGU/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": "2", + "initialLeverage": "50", + "notionalCap": "60000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "symbol": "PENGU/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.02", + "cum": "350.0" + } + }, + { + "tier": 4.0, + "symbol": "PENGU/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.025", + "cum": "1850.0" + } + }, + { + "tier": 5.0, + "symbol": "PENGU/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "3000000", + "notionalFloor": "600000", + "maintMarginRatio": "0.05", + "cum": "16850.0" + } + }, + { + "tier": 6.0, + "symbol": "PENGU/USDT:USDT", + "currency": "USDT", + "minNotional": 3000000.0, + "maxNotional": 6000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "6000000", + "notionalFloor": "3000000", + "maintMarginRatio": "0.1", + "cum": "166850.0" + } + }, + { + "tier": 7.0, + "symbol": "PENGU/USDT:USDT", + "currency": "USDT", + "minNotional": 6000000.0, + "maxNotional": 7500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "7500000", + "notionalFloor": "6000000", + "maintMarginRatio": "0.125", + "cum": "316850.0" + } + }, + { + "tier": 8.0, + "symbol": "PENGU/USDT:USDT", + "currency": "USDT", + "minNotional": 7500000.0, + "maxNotional": 15000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "15000000", + "notionalFloor": "7500000", + "maintMarginRatio": "0.25", + "cum": "1254350.0" + } + }, + { + "tier": 9.0, + "symbol": "PENGU/USDT:USDT", + "currency": "USDT", + "minNotional": 15000000.0, + "maxNotional": 30000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "30000000", + "notionalFloor": "15000000", + "maintMarginRatio": "0.5", + "cum": "5004350.0" + } + } + ], "PEOPLE/USDT:USDT": [ { "tier": 1.0, @@ -38084,13 +41511,13 @@ "symbol": "PEOPLE/USDT:USDT", "currency": "USDT", "minNotional": 400000.0, - "maxNotional": 2000000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "2000000", + "notionalCap": "1500000", "notionalFloor": "400000", "maintMarginRatio": "0.05", "cum": "11250.0" @@ -38100,68 +41527,68 @@ "tier": 6.0, "symbol": "PEOPLE/USDT:USDT", "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 4000000.0, + "minNotional": 1500000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "4000000", - "notionalFloor": "2000000", + "notionalCap": "2000000", + "notionalFloor": "1500000", "maintMarginRatio": "0.1", - "cum": "111250.0" + "cum": "86250.0" } }, { "tier": 7.0, "symbol": "PEOPLE/USDT:USDT", "currency": "USDT", - "minNotional": 4000000.0, - "maxNotional": 5000000.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "4000000", + "notionalCap": "2500000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", - "cum": "211250.0" + "cum": "136250.0" } }, { "tier": 8.0, "symbol": "PEOPLE/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 2500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "3000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.25", - "cum": "836250.0" + "cum": "448750.0" } }, { "tier": 9.0, "symbol": "PEOPLE/USDT:USDT", "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 20000000.0, + "minNotional": 3000000.0, + "maxNotional": 3500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "notionalCap": "3500000", + "notionalFloor": "3000000", "maintMarginRatio": "0.5", - "cum": "3336250.0" + "cum": "1198750.0" } } ], @@ -38286,6 +41713,161 @@ } } ], + "PHA/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "PHA/USDT:USDT", + "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, + "symbol": "PHA/USDT:USDT", + "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, + "symbol": "PHA/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "PHA/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "PHA/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "PHA/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "PHA/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "PHA/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "PHA/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], "PHB/USDT:USDT": [ { "tier": 1.0, @@ -38390,6 +41972,127 @@ } } ], + "PIPPIN/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "PIPPIN/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.02", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "PIPPIN/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "PIPPIN/USDT:USDT", + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "650.0" + } + }, + { + "tier": 4.0, + "symbol": "PIPPIN/USDT:USDT", + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10650.0" + } + }, + { + "tier": 5.0, + "symbol": "PIPPIN/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "23150.0" + } + }, + { + "tier": 6.0, + "symbol": "PIPPIN/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "116900.0" + } + }, + { + "tier": 7.0, + "symbol": "PIPPIN/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "491900.0" + } + } + ], "PIXEL/USDT:USDT": [ { "tier": 1.0, @@ -38551,13 +42254,13 @@ "symbol": "PNUT/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 10000.0, + "maxNotional": 20000.0, "maintenanceMarginRate": 0.01, "maxLeverage": 75.0, "info": { "bracket": "1", "initialLeverage": "75", - "notionalCap": "10000", + "notionalCap": "20000", "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" @@ -38567,136 +42270,136 @@ "tier": 2.0, "symbol": "PNUT/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 100000.0, + "minNotional": 20000.0, + "maxNotional": 160000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": "2", "initialLeverage": "50", - "notionalCap": "100000", - "notionalFloor": "10000", + "notionalCap": "160000", + "notionalFloor": "20000", "maintMarginRatio": "0.015", - "cum": "50.0" + "cum": "100.0" } }, { "tier": 3.0, "symbol": "PNUT/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 500000.0, + "minNotional": 160000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "3", "initialLeverage": "25", - "notionalCap": "500000", - "notionalFloor": "100000", + "notionalCap": "800000", + "notionalFloor": "160000", "maintMarginRatio": "0.02", - "cum": "550.0" + "cum": "900.0" } }, { "tier": 4.0, "symbol": "PNUT/USDT:USDT", "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 800000.0, + "maxNotional": 1600000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "4", "initialLeverage": "20", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "1600000", + "notionalFloor": "800000", "maintMarginRatio": "0.025", - "cum": "3050.0" + "cum": "4900.0" } }, { "tier": 5.0, "symbol": "PNUT/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 5000000.0, + "minNotional": 1600000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "5000000", - "notionalFloor": "1000000", + "notionalCap": "8000000", + "notionalFloor": "1600000", "maintMarginRatio": "0.05", - "cum": "28050.0" + "cum": "44900.0" } }, { "tier": 6.0, "symbol": "PNUT/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 8000000.0, + "maxNotional": 16000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "16000000", + "notionalFloor": "8000000", "maintMarginRatio": "0.1", - "cum": "278050.0" + "cum": "444900.0" } }, { "tier": 7.0, "symbol": "PNUT/USDT:USDT", "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 12500000.0, + "minNotional": 16000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "12500000", - "notionalFloor": "10000000", + "notionalCap": "20000000", + "notionalFloor": "16000000", "maintMarginRatio": "0.125", - "cum": "528050.0" + "cum": "844900.0" } }, { "tier": 8.0, "symbol": "PNUT/USDT:USDT", "currency": "USDT", - "minNotional": 12500000.0, - "maxNotional": 25000000.0, + "minNotional": 20000000.0, + "maxNotional": 40000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "25000000", - "notionalFloor": "12500000", + "notionalCap": "40000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.25", - "cum": "2090550.0" + "cum": "3344900.0" } }, { "tier": 9.0, "symbol": "PNUT/USDT:USDT", "currency": "USDT", - "minNotional": 25000000.0, - "maxNotional": 50000000.0, + "minNotional": 40000000.0, + "maxNotional": 80000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "25000000", + "notionalCap": "80000000", + "notionalFloor": "40000000", "maintMarginRatio": "0.5", - "cum": "8340550.0" + "cum": "13344900.0" } } ], @@ -39001,10 +42704,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -39018,10 +42721,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -39035,10 +42738,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -39052,10 +42755,10 @@ "minNotional": 30000.0, "maxNotional": 60000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "60000", "notionalFloor": "30000", "maintMarginRatio": "0.025", @@ -39069,10 +42772,10 @@ "minNotional": 60000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "300000", "notionalFloor": "60000", "maintMarginRatio": "0.05", @@ -39135,13 +42838,13 @@ "symbol": "PONKE/USDT:USDT", "currency": "USDT", "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "3000000", + "notionalCap": "2000000", "notionalFloor": "1500000", "maintMarginRatio": "0.5", "cum": "500475.0" @@ -39156,10 +42859,10 @@ "minNotional": 0.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -39173,10 +42876,10 @@ "minNotional": 10000.0, "maxNotional": 100000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "100000", "notionalFloor": "10000", "maintMarginRatio": "0.015", @@ -39190,10 +42893,10 @@ "minNotional": 100000.0, "maxNotional": 500000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "500000", "notionalFloor": "100000", "maintMarginRatio": "0.02", @@ -39207,10 +42910,10 @@ "minNotional": 500000.0, "maxNotional": 1000000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "1000000", "notionalFloor": "500000", "maintMarginRatio": "0.025", @@ -39224,10 +42927,10 @@ "minNotional": 1000000.0, "maxNotional": 5000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "5000000", "notionalFloor": "1000000", "maintMarginRatio": "0.05", @@ -39290,13 +42993,13 @@ "symbol": "POPCAT/USDT:USDT", "currency": "USDT", "minNotional": 25000000.0, - "maxNotional": 50000000.0, + "maxNotional": 30000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "50000000", + "notionalCap": "30000000", "notionalFloor": "25000000", "maintMarginRatio": "0.5", "cum": "8340550.0" @@ -39545,6 +43248,161 @@ } } ], + "PROM/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "PROM/USDT:USDT", + "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, + "symbol": "PROM/USDT:USDT", + "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, + "symbol": "PROM/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "PROM/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "PROM/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "PROM/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "PROM/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "PROM/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "PROM/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], "PYTH/USDT:USDT": [ { "tier": 1.0, @@ -40912,13 +44770,13 @@ "symbol": "REEF/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 1000000.0, + "maxNotional": 600000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "1000000", + "notionalCap": "600000", "notionalFloor": "500000", "maintMarginRatio": "0.25", "cum": "83225.0" @@ -40928,17 +44786,17 @@ "tier": 7.0, "symbol": "REEF/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 1500000.0, + "minNotional": 600000.0, + "maxNotional": 650000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "1500000", - "notionalFloor": "1000000", + "notionalCap": "650000", + "notionalFloor": "600000", "maintMarginRatio": "0.5", - "cum": "333225.0" + "cum": "233225.0" } } ], @@ -40950,10 +44808,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -40967,10 +44825,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -40984,10 +44842,10 @@ "minNotional": 10000.0, "maxNotional": 20000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -41001,10 +44859,10 @@ "minNotional": 20000.0, "maxNotional": 40000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "40000", "notionalFloor": "20000", "maintMarginRatio": "0.025", @@ -41018,10 +44876,10 @@ "minNotional": 40000.0, "maxNotional": 200000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "200000", "notionalFloor": "40000", "maintMarginRatio": "0.05", @@ -41084,13 +44942,13 @@ "symbol": "REI/USDT:USDT", "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "2000000", + "notionalCap": "1500000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "333675.0" @@ -42347,10 +46205,10 @@ "minNotional": 0.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -42364,10 +46222,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.015", @@ -42381,10 +46239,10 @@ "minNotional": 30000.0, "maxNotional": 150000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "150000", "notionalFloor": "30000", "maintMarginRatio": "0.02", @@ -42398,10 +46256,10 @@ "minNotional": 150000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "300000", "notionalFloor": "150000", "maintMarginRatio": "0.025", @@ -42415,10 +46273,10 @@ "minNotional": 300000.0, "maxNotional": 1500000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "1500000", "notionalFloor": "300000", "maintMarginRatio": "0.05", @@ -42430,13 +46288,13 @@ "symbol": "RUNE/USDT:USDT", "currency": "USDT", "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "3000000", + "notionalCap": "2000000", "notionalFloor": "1500000", "maintMarginRatio": "0.1", "cum": "83450.0" @@ -42446,51 +46304,51 @@ "tier": 7.0, "symbol": "RUNE/USDT:USDT", "currency": "USDT", - "minNotional": 3000000.0, - "maxNotional": 3750000.0, + "minNotional": 2000000.0, + "maxNotional": 2200000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "3750000", - "notionalFloor": "3000000", + "notionalCap": "2200000", + "notionalFloor": "2000000", "maintMarginRatio": "0.125", - "cum": "158450.0" + "cum": "133450.0" } }, { "tier": 8.0, "symbol": "RUNE/USDT:USDT", "currency": "USDT", - "minNotional": 3750000.0, - "maxNotional": 7500000.0, + "minNotional": 2200000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "7500000", - "notionalFloor": "3750000", + "notionalCap": "2500000", + "notionalFloor": "2200000", "maintMarginRatio": "0.25", - "cum": "627200.0" + "cum": "408450.0" } }, { "tier": 9.0, "symbol": "RUNE/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 15000000.0, + "minNotional": 2500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "15000000", - "notionalFloor": "7500000", + "notionalCap": "3000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "2502200.0" + "cum": "1033450.0" } } ], @@ -42632,6 +46490,161 @@ } } ], + "S/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "S/USDT:USDT", + "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, + "symbol": "S/USDT:USDT", + "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, + "symbol": "S/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "S/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "S/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "S/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "S/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "S/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "S/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], "SAFE/USDT:USDT": [ { "tier": 1.0, @@ -42948,13 +46961,13 @@ "symbol": "SAND/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.006, - "maxLeverage": 50.0, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "75", + "notionalCap": "25000", "notionalFloor": "0", "maintMarginRatio": "0.006", "cum": "0.0" @@ -42964,136 +46977,153 @@ "tier": 2.0, "symbol": "SAND/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.007, - "maxLeverage": 40.0, + "minNotional": 25000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, "info": { "bracket": "2", - "initialLeverage": "40", - "notionalCap": "25000", - "notionalFloor": "5000", - "maintMarginRatio": "0.007", - "cum": "5.0" + "initialLeverage": "50", + "notionalCap": "50000", + "notionalFloor": "25000", + "maintMarginRatio": "0.01", + "cum": "100.0" } }, { "tier": 3.0, "symbol": "SAND/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 25.0, + "minNotional": 50000.0, + "maxNotional": 80000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 40.0, "info": { "bracket": "3", - "initialLeverage": "25", - "notionalCap": "50000", - "notionalFloor": "25000", - "maintMarginRatio": "0.01", - "cum": "80.0" + "initialLeverage": "40", + "notionalCap": "80000", + "notionalFloor": "50000", + "maintMarginRatio": "0.015", + "cum": "350.0" } }, { "tier": 4.0, "symbol": "SAND/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 400000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 80000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "4", - "initialLeverage": "20", - "notionalCap": "400000", - "notionalFloor": "50000", - "maintMarginRatio": "0.025", - "cum": "830.0" + "initialLeverage": "25", + "notionalCap": "300000", + "notionalFloor": "80000", + "maintMarginRatio": "0.02", + "cum": "750.0" } }, { "tier": 5.0, "symbol": "SAND/USDT:USDT", "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 800000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "5", - "initialLeverage": "10", - "notionalCap": "800000", - "notionalFloor": "400000", - "maintMarginRatio": "0.05", - "cum": "10830.0" + "initialLeverage": "20", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.025", + "cum": "2250.0" } }, { "tier": 6.0, "symbol": "SAND/USDT:USDT", "currency": "USDT", - "minNotional": 800000.0, - "maxNotional": 2000000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 600000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "6", - "initialLeverage": "5", - "notionalCap": "2000000", - "notionalFloor": "800000", - "maintMarginRatio": "0.1", - "cum": "50830.0" + "initialLeverage": "10", + "notionalCap": "3000000", + "notionalFloor": "600000", + "maintMarginRatio": "0.05", + "cum": "17250.0" } }, { "tier": 7.0, "symbol": "SAND/USDT:USDT", "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 5000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 3000000.0, + "maxNotional": 6000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "7", - "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", - "maintMarginRatio": "0.125", - "cum": "100830.0" + "initialLeverage": "5", + "notionalCap": "6000000", + "notionalFloor": "3000000", + "maintMarginRatio": "0.1", + "cum": "167250.0" } }, { "tier": 8.0, "symbol": "SAND/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 12000000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, + "minNotional": 6000000.0, + "maxNotional": 7500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "8", - "initialLeverage": "2", - "notionalCap": "12000000", - "notionalFloor": "5000000", - "maintMarginRatio": "0.25", - "cum": "725830.0" + "initialLeverage": "4", + "notionalCap": "7500000", + "notionalFloor": "6000000", + "maintMarginRatio": "0.125", + "cum": "317250.0" } }, { "tier": 9.0, "symbol": "SAND/USDT:USDT", "currency": "USDT", - "minNotional": 12000000.0, - "maxNotional": 20000000.0, + "minNotional": 7500000.0, + "maxNotional": 15000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "9", + "initialLeverage": "2", + "notionalCap": "15000000", + "notionalFloor": "7500000", + "maintMarginRatio": "0.25", + "cum": "1254750.0" + } + }, + { + "tier": 10.0, + "symbol": "SAND/USDT:USDT", + "currency": "USDT", + "minNotional": 15000000.0, + "maxNotional": 30000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "9", + "bracket": "10", "initialLeverage": "1", - "notionalCap": "20000000", - "notionalFloor": "12000000", + "notionalCap": "30000000", + "notionalFloor": "15000000", "maintMarginRatio": "0.5", - "cum": "3725830.0" + "cum": "5004750.0" } } ], @@ -43774,13 +47804,13 @@ "symbol": "SEI/USDT:USDT", "currency": "USDT", "minNotional": 10000000.0, - "maxNotional": 12500000.0, + "maxNotional": 11000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "12500000", + "notionalCap": "11000000", "notionalFloor": "10000000", "maintMarginRatio": "0.125", "cum": "528050.0" @@ -43790,34 +47820,34 @@ "tier": 8.0, "symbol": "SEI/USDT:USDT", "currency": "USDT", - "minNotional": 12500000.0, - "maxNotional": 25000000.0, + "minNotional": 11000000.0, + "maxNotional": 12000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "25000000", - "notionalFloor": "12500000", + "notionalCap": "12000000", + "notionalFloor": "11000000", "maintMarginRatio": "0.25", - "cum": "2090550.0" + "cum": "1903050.0" } }, { "tier": 9.0, "symbol": "SEI/USDT:USDT", "currency": "USDT", - "minNotional": 25000000.0, - "maxNotional": 50000000.0, + "minNotional": 12000000.0, + "maxNotional": 13000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "25000000", + "notionalCap": "13000000", + "notionalFloor": "12000000", "maintMarginRatio": "0.5", - "cum": "8340550.0" + "cum": "4903050.0" } } ], @@ -44088,10 +48118,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -44105,10 +48135,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -44122,10 +48152,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -44139,10 +48169,10 @@ "minNotional": 30000.0, "maxNotional": 60000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "60000", "notionalFloor": "30000", "maintMarginRatio": "0.025", @@ -44156,10 +48186,10 @@ "minNotional": 60000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "300000", "notionalFloor": "60000", "maintMarginRatio": "0.05", @@ -44205,13 +48235,13 @@ "symbol": "SLERF/USDT:USDT", "currency": "USDT", "minNotional": 750000.0, - "maxNotional": 1500000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "1500000", + "notionalCap": "1000000", "notionalFloor": "750000", "maintMarginRatio": "0.25", "cum": "125475.0" @@ -44221,17 +48251,17 @@ "tier": 9.0, "symbol": "SLERF/USDT:USDT", "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "minNotional": 1000000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "3000000", - "notionalFloor": "1500000", + "notionalCap": "1500000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", - "cum": "500475.0" + "cum": "375475.0" } } ], @@ -44959,6 +48989,316 @@ } } ], + "SOLV/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "SOLV/USDT:USDT", + "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, + "symbol": "SOLV/USDT:USDT", + "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, + "symbol": "SOLV/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "SOLV/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "SOLV/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "SOLV/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "SOLV/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "SOLV/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "SOLV/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], + "SONIC/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "SONIC/USDT:USDT", + "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, + "symbol": "SONIC/USDT:USDT", + "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, + "symbol": "SONIC/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "SONIC/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "SONIC/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "SONIC/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "SONIC/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "SONIC/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "SONIC/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], "SPELL/USDT:USDT": [ { "tier": 1.0, @@ -44967,10 +49307,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.015", @@ -44984,10 +49324,10 @@ "minNotional": 5000.0, "maxNotional": 20000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "20000", "notionalFloor": "5000", "maintMarginRatio": "0.02", @@ -45001,10 +49341,10 @@ "minNotional": 20000.0, "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "3", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "25000", "notionalFloor": "20000", "maintMarginRatio": "0.025", @@ -45018,10 +49358,10 @@ "minNotional": 25000.0, "maxNotional": 200000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "4", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "200000", "notionalFloor": "25000", "maintMarginRatio": "0.05", @@ -45084,13 +49424,13 @@ "symbol": "SPELL/USDT:USDT", "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "2000000", + "notionalCap": "1500000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "333250.0" @@ -45105,10 +49445,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -45122,10 +49462,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -45139,10 +49479,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -45156,10 +49496,10 @@ "minNotional": 30000.0, "maxNotional": 60000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "60000", "notionalFloor": "30000", "maintMarginRatio": "0.025", @@ -45173,10 +49513,10 @@ "minNotional": 60000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "300000", "notionalFloor": "60000", "maintMarginRatio": "0.05", @@ -45239,13 +49579,13 @@ "symbol": "SPX/USDT:USDT", "currency": "USDT", "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "3000000", + "notionalCap": "2000000", "notionalFloor": "1500000", "maintMarginRatio": "0.5", "cum": "500475.0" @@ -45415,10 +49755,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.015", @@ -45432,10 +49772,10 @@ "minNotional": 5000.0, "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "2", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "25000", "notionalFloor": "5000", "maintMarginRatio": "0.025", @@ -45449,10 +49789,10 @@ "minNotional": 25000.0, "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "3", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "100000", "notionalFloor": "25000", "maintMarginRatio": "0.05", @@ -45657,10 +49997,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "1", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.02", @@ -45674,10 +50014,10 @@ "minNotional": 5000.0, "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "2", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "25000", "notionalFloor": "5000", "maintMarginRatio": "0.025", @@ -45691,10 +50031,10 @@ "minNotional": 25000.0, "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 8.0, + "maxLeverage": 6.0, "info": { "bracket": "3", - "initialLeverage": "8", + "initialLeverage": "6", "notionalCap": "100000", "notionalFloor": "25000", "maintMarginRatio": "0.05", @@ -45740,13 +50080,13 @@ "symbol": "STMX/USDT:USDT", "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 3000000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "3000000", + "notionalCap": "1500000", "notionalFloor": "1000000", "maintMarginRatio": "0.5", "cum": "386900.0" @@ -46262,10 +50602,10 @@ "minNotional": 0.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -46279,10 +50619,10 @@ "minNotional": 10000.0, "maxNotional": 60000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "60000", "notionalFloor": "10000", "maintMarginRatio": "0.015", @@ -46296,10 +50636,10 @@ "minNotional": 60000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "300000", "notionalFloor": "60000", "maintMarginRatio": "0.02", @@ -46313,10 +50653,10 @@ "minNotional": 300000.0, "maxNotional": 600000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "600000", "notionalFloor": "300000", "maintMarginRatio": "0.025", @@ -46330,10 +50670,10 @@ "minNotional": 600000.0, "maxNotional": 3000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "3000000", "notionalFloor": "600000", "maintMarginRatio": "0.05", @@ -46345,13 +50685,13 @@ "symbol": "STX/USDT:USDT", "currency": "USDT", "minNotional": 3000000.0, - "maxNotional": 6000000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "6000000", + "notionalCap": "4000000", "notionalFloor": "3000000", "maintMarginRatio": "0.1", "cum": "166850.0" @@ -46361,51 +50701,51 @@ "tier": 7.0, "symbol": "STX/USDT:USDT", "currency": "USDT", - "minNotional": 6000000.0, - "maxNotional": 7500000.0, + "minNotional": 4000000.0, + "maxNotional": 4500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "7500000", - "notionalFloor": "6000000", + "notionalCap": "4500000", + "notionalFloor": "4000000", "maintMarginRatio": "0.125", - "cum": "316850.0" + "cum": "266850.0" } }, { "tier": 8.0, "symbol": "STX/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 15000000.0, + "minNotional": 4500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "15000000", - "notionalFloor": "7500000", + "notionalCap": "5000000", + "notionalFloor": "4500000", "maintMarginRatio": "0.25", - "cum": "1254350.0" + "cum": "829350.0" } }, { "tier": 9.0, "symbol": "STX/USDT:USDT", "currency": "USDT", - "minNotional": 15000000.0, - "maxNotional": 30000000.0, + "minNotional": 5000000.0, + "maxNotional": 5500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "15000000", + "notionalCap": "5500000", + "notionalFloor": "5000000", "maintMarginRatio": "0.5", - "cum": "5004350.0" + "cum": "2079350.0" } } ], @@ -47133,6 +51473,161 @@ } } ], + "SWARMS/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "SWARMS/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "SWARMS/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "SWARMS/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 15.0, + "info": { + "bracket": "3", + "initialLeverage": "15", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "SWARMS/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "SWARMS/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, + "info": { + "bracket": "5", + "initialLeverage": "8", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "SWARMS/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "SWARMS/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "SWARMS/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "SWARMS/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "2000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], "SWELL/USDT:USDT": [ { "tier": 1.0, @@ -47141,10 +51636,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -47158,10 +51653,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.015", @@ -47175,10 +51670,10 @@ "minNotional": 10000.0, "maxNotional": 30000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "30000", "notionalFloor": "10000", "maintMarginRatio": "0.02", @@ -47192,10 +51687,10 @@ "minNotional": 30000.0, "maxNotional": 60000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "60000", "notionalFloor": "30000", "maintMarginRatio": "0.025", @@ -47209,10 +51704,10 @@ "minNotional": 60000.0, "maxNotional": 300000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "300000", "notionalFloor": "60000", "maintMarginRatio": "0.05", @@ -47258,13 +51753,13 @@ "symbol": "SWELL/USDT:USDT", "currency": "USDT", "minNotional": 750000.0, - "maxNotional": 1500000.0, + "maxNotional": 1000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "1500000", + "notionalCap": "1000000", "notionalFloor": "750000", "maintMarginRatio": "0.25", "cum": "125475.0" @@ -47274,17 +51769,17 @@ "tier": 9.0, "symbol": "SWELL/USDT:USDT", "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "minNotional": 1000000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "3000000", - "notionalFloor": "1500000", + "notionalCap": "1500000", + "notionalFloor": "1000000", "maintMarginRatio": "0.5", - "cum": "500475.0" + "cum": "375475.0" } } ], @@ -48224,13 +52719,13 @@ "symbol": "THETA/USDT:USDT", "currency": "USDT", "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "4", "initialLeverage": "5", - "notionalCap": "2000000", + "notionalCap": "1500000", "notionalFloor": "1000000", "maintMarginRatio": "0.1", "cum": "57750.0" @@ -48240,68 +52735,68 @@ "tier": 5.0, "symbol": "THETA/USDT:USDT", "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 5000000.0, + "minNotional": 1500000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "2000000", + "notionalCap": "2000000", + "notionalFloor": "1500000", "maintMarginRatio": "0.125", - "cum": "107750.0" + "cum": "95250.0" } }, { "tier": 6.0, "symbol": "THETA/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 2000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.1665, "maxLeverage": 3.0, "info": { "bracket": "6", "initialLeverage": "3", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "2500000", + "notionalFloor": "2000000", "maintMarginRatio": "0.1665", - "cum": "315250.0" + "cum": "178250.0" } }, { "tier": 7.0, "symbol": "THETA/USDT:USDT", "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 20000000.0, + "minNotional": 2500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "notionalCap": "3000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.25", - "cum": "1150250.0" + "cum": "387000.0" } }, { "tier": 8.0, "symbol": "THETA/USDT:USDT", "currency": "USDT", - "minNotional": 20000000.0, - "maxNotional": 30000000.0, + "minNotional": 3000000.0, + "maxNotional": 3500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "20000000", + "notionalCap": "3500000", + "notionalFloor": "3000000", "maintMarginRatio": "0.5", - "cum": "6150250.0" + "cum": "1137000.0" } } ], @@ -48882,10 +53377,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.015", @@ -48899,10 +53394,10 @@ "minNotional": 5000.0, "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "2", - "initialLeverage": "20", + "initialLeverage": "10", "notionalCap": "25000", "notionalFloor": "5000", "maintMarginRatio": "0.025", @@ -48916,10 +53411,10 @@ "minNotional": 25000.0, "maxNotional": 100000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "3", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "100000", "notionalFloor": "25000", "maintMarginRatio": "0.05", @@ -49296,10 +53791,10 @@ "minNotional": 0.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 10.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "10", "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", @@ -49313,10 +53808,10 @@ "minNotional": 10000.0, "maxNotional": 20000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 9.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "9", "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.015", @@ -49330,10 +53825,10 @@ "minNotional": 20000.0, "maxNotional": 100000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 8.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "8", "notionalCap": "100000", "notionalFloor": "20000", "maintMarginRatio": "0.02", @@ -49347,10 +53842,10 @@ "minNotional": 100000.0, "maxNotional": 200000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 7.0, "info": { "bracket": "4", - "initialLeverage": "20", + "initialLeverage": "7", "notionalCap": "200000", "notionalFloor": "100000", "maintMarginRatio": "0.025", @@ -49364,10 +53859,10 @@ "minNotional": 200000.0, "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 6.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "6", "notionalCap": "1000000", "notionalFloor": "200000", "maintMarginRatio": "0.05", @@ -49396,13 +53891,13 @@ "symbol": "TROY/USDT:USDT", "currency": "USDT", "minNotional": 2000000.0, - "maxNotional": 2500000.0, + "maxNotional": 2100000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "2500000", + "notionalCap": "2100000", "notionalFloor": "2000000", "maintMarginRatio": "0.125", "cum": "105650.0" @@ -49412,34 +53907,34 @@ "tier": 8.0, "symbol": "TROY/USDT:USDT", "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 2100000.0, + "maxNotional": 2200000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "2200000", + "notionalFloor": "2100000", "maintMarginRatio": "0.25", - "cum": "418150.0" + "cum": "368150.0" } }, { "tier": 9.0, "symbol": "TROY/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 2200000.0, + "maxNotional": 2300000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "2300000", + "notionalFloor": "2200000", "maintMarginRatio": "0.5", - "cum": "1668150.0" + "cum": "918150.0" } } ], @@ -49598,6 +54093,161 @@ } } ], + "TRUMP/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "TRUMP/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": "1", + "initialLeverage": "50", + "notionalCap": "10000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "TRUMP/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 25.0, + "info": { + "bracket": "2", + "initialLeverage": "25", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "symbol": "TRUMP/USDT:USDT", + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 15.0, + "info": { + "bracket": "3", + "initialLeverage": "15", + "notionalCap": "100000", + "notionalFloor": "20000", + "maintMarginRatio": "0.02", + "cum": "150.0" + } + }, + { + "tier": 4.0, + "symbol": "TRUMP/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "100000", + "maintMarginRatio": "0.025", + "cum": "650.0" + } + }, + { + "tier": 5.0, + "symbol": "TRUMP/USDT:USDT", + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, + "info": { + "bracket": "5", + "initialLeverage": "8", + "notionalCap": "1000000", + "notionalFloor": "200000", + "maintMarginRatio": "0.05", + "cum": "5650.0" + } + }, + { + "tier": 6.0, + "symbol": "TRUMP/USDT:USDT", + "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, + "symbol": "TRUMP/USDT:USDT", + "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, + "symbol": "TRUMP/USDT:USDT", + "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, + "symbol": "TRUMP/USDT:USDT", + "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" + } + } + ], "TRX/USDT:USDT": [ { "tier": 1.0, @@ -49861,13 +54511,13 @@ "symbol": "TURBO/USDT:USDT", "currency": "USDT", "minNotional": 3000000.0, - "maxNotional": 6000000.0, + "maxNotional": 3500000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "6000000", + "notionalCap": "3500000", "notionalFloor": "3000000", "maintMarginRatio": "0.1", "cum": "166850.0" @@ -49877,51 +54527,51 @@ "tier": 7.0, "symbol": "TURBO/USDT:USDT", "currency": "USDT", - "minNotional": 6000000.0, - "maxNotional": 7500000.0, + "minNotional": 3500000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "7500000", - "notionalFloor": "6000000", + "notionalCap": "4000000", + "notionalFloor": "3500000", "maintMarginRatio": "0.125", - "cum": "316850.0" + "cum": "254350.0" } }, { "tier": 8.0, "symbol": "TURBO/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 15000000.0, + "minNotional": 4000000.0, + "maxNotional": 4500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "15000000", - "notionalFloor": "7500000", + "notionalCap": "4500000", + "notionalFloor": "4000000", "maintMarginRatio": "0.25", - "cum": "1254350.0" + "cum": "754350.0" } }, { "tier": 9.0, "symbol": "TURBO/USDT:USDT", "currency": "USDT", - "minNotional": 15000000.0, - "maxNotional": 30000000.0, + "minNotional": 4500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "30000000", - "notionalFloor": "15000000", + "notionalCap": "5000000", + "notionalFloor": "4500000", "maintMarginRatio": "0.5", - "cum": "5004350.0" + "cum": "1879350.0" } } ], @@ -50736,6 +55386,161 @@ } } ], + "USUAL/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "USUAL/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "10000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "USUAL/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "symbol": "USUAL/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 150000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 15.0, + "info": { + "bracket": "3", + "initialLeverage": "15", + "notionalCap": "150000", + "notionalFloor": "30000", + "maintMarginRatio": "0.02", + "cum": "200.0" + } + }, + { + "tier": 4.0, + "symbol": "USUAL/USDT:USDT", + "currency": "USDT", + "minNotional": 150000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "300000", + "notionalFloor": "150000", + "maintMarginRatio": "0.025", + "cum": "950.0" + } + }, + { + "tier": 5.0, + "symbol": "USUAL/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, + "info": { + "bracket": "5", + "initialLeverage": "8", + "notionalCap": "1500000", + "notionalFloor": "300000", + "maintMarginRatio": "0.05", + "cum": "8450.0" + } + }, + { + "tier": 6.0, + "symbol": "USUAL/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.1", + "cum": "83450.0" + } + }, + { + "tier": 7.0, + "symbol": "USUAL/USDT:USDT", + "currency": "USDT", + "minNotional": 3000000.0, + "maxNotional": 3750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "3750000", + "notionalFloor": "3000000", + "maintMarginRatio": "0.125", + "cum": "158450.0" + } + }, + { + "tier": 8.0, + "symbol": "USUAL/USDT:USDT", + "currency": "USDT", + "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, + "symbol": "USUAL/USDT:USDT", + "currency": "USDT", + "minNotional": 7500000.0, + "maxNotional": 8500000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "8500000", + "notionalFloor": "7500000", + "maintMarginRatio": "0.5", + "cum": "2502200.0" + } + } + ], "UXLINK/USDT:USDT": [ { "tier": 1.0, @@ -50891,6 +55696,161 @@ } } ], + "VANA/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "VANA/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "10000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "VANA/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "20000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" + } + }, + { + "tier": 3.0, + "symbol": "VANA/USDT:USDT", + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 15.0, + "info": { + "bracket": "3", + "initialLeverage": "15", + "notionalCap": "100000", + "notionalFloor": "20000", + "maintMarginRatio": "0.02", + "cum": "150.0" + } + }, + { + "tier": 4.0, + "symbol": "VANA/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "100000", + "maintMarginRatio": "0.025", + "cum": "650.0" + } + }, + { + "tier": 5.0, + "symbol": "VANA/USDT:USDT", + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, + "info": { + "bracket": "5", + "initialLeverage": "8", + "notionalCap": "1000000", + "notionalFloor": "200000", + "maintMarginRatio": "0.05", + "cum": "5650.0" + } + }, + { + "tier": 6.0, + "symbol": "VANA/USDT:USDT", + "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, + "symbol": "VANA/USDT:USDT", + "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, + "symbol": "VANA/USDT:USDT", + "currency": "USDT", + "minNotional": 2500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "3000000", + "notionalFloor": "2500000", + "maintMarginRatio": "0.25", + "cum": "418150.0" + } + }, + { + "tier": 9.0, + "symbol": "VANA/USDT:USDT", + "currency": "USDT", + "minNotional": 3000000.0, + "maxNotional": 3500000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "3500000", + "notionalFloor": "3000000", + "maintMarginRatio": "0.5", + "cum": "1168150.0" + } + } + ], "VANRY/USDT:USDT": [ { "tier": 1.0, @@ -51012,6 +55972,161 @@ } } ], + "VELODROME/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "VELODROME/USDT:USDT", + "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, + "symbol": "VELODROME/USDT:USDT", + "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, + "symbol": "VELODROME/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "VELODROME/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "VELODROME/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "VELODROME/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "VELODROME/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "VELODROME/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "VELODROME/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], "VET/USDT:USDT": [ { "tier": 1.0, @@ -51020,10 +56135,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.006, - "maxLeverage": 50.0, + "maxLeverage": 75.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "75", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.006", @@ -51037,10 +56152,10 @@ "minNotional": 5000.0, "maxNotional": 25000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 25.0, + "maxLeverage": 50.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "50", "notionalCap": "25000", "notionalFloor": "5000", "maintMarginRatio": "0.01", @@ -51052,101 +56167,135 @@ "symbol": "VET/USDT:USDT", "currency": "USDT", "minNotional": 25000.0, - "maxNotional": 200000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 40000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 40.0, "info": { "bracket": "3", - "initialLeverage": "20", - "notionalCap": "200000", + "initialLeverage": "40", + "notionalCap": "40000", "notionalFloor": "25000", - "maintMarginRatio": "0.025", - "cum": "395.0" + "maintMarginRatio": "0.015", + "cum": "145.0" } }, { "tier": 4.0, "symbol": "VET/USDT:USDT", "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 400000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 40000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "4", - "initialLeverage": "10", - "notionalCap": "400000", - "notionalFloor": "200000", - "maintMarginRatio": "0.05", - "cum": "5395.0" + "initialLeverage": "25", + "notionalCap": "200000", + "notionalFloor": "40000", + "maintMarginRatio": "0.02", + "cum": "345.0" } }, { "tier": 5.0, "symbol": "VET/USDT:USDT", "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "5", - "initialLeverage": "5", - "notionalCap": "1000000", - "notionalFloor": "400000", - "maintMarginRatio": "0.1", - "cum": "25395.0" + "initialLeverage": "20", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.025", + "cum": "1345.0" } }, { "tier": 6.0, "symbol": "VET/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 5000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 400000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "6", - "initialLeverage": "4", - "notionalCap": "5000000", - "notionalFloor": "1000000", - "maintMarginRatio": "0.125", - "cum": "50395.0" + "initialLeverage": "10", + "notionalCap": "2000000", + "notionalFloor": "400000", + "maintMarginRatio": "0.05", + "cum": "11345.0" } }, { "tier": 7.0, "symbol": "VET/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 6000000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, + "minNotional": 2000000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "7", - "initialLeverage": "2", - "notionalCap": "6000000", - "notionalFloor": "5000000", - "maintMarginRatio": "0.25", - "cum": "675395.0" + "initialLeverage": "5", + "notionalCap": "4000000", + "notionalFloor": "2000000", + "maintMarginRatio": "0.1", + "cum": "111345.0" } }, { "tier": 8.0, "symbol": "VET/USDT:USDT", "currency": "USDT", - "minNotional": 6000000.0, + "minNotional": 4000000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "8", + "initialLeverage": "4", + "notionalCap": "5000000", + "notionalFloor": "4000000", + "maintMarginRatio": "0.125", + "cum": "211345.0" + } + }, + { + "tier": 9.0, + "symbol": "VET/USDT:USDT", + "currency": "USDT", + "minNotional": 5000000.0, "maxNotional": 10000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "9", + "initialLeverage": "2", + "notionalCap": "10000000", + "notionalFloor": "5000000", + "maintMarginRatio": "0.25", + "cum": "836345.0" + } + }, + { + "tier": 10.0, + "symbol": "VET/USDT:USDT", + "currency": "USDT", + "minNotional": 10000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "10", "initialLeverage": "1", - "notionalCap": "10000000", - "notionalFloor": "6000000", + "notionalCap": "20000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.5", - "cum": "2175395.0" + "cum": "3336345.0" } } ], @@ -51305,19 +56454,140 @@ } } ], + "VINE/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "VINE/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.02", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "VINE/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "VINE/USDT:USDT", + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "650.0" + } + }, + { + "tier": 4.0, + "symbol": "VINE/USDT:USDT", + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10650.0" + } + }, + { + "tier": 5.0, + "symbol": "VINE/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "23150.0" + } + }, + { + "tier": 6.0, + "symbol": "VINE/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "116900.0" + } + }, + { + "tier": 7.0, + "symbol": "VINE/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "491900.0" + } + } + ], "VIRTUAL/USDT:USDT": [ { "tier": 1.0, "symbol": "VIRTUAL/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "75", - "notionalCap": "5000", + "initialLeverage": "25", + "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.01", "cum": "0.0" @@ -51327,136 +56597,136 @@ "tier": 2.0, "symbol": "VIRTUAL/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, + "minNotional": 10000.0, + "maxNotional": 40000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "50", - "notionalCap": "10000", - "notionalFloor": "5000", + "initialLeverage": "20", + "notionalCap": "40000", + "notionalFloor": "10000", "maintMarginRatio": "0.015", - "cum": "25.0" + "cum": "50.0" } }, { "tier": 3.0, "symbol": "VIRTUAL/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 30000.0, + "minNotional": 40000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "3", - "initialLeverage": "25", - "notionalCap": "30000", - "notionalFloor": "10000", + "initialLeverage": "15", + "notionalCap": "200000", + "notionalFloor": "40000", "maintMarginRatio": "0.02", - "cum": "75.0" + "cum": "250.0" } }, { "tier": 4.0, "symbol": "VIRTUAL/USDT:USDT", "currency": "USDT", - "minNotional": 30000.0, - "maxNotional": 60000.0, + "minNotional": 200000.0, + "maxNotional": 400000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", - "notionalCap": "60000", - "notionalFloor": "30000", + "initialLeverage": "10", + "notionalCap": "400000", + "notionalFloor": "200000", "maintMarginRatio": "0.025", - "cum": "225.0" + "cum": "1250.0" } }, { "tier": 5.0, "symbol": "VIRTUAL/USDT:USDT", "currency": "USDT", - "minNotional": 60000.0, - "maxNotional": 300000.0, + "minNotional": 400000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "5", - "initialLeverage": "10", - "notionalCap": "300000", - "notionalFloor": "60000", + "initialLeverage": "8", + "notionalCap": "2000000", + "notionalFloor": "400000", "maintMarginRatio": "0.05", - "cum": "1725.0" + "cum": "11250.0" } }, { "tier": 6.0, "symbol": "VIRTUAL/USDT:USDT", "currency": "USDT", - "minNotional": 300000.0, - "maxNotional": 600000.0, + "minNotional": 2000000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "6", "initialLeverage": "5", - "notionalCap": "600000", - "notionalFloor": "300000", + "notionalCap": "4000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.1", - "cum": "16725.0" + "cum": "111250.0" } }, { "tier": 7.0, "symbol": "VIRTUAL/USDT:USDT", "currency": "USDT", - "minNotional": 600000.0, - "maxNotional": 750000.0, + "minNotional": 4000000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "7", "initialLeverage": "4", - "notionalCap": "750000", - "notionalFloor": "600000", + "notionalCap": "5000000", + "notionalFloor": "4000000", "maintMarginRatio": "0.125", - "cum": "31725.0" + "cum": "211250.0" } }, { "tier": 8.0, "symbol": "VIRTUAL/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1500000.0, + "minNotional": 5000000.0, + "maxNotional": 10000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "8", "initialLeverage": "2", - "notionalCap": "1500000", - "notionalFloor": "750000", + "notionalCap": "10000000", + "notionalFloor": "5000000", "maintMarginRatio": "0.25", - "cum": "125475.0" + "cum": "836250.0" } }, { "tier": 9.0, "symbol": "VIRTUAL/USDT:USDT", "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 3000000.0, + "minNotional": 10000000.0, + "maxNotional": 11000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "9", "initialLeverage": "1", - "notionalCap": "3000000", - "notionalFloor": "1500000", + "notionalCap": "11000000", + "notionalFloor": "10000000", "maintMarginRatio": "0.5", - "cum": "500475.0" + "cum": "3336250.0" } } ], @@ -51598,6 +56868,282 @@ } } ], + "VTHO/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "VTHO/USDT:USDT", + "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, + "symbol": "VTHO/USDT:USDT", + "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, + "symbol": "VTHO/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "3", + "initialLeverage": "25", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "VTHO/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "4", + "initialLeverage": "20", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "VTHO/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "5", + "initialLeverage": "10", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "VTHO/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "VTHO/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "VTHO/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "VTHO/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "500475.0" + } + } + ], + "VVV/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "VVV/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.02", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "VVV/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "25000", + "notionalFloor": "5000", + "maintMarginRatio": "0.025", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "VVV/USDT:USDT", + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "3", + "initialLeverage": "10", + "notionalCap": "200000", + "notionalFloor": "25000", + "maintMarginRatio": "0.05", + "cum": "650.0" + } + }, + { + "tier": 4.0, + "symbol": "VVV/USDT:USDT", + "currency": "USDT", + "minNotional": 200000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "4", + "initialLeverage": "5", + "notionalCap": "500000", + "notionalFloor": "200000", + "maintMarginRatio": "0.1", + "cum": "10650.0" + } + }, + { + "tier": 5.0, + "symbol": "VVV/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "5", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "500000", + "maintMarginRatio": "0.125", + "cum": "23150.0" + } + }, + { + "tier": 6.0, + "symbol": "VVV/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "6", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "116900.0" + } + }, + { + "tier": 7.0, + "symbol": "VVV/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "7", + "initialLeverage": "1", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.5", + "cum": "491900.0" + } + } + ], "W/USDT:USDT": [ { "tier": 1.0, @@ -52483,13 +58029,13 @@ "symbol": "WLD/USDT:USDT", "currency": "USDT", "minNotional": 60000.0, - "maxNotional": 100000.0, + "maxNotional": 160000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 40.0, "info": { "bracket": "3", "initialLeverage": "40", - "notionalCap": "100000", + "notionalCap": "160000", "notionalFloor": "60000", "maintMarginRatio": "0.015", "cum": "320.0" @@ -52499,119 +58045,119 @@ "tier": 4.0, "symbol": "WLD/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 500000.0, + "minNotional": 160000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "4", "initialLeverage": "25", - "notionalCap": "500000", - "notionalFloor": "100000", + "notionalCap": "800000", + "notionalFloor": "160000", "maintMarginRatio": "0.02", - "cum": "820.0" + "cum": "1120.0" } }, { "tier": 5.0, "symbol": "WLD/USDT:USDT", "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.0, + "minNotional": 800000.0, + "maxNotional": 1600000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "5", "initialLeverage": "20", - "notionalCap": "1000000", - "notionalFloor": "500000", + "notionalCap": "1600000", + "notionalFloor": "800000", "maintMarginRatio": "0.025", - "cum": "3320.0" + "cum": "5120.0" } }, { "tier": 6.0, "symbol": "WLD/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 5000000.0, + "minNotional": 1600000.0, + "maxNotional": 8000000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "6", "initialLeverage": "10", - "notionalCap": "5000000", - "notionalFloor": "1000000", + "notionalCap": "8000000", + "notionalFloor": "1600000", "maintMarginRatio": "0.05", - "cum": "28320.0" + "cum": "45120.0" } }, { "tier": 7.0, "symbol": "WLD/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 8000000.0, + "maxNotional": 16000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "7", "initialLeverage": "5", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "16000000", + "notionalFloor": "8000000", "maintMarginRatio": "0.1", - "cum": "278320.0" + "cum": "445120.0" } }, { "tier": 8.0, "symbol": "WLD/USDT:USDT", "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 12500000.0, + "minNotional": 16000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "8", "initialLeverage": "4", - "notionalCap": "12500000", - "notionalFloor": "10000000", + "notionalCap": "20000000", + "notionalFloor": "16000000", "maintMarginRatio": "0.125", - "cum": "528320.0" + "cum": "845120.0" } }, { "tier": 9.0, "symbol": "WLD/USDT:USDT", "currency": "USDT", - "minNotional": 12500000.0, - "maxNotional": 25000000.0, + "minNotional": 20000000.0, + "maxNotional": 40000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "9", "initialLeverage": "2", - "notionalCap": "25000000", - "notionalFloor": "12500000", + "notionalCap": "40000000", + "notionalFloor": "20000000", "maintMarginRatio": "0.25", - "cum": "2090820.0" + "cum": "3345120.0" } }, { "tier": 10.0, "symbol": "WLD/USDT:USDT", "currency": "USDT", - "minNotional": 25000000.0, - "maxNotional": 50000000.0, + "minNotional": 40000000.0, + "maxNotional": 80000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "10", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "25000000", + "notionalCap": "80000000", + "notionalFloor": "40000000", "maintMarginRatio": "0.5", - "cum": "8340820.0" + "cum": "13345120.0" } } ], @@ -53141,10 +58687,10 @@ "minNotional": 0.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 20.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "0", "maintMarginRatio": "0.015", @@ -53158,10 +58704,10 @@ "minNotional": 10000.0, "maxNotional": 150000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 15.0, "info": { "bracket": "2", - "initialLeverage": "20", + "initialLeverage": "15", "notionalCap": "150000", "notionalFloor": "10000", "maintMarginRatio": "0.025", @@ -53175,10 +58721,10 @@ "minNotional": 150000.0, "maxNotional": 250000.0, "maintenanceMarginRate": 0.03, - "maxLeverage": 15.0, + "maxLeverage": 10.0, "info": { "bracket": "3", - "initialLeverage": "15", + "initialLeverage": "10", "notionalCap": "250000", "notionalFloor": "150000", "maintMarginRatio": "0.03", @@ -53192,10 +58738,10 @@ "minNotional": 250000.0, "maxNotional": 1000000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 8.0, "info": { "bracket": "4", - "initialLeverage": "10", + "initialLeverage": "8", "notionalCap": "1000000", "notionalFloor": "250000", "maintMarginRatio": "0.05", @@ -53224,13 +58770,13 @@ "symbol": "XMR/USDT:USDT", "currency": "USDT", "minNotional": 2000000.0, - "maxNotional": 5000000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "5000000", + "notionalCap": "3000000", "notionalFloor": "2000000", "maintMarginRatio": "0.125", "cum": "105850.0" @@ -53240,34 +58786,34 @@ "tier": 7.0, "symbol": "XMR/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 10000000.0, + "minNotional": 3000000.0, + "maxNotional": 3500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "10000000", - "notionalFloor": "5000000", + "notionalCap": "3500000", + "notionalFloor": "3000000", "maintMarginRatio": "0.25", - "cum": "730850.0" + "cum": "480850.0" } }, { "tier": 8.0, "symbol": "XMR/USDT:USDT", "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 20000000.0, + "minNotional": 3500000.0, + "maxNotional": 4000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "20000000", - "notionalFloor": "10000000", + "notionalCap": "4000000", + "notionalFloor": "3500000", "maintMarginRatio": "0.5", - "cum": "3230850.0" + "cum": "1355850.0" } } ], @@ -53279,10 +58825,10 @@ "minNotional": 0.0, "maxNotional": 5000.0, "maintenanceMarginRate": 0.005, - "maxLeverage": 75.0, + "maxLeverage": 50.0, "info": { "bracket": "1", - "initialLeverage": "75", + "initialLeverage": "50", "notionalCap": "5000", "notionalFloor": "0", "maintMarginRatio": "0.005", @@ -53296,10 +58842,10 @@ "minNotional": 5000.0, "maxNotional": 10000.0, "maintenanceMarginRate": 0.006, - "maxLeverage": 50.0, + "maxLeverage": 25.0, "info": { "bracket": "2", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.006", @@ -53313,10 +58859,10 @@ "minNotional": 10000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.01, - "maxLeverage": 40.0, + "maxLeverage": 20.0, "info": { "bracket": "3", - "initialLeverage": "40", + "initialLeverage": "20", "notionalCap": "50000", "notionalFloor": "10000", "maintMarginRatio": "0.01", @@ -53330,10 +58876,10 @@ "minNotional": 50000.0, "maxNotional": 750000.0, "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxLeverage": 15.0, "info": { "bracket": "4", - "initialLeverage": "25", + "initialLeverage": "15", "notionalCap": "750000", "notionalFloor": "50000", "maintMarginRatio": "0.02", @@ -53345,37 +58891,54 @@ "symbol": "XRP/USDC:USDC", "currency": "USDC", "minNotional": 750000.0, - "maxNotional": 3000000.0, - "maintenanceMarginRate": 0.05, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.025, "maxLeverage": 10.0, "info": { "bracket": "5", "initialLeverage": "10", - "notionalCap": "3000000", + "notionalCap": "1000000", "notionalFloor": "750000", - "maintMarginRatio": "0.05", - "cum": "23045.0" + "maintMarginRatio": "0.025", + "cum": "4295.0" } }, { "tier": 6.0, "symbol": "XRP/USDC:USDC", "currency": "USDC", + "minNotional": 1000000.0, + "maxNotional": 3000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, + "info": { + "bracket": "6", + "initialLeverage": "8", + "notionalCap": "3000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.05", + "cum": "29295.0" + } + }, + { + "tier": 7.0, + "symbol": "XRP/USDC:USDC", + "currency": "USDC", "minNotional": 3000000.0, "maxNotional": 10000000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "6", + "bracket": "7", "initialLeverage": "5", "notionalCap": "10000000", "notionalFloor": "3000000", "maintMarginRatio": "0.1", - "cum": "173045.0" + "cum": "179295.0" } }, { - "tier": 7.0, + "tier": 8.0, "symbol": "XRP/USDC:USDC", "currency": "USDC", "minNotional": 10000000.0, @@ -53383,63 +58946,46 @@ "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "7", + "bracket": "8", "initialLeverage": "4", "notionalCap": "12000000", "notionalFloor": "10000000", "maintMarginRatio": "0.125", - "cum": "423045.0" - } - }, - { - "tier": 8.0, - "symbol": "XRP/USDC:USDC", - "currency": "USDC", - "minNotional": 12000000.0, - "maxNotional": 20000000.0, - "maintenanceMarginRate": 0.15, - "maxLeverage": 3.0, - "info": { - "bracket": "8", - "initialLeverage": "3", - "notionalCap": "20000000", - "notionalFloor": "12000000", - "maintMarginRatio": "0.15", - "cum": "723045.0" + "cum": "429295.0" } }, { "tier": 9.0, "symbol": "XRP/USDC:USDC", "currency": "USDC", - "minNotional": 20000000.0, - "maxNotional": 30000000.0, + "minNotional": 12000000.0, + "maxNotional": 15000000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "9", "initialLeverage": "2", - "notionalCap": "30000000", - "notionalFloor": "20000000", + "notionalCap": "15000000", + "notionalFloor": "12000000", "maintMarginRatio": "0.25", - "cum": "2723045.0" + "cum": "1929295.0" } }, { "tier": 10.0, "symbol": "XRP/USDC:USDC", "currency": "USDC", - "minNotional": 30000000.0, - "maxNotional": 50000000.0, + "minNotional": 15000000.0, + "maxNotional": 20000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "10", "initialLeverage": "1", - "notionalCap": "50000000", - "notionalFloor": "30000000", + "notionalCap": "20000000", + "notionalFloor": "15000000", "maintMarginRatio": "0.5", - "cum": "10223045.0" + "cum": "5679295.0" } } ], @@ -54262,10 +59808,10 @@ "minNotional": 0.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxLeverage": 10.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "10", "notionalCap": "50000", "notionalFloor": "0", "maintMarginRatio": "0.015", @@ -54279,10 +59825,10 @@ "minNotional": 50000.0, "maxNotional": 150000.0, "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxLeverage": 8.0, "info": { "bracket": "2", - "initialLeverage": "20", + "initialLeverage": "8", "notionalCap": "150000", "notionalFloor": "50000", "maintMarginRatio": "0.025", @@ -54296,10 +59842,10 @@ "minNotional": 150000.0, "maxNotional": 250000.0, "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxLeverage": 6.0, "info": { "bracket": "3", - "initialLeverage": "10", + "initialLeverage": "6", "notionalCap": "250000", "notionalFloor": "150000", "maintMarginRatio": "0.05", @@ -54362,13 +59908,13 @@ "symbol": "ZEC/USDT:USDT", "currency": "USDT", "minNotional": 2000000.0, - "maxNotional": 5000000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "7", "initialLeverage": "1", - "notionalCap": "5000000", + "notionalCap": "2500000", "notionalFloor": "2000000", "maintMarginRatio": "0.5", "cum": "654250.0" @@ -54381,15 +59927,15 @@ "symbol": "ZEN/USDT:USDT", "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" } }, @@ -54397,119 +59943,291 @@ "tier": 2.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 20000.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": "20000", - "notionalFloor": "5000", - "maintMarginRatio": "0.02", - "cum": "25.0" + "initialLeverage": "50", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.015", + "cum": "50.0" } }, { "tier": 3.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 25000.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": "25000", - "notionalFloor": "20000", - "maintMarginRatio": "0.025", - "cum": "125.0" + "initialLeverage": "25", + "notionalCap": "150000", + "notionalFloor": "30000", + "maintMarginRatio": "0.02", + "cum": "200.0" } }, { "tier": 4.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 200000.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": "200000", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "750.0" + "initialLeverage": "20", + "notionalCap": "300000", + "notionalFloor": "150000", + "maintMarginRatio": "0.025", + "cum": "950.0" } }, { "tier": 5.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 400000.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": "400000", - "notionalFloor": "200000", - "maintMarginRatio": "0.1", - "cum": "10750.0" + "initialLeverage": "10", + "notionalCap": "1500000", + "notionalFloor": "300000", + "maintMarginRatio": "0.05", + "cum": "8450.0" } }, { "tier": 6.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 500000.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": "500000", - "notionalFloor": "400000", - "maintMarginRatio": "0.125", - "cum": "20750.0" + "initialLeverage": "5", + "notionalCap": "3000000", + "notionalFloor": "1500000", + "maintMarginRatio": "0.1", + "cum": "83450.0" } }, { "tier": 7.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 1000000.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": "1000000", - "notionalFloor": "500000", - "maintMarginRatio": "0.25", - "cum": "83250.0" + "initialLeverage": "4", + "notionalCap": "3750000", + "notionalFloor": "3000000", + "maintMarginRatio": "0.125", + "cum": "158450.0" } }, { "tier": 8.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.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, + "symbol": "ZEN/USDT:USDT", + "currency": "USDT", + "minNotional": 7500000.0, + "maxNotional": 15000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "9", + "initialLeverage": "1", + "notionalCap": "15000000", + "notionalFloor": "7500000", + "maintMarginRatio": "0.5", + "cum": "2502200.0" + } + } + ], + "ZEREBRO/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "ZEREBRO/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.01", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "ZEREBRO/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 20.0, + "info": { + "bracket": "2", + "initialLeverage": "20", + "notionalCap": "10000", + "notionalFloor": "5000", + "maintMarginRatio": "0.015", + "cum": "25.0" + } + }, + { + "tier": 3.0, + "symbol": "ZEREBRO/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 15.0, + "info": { + "bracket": "3", + "initialLeverage": "15", + "notionalCap": "30000", + "notionalFloor": "10000", + "maintMarginRatio": "0.02", + "cum": "75.0" + } + }, + { + "tier": 4.0, + "symbol": "ZEREBRO/USDT:USDT", + "currency": "USDT", + "minNotional": 30000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 10.0, + "info": { + "bracket": "4", + "initialLeverage": "10", + "notionalCap": "60000", + "notionalFloor": "30000", + "maintMarginRatio": "0.025", + "cum": "225.0" + } + }, + { + "tier": 5.0, + "symbol": "ZEREBRO/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 8.0, + "info": { + "bracket": "5", + "initialLeverage": "8", + "notionalCap": "300000", + "notionalFloor": "60000", + "maintMarginRatio": "0.05", + "cum": "1725.0" + } + }, + { + "tier": 6.0, + "symbol": "ZEREBRO/USDT:USDT", + "currency": "USDT", + "minNotional": 300000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "6", + "initialLeverage": "5", + "notionalCap": "600000", + "notionalFloor": "300000", + "maintMarginRatio": "0.1", + "cum": "16725.0" + } + }, + { + "tier": 7.0, + "symbol": "ZEREBRO/USDT:USDT", + "currency": "USDT", + "minNotional": 600000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "7", + "initialLeverage": "4", + "notionalCap": "750000", + "notionalFloor": "600000", + "maintMarginRatio": "0.125", + "cum": "31725.0" + } + }, + { + "tier": 8.0, + "symbol": "ZEREBRO/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "8", + "initialLeverage": "2", + "notionalCap": "1500000", + "notionalFloor": "750000", + "maintMarginRatio": "0.25", + "cum": "125475.0" + } + }, + { + "tier": 9.0, + "symbol": "ZEREBRO/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "9", "initialLeverage": "1", "notionalCap": "2000000", - "notionalFloor": "1000000", + "notionalFloor": "1500000", "maintMarginRatio": "0.5", - "cum": "333250.0" + "cum": "500475.0" } } ], diff --git a/freqtrade/exchange/binance_public_data.py b/freqtrade/exchange/binance_public_data.py index fdc9a993d..42eda09bf 100644 --- a/freqtrade/exchange/binance_public_data.py +++ b/freqtrade/exchange/binance_public_data.py @@ -63,13 +63,6 @@ async def download_archive_ohlcv( available in the time range """ try: - if candle_type == CandleType.SPOT: - asset_type_url_segment = "spot" - elif candle_type == CandleType.FUTURES: - asset_type_url_segment = "futures/um" - else: - raise ValueError(f"Unsupported CandleType: {candle_type}") - symbol = markets[pair]["id"] start = dt_from_ts(since_ms) @@ -82,7 +75,7 @@ async def download_archive_ohlcv( if start >= end: return DataFrame() df = await _download_archive_ohlcv( - asset_type_url_segment, symbol, pair, timeframe, start, end, stop_on_404 + symbol, pair, timeframe, candle_type, start, end, stop_on_404 ) logger.debug( f"Downloaded data for {pair} from https://data.binance.vision with length {len(df)}." @@ -110,10 +103,10 @@ def concat_safe(dfs) -> DataFrame: async def _download_archive_ohlcv( - asset_type_url_segment: str, symbol: str, pair: str, timeframe: str, + candle_type: CandleType, start: date, end: date, stop_on_404: bool, @@ -128,9 +121,7 @@ async def _download_archive_ohlcv( # the HTTP connections has been throttled by TCPConnector for dates in chunks(list(date_range(start, end)), 1000): tasks = [ - asyncio.create_task( - get_daily_ohlcv(asset_type_url_segment, symbol, timeframe, date, session) - ) + asyncio.create_task(get_daily_ohlcv(symbol, timeframe, candle_type, date, session)) for date in dates ] for task in tasks: @@ -196,14 +187,24 @@ def binance_vision_zip_name(symbol: str, timeframe: str, date: date) -> str: return f"{symbol}-{timeframe}-{date.strftime('%Y-%m-%d')}.zip" -def binance_vision_zip_url( - asset_type_url_segment: str, symbol: str, timeframe: str, date: date +def candle_type_to_url_segment(candle_type: CandleType) -> str: + if candle_type == CandleType.SPOT: + return "spot" + elif candle_type == CandleType.FUTURES: + return "futures/um" + else: + raise ValueError(f"Unsupported CandleType: {candle_type}") + + +def binance_vision_ohlcv_zip_url( + symbol: str, timeframe: str, candle_type: CandleType, date: date ) -> str: """ example urls: https://data.binance.vision/data/spot/daily/klines/BTCUSDT/1s/BTCUSDT-1s-2023-10-27.zip https://data.binance.vision/data/futures/um/daily/klines/BTCUSDT/1h/BTCUSDT-1h-2023-10-27.zip """ + asset_type_url_segment = candle_type_to_url_segment(candle_type) url = ( f"https://data.binance.vision/data/{asset_type_url_segment}/daily/klines/{symbol}" f"/{timeframe}/{binance_vision_zip_name(symbol, timeframe, date)}" @@ -212,9 +213,9 @@ def binance_vision_zip_url( async def get_daily_ohlcv( - asset_type_url_segment: str, symbol: str, timeframe: str, + candle_type: CandleType, date: date, session: aiohttp.ClientSession, retry_count: int = 3, @@ -224,9 +225,9 @@ async def get_daily_ohlcv( Get daily OHLCV from https://data.binance.vision See https://github.com/binance/binance-public-data - :asset_type_url_segment: `spot` or `futures/um` :symbol: binance symbol name, e.g. BTCUSDT :timeframe: e.g. 1m, 1h + :candle_type: SPOT or FUTURES :date: the returned DataFrame will cover the entire day of `date` in UTC :session: an aiohttp.ClientSession instance :retry_count: times to retry before returning the exceptions @@ -234,7 +235,7 @@ async def get_daily_ohlcv( :return: A dataframe containing columns date,open,high,low,close,volume """ - url = binance_vision_zip_url(asset_type_url_segment, symbol, timeframe, date) + url = binance_vision_ohlcv_zip_url(symbol, timeframe, candle_type, date) logger.debug(f"download data from binance: {url}") diff --git a/freqtrade/exchange/bybit.py b/freqtrade/exchange/bybit.py index 72cfe52c0..d75aafdc8 100644 --- a/freqtrade/exchange/bybit.py +++ b/freqtrade/exchange/bybit.py @@ -31,7 +31,6 @@ class Bybit(Exchange): unified_account = False _ft_has: FtHas = { - "ohlcv_candle_limit": 1000, "ohlcv_has_history": True, "order_time_in_force": ["GTC", "FOK", "IOC", "PO"], "ws_enabled": True, diff --git a/freqtrade/exchange/check_exchange.py b/freqtrade/exchange/check_exchange.py index 6d82bae04..1402e40cc 100644 --- a/freqtrade/exchange/check_exchange.py +++ b/freqtrade/exchange/check_exchange.py @@ -36,7 +36,7 @@ def check_exchange(config: Config, check_for_bad: bool = True) -> bool: f"This command requires a configured exchange. You should either use " f"`--exchange ` or specify a configuration file via `--config`.\n" f"The following exchanges are available for Freqtrade: " - f'{", ".join(available_exchanges())}' + f"{', '.join(available_exchanges())}" ) if not is_exchange_known_ccxt(exchange): @@ -44,21 +44,21 @@ def check_exchange(config: Config, check_for_bad: bool = True) -> bool: f'Exchange "{exchange}" is not known to the ccxt library ' f"and therefore not available for the bot.\n" f"The following exchanges are available for Freqtrade: " - f'{", ".join(available_exchanges())}' + f"{', '.join(available_exchanges())}" ) valid, reason, _ = validate_exchange(exchange) if not valid: if check_for_bad: raise OperationalException( - f'Exchange "{exchange}" will not work with Freqtrade. ' f"Reason: {reason}" + f'Exchange "{exchange}" will not work with Freqtrade. Reason: {reason}' ) else: logger.warning(f'Exchange "{exchange}" will not work with Freqtrade. Reason: {reason}') if MAP_EXCHANGE_CHILDCLASS.get(exchange, exchange) in SUPPORTED_EXCHANGES: logger.info( - f'Exchange "{exchange}" is officially supported ' f"by the Freqtrade development team." + f'Exchange "{exchange}" is officially supported by the Freqtrade development team.' ) else: logger.warning( diff --git a/freqtrade/exchange/coinbasepro.py b/freqtrade/exchange/coinbasepro.py deleted file mode 100644 index cc561e8ce..000000000 --- a/freqtrade/exchange/coinbasepro.py +++ /dev/null @@ -1,24 +0,0 @@ -"""CoinbasePro exchange subclass""" - -import logging - -from freqtrade.exchange import Exchange -from freqtrade.exchange.exchange_types import FtHas - - -logger = logging.getLogger(__name__) - - -class Coinbasepro(Exchange): - """ - CoinbasePro exchange class. Contains adjustments needed for Freqtrade to work - with this exchange. - - Please note that this exchange is not included in the list of exchanges - officially supported by the Freqtrade development team. So some features - may still not work as expected. - """ - - _ft_has: FtHas = { - "ohlcv_candle_limit": 300, - } diff --git a/freqtrade/exchange/common.py b/freqtrade/exchange/common.py index 4fd9cea63..617fe0d01 100644 --- a/freqtrade/exchange/common.py +++ b/freqtrade/exchange/common.py @@ -40,6 +40,9 @@ BAD_EXCHANGES = { "bitmex": "Various reasons.", "probit": "Requires additional, regular calls to `signIn()`.", "poloniex": "Does not provide fetch_order endpoint to fetch both open and closed orders.", + "kucoinfutures": "Unsupported futures exchange.", + "poloniexfutures": "Unsupported futures exchange.", + "binancecoinm": "Unsupported futures exchange.", } MAP_EXCHANGE_CHILDCLASS = { @@ -47,6 +50,7 @@ MAP_EXCHANGE_CHILDCLASS = { "binanceje": "binance", "binanceusdm": "binance", "okex": "okx", + "myokx": "okx", "gateio": "gate", "huboi": "htx", } diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 811426839..aaf5f2225 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -12,7 +12,7 @@ from copy import deepcopy from datetime import datetime, timedelta, timezone from math import floor, isnan from threading import Lock -from typing import Any, Literal, TypeGuard +from typing import Any, Literal, TypeGuard, TypeVar import ccxt import ccxt.pro as ccxt_pro @@ -113,6 +113,8 @@ from freqtrade.util.periodic_cache import PeriodicCache logger = logging.getLogger(__name__) +T = TypeVar("T") + class Exchange: # Parameters to add directly to buy/sell calls (like agreeing to trading agreement) @@ -131,7 +133,6 @@ class Exchange: "stoploss_order_types": {}, "order_time_in_force": ["GTC"], "ohlcv_params": {}, - "ohlcv_candle_limit": 500, "ohlcv_has_history": True, # Some exchanges (Kraken) don't provide history via ohlcv "ohlcv_partial_candle": True, "ohlcv_require_since": False, @@ -156,6 +157,7 @@ class Exchange: # Override createMarketBuyOrderRequiresPrice where ccxt has it wrong "marketOrderRequiresPrice": False, "exchange_has_overrides": {}, # Dictionary overriding ccxt's "has". + "proxy_coin_mapping": {}, # Mapping for proxy coins # Expected to be in the format {"fetchOHLCV": True} or {"fetchOHLCV": False} "ws_enabled": False, # Set to true for exchanges with tested websocket support } @@ -275,6 +277,11 @@ class Exchange: logger.info(f'Using Exchange "{self.name}"') self.required_candle_call_count = 1 + # Converts the interval provided in minutes in config to seconds + self.markets_refresh_interval: int = ( + exchange_conf.get("markets_refresh_interval", 60) * 60 * 1000 + ) + if validate: # Initial markets load self.reload_markets(True, load_leverage_tiers=False) @@ -284,11 +291,6 @@ class Exchange: self._startup_candle_count, config.get("timeframe", "") ) - # Converts the interval provided in minutes in config to seconds - self.markets_refresh_interval: int = ( - exchange_conf.get("markets_refresh_interval", 60) * 60 * 1000 - ) - if self.trading_mode != TradingMode.SPOT and load_leverage_tiers: self.fill_leverage_tiers() self.additional_exchange_init() @@ -465,7 +467,12 @@ class Exchange: :return: Candle limit as integer """ - fallback_val = self._ft_has.get("ohlcv_candle_limit") + ccxt_val = self.features( + "spot" if candle_type == CandleType.SPOT else "futures", "fetchOHLCV", "limit", 500 + ) + if not isinstance(ccxt_val, float | int): + ccxt_val = 500 + fallback_val = self._ft_has.get("ohlcv_candle_limit", ccxt_val) if candle_type == CandleType.FUNDING_RATE: fallback_val = self._ft_has.get("funding_fee_candle_limit", fallback_val) return int( @@ -547,6 +554,7 @@ class Exchange: and ( self.precisionMode != TICK_SIZE # Too low precision will falsify calculations + or market.get("precision", {}).get("price") is None or market.get("precision", {}).get("price") > 1e-11 ) and ( @@ -640,7 +648,8 @@ class Exchange: def _load_async_markets(self, reload: bool = False) -> dict[str, Any]: try: - markets = self.loop.run_until_complete(self._api_reload_markets(reload=reload)) + with self._loop_lock: + markets = self.loop.run_until_complete(self._api_reload_markets(reload=reload)) if isinstance(markets, Exception): raise markets @@ -885,6 +894,24 @@ class Exchange: return self._ft_has["exchange_has_overrides"][endpoint] return endpoint in self._api_async.has and self._api_async.has[endpoint] + def features( + self, market_type: Literal["spot", "futures"], endpoint, attribute, default: T + ) -> T: + """ + Returns the exchange features for the given markettype + https://docs.ccxt.com/#/README?id=features + attributes are in a nested dict, with spot and swap.linear + e.g. spot.fetchOHLCV.limit + swap.linear.fetchOHLCV.limit + """ + feat = ( + self._api_async.features.get("spot", {}) + if market_type == "spot" + else self._api_async.features.get("swap", {}).get("linear", {}) + ) + + return feat.get(endpoint, {}).get(attribute, default) + def get_precision_amount(self, pair: str) -> float | None: """ Returns the amount precision of the exchange. @@ -1863,6 +1890,14 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(e) from e + def get_proxy_coin(self) -> str: + """ + Get the proxy coin for the given coin + Falls back to the stake currency if no proxy coin is found + :return: Proxy coin or stake currency + """ + return self._config["stake_currency"] + def get_conversion_rate(self, coin: str, currency: str) -> float | None: """ Quick and cached way to get conversion rate one currency to the other. @@ -1872,6 +1907,11 @@ class Exchange: :returns: Conversion rate from coin to currency :raises: ExchangeErrors """ + + if (proxy_coin := self._ft_has["proxy_coin_mapping"].get(coin, None)) is not None: + coin = proxy_coin + if (proxy_currency := self._ft_has["proxy_coin_mapping"].get(currency, None)) is not None: + currency = proxy_currency if coin == currency: return 1.0 tickers = self.get_tickers(cached=True) @@ -1889,7 +1929,7 @@ class Exchange: ) ticker = tickers_other.get(pair, None) if ticker: - rate: float | None = ticker.get("last", None) + rate: float | None = safe_value_fallback2(ticker, ticker, "last", "ask", None) if rate and pair.startswith(currency) and not pair.endswith(currency): rate = 1.0 / rate return rate @@ -2251,13 +2291,11 @@ class Exchange: # If cost is None or 0.0 -> falsy, return None return None try: - for comb in self.get_valid_pair_combination( + fee_to_quote_rate = self.get_conversion_rate( fee_curr, self._config["stake_currency"] - ): - tick = self.fetch_ticker(comb) - fee_to_quote_rate = safe_value_fallback2(tick, tick, "last", "ask") - if tick: - break + ) + if not fee_to_quote_rate: + raise ValueError("Conversion rate not found.") except (ValueError, ExchangeError): fee_to_quote_rate = self._config["exchange"].get("unknown_fee_rate", None) if not fee_to_quote_rate: @@ -2305,15 +2343,16 @@ class Exchange: :param until_ms: Timestamp in milliseconds to get history up to :return: Dataframe with candle (OHLCV) data """ - pair, _, _, data, _ = self.loop.run_until_complete( - self._async_get_historic_ohlcv( - pair=pair, - timeframe=timeframe, - since_ms=since_ms, - until_ms=until_ms, - candle_type=candle_type, + with self._loop_lock: + pair, _, _, data, _ = self.loop.run_until_complete( + self._async_get_historic_ohlcv( + pair=pair, + timeframe=timeframe, + since_ms=since_ms, + until_ms=until_ms, + candle_type=candle_type, + ) ) - ) logger.debug(f"Downloaded data for {pair} from ccxt with length {len(data)}.") return ohlcv_to_dataframe(data, timeframe, pair, fill_missing=False, drop_incomplete=True) @@ -2379,35 +2418,35 @@ class Exchange: if cache and (pair, timeframe, candle_type) in self._klines: candle_limit = self.ohlcv_candle_limit(timeframe, candle_type) - min_date = int(date_minus_candles(timeframe, candle_limit - 5).timestamp()) + min_ts = dt_ts(date_minus_candles(timeframe, candle_limit - 5)) if self._exchange_ws: - candle_date = int(timeframe_to_prev_date(timeframe).timestamp() * 1000) - prev_candle_date = int(date_minus_candles(timeframe, 1).timestamp() * 1000) - candles = self._exchange_ws.ccxt_object.ohlcvs.get(pair, {}).get(timeframe) - half_candle = int(candle_date - (candle_date - prev_candle_date) * 0.5) + candle_ts = dt_ts(timeframe_to_prev_date(timeframe)) + prev_candle_ts = dt_ts(date_minus_candles(timeframe, 1)) + candles = self._exchange_ws.ohlcvs(pair, timeframe) + half_candle = int(candle_ts - (candle_ts - prev_candle_ts) * 0.5) last_refresh_time = int( self._exchange_ws.klines_last_refresh.get((pair, timeframe, candle_type), 0) ) if ( candles - and candles[-1][0] >= prev_candle_date + and candles[-1][0] >= prev_candle_ts and last_refresh_time >= half_candle ): # Usable result, candle contains the previous candle. # Also, we check if the last refresh time is no more than half the candle ago. logger.debug(f"reuse watch result for {pair}, {timeframe}, {last_refresh_time}") - return self._exchange_ws.get_ohlcv(pair, timeframe, candle_type, candle_date) + return self._exchange_ws.get_ohlcv(pair, timeframe, candle_type, candle_ts) logger.info( - f"Failed to reuse watch {pair}, {timeframe}, {candle_date < last_refresh_time}," - f" {candle_date}, {last_refresh_time}, " - f"{format_ms_time(candle_date)}, {format_ms_time(last_refresh_time)} " + f"Failed to reuse watch {pair}, {timeframe}, {candle_ts < last_refresh_time}," + f" {candle_ts}, {last_refresh_time}, " + f"{format_ms_time(candle_ts)}, {format_ms_time(last_refresh_time)} " ) # Check if 1 call can get us updated candles without hole in the data. - if min_date < self._pairs_last_refresh_time.get((pair, timeframe, candle_type), 0): + if min_ts < self._pairs_last_refresh_time.get((pair, timeframe, candle_type), 0): # Cache can be used - do one-off call. not_all_data = False else: @@ -2485,7 +2524,7 @@ class Exchange: # keeping last candle time as last refreshed time of the pair if ticks and cache: idx = -2 if drop_incomplete and len(ticks) > 1 else -1 - self._pairs_last_refresh_time[(pair, timeframe, c_type)] = ticks[idx][0] // 1000 + self._pairs_last_refresh_time[(pair, timeframe, c_type)] = ticks[idx][0] # keeping parsed dataframe in cache ohlcv_df = ohlcv_to_dataframe( ticks, timeframe, pair=pair, fill_missing=True, drop_incomplete=drop_incomplete @@ -2598,10 +2637,10 @@ class Exchange: def _now_is_time_to_refresh(self, pair: str, timeframe: str, candle_type: CandleType) -> bool: # Timeframe in seconds - interval_in_sec = timeframe_to_seconds(timeframe) + interval_in_sec = timeframe_to_msecs(timeframe) plr = self._pairs_last_refresh_time.get((pair, timeframe, candle_type), 0) + interval_in_sec # current,active candle open date - now = int(timeframe_to_prev_date(timeframe).timestamp()) + now = dt_ts(timeframe_to_prev_date(timeframe)) return plr < now @retrier_async @@ -2947,7 +2986,7 @@ class Exchange: return trades[-1].get("timestamp") async def _async_get_trade_history_id_startup( - self, pair: str, since: int | None + self, pair: str, since: int ) -> tuple[list[list], str]: """ override for initial trade_history_id call @@ -2955,7 +2994,7 @@ class Exchange: return await self._async_fetch_trades(pair, since=since) async def _async_get_trade_history_id( - self, pair: str, until: int, since: int | None = None, from_id: str | None = None + self, pair: str, *, until: int, since: int, from_id: str | None = None ) -> tuple[str, list[list]]: """ Asynchronously gets trade history using fetch_trades @@ -2991,8 +3030,7 @@ class Exchange: trades.extend(t[x]) if from_id == from_id_next or t[-1][0] > until: logger.debug( - f"Stopping because from_id did not change. " - f"Reached {t[-1][0]} > {until}" + f"Stopping because from_id did not change. Reached {t[-1][0]} > {until}" ) # Reached the end of the defined-download period - add last trade as well. if has_overlap: @@ -3010,7 +3048,7 @@ class Exchange: return (pair, trades) async def _async_get_trade_history_time( - self, pair: str, until: int, since: int | None = None + self, pair: str, until: int, since: int ) -> tuple[str, list[list]]: """ Asynchronously gets trade history using fetch_trades, @@ -3051,7 +3089,7 @@ class Exchange: async def _async_get_trade_history( self, pair: str, - since: int | None = None, + since: int, until: int | None = None, from_id: str | None = None, ) -> tuple[str, list[list]]: @@ -3082,7 +3120,7 @@ class Exchange: def get_historic_trades( self, pair: str, - since: int | None = None, + since: int, until: int | None = None, from_id: str | None = None, ) -> tuple[str, list]: @@ -3642,7 +3680,7 @@ class Exchange: liquidation_price_buffer = ( liquidation_price - buffer_amount if is_short else liquidation_price + buffer_amount ) - return self.price_to_precision(pair, max(liquidation_price_buffer, 0.0)) + return max(liquidation_price_buffer, 0.0) else: return None @@ -3666,7 +3704,7 @@ class Exchange: Wherein, "+" or "-" depends on whether the contract goes long or short: "-" for long, and "+" for short. - okex: https://www.okx.com/support/hc/en-us/articles/ + okx: https://www.okx.com/support/hc/en-us/articles/ 360053909592-VI-Introduction-to-the-isolated-mode-of-Single-Multi-currency-Portfolio-margin :param pair: Pair to calculate liquidation price for diff --git a/freqtrade/exchange/exchange_types.py b/freqtrade/exchange/exchange_types.py index 69741dc65..9687057bd 100644 --- a/freqtrade/exchange/exchange_types.py +++ b/freqtrade/exchange/exchange_types.py @@ -47,6 +47,8 @@ class FtHas(TypedDict, total=False): needs_trading_fees: bool order_props_in_contracts: list[Literal["amount", "cost", "filled", "remaining"]] + proxy_coin_mapping: dict[str, str] + # Websocket control ws_enabled: bool diff --git a/freqtrade/exchange/exchange_utils.py b/freqtrade/exchange/exchange_utils.py index 1c9894242..d20ff13f9 100644 --- a/freqtrade/exchange/exchange_utils.py +++ b/freqtrade/exchange/exchange_utils.py @@ -23,6 +23,7 @@ from freqtrade.exchange.common import ( BAD_EXCHANGES, EXCHANGE_HAS_OPTIONAL, EXCHANGE_HAS_REQUIRED, + MAP_EXCHANGE_CHILDCLASS, SUPPORTED_EXCHANGES, ) from freqtrade.exchange.exchange_utils_timeframe import timeframe_to_minutes, timeframe_to_prev_date @@ -91,21 +92,24 @@ def validate_exchange(exchange: str) -> tuple[bool, str, ccxt.Exchange | None]: def _build_exchange_list_entry( exchange_name: str, exchangeClasses: dict[str, Any] ) -> ValidExchangesType: + exchange_name = exchange_name.lower() valid, comment, ex_mod = validate_exchange(exchange_name) + mapped_exchange_name = MAP_EXCHANGE_CHILDCLASS.get(exchange_name, exchange_name).lower() + is_alias = getattr(ex_mod, "alias", False) result: ValidExchangesType = { "name": getattr(ex_mod, "name", exchange_name), "classname": exchange_name, "valid": valid, - "supported": exchange_name.lower() in SUPPORTED_EXCHANGES, + "supported": mapped_exchange_name in SUPPORTED_EXCHANGES and not is_alias, "comment": comment, "dex": getattr(ex_mod, "dex", False), - "is_alias": getattr(ex_mod, "alias", False), + "is_alias": is_alias, "alias_for": inspect.getmro(ex_mod.__class__)[1]().id if getattr(ex_mod, "alias", False) else None, "trade_modes": [{"trading_mode": "spot", "margin_mode": ""}], } - if resolved := exchangeClasses.get(exchange_name.lower()): + if resolved := exchangeClasses.get(mapped_exchange_name): supported_modes = [{"trading_mode": "spot", "margin_mode": ""}] + [ {"trading_mode": tm.value, "margin_mode": mm.value} for tm, mm in resolved["class"]._supported_trading_mode_margin_pairs diff --git a/freqtrade/exchange/exchange_ws.py b/freqtrade/exchange/exchange_ws.py index bddd2ca86..cbc9772a3 100644 --- a/freqtrade/exchange/exchange_ws.py +++ b/freqtrade/exchange/exchange_ws.py @@ -9,9 +9,11 @@ import ccxt from freqtrade.constants import Config, PairWithTimeframe from freqtrade.enums.candletype import CandleType +from freqtrade.exceptions import TemporaryError +from freqtrade.exchange.common import retrier from freqtrade.exchange.exchange import timeframe_to_seconds from freqtrade.exchange.exchange_types import OHLCVResponse -from freqtrade.util import dt_ts, format_ms_time +from freqtrade.util import dt_ts, format_ms_time, format_ms_time_det logger = logging.getLogger(__name__) @@ -20,7 +22,7 @@ logger = logging.getLogger(__name__) class ExchangeWS: def __init__(self, config: Config, ccxt_object: ccxt.Exchange) -> None: self.config = config - self.ccxt_object = ccxt_object + self._ccxt_object = ccxt_object self._background_tasks: set[asyncio.Task] = set() self._klines_watching: set[PairWithTimeframe] = set() @@ -68,10 +70,10 @@ class ExchangeWS: async def _cleanup_async(self) -> None: try: - await self.ccxt_object.close() + await self._ccxt_object.close() # Clear the cache. # Not doing this will cause problems on startup with dynamic pairlists - self.ccxt_object.ohlcvs.clear() + self._ccxt_object.ohlcvs.clear() except Exception: logger.exception("Exception in _cleanup_async") finally: @@ -81,7 +83,22 @@ class ExchangeWS: """ Remove history for a pair/timeframe combination from ccxt cache """ - self.ccxt_object.ohlcvs.get(paircomb[0], {}).pop(paircomb[1], None) + self._ccxt_object.ohlcvs.get(paircomb[0], {}).pop(paircomb[1], None) + self.klines_last_refresh.pop(paircomb, None) + + @retrier(retries=3) + def ohlcvs(self, pair: str, timeframe: str) -> list[list]: + """ + Returns a copy of the klines for a pair/timeframe combination + Note: this will only contain the data received from the websocket + so the data will build up over time. + """ + try: + return deepcopy(self._ccxt_object.ohlcvs.get(pair, {}).get(timeframe, [])) + except RuntimeError as e: + # Capture runtime errors and retry + # TemporaryError does not cause backoff - so we're essentially retrying immediately + raise TemporaryError(f"Error deepcopying: {e}") from e def cleanup_expired(self) -> None: """ @@ -122,6 +139,15 @@ class ExchangeWS: ) ) + async def _unwatch_ohlcv(self, pair: str, timeframe: str, candle_type: CandleType) -> None: + try: + await self._ccxt_object.un_watch_ohlcv_for_symbols([[pair, timeframe]]) + except ccxt.NotSupported as e: + logger.debug("un_watch_ohlcv_for_symbols not supported: %s", e) + pass + except Exception: + logger.exception("Exception in _unwatch_ohlcv") + def _continuous_stopped( self, task: asyncio.Task, pair: str, timeframe: str, candle_type: CandleType ): @@ -134,6 +160,10 @@ class ExchangeWS: result = str(result1) logger.info(f"{pair}, {timeframe}, {candle_type} - Task finished - {result}") + asyncio.run_coroutine_threadsafe( + self._unwatch_ohlcv(pair, timeframe, candle_type), loop=self._loop + ) + self._klines_scheduled.discard((pair, timeframe, candle_type)) self._pop_history((pair, timeframe, candle_type)) @@ -143,11 +173,11 @@ class ExchangeWS: try: while (pair, timeframe, candle_type) in self._klines_watching: start = dt_ts() - data = await self.ccxt_object.watch_ohlcv(pair, timeframe) + data = await self._ccxt_object.watch_ohlcv(pair, timeframe) self.klines_last_refresh[(pair, timeframe, candle_type)] = dt_ts() logger.debug( f"watch done {pair}, {timeframe}, data {len(data)} " - f"in {dt_ts() - start:.2f}s" + f"in {(dt_ts() - start) / 1000:.3f}s" ) except ccxt.ExchangeClosedByUser: logger.debug("Exchange connection closed by user") @@ -171,24 +201,27 @@ class ExchangeWS: pair: str, timeframe: str, candle_type: CandleType, - candle_date: int, + candle_ts: int, ) -> OHLCVResponse: """ Returns cached klines from ccxt's "watch" cache. - :param candle_date: timestamp of the end-time of the candle. + :param candle_ts: timestamp of the end-time of the candle we expect. """ # Deepcopy the response - as it might be modified in the background as new messages arrive - candles = deepcopy(self.ccxt_object.ohlcvs.get(pair, {}).get(timeframe)) + candles = self.ohlcvs(pair, timeframe) refresh_date = self.klines_last_refresh[(pair, timeframe, candle_type)] - drop_hint = False - if refresh_date > candle_date: - # Refreshed after candle was complete. - # logger.info(f"{candles[-1][0]} >= {candle_date}") - drop_hint = candles[-1][0] >= candle_date + received_ts = candles[-1][0] if candles else 0 + drop_hint = received_ts >= candle_ts + if received_ts > refresh_date: + logger.warning( + f"{pair}, {timeframe} - Candle date > last refresh " + f"({format_ms_time(received_ts)} > {format_ms_time_det(refresh_date)}). " + "This usually suggests a problem with time synchronization." + ) logger.debug( f"watch result for {pair}, {timeframe} with length {len(candles)}, " - f"{format_ms_time(candles[-1][0])}, " - f"lref={format_ms_time(refresh_date)}, " - f"candle_date={format_ms_time(candle_date)}, {drop_hint=}" + f"r_ts={format_ms_time(received_ts)}, " + f"lref={format_ms_time_det(refresh_date)}, " + f"candle_ts={format_ms_time(candle_ts)}, {drop_hint=}" ) return pair, timeframe, candle_type, candles, drop_hint diff --git a/freqtrade/exchange/gate.py b/freqtrade/exchange/gate.py index d9473043f..95e1a00ca 100644 --- a/freqtrade/exchange/gate.py +++ b/freqtrade/exchange/gate.py @@ -30,7 +30,6 @@ class Gate(Exchange): unified_account = False _ft_has: FtHas = { - "ohlcv_candle_limit": 1000, "order_time_in_force": ["GTC", "IOC"], "stoploss_on_exchange": True, "stoploss_order_types": {"limit": "limit"}, diff --git a/freqtrade/exchange/htx.py b/freqtrade/exchange/htx.py index ba158d4c5..9b112ff45 100644 --- a/freqtrade/exchange/htx.py +++ b/freqtrade/exchange/htx.py @@ -21,7 +21,6 @@ class Htx(Exchange): "stop_price_param": "stopPrice", "stop_price_prop": "stopPrice", "stoploss_order_types": {"limit": "stop-limit"}, - "ohlcv_candle_limit": 1000, "l2_limit_range": [5, 10, 20], "l2_limit_range_required": False, "ohlcv_candle_limit_per_timeframe": { diff --git a/freqtrade/exchange/hyperliquid.py b/freqtrade/exchange/hyperliquid.py index 6588af0da..a75a77892 100644 --- a/freqtrade/exchange/hyperliquid.py +++ b/freqtrade/exchange/hyperliquid.py @@ -1,6 +1,7 @@ """Hyperliquid exchange subclass""" import logging +from copy import deepcopy from datetime import datetime from freqtrade.constants import BuySell @@ -21,7 +22,6 @@ class Hyperliquid(Exchange): _ft_has: FtHas = { "ohlcv_has_history": False, - "ohlcv_candle_limit": 5000, "l2_limit_range": [20], "trades_has_history": False, "tickers_have_bid_ask": False, @@ -157,9 +157,15 @@ class Hyperliquid(Exchange): logger.warning(f"Could not update funding fees for {pair}.") return 0.0 - def fetch_order(self, order_id: str, pair: str, params: dict | None = None) -> CcxtOrder: - order = super().fetch_order(order_id, pair, params) - + def _adjust_hyperliquid_order( + self, + order: dict, + ) -> dict: + """ + Adjusts order response for Hyperliquid + :param order: Order response from Hyperliquid + :return: Adjusted order response + """ if ( order["average"] is None and order["status"] in ("canceled", "closed") @@ -168,7 +174,9 @@ class Hyperliquid(Exchange): # Hyperliquid does not fill the average price in the order response # Fetch trades to calculate the average price to have the actual price # the order was executed at - trades = self.get_trades_for_order(order_id, pair, since=dt_from_ts(order["timestamp"])) + trades = self.get_trades_for_order( + order["id"], order["symbol"], since=dt_from_ts(order["timestamp"]) + ) if trades: total_amount = sum(t["amount"] for t in trades) @@ -177,5 +185,23 @@ class Hyperliquid(Exchange): if total_amount else None ) + return order + + def fetch_order(self, order_id: str, pair: str, params: dict | None = None) -> CcxtOrder: + order = super().fetch_order(order_id, pair, params) + + order = self._adjust_hyperliquid_order(order) + self._log_exchange_response("fetch_order2", order) return order + + def fetch_orders( + self, pair: str, since: datetime, params: dict | None = None + ) -> list[CcxtOrder]: + orders = super().fetch_orders(pair, since, params) + for idx, order in enumerate(deepcopy(orders)): + order2 = self._adjust_hyperliquid_order(order) + orders[idx] = order2 + + self._log_exchange_response("fetch_orders2", orders) + return orders diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index 8573d611d..03c97569a 100644 --- a/freqtrade/exchange/kraken.py +++ b/freqtrade/exchange/kraken.py @@ -26,7 +26,6 @@ class Kraken(Exchange): "stop_price_prop": "stopLossPrice", "stoploss_order_types": {"limit": "limit", "market": "market"}, "order_time_in_force": ["GTC", "IOC", "PO"], - "ohlcv_candle_limit": 720, "ohlcv_has_history": False, "trades_pagination": "id", "trades_pagination_arg": "since", @@ -70,6 +69,7 @@ class Kraken(Exchange): consolidated: CcxtBalances = {} for currency, balance in balances.items(): base_currency = currency[:-2] if currency.endswith(".F") else currency + base_currency = self._api.commonCurrencies.get(base_currency, base_currency) if base_currency in consolidated: consolidated[base_currency]["free"] += balance["free"] consolidated[base_currency]["used"] += balance["used"] diff --git a/freqtrade/exchange/kucoin.py b/freqtrade/exchange/kucoin.py index de033dcc0..17afbd63a 100644 --- a/freqtrade/exchange/kucoin.py +++ b/freqtrade/exchange/kucoin.py @@ -28,7 +28,6 @@ class Kucoin(Exchange): "l2_limit_range": [20, 100], "l2_limit_range_required": False, "order_time_in_force": ["GTC", "FOK", "IOC"], - "ohlcv_candle_limit": 1500, } def _get_stop_params(self, side: BuySell, ordertype: str, stop_price: float) -> dict: diff --git a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py index 3c3c84804..ffa1fe2e3 100644 --- a/freqtrade/freqai/RL/BaseReinforcementLearningModel.py +++ b/freqtrade/freqai/RL/BaseReinforcementLearningModel.py @@ -138,8 +138,8 @@ class BaseReinforcementLearningModel(IFreqaiModel): ) logger.info( - f'Training model on {len(dk.data_dictionary["train_features"].columns)}' - f' features and {len(dd["train_features"])} data points' + f"Training model on {len(dk.data_dictionary['train_features'].columns)}" + f" features and {len(dd['train_features'])} data points" ) self.set_train_and_eval_environments(dd, prices_train, prices_test, dk) @@ -346,8 +346,7 @@ class BaseReinforcementLearningModel(IFreqaiModel): ) elif prices_train.empty: raise OperationalException( - "No prices found, please follow log warning " - "instructions to correct the strategy." + "No prices found, please follow log warning instructions to correct the strategy." ) prices_train.rename(columns=rename_dict, inplace=True) diff --git a/freqtrade/freqai/base_models/FreqaiMultiOutputClassifier.py b/freqtrade/freqai/base_models/FreqaiMultiOutputClassifier.py index c83a19bb5..ba6c36eac 100644 --- a/freqtrade/freqai/base_models/FreqaiMultiOutputClassifier.py +++ b/freqtrade/freqai/base_models/FreqaiMultiOutputClassifier.py @@ -3,7 +3,7 @@ from sklearn.base import is_classifier from sklearn.multioutput import MultiOutputClassifier, _fit_estimator from sklearn.utils.multiclass import check_classification_targets from sklearn.utils.parallel import Parallel, delayed -from sklearn.utils.validation import has_fit_parameter +from sklearn.utils.validation import has_fit_parameter, validate_data from freqtrade.exceptions import OperationalException @@ -36,15 +36,14 @@ class FreqaiMultiOutputClassifier(MultiOutputClassifier): if not hasattr(self.estimator, "fit"): raise ValueError("The base estimator should implement a fit method") - y = self._validate_data(X="no_validation", y=y, multi_output=True) + y = validate_data(self, X="no_validation", y=y, multi_output=True) if is_classifier(self): check_classification_targets(y) if y.ndim == 1: raise ValueError( - "y must have at least two dimensions for " - "multi-output regression but has only one." + "y must have at least two dimensions for multi-output regression but has only one." ) if sample_weight is not None and not has_fit_parameter(self.estimator, "sample_weight"): diff --git a/freqtrade/freqai/base_models/FreqaiMultiOutputRegressor.py b/freqtrade/freqai/base_models/FreqaiMultiOutputRegressor.py index 32ab0e16d..15c8db256 100644 --- a/freqtrade/freqai/base_models/FreqaiMultiOutputRegressor.py +++ b/freqtrade/freqai/base_models/FreqaiMultiOutputRegressor.py @@ -1,42 +1,36 @@ from sklearn.multioutput import MultiOutputRegressor, _fit_estimator from sklearn.utils.parallel import Parallel, delayed -from sklearn.utils.validation import has_fit_parameter +from sklearn.utils.validation import has_fit_parameter, validate_data class FreqaiMultiOutputRegressor(MultiOutputRegressor): def fit(self, X, y, sample_weight=None, fit_params=None): """Fit the model to data, separately for each output variable. - Parameters - ---------- - X : {array-like, sparse matrix} of shape (n_samples, n_features) + :param X: {array-like, sparse matrix} of shape (n_samples, n_features) The input data. - y : {array-like, sparse matrix} of shape (n_samples, n_outputs) + :param y: {array-like, sparse matrix} of shape (n_samples, n_outputs) Multi-output targets. An indicator matrix turns on multilabel estimation. - sample_weight : array-like of shape (n_samples,), default=None + :param sample_weight: array-like of shape (n_samples,), default=None Sample weights. If `None`, then samples are equally weighted. Only supported if the underlying regressor supports sample weights. - fit_params : A list of dicts for the fit_params + + :param fit_params: A list of dicts for the fit_params Parameters passed to the ``estimator.fit`` method of each step. Each dict may contain same or different values (e.g. different eval_sets or init_models) - .. versionadded:: 0.23 - Returns - ------- - self : object - Returns a fitted instance. + """ if not hasattr(self.estimator, "fit"): raise ValueError("The base estimator should implement a fit method") - y = self._validate_data(X="no_validation", y=y, multi_output=True) + y = validate_data(self, X="no_validation", y=y, multi_output=True) if y.ndim == 1: raise ValueError( - "y must have at least two dimensions for " - "multi-output regression but has only one." + "y must have at least two dimensions for multi-output regression but has only one." ) if sample_weight is not None and not has_fit_parameter(self.estimator, "sample_weight"): diff --git a/freqtrade/freqai/freqai_interface.py b/freqtrade/freqai/freqai_interface.py index 7d7d605d7..939039094 100644 --- a/freqtrade/freqai/freqai_interface.py +++ b/freqtrade/freqai/freqai_interface.py @@ -426,7 +426,7 @@ class IFreqaiModel(ABC): # append the historic data once per round if self.dd.historic_data: self.dd.update_historic_data(strategy, dk) - logger.debug(f'Updating historic data on pair {metadata["pair"]}') + logger.debug(f"Updating historic data on pair {metadata['pair']}") self.track_current_candle() (_, new_trained_timerange, data_load_timerange) = dk.check_if_new_training_required( @@ -773,7 +773,7 @@ class IFreqaiModel(ABC): """ current_pairlist = self.config.get("exchange", {}).get("pair_whitelist") if not self.dd.pair_dict: - logger.info("Set fresh train queue from whitelist. Queue: {current_pairlist}") + logger.info(f"Set fresh train queue from whitelist. Queue: {current_pairlist}") return deque(current_pairlist) best_queue = deque() @@ -789,7 +789,7 @@ class IFreqaiModel(ABC): best_queue.appendleft(pair) logger.info( - "Set existing queue from trained timestamps. Best approximation queue: {best_queue}" + f"Set existing queue from trained timestamps. Best approximation queue: {best_queue}" ) return best_queue diff --git a/freqtrade/freqai/prediction_models/SKLearnRandomForestClassifier.py b/freqtrade/freqai/prediction_models/SKLearnRandomForestClassifier.py index 18685f78a..b008a8ecd 100644 --- a/freqtrade/freqai/prediction_models/SKLearnRandomForestClassifier.py +++ b/freqtrade/freqai/prediction_models/SKLearnRandomForestClassifier.py @@ -45,8 +45,7 @@ class SKLearnRandomForestClassifier(BaseClassifierModel): if self.freqai_info.get("continual_learning", False): logger.warning( - "Continual learning is not supported for " - "SKLearnRandomForestClassifier, ignoring." + "Continual learning is not supported for SKLearnRandomForestClassifier, ignoring." ) train_weights = data_dictionary["train_weights"] diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index d29f56914..7d70c6336 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -64,7 +64,7 @@ from freqtrade.rpc.rpc_types import ( ) from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper -from freqtrade.util import FtPrecise, MeasureTime +from freqtrade.util import FtPrecise, MeasureTime, dt_from_ts from freqtrade.util.migrations.binance_mig import migrate_binance_futures_names from freqtrade.wallets import Wallets @@ -162,6 +162,7 @@ class FreqtradeBot(LoggingMixin): def update(): self.update_funding_fees() + self.update_all_liquidation_prices() self.wallets.update() # This would be more efficient if scheduled in utc time, and performed at each @@ -532,9 +533,8 @@ class FreqtradeBot(LoggingMixin): logger.info(f"Found previously unknown order {order['id']} for {trade.pair}.") order_obj = Order.parse_from_ccxt_object(order, trade.pair, order["side"]) - order_obj.order_filled_date = datetime.fromtimestamp( - safe_value_fallback(order, "lastTradeTimestamp", "timestamp") // 1000, - tz=timezone.utc, + order_obj.order_filled_date = dt_from_ts( + safe_value_fallback(order, "lastTradeTimestamp", "timestamp") ) trade.orders.append(order_obj) Trade.commit() @@ -730,7 +730,7 @@ class FreqtradeBot(LoggingMixin): for trade in Trade.get_open_trades(): # If there is any open orders, wait for them to finish. # TODO Remove to allow mul open orders - if not trade.has_open_orders: + if trade.has_open_position or trade.has_open_orders: # Do a wallets update (will be ratelimited to once per hour) self.wallets.update(False) try: @@ -808,7 +808,10 @@ class FreqtradeBot(LoggingMixin): ) if amount == 0.0: - logger.info("Amount to exit is 0.0 due to exchange limits - not exiting.") + logger.info( + f"Wanted to exit of {stake_amount} amount, " + "but exit amount is now 0.0 due to exchange limits - not exiting." + ) return remaining = (trade.amount - amount) * current_exit_rate @@ -923,6 +926,10 @@ class FreqtradeBot(LoggingMixin): ): logger.info(f"User denied entry for {pair}.") return False + + if trade and self.handle_similar_open_order(trade, enter_limit_requested, amount, side): + return False + order = self.exchange.create_order( pair=pair, ordertype=order_type, @@ -1029,7 +1036,6 @@ class FreqtradeBot(LoggingMixin): # This is additional entry, we reset fee_open_currency so timeout checking can work trade.is_open = True trade.fee_open_currency = None - trade.open_rate_requested = enter_limit_requested trade.set_funding_fees(funding_fees) trade.orders.append(order_obj) @@ -1197,7 +1203,7 @@ class FreqtradeBot(LoggingMixin): trade.pair, side="entry", is_short=trade.is_short, refresh=False ) stake_amount = trade.stake_amount - if not fill: + if not fill and trade.nr_of_successful_entries > 0: # If we have open orders, we need to add the stake amount of the open orders # as it's not yet included in the trade.stake_amount stake_amount += sum( @@ -1303,8 +1309,8 @@ class FreqtradeBot(LoggingMixin): logger.warning( f"Unable to handle stoploss on exchange for {trade.pair}: {exception}" ) - # Check if we can sell our current pair - if not trade.has_open_orders and trade.is_open and self.handle_trade(trade): + # Check if we can exit our current position for this trade + if trade.has_open_position and trade.is_open and self.handle_trade(trade): trades_closed += 1 except DependencyException as exception: @@ -1448,9 +1454,7 @@ class FreqtradeBot(LoggingMixin): self.handle_protections(trade.pair, trade.trade_direction) return True - if trade.has_open_orders or not trade.is_open: - # Trade has an open order, Stoploss-handling can't happen in this case - # as the Amount on the exchange is tied up in another trade. + if not trade.has_open_position or not trade.is_open: # The trade can be closed already (sell-order fill confirmation came in this iteration) return False @@ -1695,7 +1699,7 @@ class FreqtradeBot(LoggingMixin): ) if not res: self.replace_order_failed( - trade, f"Could not cancel order for {trade}, therefore not replacing." + trade, f"Could not fully cancel order for {trade}, therefore not replacing." ) return if adjusted_entry_price: @@ -1718,6 +1722,31 @@ class FreqtradeBot(LoggingMixin): logger.warning(f"Unable to replace order for {trade.pair}: {exception}") self.replace_order_failed(trade, f"Could not replace order for {trade}.") + def cancel_open_orders_of_trade( + self, trade: Trade, sides: list[str], reason: str, replacing: bool = False + ) -> None: + """ + Cancel trade orders of specified sides that are currently open + :param trade: Trade object of the trade we're analyzing + :param reason: The reason for that cancellation + :param sides: The sides where cancellation should take place + :return: None + """ + + for open_order in trade.open_orders: + try: + order = self.exchange.fetch_order(open_order.order_id, trade.pair) + except ExchangeError: + logger.info("Can't query order for %s due to %s", trade, traceback.format_exc()) + continue + + if order["side"] in sides: + if order["side"] == trade.entry_side: + self.handle_cancel_enter(trade, order, open_order, reason, replacing) + + elif order["side"] == trade.exit_side: + self.handle_cancel_exit(trade, order, open_order, reason) + def cancel_all_open_orders(self) -> None: """ Cancel all orders that are currently open @@ -1725,24 +1754,44 @@ class FreqtradeBot(LoggingMixin): """ for trade in Trade.get_open_trades(): - for open_order in trade.open_orders: - try: - order = self.exchange.fetch_order(open_order.order_id, trade.pair) - except ExchangeError: - logger.info("Can't query order for %s due to %s", trade, traceback.format_exc()) - continue + self.cancel_open_orders_of_trade( + trade, [trade.entry_side, trade.exit_side], constants.CANCEL_REASON["ALL_CANCELLED"] + ) - if order["side"] == trade.entry_side: - self.handle_cancel_enter( - trade, order, open_order, constants.CANCEL_REASON["ALL_CANCELLED"] - ) - - elif order["side"] == trade.exit_side: - self.handle_cancel_exit( - trade, order, open_order, constants.CANCEL_REASON["ALL_CANCELLED"] - ) Trade.commit() + def handle_similar_open_order( + self, trade: Trade, price: float, amount: float, side: str + ) -> bool: + """ + Keep existing open order if same amount and side otherwise cancel + :param trade: Trade object of the trade we're analyzing + :param price: Limit price of the potential new order + :param amount: Quantity of assets of the potential new order + :param side: Side of the potential new order + :return: True if an existing similar order was found + """ + if trade.has_open_orders: + oo = trade.select_order(side, True) + if oo is not None: + if (price == oo.price) and (side == oo.side) and (amount == oo.amount): + logger.info( + f"A similar open order was found for {trade.pair}. " + f"Keeping existing {trade.exit_side} order. {price=}, {amount=}" + ) + return True + # cancel open orders of this trade if order is different + self.cancel_open_orders_of_trade( + trade, + [trade.entry_side, trade.exit_side], + constants.CANCEL_REASON["REPLACE"], + True, + ) + Trade.commit() + return False + + return False + def handle_cancel_enter( self, trade: Trade, @@ -1924,7 +1973,11 @@ class FreqtradeBot(LoggingMixin): return amount trade_base_currency = self.exchange.get_pair_base_currency(pair) - wallet_amount = self.wallets.get_free(trade_base_currency) + # Free + Used - open orders will eventually still be canceled. + wallet_amount = self.wallets.get_free(trade_base_currency) + self.wallets.get_used( + trade_base_currency + ) + logger.debug(f"{pair} - Wallet: {wallet_amount} - Trade-amount: {amount}") if wallet_amount >= amount: return amount @@ -2017,6 +2070,10 @@ class FreqtradeBot(LoggingMixin): logger.info(f"User denied exit for {trade.pair}.") return False + if trade.has_open_orders: + if self.handle_similar_open_order(trade, limit, amount, trade.exit_side): + return False + try: # Execute sell and update trade record order = self.exchange.create_order( diff --git a/freqtrade/leverage/liquidation_price.py b/freqtrade/leverage/liquidation_price.py index 4b49e4551..af34f43fd 100644 --- a/freqtrade/leverage/liquidation_price.py +++ b/freqtrade/leverage/liquidation_price.py @@ -28,26 +28,27 @@ def update_liquidation_prices( if dry_run: # Parameters only needed for cross margin total_wallet_stake = wallets.get_collateral() - - logger.info( - "Updating liquidation price for all open trades. " - f"Collateral {total_wallet_stake} {stake_currency}." - ) - open_trades = Trade.get_open_trades() - for t in open_trades: - # TODO: This should be done in a batch update - t.set_liquidation_price( - exchange.get_liquidation_price( - pair=t.pair, - open_rate=t.open_rate, - is_short=t.is_short, - amount=t.amount, - stake_amount=t.stake_amount, - leverage=t.leverage, - wallet_balance=total_wallet_stake, - open_trades=open_trades, - ) + logger.info( + "Updating liquidation price for all open trades. " + f"Collateral {total_wallet_stake} {stake_currency}." ) + + open_trades: list[Trade] = Trade.get_open_trades() + for t in open_trades: + if t.has_open_position: + # TODO: This should be done in a batch update + t.set_liquidation_price( + exchange.get_liquidation_price( + pair=t.pair, + open_rate=t.open_rate, + is_short=t.is_short, + amount=t.amount, + stake_amount=t.stake_amount, + leverage=t.leverage, + wallet_balance=total_wallet_stake, + open_trades=open_trades, + ) + ) elif trade: trade.set_liquidation_price( exchange.get_liquidation_price( diff --git a/freqtrade/loggers/__init__.py b/freqtrade/loggers/__init__.py index 7e18d3cba..e6e2dd18f 100644 --- a/freqtrade/loggers/__init__.py +++ b/freqtrade/loggers/__init__.py @@ -3,11 +3,16 @@ from logging import Formatter from logging.handlers import RotatingFileHandler, SysLogHandler from pathlib import Path +from rich.console import Console + from freqtrade.constants import Config from freqtrade.exceptions import OperationalException from freqtrade.loggers.buffering_handler import FTBufferingHandler +from freqtrade.loggers.ft_rich_handler import FtRichHandler from freqtrade.loggers.set_log_levels import set_loggers -from freqtrade.loggers.std_err_stream_handler import FTStdErrStreamHandler + + +# from freqtrade.loggers.std_err_stream_handler import FTStdErrStreamHandler logger = logging.getLogger(__name__) @@ -17,6 +22,8 @@ LOGFORMAT = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" bufferHandler = FTBufferingHandler(1000) bufferHandler.setFormatter(Formatter(LOGFORMAT)) +error_console = Console(stderr=True, color_system=None) + def get_existing_handlers(handlertype): """ @@ -33,8 +40,16 @@ def setup_logging_pre() -> None: logging handlers after the real initialization, because we don't know which ones the user desires beforehand. """ + rh = FtRichHandler(console=error_console) + rh.setFormatter(Formatter("%(message)s")) logging.basicConfig( - level=logging.INFO, format=LOGFORMAT, handlers=[FTStdErrStreamHandler(), bufferHandler] + level=logging.INFO, + format=LOGFORMAT, + handlers=[ + # FTStdErrStreamHandler(), + rh, + bufferHandler, + ], ) @@ -45,6 +60,9 @@ def setup_logging(config: Config) -> None: # Log level verbosity = config["verbosity"] logging.root.addHandler(bufferHandler) + if config.get("print_colorized", True): + logger.info("Enabling colorized output.") + error_console._color_system = error_console._detect_color_system() logfile = config.get("logfile") diff --git a/freqtrade/loggers/ft_rich_handler.py b/freqtrade/loggers/ft_rich_handler.py new file mode 100644 index 000000000..dba402f17 --- /dev/null +++ b/freqtrade/loggers/ft_rich_handler.py @@ -0,0 +1,44 @@ +from datetime import datetime +from logging import Handler + +from rich._null_file import NullFile +from rich.console import Console +from rich.text import Text + + +class FtRichHandler(Handler): + """ + Basic colorized logging handler using Rich. + Does not support all features of the standard logging handler, and uses a hard-coded log format + """ + + def __init__(self, console: Console, *args, **kwargs) -> None: + super().__init__(*args, **kwargs) + self._console = console + + def emit(self, record): + try: + msg = self.format(record) + # Format log message + log_time = Text( + datetime.fromtimestamp(record.created).strftime("%Y-%m-%d %H:%M:%S,%f")[:-3], + ) + name = Text(record.name, style="violet") + log_level = Text(record.levelname, style=f"logging.level.{record.levelname.lower()}") + gray_sep = Text(" - ", style="gray46") + + if isinstance(self._console.file, NullFile): + # Handles pythonw, where stdout/stderr are null, and we return NullFile + # instance from Console.file. In this case, we still want to make a log record + # even though we won't be writing anything to a file. + self.handleError(record) + return + + self._console.print( + Text() + log_time + gray_sep + name + gray_sep + log_level + gray_sep + msg + ) + + except RecursionError: + raise + except Exception: + self.handleError(record) diff --git a/freqtrade/main.py b/freqtrade/main.py index bd815b746..d4aab3468 100755 --- a/freqtrade/main.py +++ b/freqtrade/main.py @@ -18,7 +18,7 @@ from freqtrade.commands import Arguments from freqtrade.constants import DOCS_LINK from freqtrade.exceptions import ConfigurationError, FreqtradeException, OperationalException from freqtrade.loggers import setup_logging_pre -from freqtrade.system import asyncio_setup, gc_set_threshold +from freqtrade.system import asyncio_setup, gc_set_threshold, print_version_info logger = logging.getLogger("freqtrade") @@ -38,7 +38,10 @@ def main(sysargv: list[str] | None = None) -> None: args = arguments.get_parsed_arg() # Call subcommand. - if "func" in args: + if args.get("version") or args.get("version_main"): + print_version_info() + return_code = 0 + elif "func" in args: logger.info(f"freqtrade {__version__}") gc_set_threshold() return_code = args["func"](args) diff --git a/freqtrade/misc.py b/freqtrade/misc.py index 5ea227984..a6dbed85e 100644 --- a/freqtrade/misc.py +++ b/freqtrade/misc.py @@ -19,6 +19,15 @@ from freqtrade.enums import SignalTagType, SignalType logger = logging.getLogger(__name__) +def dump_json_to_file(file_obj: TextIO, data: Any) -> None: + """ + Dump JSON data into a file object + :param file_obj: File object to write to + :param data: JSON Data to save + """ + rapidjson.dump(data, file_obj, default=str, number_mode=rapidjson.NM_NATIVE) + + def file_dump_json(filename: Path, data: Any, is_zip: bool = False, log: bool = True) -> None: """ Dump JSON data into a file @@ -35,32 +44,16 @@ def file_dump_json(filename: Path, data: Any, is_zip: bool = False, log: bool = logger.info(f'dumping json to "{filename}"') with gzip.open(filename, "wt", encoding="utf-8") as fpz: - rapidjson.dump(data, fpz, default=str, number_mode=rapidjson.NM_NATIVE) + dump_json_to_file(fpz, data) else: if log: logger.info(f'dumping json to "{filename}"') with filename.open("w") as fp: - rapidjson.dump(data, fp, default=str, number_mode=rapidjson.NM_NATIVE) + dump_json_to_file(fp, data) logger.debug(f'done json to "{filename}"') -def file_dump_joblib(filename: Path, data: Any, log: bool = True) -> None: - """ - Dump object data into a file - :param filename: file to create - :param data: Object data to save - :return: - """ - import joblib - - if log: - logger.info(f'dumping joblib to "{filename}"') - with filename.open("wb") as fp: - joblib.dump(data, fp) - logger.debug(f'done joblib dump to "{filename}"') - - def json_load(datafile: TextIO) -> Any: """ load data with rapidjson diff --git a/freqtrade/optimize/analysis/lookahead.py b/freqtrade/optimize/analysis/lookahead.py index 3daad027b..70cb6913d 100755 --- a/freqtrade/optimize/analysis/lookahead.py +++ b/freqtrade/optimize/analysis/lookahead.py @@ -208,8 +208,7 @@ class LookaheadAnalysis(BaseAnalysis): found_signals: int = self.full_varHolder.result["results"].shape[0] + 1 if found_signals >= self.targeted_trade_amount: logger.info( - f"Found {found_signals} trades, " - f"calculating {self.targeted_trade_amount} trades." + f"Found {found_signals} trades, calculating {self.targeted_trade_amount} trades." ) elif self.targeted_trade_amount >= found_signals >= self.minimum_trade_amount: logger.info(f"Only found {found_signals} trades. Calculating all available trades.") diff --git a/freqtrade/optimize/analysis/recursive_helpers.py b/freqtrade/optimize/analysis/recursive_helpers.py index 5877a5864..1d2ccdd0b 100644 --- a/freqtrade/optimize/analysis/recursive_helpers.py +++ b/freqtrade/optimize/analysis/recursive_helpers.py @@ -112,6 +112,5 @@ class RecursiveAnalysisSubFunctions: ) else: logger.error( - "There was no strategy specified through --strategy " - "or timeframe was not specified." + "There was no strategy specified through --strategy or timeframe was not specified." ) diff --git a/freqtrade/optimize/backtest_caching.py b/freqtrade/optimize/backtest_caching.py index 7143da006..9c3e2f343 100644 --- a/freqtrade/optimize/backtest_caching.py +++ b/freqtrade/optimize/backtest_caching.py @@ -40,4 +40,4 @@ def get_strategy_run_id(strategy) -> str: def get_backtest_metadata_filename(filename: Path | str) -> Path: """Return metadata filename for specified backtest results file.""" filename = Path(filename) - return filename.parent / Path(f"{filename.stem}.meta{filename.suffix}") + return filename.parent / Path(f"{filename.stem}.meta.json") diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index bf0b52e72..d3f1bc63b 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -7,7 +7,7 @@ This module contains the backtesting logic import logging from collections import defaultdict from copy import deepcopy -from datetime import datetime, timedelta, timezone +from datetime import datetime, timedelta from typing import Any from numpy import nan @@ -63,7 +63,7 @@ from freqtrade.plugins.protectionmanager import ProtectionManager from freqtrade.resolvers import ExchangeResolver, StrategyResolver from freqtrade.strategy.interface import IStrategy from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper -from freqtrade.util import FtPrecise +from freqtrade.util import FtPrecise, dt_now from freqtrade.util.migrations import migrate_data from freqtrade.wallets import Wallets @@ -832,6 +832,10 @@ class Backtesting: amount = amount_to_contract_precision( amount or trade.amount, trade.amount_precision, self.precision_mode, trade.contract_size ) + + if self.handle_similar_order(trade, close_rate, amount, trade.exit_side, exit_candle_time): + return None + order = Order( id=self.order_id_counter, ft_trade_id=trade.id, @@ -851,7 +855,7 @@ class Backtesting: amount=amount, filled=0, remaining=amount, - cost=amount * close_rate, + cost=amount * close_rate * (1 + self.fee), ft_order_tag=exit_reason, ) order._trade_bt = trade @@ -1117,6 +1121,10 @@ class Backtesting: orders=[], ) LocalTrade.add_bt_trade(trade) + elif self.handle_similar_order( + trade, propose_rate, amount, trade.entry_side, current_time + ): + return None trade.adjust_stop_loss(trade.open_rate, self.strategy.stoploss, initial=True) @@ -1158,9 +1166,13 @@ class Backtesting: """ for pair in open_trades.keys(): for trade in list(open_trades[pair]): - if trade.has_open_orders and trade.nr_of_successful_entries == 0: + if ( + trade.has_open_orders and trade.nr_of_successful_entries == 0 + ) or not trade.has_open_position: # Ignore trade if entry-order did not fill yet + LocalTrade.remove_bt_trade(trade) continue + exit_row = data[pair][-1] self._exit_trade( trade, exit_row, exit_row[OPEN_IDX], trade.amount, ExitType.FORCE_EXIT.value @@ -1215,6 +1227,37 @@ class Backtesting: # default maintain trade return False + def cancel_open_orders(self, trade: LocalTrade, current_time: datetime): + """ + Cancel all open orders for the given trade. + """ + for order in [o for o in trade.orders if o.ft_is_open]: + if order.side == trade.entry_side: + self.canceled_entry_orders += 1 + # elif order.side == trade.exit_side: + # self.canceled_exit_orders += 1 + # canceled orders are removed from the trade + del trade.orders[trade.orders.index(order)] + + def handle_similar_order( + self, trade: LocalTrade, price: float, amount: float, side: str, current_time: datetime + ) -> bool: + """ + Handle similar order for the given trade. + """ + if trade.has_open_orders: + oo = trade.select_order(side, True) + if oo: + if (price == oo.price) and (side == oo.side) and (amount == oo.amount): + # logger.info( + # f"A similar open order was found for {trade.pair}. " + # f"Keeping existing {trade.exit_side} order. {price=}, {amount=}" + # ) + return True + self.cancel_open_orders(trade, current_time) + + return False + def check_order_cancel( self, trade: LocalTrade, order: Order, current_time: datetime ) -> bool | None: @@ -1335,28 +1378,6 @@ class Backtesting: current_time: datetime, trade_dir: LongShort | None, can_enter: bool, - ) -> None: - """ - Conditionally call backtest_loop_inner a 2nd time if shorting is enabled, - a position closed and a new signal in the other direction is available. - """ - if not self._can_short or trade_dir is None: - # No need to reverse position if shorting is disabled or there's no new signal - self.backtest_loop_inner(row, pair, current_time, trade_dir, can_enter) - else: - for _ in (0, 1): - a = self.backtest_loop_inner(row, pair, current_time, trade_dir, can_enter) - if not a or a == trade_dir: - # the trade didn't close or position change is in the same direction - break - - def backtest_loop_inner( - self, - row: tuple, - pair: str, - current_time: datetime, - trade_dir: LongShort | None, - can_enter: bool, ) -> LongShort | None: """ NOTE: This method is used by Hyperopt at each iteration. Please keep it optimized. @@ -1386,7 +1407,7 @@ class Backtesting: and (self._position_stacking or len(LocalTrade.bt_trades_open_pp[pair]) == 0) and not PairLocks.is_pair_locked(pair, row[DATE_IDX], trade_dir) ): - if self.trade_slot_available(LocalTrade.bt_open_open_trade_count_candle): + if self.trade_slot_available(LocalTrade.bt_open_open_trade_count): trade = self._enter_trade(pair, row, trade_dir) if trade: self.wallets.update() @@ -1400,7 +1421,7 @@ class Backtesting: self.wallets.update() # 4. Create exit orders (if any) - if not trade.has_open_orders: + if trade.has_open_position: self._check_trade_exit(trade, row, current_time) # Place exit order if necessary # 5. Process exit orders. @@ -1412,27 +1433,163 @@ class Backtesting: return exiting_dir return None + def get_detail_data(self, pair: str, row: tuple) -> list[tuple] | None: + """ + Spread into detail data + """ + current_detail_time: datetime = row[DATE_IDX].to_pydatetime() + exit_candle_end = current_detail_time + self.timeframe_td + detail_data = self.detail_data[pair] + detail_data = detail_data.loc[ + (detail_data["date"] >= current_detail_time) & (detail_data["date"] < exit_candle_end) + ].copy() + + if len(detail_data) == 0: + return None + detail_data.loc[:, "enter_long"] = row[LONG_IDX] + detail_data.loc[:, "exit_long"] = row[ELONG_IDX] + detail_data.loc[:, "enter_short"] = row[SHORT_IDX] + detail_data.loc[:, "exit_short"] = row[ESHORT_IDX] + detail_data.loc[:, "enter_tag"] = row[ENTER_TAG_IDX] + detail_data.loc[:, "exit_tag"] = row[EXIT_TAG_IDX] + return detail_data[HEADERS].values.tolist() + + def _time_generator(self, start_date: datetime, end_date: datetime): + current_time = start_date + self.timeframe_td + while current_time <= end_date: + yield current_time + current_time += self.timeframe_td + + def _time_generator_det(self, start_date: datetime, end_date: datetime): + """ + Loop for each detail candle. + Yields only the start date if no detail timeframe is set. + """ + if not self.timeframe_detail_td: + yield start_date, True, False, 0 + return + + current_time = start_date + i = 0 + while current_time <= end_date: + yield current_time, i == 0, True, i + i += 1 + current_time += self.timeframe_detail_td + + def _time_pair_generator_det(self, current_time: datetime, pairs: list[str]): + for current_time_det, is_first, has_detail, idx in self._time_generator_det( + current_time, current_time + self.timeframe_td + ): + # Pairs that have open trades should be processed first + new_pairlist = list(dict.fromkeys([t.pair for t in LocalTrade.bt_trades_open] + pairs)) + for pair in new_pairlist: + yield current_time_det, is_first, has_detail, idx, pair + def time_pair_generator( - self, start_date: datetime, end_date: datetime, increment: timedelta, pairs: list[str] + self, + start_date: datetime, + end_date: datetime, + pairs: list[str], + data: dict[str, list[tuple]], ): """ Backtest time and pair generator + :returns: generator of (current_time, pair, row, is_last_row, trade_dir) + where is_last_row is a boolean indicating if this is the data end date. """ - current_time = start_date + increment + current_time = start_date + self.timeframe_td self.progress.init_step( BacktestState.BACKTEST, int((end_date - start_date) / self.timeframe_td) ) - while current_time <= end_date: - is_first = True - # Pairs that have open trades should be processed first - new_pairlist = list(dict.fromkeys([t.pair for t in LocalTrade.bt_trades_open] + pairs)) + # Indexes per pair, so some pairs are allowed to have a missing start. + indexes: dict = defaultdict(int) - for pair in new_pairlist: - yield current_time, pair, is_first - is_first = False + for current_time in self._time_generator(start_date, end_date): + # Loop for each main candle. + self.check_abort() + # Reset open trade count for this candle + # Critical to avoid exceeding max_open_trades in backtesting + # when timeframe-detail is used and trades close within the opening candle. + strategy_safe_wrapper(self.strategy.bot_loop_start, supress_error=True)( + current_time=current_time + ) + pair_detail_cache: dict[str, list[tuple]] = {} + pair_tradedir_cache: dict[str, LongShort | None] = {} + pairs_with_open_trades = [t.pair for t in LocalTrade.bt_trades_open] + for current_time_det, is_first, has_detail, idx, pair in self._time_pair_generator_det( + current_time, pairs + ): + # Loop for each detail candle (if necessary) and pair + # Yields only the main date if no detail timeframe is set. + + # Pairs that have open trades should be processed first + trade_dir: LongShort | None = None + if is_first: + # Main candle + row_index = indexes[pair] + row = self.validate_row(data, pair, row_index, current_time) + if not row: + continue + + row_index += 1 + indexes[pair] = row_index + is_last_row = current_time == end_date + self.dataprovider._set_dataframe_max_index(self.required_startup + row_index) + trade_dir = self.check_for_trade_entry(row) + pair_tradedir_cache[pair] = trade_dir + + else: + # Detail candle - from cache. + detail_data = pair_detail_cache.get(pair) + if detail_data is None or len(detail_data) <= idx: + # logger.info(f"skipping {pair}, {current_time_det}, {trade_dir}") + continue + row = detail_data[idx] + trade_dir = pair_tradedir_cache.get(pair) + + if self.strategy.ignore_expired_candle( + current_time - self.timeframe_td, # last closed candle is 1 timeframe away. + current_time_det, + self.timeframe_secs, + trade_dir is not None, + ): + # Ignore late entries eventually + trade_dir = None + + self.dataprovider._set_dataframe_max_date(current_time_det) + + pair_has_open_trades = len(LocalTrade.bt_trades_open_pp[pair]) > 0 + if pair in pairs_with_open_trades and not pair_has_open_trades: + # Pair has had open trades which closed in the current main candle. + # Skip this pair for this timeframe + continue + if pair_has_open_trades and pair not in pairs_with_open_trades: + # auto-lock for pairs that have open trades + # Necessary for detail - to capture trades that open and close within + # the same main candle + pairs_with_open_trades.append(pair) + + if ( + is_first + and (trade_dir is not None or pair_has_open_trades) + and has_detail + and pair not in pair_detail_cache + and pair in self.detail_data + and row + ): + # Spread candle into detail timeframe and cache that - + # only once per main candle + # and only if we can expect activity. + pair_detail = self.get_detail_data(pair, row) + if pair_detail is not None: + pair_detail_cache[pair] = pair_detail + row = pair_detail_cache[pair][idx] + + is_last_row = current_time_det == end_date + + yield current_time_det, pair, row, is_last_row, trade_dir self.progress.increment() - current_time += increment def backtest(self, processed: dict, start_date: datetime, end_date: datetime) -> dict[str, Any]: """ @@ -1455,77 +1612,26 @@ class Backtesting: # (looping lists is a lot faster than pandas DataFrames) data: dict = self._get_ohlcv_as_lists(processed) - # Indexes per pair, so some pairs are allowed to have a missing start. - indexes: dict = defaultdict(int) - # Loop timerange and get candle for each pair at that point in time - for current_time, pair, is_first_call in self.time_pair_generator( - start_date, end_date, self.timeframe_td, list(data.keys()) - ): - if is_first_call: - self.check_abort() - # Reset open trade count for this candle - # Critical to avoid exceeding max_open_trades in backtesting - # when timeframe-detail is used and trades close within the opening candle. - LocalTrade.bt_open_open_trade_count_candle = LocalTrade.bt_open_open_trade_count - strategy_safe_wrapper(self.strategy.bot_loop_start, supress_error=True)( - current_time=current_time - ) - row_index = indexes[pair] - row = self.validate_row(data, pair, row_index, current_time) - if not row: - continue - - row_index += 1 - indexes[pair] = row_index - is_last_row = current_time == end_date - self.dataprovider._set_dataframe_max_index(self.required_startup + row_index) - self.dataprovider._set_dataframe_max_date(current_time) - current_detail_time: datetime = row[DATE_IDX].to_pydatetime() - trade_dir: LongShort | None = self.check_for_trade_entry(row) - - if ( - (trade_dir is not None or len(LocalTrade.bt_trades_open_pp[pair]) > 0) - and self.timeframe_detail - and pair in self.detail_data - ): - # Spread out into detail timeframe. - # Should only happen when we are either in a trade for this pair - # or when we got the signal for a new trade. - exit_candle_end = current_detail_time + self.timeframe_td - - detail_data = self.detail_data[pair] - detail_data = detail_data.loc[ - (detail_data["date"] >= current_detail_time) - & (detail_data["date"] < exit_candle_end) - ].copy() - if len(detail_data) == 0: - # Fall back to "regular" data if no detail data was found for this candle - self.dataprovider._set_dataframe_max_date(current_time) - self.backtest_loop(row, pair, current_time, trade_dir, not is_last_row) - continue - detail_data.loc[:, "enter_long"] = row[LONG_IDX] - detail_data.loc[:, "exit_long"] = row[ELONG_IDX] - detail_data.loc[:, "enter_short"] = row[SHORT_IDX] - detail_data.loc[:, "exit_short"] = row[ESHORT_IDX] - detail_data.loc[:, "enter_tag"] = row[ENTER_TAG_IDX] - detail_data.loc[:, "exit_tag"] = row[EXIT_TAG_IDX] - is_first = True - current_time_det = current_time - for det_row in detail_data[HEADERS].values.tolist(): - self.dataprovider._set_dataframe_max_date(current_time_det) - self.backtest_loop( - det_row, - pair, - current_time_det, - trade_dir, - is_first and not is_last_row, - ) - current_time_det += self.timeframe_detail_td - is_first = False - else: - self.dataprovider._set_dataframe_max_date(current_time) + for ( + current_time, + pair, + row, + is_last_row, + trade_dir, + ) in self.time_pair_generator(start_date, end_date, list(data.keys()), data): + if not self._can_short or trade_dir is None: + # No need to reverse position if shorting is disabled or there's no new signal self.backtest_loop(row, pair, current_time, trade_dir, not is_last_row) + else: + # Conditionally call backtest_loop a 2nd time if shorting is enabled, + # a position closed and a new signal in the other direction is available. + + for _ in (0, 1): + a = self.backtest_loop(row, pair, current_time, trade_dir, not is_last_row) + if not a or a == trade_dir: + # the trade didn't close or position change is in the same direction + break self.handle_left_open(LocalTrade.bt_trades_open_pp, data=data) self.wallets.update() @@ -1550,7 +1656,7 @@ class Backtesting: self.progress.init_step(BacktestState.ANALYZE, 0) strategy_name = strat.get_strategy_name() logger.info(f"Running backtesting for Strategy {strategy_name}") - backtest_start_time = datetime.now(timezone.utc) + backtest_start_time = dt_now() self._set_strategy(strat) # need to reprocess data every time to populate signals @@ -1577,7 +1683,7 @@ class Backtesting: start_date=min_date, end_date=max_date, ) - backtest_end_time = datetime.now(timezone.utc) + backtest_end_time = dt_now() results.update( { "run_id": self.run_ids.get(strategy_name, ""), @@ -1604,14 +1710,14 @@ class Backtesting: def _get_min_cached_backtest_date(self): min_backtest_date = None backtest_cache_age = self.config.get("backtest_cache", constants.BACKTEST_CACHE_DEFAULT) - if self.timerange.stopts == 0 or self.timerange.stopdt > datetime.now(tz=timezone.utc): + if self.timerange.stopts == 0 or self.timerange.stopdt > dt_now(): logger.warning("Backtest result caching disabled due to use of open-ended timerange.") elif backtest_cache_age == "day": - min_backtest_date = datetime.now(tz=timezone.utc) - timedelta(days=1) + min_backtest_date = dt_now() - timedelta(days=1) elif backtest_cache_age == "week": - min_backtest_date = datetime.now(tz=timezone.utc) - timedelta(weeks=1) + min_backtest_date = dt_now() - timedelta(weeks=1) elif backtest_cache_age == "month": - min_backtest_date = datetime.now(tz=timezone.utc) - timedelta(weeks=4) + min_backtest_date = dt_now() - timedelta(weeks=4) return min_backtest_date def load_prior_backtest(self): diff --git a/freqtrade/optimize/hyperopt/hyperopt.py b/freqtrade/optimize/hyperopt/hyperopt.py index 253691d4a..ef71860a5 100644 --- a/freqtrade/optimize/hyperopt/hyperopt.py +++ b/freqtrade/optimize/hyperopt/hyperopt.py @@ -6,7 +6,6 @@ This module contains the hyperopt logic import logging import random -import sys from datetime import datetime from math import ceil from multiprocessing import Manager @@ -15,8 +14,6 @@ from typing import Any import rapidjson from joblib import Parallel, cpu_count, delayed, wrap_non_picklable_objects -from joblib.externals import cloudpickle -from rich.console import Console from freqtrade.constants import FTHYPT_FILEVERSION, LAST_BT_RESULT_FN, Config from freqtrade.enums import HyperoptState @@ -93,7 +90,6 @@ class Hyperopt: self.print_all = self.config.get("print_all", False) self.hyperopt_table_header = 0 - self.print_colorized = self.config.get("print_colorized", False) self.print_json = self.config.get("print_json", False) self.hyperopter = HyperOptimizer(self.config) @@ -112,17 +108,6 @@ class Hyperopt: logger.info(f"Removing `{p}`.") p.unlink() - def hyperopt_pickle_magic(self, bases) -> None: - """ - Hyperopt magic to allow strategy inheritance across files. - For this to properly work, we need to register the module of the imported class - to pickle as value. - """ - for modules in bases: - if modules.__name__ != "IStrategy": - cloudpickle.register_pickle_by_value(sys.modules[modules.__module__]) - self.hyperopt_pickle_magic(modules.__bases__) - def _save_result(self, epoch: dict) -> None: """ Save hyperopt results to file @@ -281,15 +266,9 @@ class Hyperopt: with Parallel(n_jobs=config_jobs) as parallel: jobs = parallel._effective_n_jobs() logger.info(f"Effective number of parallel workers used: {jobs}") - console = Console( - color_system="auto" if self.print_colorized else None, - ) # Define progressbar - with get_progress_tracker( - console=console, - cust_callables=[self._hyper_out], - ) as pbar: + with get_progress_tracker(cust_callables=[self._hyper_out]) as pbar: task = pbar.add_task("Epochs", total=self.total_epochs) start = 0 diff --git a/freqtrade/optimize/hyperopt/hyperopt_optimizer.py b/freqtrade/optimize/hyperopt/hyperopt_optimizer.py index c8d18d224..9e4995aca 100644 --- a/freqtrade/optimize/hyperopt/hyperopt_optimizer.py +++ b/freqtrade/optimize/hyperopt/hyperopt_optimizer.py @@ -114,7 +114,7 @@ class HyperOptimizer: def get_strategy_name(self) -> str: return self.backtesting.strategy.get_strategy_name() - def hyperopt_pickle_magic(self, bases) -> None: + def hyperopt_pickle_magic(self, bases: tuple[type, ...]) -> None: """ Hyperopt magic to allow strategy inheritance across files. For this to properly work, we need to register the module of the imported class @@ -122,7 +122,8 @@ class HyperOptimizer: """ for modules in bases: if modules.__name__ != "IStrategy": - cloudpickle.register_pickle_by_value(sys.modules[modules.__module__]) + if mod := sys.modules.get(modules.__module__): + cloudpickle.register_pickle_by_value(mod) self.hyperopt_pickle_magic(modules.__bases__) def _get_params_dict( diff --git a/freqtrade/optimize/hyperopt_loss/hyperopt_loss_multi_metric.py b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_multi_metric.py index adffdfb0b..0918c2c92 100644 --- a/freqtrade/optimize/hyperopt_loss/hyperopt_loss_multi_metric.py +++ b/freqtrade/optimize/hyperopt_loss/hyperopt_loss_multi_metric.py @@ -68,11 +68,8 @@ class MultiMetricHyperOptLoss(IHyperOptLoss): log_profit_factor = np.log(profit_factor + PF_CONST) # Calculate expectancy - expectancy, expectancy_ratio = calculate_expectancy(results) - if expectancy_ratio > 10: - log_expectancy_ratio = np.log(1.01) - else: - log_expectancy_ratio = np.log(expectancy_ratio + EXPECTANCY_CONST) + _, expectancy_ratio = calculate_expectancy(results) + log_expectancy_ratio = np.log(min(10, expectancy_ratio) + EXPECTANCY_CONST) # Calculate winrate winning_trades = results.loc[results["profit_abs"] > 0] diff --git a/freqtrade/optimize/hyperopt_tools.py b/freqtrade/optimize/hyperopt_tools.py index 964afc2ab..ff4747781 100644 --- a/freqtrade/optimize/hyperopt_tools.py +++ b/freqtrade/optimize/hyperopt_tools.py @@ -374,7 +374,6 @@ class HyperoptTools: trials = json_normalize(results, max_level=1) trials["Best"] = "" - trials["Stake currency"] = config["stake_currency"] base_metrics = [ "Best", @@ -383,11 +382,13 @@ class HyperoptTools: "results_metrics.profit_mean", "results_metrics.profit_median", "results_metrics.profit_total", - "Stake currency", + "results_metrics.stake_currency", "results_metrics.profit_total_abs", "results_metrics.holding_avg", "results_metrics.trade_count_long", "results_metrics.trade_count_short", + "results_metrics.max_drawdown_abs", + "results_metrics.max_drawdown_account", "loss", "is_initial_point", "is_best", @@ -409,6 +410,8 @@ class HyperoptTools: "Avg duration", "Trade count long", "Trade count short", + "Max drawdown", + "Max drawdown percent", "Objective", "is_initial_point", "is_best", @@ -432,6 +435,9 @@ class HyperoptTools: trials["Avg profit"] = trials["Avg profit"].apply( lambda x: f"{x * perc_multi:,.2f}%" if not isna(x) else "" ) + trials["Max drawdown percent"] = trials["Max drawdown percent"].apply( + lambda x: f"{x * perc_multi:,.2f}%" if not isna(x) else "" + ) trials["Objective"] = trials["Objective"].apply( lambda x: f"{x:,.5f}" if x != 100000 else "" ) diff --git a/freqtrade/optimize/optimize_reports/bt_output.py b/freqtrade/optimize/optimize_reports/bt_output.py index 4fdc03d7b..914498092 100644 --- a/freqtrade/optimize/optimize_reports/bt_output.py +++ b/freqtrade/optimize/optimize_reports/bt_output.py @@ -163,15 +163,15 @@ def text_table_strategy(strategy_results, stake_currency: str, title: str): # Align drawdown string on the center two space separator. if "max_drawdown_account" in strategy_results[0]: - drawdown = [f'{t["max_drawdown_account"] * 100:.2f}' for t in strategy_results] + drawdown = [f"{t['max_drawdown_account'] * 100:.2f}" for t in strategy_results] else: # Support for prior backtest results - drawdown = [f'{t["max_drawdown_per"]:.2f}' for t in strategy_results] + drawdown = [f"{t['max_drawdown_per']:.2f}" for t in strategy_results] dd_pad_abs = max([len(t["max_drawdown_abs"]) for t in strategy_results]) dd_pad_per = max([len(dd) for dd in drawdown]) drawdown = [ - f'{t["max_drawdown_abs"]:>{dd_pad_abs}} {stake_currency} {dd:>{dd_pad_per}}%' + f"{t['max_drawdown_abs']:>{dd_pad_abs}} {stake_currency} {dd:>{dd_pad_per}}%" for t, dd in zip(strategy_results, drawdown, strict=False) ] @@ -315,7 +315,7 @@ def text_table_add_metrics(strat_results: dict) -> None: ( "Profit factor", ( - f'{strat_results["profit_factor"]:.2f}' + f"{strat_results['profit_factor']:.2f}" if "profit_factor" in strat_results else "N/A" ), diff --git a/freqtrade/optimize/optimize_reports/bt_storage.py b/freqtrade/optimize/optimize_reports/bt_storage.py index cd47aab18..d0c5d7fb7 100644 --- a/freqtrade/optimize/optimize_reports/bt_storage.py +++ b/freqtrade/optimize/optimize_reports/bt_storage.py @@ -1,18 +1,33 @@ import logging +from io import BytesIO, StringIO from pathlib import Path +from typing import Any +from zipfile import ZIP_DEFLATED, ZipFile from pandas import DataFrame from freqtrade.constants import LAST_BT_RESULT_FN from freqtrade.enums.runmode import RunMode from freqtrade.ft_types import BacktestResultType -from freqtrade.misc import file_dump_joblib, file_dump_json +from freqtrade.misc import dump_json_to_file, file_dump_json from freqtrade.optimize.backtest_caching import get_backtest_metadata_filename logger = logging.getLogger(__name__) +def file_dump_joblib(file_obj: BytesIO, data: Any, log: bool = True) -> None: + """ + Dump object data into a file + :param filename: file to create + :param data: Object data to save + :return: + """ + import joblib + + joblib.dump(data, file_obj) + + def _generate_filename(recordfilename: Path, appendix: str, suffix: str) -> Path: """ Generates a filename based on the provided parameters. @@ -39,72 +54,59 @@ def store_backtest_results( analysis_results: dict[str, dict[str, DataFrame]] | None = None, ) -> Path: """ - Stores backtest results and analysis data + Stores backtest results and analysis data in a zip file, with metadata stored separately + for convenience. :param config: Configuration dictionary :param stats: Dataframe containing the backtesting statistics :param dtappendix: Datetime to use for the filename :param market_change_data: Dataframe containing market change data :param analysis_results: Dictionary containing analysis results """ - - # Path object, which can either be a filename or a directory. - # Filenames will be appended with a timestamp right before the suffix - # while for directories, /backtest-result-.json will be used as filename recordfilename: Path = config["exportfilename"] - filename = _generate_filename(recordfilename, dtappendix, ".json") + zip_filename = _generate_filename(recordfilename, dtappendix, ".zip") + base_filename = _generate_filename(recordfilename, dtappendix, "") + json_filename = _generate_filename(recordfilename, dtappendix, ".json") - # Store metadata separately. - file_dump_json(get_backtest_metadata_filename(filename), stats["metadata"]) - # Don't mutate the original stats dict. - stats_copy = { - "strategy": stats["strategy"], - "strategy_comparison": stats["strategy_comparison"], - } + # Store metadata separately with .json extension + file_dump_json(get_backtest_metadata_filename(json_filename), stats["metadata"]) - file_dump_json(filename, stats_copy) + # Store latest backtest info separately + latest_filename = Path.joinpath(zip_filename.parent, LAST_BT_RESULT_FN) + file_dump_json(latest_filename, {"latest_backtest": str(zip_filename.name)}, log=False) - latest_filename = Path.joinpath(filename.parent, LAST_BT_RESULT_FN) - file_dump_json(latest_filename, {"latest_backtest": str(filename.name)}) + # Create zip file and add the files + with ZipFile(zip_filename, "w", ZIP_DEFLATED) as zipf: + # Store stats + stats_copy = { + "strategy": stats["strategy"], + "strategy_comparison": stats["strategy_comparison"], + } + stats_buf = StringIO() + dump_json_to_file(stats_buf, stats_copy) + zipf.writestr(json_filename.name, stats_buf.getvalue()) - if market_change_data is not None: - filename_mc = _generate_filename(recordfilename, f"{dtappendix}_market_change", ".feather") - market_change_data.reset_index().to_feather( - filename_mc, compression_level=9, compression="lz4" - ) + # Add market change data if present + if market_change_data is not None: + market_change_name = f"{base_filename.stem}_market_change.feather" + market_change_buf = BytesIO() + market_change_data.reset_index().to_feather( + market_change_buf, compression_level=9, compression="lz4" + ) + market_change_buf.seek(0) + zipf.writestr(market_change_name, market_change_buf.getvalue()) - if ( - config.get("export", "none") == "signals" - and analysis_results is not None - and config.get("runmode", RunMode.OTHER) == RunMode.BACKTEST - ): - _store_backtest_analysis_data( - recordfilename, analysis_results["signals"], dtappendix, "signals" - ) - _store_backtest_analysis_data( - recordfilename, analysis_results["rejected"], dtappendix, "rejected" - ) - _store_backtest_analysis_data( - recordfilename, analysis_results["exited"], dtappendix, "exited" - ) + # Add analysis results if present and running in backtest mode + if ( + config.get("export", "none") == "signals" + and analysis_results is not None + and config.get("runmode", RunMode.OTHER) == RunMode.BACKTEST + ): + for name in ["signals", "rejected", "exited"]: + if name in analysis_results: + analysis_name = f"{base_filename.stem}_{name}.pkl" + analysis_buf = BytesIO() + file_dump_joblib(analysis_buf, analysis_results[name]) + analysis_buf.seek(0) + zipf.writestr(analysis_name, analysis_buf.getvalue()) - return filename - - -def _store_backtest_analysis_data( - recordfilename: Path, data: dict[str, dict], dtappendix: str, name: str -) -> Path: - """ - Stores backtest trade candles for analysis - :param recordfilename: Path object, which can either be a filename or a directory. - Filenames will be appended with a timestamp right before the suffix - while for directories, /backtest-result-_.pkl will be used - as filename - :param candles: Dict containing the backtesting data for analysis - :param dtappendix: Datetime to use for the filename - :param name: Name to use for the file, e.g. signals, rejected - """ - filename = _generate_filename(recordfilename, f"{dtappendix}_{name}", ".pkl") - - file_dump_joblib(filename, data) - - return filename + return zip_filename diff --git a/freqtrade/optimize/optimize_reports/optimize_reports.py b/freqtrade/optimize/optimize_reports/optimize_reports.py index 23119077b..32ef864e2 100644 --- a/freqtrade/optimize/optimize_reports/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports/optimize_reports.py @@ -108,7 +108,14 @@ def _generate_result_line( } -def generate_pair_metrics( +def calculate_trade_volume(trades_dict: list[dict[str, Any]]) -> float: + # Aggregate the total volume traded from orders.cost. + # Orders is a nested dictionary within the trades list. + + return sum(sum(order["cost"] for order in trade.get("orders", [])) for trade in trades_dict) + + +def generate_pair_metrics( # pairlist: list[str], stake_currency: str, starting_balance: float, @@ -368,7 +375,7 @@ def generate_strategy_stats( :param market_change: float indicating the market change :return: Dictionary containing results per strategy and a strategy summary. """ - results: dict[str, DataFrame] = content["results"] + results: DataFrame = content["results"] if not isinstance(results, DataFrame): return {} config = content["config"] @@ -431,8 +438,9 @@ def generate_strategy_stats( expectancy, expectancy_ratio = calculate_expectancy(results) backtest_days = (max_date - min_date).days or 1 + trades_dict = results.to_dict(orient="records") strat_stats = { - "trades": results.to_dict(orient="records"), + "trades": trades_dict, "locks": [lock.to_json() for lock in content["locks"]], "best_pair": best_pair, "worst_pair": worst_pair, @@ -444,7 +452,7 @@ def generate_strategy_stats( "total_trades": len(results), "trade_count_long": len(results.loc[~results["is_short"]]), "trade_count_short": len(results.loc[results["is_short"]]), - "total_volume": float(results["stake_amount"].sum()), + "total_volume": calculate_trade_volume(trades_dict), "avg_stake_amount": results["stake_amount"].mean() if len(results) > 0 else 0, "profit_mean": results["profit_ratio"].mean() if len(results) > 0 else 0, "profit_median": results["profit_ratio"].median() if len(results) > 0 else 0, diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index bfcf83457..f35f021cf 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -75,8 +75,7 @@ def init_db(db_url: str) -> None: engine = create_engine(db_url, future=True, **kwargs) except NoSuchModuleError: raise OperationalException( - f"Given value for db_url: '{db_url}' " - f"is no valid database URL! (See {_SQL_DOCS_URL})" + f"Given value for db_url: '{db_url}' is no valid database URL! (See {_SQL_DOCS_URL})" ) # https://docs.sqlalchemy.org/en/13/orm/contextual.html#thread-local-scope diff --git a/freqtrade/persistence/pairlock.py b/freqtrade/persistence/pairlock.py index 01549fb63..6c98a607d 100644 --- a/freqtrade/persistence/pairlock.py +++ b/freqtrade/persistence/pairlock.py @@ -39,7 +39,7 @@ class PairLock(ModelBase): @staticmethod def query_pair_locks( - pair: str | None, now: datetime, side: str = "*" + pair: str | None, now: datetime, side: str | None = None ) -> ScalarResult["PairLock"]: """ Get all currently active locks for this pair @@ -53,9 +53,9 @@ class PairLock(ModelBase): ] if pair: filters.append(PairLock.pair == pair) - if side != "*": + if side is not None and side != "*": filters.append(or_(PairLock.side == side, PairLock.side == "*")) - else: + elif side is not None: filters.append(PairLock.side == "*") return PairLock.session.scalars(select(PairLock).filter(*filters)) diff --git a/freqtrade/persistence/pairlock_middleware.py b/freqtrade/persistence/pairlock_middleware.py index c182cf534..0adbcc8be 100644 --- a/freqtrade/persistence/pairlock_middleware.py +++ b/freqtrade/persistence/pairlock_middleware.py @@ -67,13 +67,14 @@ class PairLocks: @staticmethod def get_pair_locks( - pair: str | None, now: datetime | None = None, side: str = "*" + pair: str | None, now: datetime | None = None, side: str | None = None ) -> Sequence[PairLock]: """ Get all currently active locks for this pair :param pair: Pair to check for. Returns all current locks if pair is empty :param now: Datetime object (generated via datetime.now(timezone.utc)). defaults to datetime.now(timezone.utc) + :param side: Side get locks for, can be 'long', 'short', '*' or None """ if not now: now = datetime.now(timezone.utc) @@ -88,7 +89,7 @@ class PairLocks: lock.lock_end_time >= now and lock.active is True and (pair is None or lock.pair == pair) - and (lock.side == "*" or lock.side == side) + and (side is None or lock.side == "*" or lock.side == side) ) ] return locks diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index c944554c5..c08dcb13b 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -6,7 +6,7 @@ import logging from collections import defaultdict from collections.abc import Sequence from dataclasses import dataclass -from datetime import datetime, timedelta, timezone +from datetime import datetime, timezone from math import isclose from typing import Any, ClassVar, Optional, cast @@ -19,6 +19,7 @@ from sqlalchemy import ( Select, String, UniqueConstraint, + case, desc, func, select, @@ -191,6 +192,7 @@ class Order(ModelBase): return ( f"Order(id={self.id}, trade={self.ft_trade_id}, order_id={self.order_id}, " f"side={self.side}, filled={self.safe_filled}, price={self.safe_price}, " + f"amount={self.amount}, " f"status={self.status}, date={self.order_date_utc:{DATETIME_PRINT_FORMAT}})" ) @@ -215,7 +217,7 @@ class Order(ModelBase): self.stop_price = safe_value_fallback(order, "stopPrice", default_value=self.stop_price) order_date = safe_value_fallback(order, "timestamp") if order_date: - self.order_date = datetime.fromtimestamp(order_date / 1000, tz=timezone.utc) + self.order_date = dt_from_ts(order_date) elif not self.order_date: self.order_date = dt_now() @@ -268,6 +270,7 @@ class Order(ModelBase): "order_filled_timestamp": dt_ts_none(self.order_filled_utc), "ft_is_entry": self.ft_order_side == entry_side, "ft_order_tag": self.ft_order_tag, + "cost": self.cost if self.cost else 0, } if not minified: resp.update( @@ -276,7 +279,6 @@ class Order(ModelBase): "order_id": self.order_id, "status": self.status, "average": round(self.average, 8) if self.average else 0, - "cost": self.cost if self.cost else 0, "filled": self.filled, "is_open": self.ft_is_open, "order_date": ( @@ -391,7 +393,6 @@ class LocalTrade: # Copy of trades_open - but indexed by pair bt_trades_open_pp: dict[str, list["LocalTrade"]] = defaultdict(list) bt_open_open_trade_count: int = 0 - bt_open_open_trade_count_candle: int = 0 bt_total_profit: float = 0 realized_profit: float = 0 @@ -599,6 +600,13 @@ class LocalTrade: ] return len(open_orders_wo_sl) > 0 + @property + def has_open_position(self) -> bool: + """ + True if there is an open position for this trade + """ + return self.amount > 0 + @property def open_sl_orders(self) -> list[Order]: """ @@ -760,7 +768,6 @@ class LocalTrade: LocalTrade.bt_trades_open = [] LocalTrade.bt_trades_open_pp = defaultdict(list) LocalTrade.bt_open_open_trade_count = 0 - LocalTrade.bt_open_open_trade_count_candle = 0 LocalTrade.bt_total_profit = 0 def adjust_min_max_rates(self, current_price: float, current_price_low: float) -> None: @@ -777,7 +784,9 @@ class LocalTrade: """ if liquidation_price is None: return - self.liquidation_price = liquidation_price + self.liquidation_price = price_to_precision( + liquidation_price, self.price_precision, self.precision_mode_price + ) def set_funding_fees(self, funding_fee: float) -> None: """ @@ -1247,7 +1256,11 @@ class LocalTrade: if current_amount_tr > 0.0: # Trade is still open # Leverage not updated, as we don't allow changing leverage through DCA at the moment. - self.open_rate = float(current_stake / current_amount) + self.open_rate = price_to_precision( + float(current_stake / current_amount), + self.price_precision, + self.precision_mode_price, + ) self.amount = current_amount_tr self.stake_amount = float(current_stake) / (self.leverage or 1.0) self.fee_open_cost = self.fee_open * float(self.max_stake_amount) @@ -1456,11 +1469,6 @@ class LocalTrade: LocalTrade.bt_trades_open.remove(trade) LocalTrade.bt_trades_open_pp[trade.pair].remove(trade) LocalTrade.bt_open_open_trade_count -= 1 - if (trade.close_date_utc - trade.open_date_utc) > timedelta(minutes=trade.timeframe): - # Only subtract trades that are open for more than 1 candle - # To avoid exceeding max_open_trades. - # Must be reset at the start of every candle during backesting. - LocalTrade.bt_open_open_trade_count_candle -= 1 LocalTrade.bt_trades.append(trade) LocalTrade.bt_total_profit += trade.close_profit_abs @@ -1470,7 +1478,6 @@ class LocalTrade: LocalTrade.bt_trades_open.append(trade) LocalTrade.bt_trades_open_pp[trade.pair].append(trade) LocalTrade.bt_open_open_trade_count += 1 - LocalTrade.bt_open_open_trade_count_candle += 1 else: LocalTrade.bt_trades.append(trade) @@ -1479,9 +1486,6 @@ class LocalTrade: LocalTrade.bt_trades_open.remove(trade) LocalTrade.bt_trades_open_pp[trade.pair].remove(trade) LocalTrade.bt_open_open_trade_count -= 1 - # TODO: The below may have odd behavior in case of canceled entries - # It might need to be removed so the trade "counts" as open for this candle. - LocalTrade.bt_open_open_trade_count_candle -= 1 @staticmethod def get_open_trades() -> list[Any]: @@ -1910,28 +1914,72 @@ class Trade(ModelBase, LocalTrade): return total_open_stake_amount or 0 @staticmethod - def get_overall_performance(minutes=None) -> list[dict[str, Any]]: + def _generic_performance_query(columns: list, filters: list, fallback: str = "") -> Select: + """ + Retrieve a generic select object to calculate performance grouped on `columns`. + Returns the following columns: + - columns + - profit_ratio + - profit_sum_abs + - count + NOTE: Not supported in Backtesting. + """ + columns_coal = [func.coalesce(c, fallback).label(c.name) for c in columns] + pair_costs = ( + select( + *columns_coal, + func.sum( + ( + func.coalesce(Order.filled, Order.amount) + * func.coalesce(Order.average, Order.price, Order.ft_price) + ) + / func.coalesce(Trade.leverage, 1) + ).label("cost_per_pair"), + ) + .join(Order, Trade.id == Order.ft_trade_id) + .filter( + *filters, + Order.ft_order_side == case((Trade.is_short.is_(True), "sell"), else_="buy"), + Order.filled > 0, + ) + .group_by(*columns) + .cte("pair_costs") + ) + trades_grouped = ( + select( + *columns_coal, + func.sum(Trade.close_profit_abs).label("profit_sum_abs"), + func.count(*columns_coal).label("count"), + ) + .filter(*filters) + .group_by(*columns_coal) + .cte("trades_grouped") + ) + q = ( + select( + *[trades_grouped.c[x.name] for x in columns], + (trades_grouped.c.profit_sum_abs / pair_costs.c.cost_per_pair).label( + "profit_ratio" + ), + trades_grouped.c.profit_sum_abs, + trades_grouped.c.count, + ) + .join(pair_costs, *[trades_grouped.c[x.name] == pair_costs.c[x.name] for x in columns]) + .order_by(desc("profit_sum_abs")) + ) + return q + + @staticmethod + def get_overall_performance(start_date: datetime | None = None) -> list[dict[str, Any]]: """ Returns List of dicts containing all Trades, including profit and trade count NOTE: Not supported in Backtesting. """ filters: list = [Trade.is_open.is_(False)] - if minutes: - start_date = datetime.now(timezone.utc) - timedelta(minutes=minutes) + if start_date: filters.append(Trade.close_date >= start_date) - - pair_rates = Trade.session.execute( - select( - Trade.pair, - func.sum(Trade.close_profit).label("profit_sum"), - func.sum(Trade.close_profit_abs).label("profit_sum_abs"), - func.count(Trade.pair).label("count"), - ) - .filter(*filters) - .group_by(Trade.pair) - .order_by(desc("profit_sum_abs")) - ).all() - + pair_rates_query = Trade._generic_performance_query([Trade.pair], filters) + pair_rates = Trade.session.execute(pair_rates_query).all() return [ { "pair": pair, @@ -1956,17 +2004,8 @@ class Trade(ModelBase, LocalTrade): if pair is not None: filters.append(Trade.pair == pair) - enter_tag_perf = Trade.session.execute( - select( - Trade.enter_tag, - func.sum(Trade.close_profit).label("profit_sum"), - func.sum(Trade.close_profit_abs).label("profit_sum_abs"), - func.count(Trade.pair).label("count"), - ) - .filter(*filters) - .group_by(Trade.enter_tag) - .order_by(desc("profit_sum_abs")) - ).all() + pair_rates_query = Trade._generic_performance_query([Trade.enter_tag], filters, "Other") + enter_tag_perf = Trade.session.execute(pair_rates_query).all() return [ { @@ -1990,17 +2029,9 @@ class Trade(ModelBase, LocalTrade): filters: list = [Trade.is_open.is_(False)] if pair is not None: filters.append(Trade.pair == pair) - sell_tag_perf = Trade.session.execute( - select( - Trade.exit_reason, - func.sum(Trade.close_profit).label("profit_sum"), - func.sum(Trade.close_profit_abs).label("profit_sum_abs"), - func.count(Trade.pair).label("count"), - ) - .filter(*filters) - .group_by(Trade.exit_reason) - .order_by(desc("profit_sum_abs")) - ).all() + + pair_rates_query = Trade._generic_performance_query([Trade.exit_reason], filters, "Other") + sell_tag_perf = Trade.session.execute(pair_rates_query).all() return [ { @@ -2081,13 +2112,9 @@ class Trade(ModelBase, LocalTrade): if start_date: filters.append(Trade.close_date >= start_date) - best_pair = Trade.session.execute( - select(Trade.pair, func.sum(Trade.close_profit).label("profit_sum")) - .filter(*filters) - .group_by(Trade.pair) - .order_by(desc("profit_sum")) - ).first() - + pair_rates_query = Trade._generic_performance_query([Trade.pair], filters) + best_pair = Trade.session.execute(pair_rates_query).first() + # returns pair, profit_ratio, abs_profit, count return best_pair @staticmethod diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 8ec7c7bae..4f486d8a1 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -145,7 +145,7 @@ def add_indicators(fig, row, indicators: dict[str, dict], data: pd.DataFrame) -> fig.add_trace(trace, row, 1) else: logger.info( - 'Indicator "%s" ignored. Reason: This indicator is not found ' "in your strategy.", + 'Indicator "%s" ignored. Reason: This indicator is not found in your strategy.', indicator, ) @@ -394,13 +394,12 @@ def add_areas(fig, row: int, data: pd.DataFrame, indicators) -> make_subplots: ) elif indicator not in data: logger.info( - 'Indicator "%s" ignored. Reason: This indicator is not ' - "found in your strategy.", + 'Indicator "%s" ignored. Reason: This indicator is not found in your strategy.', indicator, ) elif indicator_b not in data: logger.info( - 'fill_to: "%s" ignored. Reason: This indicator is not ' "in your strategy.", + 'fill_to: "%s" ignored. Reason: This indicator is not in your strategy.', indicator_b, ) return fig diff --git a/freqtrade/plugins/pairlist/OffsetFilter.py b/freqtrade/plugins/pairlist/OffsetFilter.py index ca508ce87..f6e5df063 100644 --- a/freqtrade/plugins/pairlist/OffsetFilter.py +++ b/freqtrade/plugins/pairlist/OffsetFilter.py @@ -79,6 +79,4 @@ class OffsetFilter(IPairList): if self._number_pairs: pairs = pairs[: self._number_pairs] - self.log_once(f"Searching {len(pairs)} pairs: {pairs}", logger.info) - return pairs diff --git a/freqtrade/plugins/pairlist/PerformanceFilter.py b/freqtrade/plugins/pairlist/PerformanceFilter.py index 06ab11c54..6e7dfe3c3 100644 --- a/freqtrade/plugins/pairlist/PerformanceFilter.py +++ b/freqtrade/plugins/pairlist/PerformanceFilter.py @@ -3,12 +3,14 @@ Performance pair list filter """ import logging +from datetime import timedelta import pandas as pd from freqtrade.exchange.exchange_types import Tickers from freqtrade.persistence import Trade from freqtrade.plugins.pairlist.IPairList import IPairList, PairlistParameter, SupportsBacktesting +from freqtrade.util.datetime_helpers import dt_now logger = logging.getLogger(__name__) @@ -69,7 +71,8 @@ class PerformanceFilter(IPairList): """ # Get the trading performance for pairs from database try: - performance = pd.DataFrame(Trade.get_overall_performance(self._minutes)) + start_date = dt_now() - timedelta(minutes=self._minutes) + performance = pd.DataFrame(Trade.get_overall_performance(start_date)) except AttributeError: # Performancefilter does not work in backtesting. self.log_once("PerformanceFilter is not available in this mode.", logger.warning) diff --git a/freqtrade/plugins/pairlist/PriceFilter.py b/freqtrade/plugins/pairlist/PriceFilter.py index ea5ef27a2..ffdd83407 100644 --- a/freqtrade/plugins/pairlist/PriceFilter.py +++ b/freqtrade/plugins/pairlist/PriceFilter.py @@ -158,8 +158,7 @@ class PriceFilter(IPairList): if self._min_price != 0: if price < self._min_price: self.log_once( - f"Removed {pair} from whitelist, " - f"because last price < {self._min_price:.8f}", + f"Removed {pair} from whitelist, because last price < {self._min_price:.8f}", logger.info, ) return False @@ -168,8 +167,7 @@ class PriceFilter(IPairList): if self._max_price != 0: if price > self._max_price: self.log_once( - f"Removed {pair} from whitelist, " - f"because last price > {self._max_price:.8f}", + f"Removed {pair} from whitelist, because last price > {self._max_price:.8f}", logger.info, ) return False diff --git a/freqtrade/plugins/pairlist/RemotePairList.py b/freqtrade/plugins/pairlist/RemotePairList.py index a0817998f..db32d5a77 100644 --- a/freqtrade/plugins/pairlist/RemotePairList.py +++ b/freqtrade/plugins/pairlist/RemotePairList.py @@ -58,13 +58,12 @@ class RemotePairList(IPairList): if self._mode not in ["whitelist", "blacklist"]: raise OperationalException( - "`mode` not configured correctly. Supported Modes " 'are "whitelist","blacklist"' + '`mode` not configured correctly. Supported Modes are "whitelist","blacklist"' ) if self._processing_mode not in ["filter", "append"]: raise OperationalException( - "`processing_mode` not configured correctly. Supported Modes " - 'are "filter","append"' + '`processing_mode` not configured correctly. Supported Modes are "filter","append"' ) if self._pairlist_pos == 0 and self._mode == "blacklist": diff --git a/freqtrade/plugins/pairlist/SpreadFilter.py b/freqtrade/plugins/pairlist/SpreadFilter.py index 51e8e802b..442de2bf2 100644 --- a/freqtrade/plugins/pairlist/SpreadFilter.py +++ b/freqtrade/plugins/pairlist/SpreadFilter.py @@ -41,8 +41,7 @@ class SpreadFilter(IPairList): Short whitelist method description - used for startup-messages """ return ( - f"{self.name} - Filtering pairs with ask/bid diff above " - f"{self._max_spread_ratio:.2%}." + f"{self.name} - Filtering pairs with ask/bid diff above {self._max_spread_ratio:.2%}." ) @staticmethod diff --git a/freqtrade/resolvers/exchange_resolver.py b/freqtrade/resolvers/exchange_resolver.py index 835c3c0af..e6edfb942 100644 --- a/freqtrade/resolvers/exchange_resolver.py +++ b/freqtrade/resolvers/exchange_resolver.py @@ -9,7 +9,7 @@ from typing import Any import freqtrade.exchange as exchanges from freqtrade.constants import Config, ExchangeConfig from freqtrade.exchange import MAP_EXCHANGE_CHILDCLASS, Exchange -from freqtrade.resolvers import IResolver +from freqtrade.resolvers.iresolver import IResolver logger = logging.getLogger(__name__) diff --git a/freqtrade/resolvers/strategy_resolver.py b/freqtrade/resolvers/strategy_resolver.py index 67751b5ae..a3ec03e2b 100644 --- a/freqtrade/resolvers/strategy_resolver.py +++ b/freqtrade/resolvers/strategy_resolver.py @@ -16,7 +16,7 @@ from freqtrade.configuration.config_validation import validate_migrated_strategy from freqtrade.constants import REQUIRED_ORDERTIF, REQUIRED_ORDERTYPES, USERPATH_STRATEGIES, Config from freqtrade.enums import TradingMode from freqtrade.exceptions import OperationalException -from freqtrade.resolvers import IResolver +from freqtrade.resolvers.iresolver import IResolver from freqtrade.strategy.interface import IStrategy diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 65631add6..278922b7d 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -99,16 +99,18 @@ def __run_backtest_bg(btconfig: Config): ApiBG.bt["data"], ApiBG.bt["bt"].all_results, min_date=min_date, max_date=max_date ) - if btconfig.get("export", "none") == "trades": - combined_res = combined_dataframes_with_rel_mean(ApiBG.bt["data"], min_date, max_date) - fn = store_backtest_results( - btconfig, - ApiBG.bt["bt"].results, - datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), - market_change_data=combined_res, - ) - ApiBG.bt["bt"].results["metadata"][strategy_name]["filename"] = str(fn.stem) - ApiBG.bt["bt"].results["metadata"][strategy_name]["strategy"] = strategy_name + if btconfig.get("export", "none") == "trades": + combined_res = combined_dataframes_with_rel_mean( + ApiBG.bt["data"], min_date, max_date + ) + fn = store_backtest_results( + btconfig, + ApiBG.bt["bt"].results, + datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), + market_change_data=combined_res, + ) + ApiBG.bt["bt"].results["metadata"][strategy_name]["filename"] = str(fn.stem) + ApiBG.bt["bt"].results["metadata"][strategy_name]["strategy"] = strategy_name logger.info("Backtest finished.") @@ -273,15 +275,18 @@ def api_backtest_history(config=Depends(get_config)): def api_backtest_history_result(filename: str, strategy: str, config=Depends(get_config)): # Get backtest result history, read from metadata files bt_results_base: Path = config["user_data_dir"] / "backtest_results" - fn = (bt_results_base / filename).with_suffix(".json") + for ext in [".zip", ".json"]: + fn = (bt_results_base / filename).with_suffix(ext) + if is_file_in_dir(fn, bt_results_base): + break + else: + raise HTTPException(status_code=404, detail="File not found.") results: dict[str, Any] = { "metadata": {}, "strategy": {}, "strategy_comparison": [], } - if not is_file_in_dir(fn, bt_results_base): - raise HTTPException(status_code=404, detail="File not found.") load_and_merge_backtest_result(strategy, fn, results) return { "status": "ended", @@ -301,9 +306,12 @@ def api_backtest_history_result(filename: str, strategy: str, config=Depends(get def api_delete_backtest_history_entry(file: str, config=Depends(get_config)): # Get backtest result history, read from metadata files bt_results_base: Path = config["user_data_dir"] / "backtest_results" - file_abs = (bt_results_base / file).with_suffix(".json") - # Ensure file is in backtest_results directory - if not is_file_in_dir(file_abs, bt_results_base): + for ext in [".zip", ".json"]: + file_abs = (bt_results_base / file).with_suffix(ext) + # Ensure file is in backtest_results directory + if is_file_in_dir(file_abs, bt_results_base): + break + else: raise HTTPException(status_code=404, detail="File not found.") delete_backtest_result(file_abs) @@ -320,10 +328,14 @@ def api_update_backtest_history_entry( ): # Get backtest result history, read from metadata files bt_results_base: Path = config["user_data_dir"] / "backtest_results" - file_abs = (bt_results_base / file).with_suffix(".json") - # Ensure file is in backtest_results directory - if not is_file_in_dir(file_abs, bt_results_base): + for ext in [".zip", ".json"]: + file_abs = (bt_results_base / file).with_suffix(ext) + # Ensure file is in backtest_results directory + if is_file_in_dir(file_abs, bt_results_base): + break + else: raise HTTPException(status_code=404, detail="File not found.") + content = {"notes": body.notes} try: update_backtest_metadata(file_abs, body.strategy, content) @@ -340,10 +352,17 @@ def api_update_backtest_history_entry( ) def api_get_backtest_market_change(file: str, config=Depends(get_config)): bt_results_base: Path = config["user_data_dir"] / "backtest_results" - file_abs = (bt_results_base / f"{file}_market_change").with_suffix(".feather") - # Ensure file is in backtest_results directory - if not is_file_in_dir(file_abs, bt_results_base): + for fn in ( + Path(file).with_suffix(".zip"), + Path(f"{file}_market_change").with_suffix(".feather"), + ): + file_abs = bt_results_base / fn + # Ensure file is in backtest_results directory + if is_file_in_dir(file_abs, bt_results_base): + break + else: raise HTTPException(status_code=404, detail="File not found.") + df = get_backtest_market_change(file_abs) return { diff --git a/freqtrade/rpc/api_server/api_download_data.py b/freqtrade/rpc/api_server/api_download_data.py index 161655725..b8c14ea13 100644 --- a/freqtrade/rpc/api_server/api_download_data.py +++ b/freqtrade/rpc/api_server/api_download_data.py @@ -57,7 +57,7 @@ def pairlists_evaluate( config_loc = deepcopy(config) config_loc["stake_currency"] = "" config_loc["pairs"] = payload.pairs - config_loc["timeframe"] = payload.timeframes + config_loc["timerange"] = payload.timerange config_loc["days"] = payload.days config_loc["timeframes"] = payload.timeframes config_loc["erase"] = payload.erase diff --git a/freqtrade/rpc/api_server/api_pair_history.py b/freqtrade/rpc/api_server/api_pair_history.py new file mode 100644 index 000000000..31fd86f46 --- /dev/null +++ b/freqtrade/rpc/api_server/api_pair_history.py @@ -0,0 +1,77 @@ +import logging +from copy import deepcopy + +from fastapi import APIRouter, Depends, HTTPException + +from freqtrade.configuration import validate_config_consistency +from freqtrade.rpc.api_server.api_pairlists import handleExchangePayload +from freqtrade.rpc.api_server.api_schemas import PairHistory, PairHistoryRequest +from freqtrade.rpc.api_server.deps import get_config, get_exchange +from freqtrade.rpc.rpc import RPC + + +logger = logging.getLogger(__name__) + +router = APIRouter() + + +@router.get("/pair_history", response_model=PairHistory, tags=["candle data"]) +def pair_history( + pair: str, + timeframe: str, + timerange: str, + strategy: str, + freqaimodel: str | None = None, + config=Depends(get_config), + exchange=Depends(get_exchange), +): + # The initial call to this endpoint can be slow, as it may need to initialize + # the exchange class. + config_loc = deepcopy(config) + config_loc.update( + { + "timeframe": timeframe, + "strategy": strategy, + "timerange": timerange, + "freqaimodel": freqaimodel if freqaimodel else config_loc.get("freqaimodel"), + } + ) + validate_config_consistency(config_loc) + try: + return RPC._rpc_analysed_history_full(config_loc, pair, timeframe, exchange, None, False) + except Exception as e: + raise HTTPException(status_code=502, detail=str(e)) + + +@router.post("/pair_history", response_model=PairHistory, tags=["candle data"]) +def pair_history_filtered(payload: PairHistoryRequest, config=Depends(get_config)): + # The initial call to this endpoint can be slow, as it may need to initialize + # the exchange class. + config_loc = deepcopy(config) + config_loc.update( + { + "timeframe": payload.timeframe, + "strategy": payload.strategy, + "timerange": payload.timerange, + "freqaimodel": ( + payload.freqaimodel if payload.freqaimodel else config_loc.get("freqaimodel") + ), + } + ) + handleExchangePayload(payload, config_loc) + exchange = get_exchange(config_loc) + + validate_config_consistency(config_loc) + + try: + return RPC._rpc_analysed_history_full( + config_loc, + payload.pair, + payload.timeframe, + exchange, + payload.columns, + payload.live_mode, + ) + except Exception as e: + logger.exception("Error in pair_history_filtered") + raise HTTPException(status_code=502, detail=str(e)) diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index db7a312c5..984507d99 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -150,6 +150,7 @@ class Profit(BaseModel): best_pair: str best_rate: float best_pair_profit_ratio: float + best_pair_profit_abs: float winning_trades: int losing_trades: int profit_factor: float @@ -523,10 +524,11 @@ class PairCandlesRequest(BaseModel): columns: list[str] | None = None -class PairHistoryRequest(PairCandlesRequest): +class PairHistoryRequest(PairCandlesRequest, ExchangeModePayloadMixin): timerange: str - strategy: str + strategy: str | None = None freqaimodel: str | None = None + live_mode: bool = False class PairHistory(BaseModel): @@ -605,6 +607,24 @@ class BacktestMarketChange(BaseModel): data: list[list[Any]] +class MarketRequest(ExchangeModePayloadMixin, BaseModel): + base: str | None = None + quote: str | None = None + + +class MarketModel(BaseModel): + symbol: str + base: str + quote: str + spot: bool + swap: bool + + +class MarketResponse(BaseModel): + markets: dict[str, MarketModel] + exchange_id: str + + class SysInfo(BaseModel): cpu_pct: list[float] ram_pct: float diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 48d14ca4b..eef6b8eef 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -1,14 +1,16 @@ import logging from copy import deepcopy +from typing import Annotated from fastapi import APIRouter, Depends, Query from fastapi.exceptions import HTTPException from freqtrade import __version__ from freqtrade.data.history import get_datahandler -from freqtrade.enums import CandleType, TradingMode +from freqtrade.enums import CandleType, RunMode, State, TradingMode from freqtrade.exceptions import OperationalException from freqtrade.rpc import RPC +from freqtrade.rpc.api_server.api_pairlists import handleExchangePayload from freqtrade.rpc.api_server.api_schemas import ( AvailablePairs, Balances, @@ -31,11 +33,12 @@ from freqtrade.rpc.api_server.api_schemas import ( Locks, LocksPayload, Logs, + MarketRequest, + MarketResponse, MixTag, OpenTradeSchema, PairCandlesRequest, PairHistory, - PairHistoryRequest, PerformanceEntry, Ping, PlotConfig, @@ -85,7 +88,8 @@ logger = logging.getLogger(__name__) # 2.35: pair_candles and pair_history endpoints as Post variant # 2.40: Add hyperopt-loss endpoint # 2.41: Add download-data endpoint -API_VERSION = 2.41 +# 2.42: Add /pair_history endpoint with live data +API_VERSION = 2.42 # Public API, requires no auth. router_public = APIRouter() @@ -218,7 +222,7 @@ def edge(rpc: RPC = Depends(get_rpc)): @router.get("/show_config", response_model=ShowConfig, tags=["info"]) def show_config(rpc: RPC | None = Depends(get_rpc_optional), config=Depends(get_config)): - state = "" + state: State | str = "" strategy_version = None if rpc: state = rpc._freqtrade.state @@ -343,58 +347,6 @@ def pair_candles_filtered(payload: PairCandlesRequest, rpc: RPC = Depends(get_rp ) -@router.get("/pair_history", response_model=PairHistory, tags=["candle data"]) -def pair_history( - pair: str, - timeframe: str, - timerange: str, - strategy: str, - freqaimodel: str | None = None, - config=Depends(get_config), - exchange=Depends(get_exchange), -): - # The initial call to this endpoint can be slow, as it may need to initialize - # the exchange class. - config = deepcopy(config) - config.update( - { - "timeframe": timeframe, - "strategy": strategy, - "timerange": timerange, - "freqaimodel": freqaimodel if freqaimodel else config.get("freqaimodel"), - } - ) - try: - return RPC._rpc_analysed_history_full(config, pair, timeframe, exchange, None) - except Exception as e: - raise HTTPException(status_code=502, detail=str(e)) - - -@router.post("/pair_history", response_model=PairHistory, tags=["candle data"]) -def pair_history_filtered( - payload: PairHistoryRequest, config=Depends(get_config), exchange=Depends(get_exchange) -): - # The initial call to this endpoint can be slow, as it may need to initialize - # the exchange class. - config = deepcopy(config) - config.update( - { - "timeframe": payload.timeframe, - "strategy": payload.strategy, - "timerange": payload.timerange, - "freqaimodel": ( - payload.freqaimodel if payload.freqaimodel else config.get("freqaimodel") - ), - } - ) - try: - return RPC._rpc_analysed_history_full( - config, payload.pair, payload.timeframe, exchange, payload.columns - ) - except Exception as e: - raise HTTPException(status_code=502, detail=str(e)) - - @router.get("/plot_config", response_model=PlotConfig, tags=["candle data"]) def plot_config( strategy: str | None = None, @@ -526,6 +478,29 @@ def list_available_pairs( return result +@router.get("/markets", response_model=MarketResponse, tags=["candle data", "webserver"]) +def markets( + query: Annotated[MarketRequest, Query()], + config=Depends(get_config), + rpc: RPC | None = Depends(get_rpc_optional), +): + if not rpc or config["runmode"] == RunMode.WEBSERVER: + # webserver mode + config_loc = deepcopy(config) + handleExchangePayload(query, config_loc) + exchange = get_exchange(config_loc) + else: + exchange = rpc._freqtrade.exchange + + return { + "markets": exchange.get_markets( + base_currencies=[query.base] if query.base else None, + quote_currencies=[query.quote] if query.quote else None, + ), + "exchange_id": exchange.id, + } + + @router.get("/sysinfo", response_model=SysInfo, tags=["info"]) def sysinfo(): return RPC._rpc_sysinfo() diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index e06e77e1e..aa81684fc 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -120,6 +120,7 @@ class ApiServer(RPCHandler): from freqtrade.rpc.api_server.api_background_tasks import router as api_bg_tasks from freqtrade.rpc.api_server.api_backtest import router as api_backtest from freqtrade.rpc.api_server.api_download_data import router as api_download_data + from freqtrade.rpc.api_server.api_pair_history import router as api_pair_history from freqtrade.rpc.api_server.api_pairlists import router as api_pairlists from freqtrade.rpc.api_server.api_v1 import router as api_v1 from freqtrade.rpc.api_server.api_v1 import router_public as api_v1_public @@ -145,6 +146,11 @@ class ApiServer(RPCHandler): prefix="/api/v1", dependencies=[Depends(http_basic_or_jwt_token), Depends(is_webserver_mode)], ) + app.include_router( + api_pair_history, + prefix="/api/v1", + dependencies=[Depends(http_basic_or_jwt_token), Depends(is_webserver_mode)], + ) app.include_router( api_pairlists, prefix="/api/v1", diff --git a/freqtrade/rpc/api_server/ws/__init__.py b/freqtrade/rpc/api_server/ws/__init__.py index b76428119..035ad6795 100644 --- a/freqtrade/rpc/api_server/ws/__init__.py +++ b/freqtrade/rpc/api_server/ws/__init__.py @@ -1,5 +1,5 @@ # isort: off -from freqtrade.rpc.api_server.ws.types import WebSocketType # noqa: F401 +from freqtrade.rpc.api_server.ws.ws_types import WebSocketType # noqa: F401 from freqtrade.rpc.api_server.ws.proxy import WebSocketProxy # noqa: F401 from freqtrade.rpc.api_server.ws.serializer import HybridJSONWebSocketSerializer # noqa: F401 from freqtrade.rpc.api_server.ws.channel import WebSocketChannel # noqa: F401 diff --git a/freqtrade/rpc/api_server/ws/channel.py b/freqtrade/rpc/api_server/ws/channel.py index d05a1c1f7..bf5d64b6c 100644 --- a/freqtrade/rpc/api_server/ws/channel.py +++ b/freqtrade/rpc/api_server/ws/channel.py @@ -15,7 +15,7 @@ from freqtrade.rpc.api_server.ws.serializer import ( HybridJSONWebSocketSerializer, WebSocketSerializer, ) -from freqtrade.rpc.api_server.ws.types import WebSocketType +from freqtrade.rpc.api_server.ws.ws_types import WebSocketType from freqtrade.rpc.api_server.ws_schemas import WSMessageSchemaType diff --git a/freqtrade/rpc/api_server/ws/proxy.py b/freqtrade/rpc/api_server/ws/proxy.py index c32494176..ed5f085b9 100644 --- a/freqtrade/rpc/api_server/ws/proxy.py +++ b/freqtrade/rpc/api_server/ws/proxy.py @@ -3,7 +3,7 @@ from typing import Any from fastapi import WebSocket as FastAPIWebSocket from websockets.asyncio.client import ClientConnection as WebSocket -from freqtrade.rpc.api_server.ws.types import WebSocketType +from freqtrade.rpc.api_server.ws.ws_types import WebSocketType class WebSocketProxy: diff --git a/freqtrade/rpc/api_server/ws/types.py b/freqtrade/rpc/api_server/ws/ws_types.py similarity index 100% rename from freqtrade/rpc/api_server/ws/types.py rename to freqtrade/rpc/api_server/ws/ws_types.py diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 931401eaa..530b35da1 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -7,7 +7,7 @@ from abc import abstractmethod from collections.abc import Generator, Sequence from datetime import date, datetime, timedelta, timezone from math import isnan -from typing import Any, Optional +from typing import Any, TYPE_CHECKING, Optional import psutil from dateutil.relativedelta import relativedelta @@ -31,7 +31,7 @@ from freqtrade.enums import ( TradingMode, ) from freqtrade.exceptions import ExchangeError, PricingError -from freqtrade.exchange import timeframe_to_minutes, timeframe_to_msecs +from freqtrade.exchange import Exchange, timeframe_to_minutes, timeframe_to_msecs from freqtrade.exchange.exchange_utils import price_to_precision from freqtrade.loggers import bufferHandler from freqtrade.persistence import KeyStoreKeys, KeyValueStore, PairLocks, Trade @@ -39,8 +39,16 @@ from freqtrade.persistence.models import PairLock from freqtrade.plugins.pairlist.pairlist_helpers import expand_pairlist from freqtrade.rpc.fiat_convert import CryptoToFiatConverter from freqtrade.rpc.rpc_types import RPCSendMsg -from freqtrade.util import decimals_per_coin, dt_now, dt_ts_def, format_date, shorten_date -from freqtrade.util.datetime_helpers import dt_humanize_delta +from freqtrade.util import ( + decimals_per_coin, + dt_from_ts, + dt_humanize_delta, + dt_now, + dt_ts, + dt_ts_def, + format_date, + shorten_date, +) from freqtrade.wallets import PositionWallet, Wallet @@ -98,6 +106,10 @@ class RPC: # Bind _fiat_converter if needed _fiat_converter: CryptoToFiatConverter | None = None + if TYPE_CHECKING: + from freqtrade.freqtradebot import FreqtradeBot + + _freqtrade: FreqtradeBot def __init__(self, freqtrade) -> None: """ @@ -201,7 +213,7 @@ class RPC: # calculate profit and send message to user if trade.is_open: try: - current_rate = self._freqtrade.exchange.get_rate( + current_rate: float = self._freqtrade.exchange.get_rate( trade.pair, side="exit", is_short=trade.is_short, refresh=False ) except (ExchangeError, PricingError): @@ -219,7 +231,7 @@ class RPC: else: # Closed trade ... - current_rate = trade.close_rate + current_rate = trade.close_rate or 0.0 current_profit = trade.close_profit or 0.0 current_profit_abs = trade.close_profit_abs or 0.0 @@ -268,6 +280,7 @@ class RPC: stoploss_entry_dist=stoploss_entry_dist, stoploss_entry_dist_ratio=round(stoploss_entry_dist_ratio, 8), open_orders=oo_details, + nr_of_successful_entries=trade.nr_of_successful_entries, ) ) results.append(trade_dict) @@ -275,83 +288,82 @@ class RPC: def _rpc_status_table( self, stake_currency: str, fiat_display_currency: str - ) -> tuple[list, list, float]: - trades: list[Trade] = Trade.get_open_trades() + ) -> tuple[list, list, float, float]: + """ + :return: list of trades, list of columns, sum of fiat profit + """ nonspot = self._config.get("trading_mode", TradingMode.SPOT) != TradingMode.SPOT - if not trades: + if not Trade.get_open_trades(): raise RPCException("no active trade") - else: - trades_list = [] - fiat_profit_sum = nan - for trade in trades: - # calculate profit and send message to user - try: - current_rate = self._freqtrade.exchange.get_rate( - trade.pair, side="exit", is_short=trade.is_short, refresh=False - ) - except (PricingError, ExchangeError): - current_rate = nan - trade_profit = nan - profit_str = f"{nan:.2%}" - else: - if trade.nr_of_successful_entries > 0: - profit = trade.calculate_profit(current_rate) - trade_profit = profit.profit_abs - profit_str = f"{profit.profit_ratio:.2%}" - else: - trade_profit = 0.0 - profit_str = f"{0.0:.2f}" - leverage = f"{trade.leverage:.3g}" - direction_str = ( - (f"S {leverage}x" if trade.is_short else f"L {leverage}x") if nonspot else "" + + trades_list = [] + fiat_profit_sum = nan + fiat_total_profit_sum = nan + for trade in self._rpc_trade_status(): + # Format profit as a string with the right sign + profit = f"{trade['profit_ratio']:.2%}" + fiat_profit = trade.get("profit_fiat", None) + if fiat_profit is None or isnan(fiat_profit): + fiat_profit = trade.get("profit_abs", 0.0) + if not isnan(fiat_profit): + profit += f" ({fiat_profit:.2f})" + fiat_profit_sum = ( + fiat_profit if isnan(fiat_profit_sum) else fiat_profit_sum + fiat_profit + ) + total_profit = trade.get("total_profit_fiat", None) + if total_profit is None or isnan(total_profit): + total_profit = trade.get("total_profit_abs", 0.0) + if not isnan(total_profit): + fiat_total_profit_sum = ( + total_profit + if isnan(fiat_total_profit_sum) + else fiat_total_profit_sum + total_profit ) - if self._fiat_converter: - fiat_profit = self._fiat_converter.convert_amount( - trade_profit, stake_currency, fiat_display_currency - ) - if not isnan(fiat_profit): - profit_str += f" ({fiat_profit:.2f})" - fiat_profit_sum = ( - fiat_profit if isnan(fiat_profit_sum) else fiat_profit_sum + fiat_profit - ) - else: - profit_str += f" ({trade_profit:.2f})" - fiat_profit_sum = ( - trade_profit if isnan(fiat_profit_sum) else fiat_profit_sum + trade_profit - ) - active_attempt_side_symbols = [ - "*" if (oo and oo.ft_order_side == trade.entry_side) else "**" - for oo in trade.open_orders - ] + # Format the active order side symbols + active_order_side = "" + orders = trade.get("orders", []) + if orders: + active_order_side = ".".join( + "*" if (o.get("is_open") and o.get("ft_is_entry")) else "**" + for o in orders + if o.get("is_open") and o.get("ft_order_side") != "stoploss" + ) - # example: '*.**.**' trying to enter, exit and exit with 3 different orders - active_attempt_side_symbols_str = ".".join(active_attempt_side_symbols) + # Direction string for non-spot + direction_str = "" + if nonspot: + leverage = trade.get("leverage", 1.0) + direction_str = f"{'S' if trade.get('is_short') else 'L'} {leverage:.3g}x" - detail_trade = [ - f"{trade.id} {direction_str}", - trade.pair + active_attempt_side_symbols_str, - shorten_date(dt_humanize_delta(trade.open_date_utc)), - profit_str, - ] + detail_trade = [ + f"{trade['trade_id']} {direction_str}", + f"{trade['pair']}{active_order_side}", + shorten_date(dt_humanize_delta(dt_from_ts(trade["open_timestamp"]))), + profit, + ] - if self._config.get("position_adjustment_enable", False): - max_entry_str = "" - if self._config.get("max_entry_position_adjustment", -1) > 0: - max_entry_str = f"/{self._config['max_entry_position_adjustment'] + 1}" - filled_entries = trade.nr_of_successful_entries - detail_trade.append(f"{filled_entries}{max_entry_str}") - trades_list.append(detail_trade) - profitcol = "Profit" - if self._fiat_converter: - profitcol += " (" + fiat_display_currency + ")" - else: - profitcol += " (" + stake_currency + ")" - - columns = ["ID L/S" if nonspot else "ID", "Pair", "Since", profitcol] + # Add number of entries if position adjustment is enabled if self._config.get("position_adjustment_enable", False): - columns.append("# Entries") - return trades_list, columns, fiat_profit_sum + max_entry_str = "" + if self._config.get("max_entry_position_adjustment", -1) > 0: + max_entry_str = f"/{self._config['max_entry_position_adjustment'] + 1}" + filled_entries = trade.get("nr_of_successful_entries", 0) + detail_trade.append(f"{filled_entries}{max_entry_str}") + + trades_list.append(detail_trade) + + columns = [ + "ID L/S" if nonspot else "ID", + "Pair", + "Since", + f"Profit ({fiat_display_currency if self._fiat_converter else stake_currency})", + ] + + if self._config.get("position_adjustment_enable", False): + columns.append("# Entries") + + return trades_list, columns, fiat_profit_sum, fiat_total_profit_sum def _rpc_timeunit_profit( self, @@ -576,8 +588,8 @@ class RPC: # Doing the sum is not right - overall profit needs to be based on initial capital profit_all_ratio_sum = sum(profit_all_ratio) if profit_all_ratio else 0.0 starting_balance = self._freqtrade.wallets.get_starting_balance() - profit_closed_ratio_fromstart = 0 - profit_all_ratio_fromstart = 0 + profit_closed_ratio_fromstart = 0.0 + profit_all_ratio_fromstart = 0.0 if starting_balance: profit_closed_ratio_fromstart = profit_closed_coin_sum / starting_balance profit_all_ratio_fromstart = profit_all_coin_sum / starting_balance @@ -654,6 +666,7 @@ class RPC: "best_pair": best_pair[0] if best_pair else "", "best_rate": round(best_pair[1] * 100, 2) if best_pair else 0, # Deprecated "best_pair_profit_ratio": best_pair[1] if best_pair else 0, + "best_pair_profit_abs": best_pair[2] if best_pair else 0, "winning_trades": winning_trades, "losing_trades": losing_trades, "profit_factor": profit_factor, @@ -678,9 +691,10 @@ class RPC: ) -> tuple[float, float]: est_stake = 0.0 est_bot_stake = 0.0 - if coin == stake_currency: + is_futures = self._config.get("trading_mode", TradingMode.SPOT) == TradingMode.FUTURES + if coin == self._freqtrade.exchange.get_proxy_coin(): est_stake = balance.total - if self._config.get("trading_mode", TradingMode.SPOT) != TradingMode.SPOT: + if is_futures: # in Futures, "total" includes the locked stake, and therefore all positions est_stake = balance.free est_bot_stake = amount @@ -690,7 +704,7 @@ class RPC: coin, stake_currency ) if rate: - est_stake = rate * balance.total + est_stake = rate * (balance.free if is_futures else balance.total) est_bot_stake = rate * amount return est_stake, est_bot_stake @@ -722,10 +736,15 @@ class RPC: if not balance.total and not balance.free: continue - trade = open_assets.get(coin, None) - is_bot_managed = coin == stake_currency or trade is not None + trade = ( + open_assets.get(coin, None) + if self._freqtrade.trading_mode != TradingMode.FUTURES + else None + ) + is_stake_currency = coin == self._freqtrade.exchange.get_proxy_coin() + is_bot_managed = is_stake_currency or trade is not None trade_amount = trade.amount if trade else 0 - if coin == stake_currency: + if is_stake_currency: trade_amount = self._freqtrade.wallets.get_available_stake_amount() try: @@ -890,10 +909,10 @@ class RPC: if amount and amount < trade.amount: # Partial exit ... min_exit_stake = self._freqtrade.exchange.get_min_pair_stake_amount( - trade.pair, current_rate, trade.stop_loss_pct + trade.pair, current_rate, trade.stop_loss_pct or 0.0 ) remaining = (trade.amount - amount) * current_rate - if remaining < min_exit_stake: + if min_exit_stake and remaining < min_exit_stake: raise RPCException(f"Remaining amount of {remaining} would be too small.") sub_amount = amount @@ -1244,7 +1263,7 @@ class RPC: for pair in add: if pair not in self._freqtrade.pairlists.blacklist: try: - expand_pairlist([pair], self._freqtrade.exchange.get_markets().keys()) + expand_pairlist([pair], list(self._freqtrade.exchange.get_markets().keys())) self._freqtrade.pairlists.blacklist.append(pair) except ValueError: @@ -1277,6 +1296,7 @@ class RPC: r.message + ("\n" + r.exc_text if r.exc_text else ""), ] for r in buffer + if hasattr(r, "message") ] # Log format: @@ -1428,7 +1448,12 @@ class RPC: @staticmethod def _rpc_analysed_history_full( - config: Config, pair: str, timeframe: str, exchange, selected_cols: list[str] | None + config: Config, + pair: str, + timeframe: str, + exchange: Exchange, + selected_cols: list[str] | None, + live: bool, ) -> dict[str, Any]: timerange_parsed = TimeRange.parse_timerange(config.get("timerange")) @@ -1436,31 +1461,53 @@ class RPC: from freqtrade.data.dataprovider import DataProvider from freqtrade.resolvers.strategy_resolver import StrategyResolver - strategy = StrategyResolver.load_strategy(config) - startup_candles = strategy.startup_candle_count + strategy_name = "" + startup_candles = 0 + if config.get("strategy"): + strategy = StrategyResolver.load_strategy(config) + startup_candles = strategy.startup_candle_count + strategy_name = strategy.get_strategy_name() - _data = load_data( - datadir=config["datadir"], - pairs=[pair], - timeframe=timeframe, - timerange=timerange_parsed, - data_format=config["dataformat_ohlcv"], - candle_type=config.get("candle_type_def", CandleType.SPOT), - startup_candles=startup_candles, - ) - if pair not in _data: - raise RPCException( - f"No data for {pair}, {timeframe} in {config.get('timerange')} found." + if live: + data = exchange.get_historic_ohlcv( + pair=pair, + timeframe=timeframe, + since_ms=timerange_parsed.startts * 1000 + if timerange_parsed.startts + else dt_ts(dt_now() - timedelta(days=30)), + is_new_pair=True, # history is never available - so always treat as new pair + candle_type=config.get("candle_type_def", CandleType.SPOT), + until_ms=timerange_parsed.stopts, ) + else: + _data = load_data( + datadir=config["datadir"], + pairs=[pair], + timeframe=timeframe, + timerange=timerange_parsed, + data_format=config["dataformat_ohlcv"], + candle_type=config.get("candle_type_def", CandleType.SPOT), + startup_candles=startup_candles, + ) + if pair not in _data: + raise RPCException( + f"No data for {pair}, {timeframe} in {config.get('timerange')} found." + ) + data = _data[pair] - strategy.dp = DataProvider(config, exchange=exchange, pairlists=None) - strategy.ft_bot_start() + if config.get("strategy"): + strategy.dp = DataProvider(config, exchange=exchange, pairlists=None) + strategy.ft_bot_start() - df_analyzed = strategy.analyze_ticker(_data[pair], {"pair": pair}) - df_analyzed = trim_dataframe(df_analyzed, timerange_parsed, startup_candles=startup_candles) + df_analyzed = strategy.analyze_ticker(data, {"pair": pair}) + df_analyzed = trim_dataframe( + df_analyzed, timerange_parsed, startup_candles=startup_candles + ) + else: + df_analyzed = data return RPC._convert_dataframe_to_dict( - strategy.get_strategy_name(), + strategy_name, pair, timeframe, df_analyzed.copy(), diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 1b5653b1d..5b4982346 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -90,6 +90,7 @@ class TimeunitMappings: def authorized_only(command_handler: Callable[..., Coroutine[Any, Any, None]]): """ Decorator to check if the message comes from the correct chat_id + can only be used with Telegram Class to decorate instance methods. :param command_handler: Telegram CommandHandler :return: decorated function """ @@ -102,13 +103,21 @@ def authorized_only(command_handler: Callable[..., Coroutine[Any, Any, None]]): # Reject unauthorized messages if update.callback_query: cchat_id = int(update.callback_query.message.chat.id) + ctopic_id = update.callback_query.message.message_thread_id else: cchat_id = int(update.message.chat_id) + ctopic_id = update.message.message_thread_id chat_id = int(self._config["telegram"]["chat_id"]) if cchat_id != chat_id: - logger.info(f"Rejected unauthorized message from: {update.message.chat_id}") - return wrapper + logger.info(f"Rejected unauthorized message from: {cchat_id}") + return None + if (topic_id := self._config["telegram"].get("topic_id")) is not None: + if str(ctopic_id) != topic_id: + # This can be quite common in multi-topic environments. + logger.debug(f"Rejected message from wrong channel: {cchat_id}, {ctopic_id}") + return None + # Rollback session to avoid getting data stored in a transaction. Trade.rollback() logger.debug("Executing handler: %s for chat_id: %s", command_handler.__name__, chat_id) @@ -291,6 +300,7 @@ class Telegram(RPCHandler): CommandHandler("marketdir", self._changemarketdir), CommandHandler("order", self._order), CommandHandler("list_custom_data", self._list_custom_data), + CommandHandler("tg_info", self._tg_info), ] callbacks = [ CallbackQueryHandler(self._status_table, pattern="update_status_table"), @@ -848,11 +858,14 @@ class Telegram(RPCHandler): :return: None """ fiat_currency = self._config.get("fiat_display_currency", "") - statlist, head, fiat_profit_sum = self._rpc._rpc_status_table( + statlist, head, fiat_profit_sum, fiat_total_profit_sum = self._rpc._rpc_status_table( self._config["stake_currency"], fiat_currency ) show_total = not isnan(fiat_profit_sum) and len(statlist) > 1 + show_total_realized = ( + not isnan(fiat_total_profit_sum) and len(statlist) > 1 and fiat_profit_sum + ) != fiat_total_profit_sum max_trades_per_msg = 50 """ Calculate the number of messages of 50 trades per message @@ -865,12 +878,22 @@ class Telegram(RPCHandler): if show_total and i == messages_count - 1: # append total line trades.append(["Total", "", "", f"{fiat_profit_sum:.2f} {fiat_currency}"]) + if show_total_realized: + trades.append( + [ + "Total", + "(incl. realized Profits)", + "", + f"{fiat_total_profit_sum:.2f} {fiat_currency}", + ] + ) message = tabulate(trades, headers=head, tablefmt="simple") if show_total and i == messages_count - 1: # insert separators line between Total lines = message.split("\n") - message = "\n".join(lines[:-1] + [lines[1]] + [lines[-1]]) + offset = 2 if show_total_realized else 1 + message = "\n".join(lines[:-offset] + [lines[1]] + lines[-offset:]) await self._send_msg( f"
{message}
", parse_mode=ParseMode.HTML, @@ -1005,6 +1028,7 @@ class Telegram(RPCHandler): avg_duration = stats["avg_duration"] best_pair = stats["best_pair"] best_pair_profit_ratio = stats["best_pair_profit_ratio"] + best_pair_profit_abs = fmt_coin(stats["best_pair_profit_abs"], stake_cur) winrate = stats["winrate"] expectancy = stats["expectancy"] expectancy_ratio = stats["expectancy_ratio"] @@ -1044,7 +1068,8 @@ class Telegram(RPCHandler): if stats["closed_trade_count"] > 0: markdown_msg += ( f"\n*Avg. Duration:* `{avg_duration}`\n" - f"*Best Performing:* `{best_pair}: {best_pair_profit_ratio:.2%}`\n" + f"*Best Performing:* `{best_pair}: {best_pair_profit_abs} " + f"({best_pair_profit_ratio:.2%})`\n" f"*Trading volume:* `{fmt_coin(stats['trading_volume'], stake_cur)}`\n" f"*Profit factor:* `{stats['profit_factor']:.2f}`\n" f"*Max Drawdown:* `{stats['max_drawdown']:.2%} " @@ -1277,7 +1302,7 @@ class Telegram(RPCHandler): else: fiat_currency = self._config.get("fiat_display_currency", "") try: - statlist, _, _ = self._rpc._rpc_status_table( + statlist, _, _, _ = self._rpc._rpc_status_table( self._config["stake_currency"], fiat_currency ) except RPCException: @@ -1806,7 +1831,7 @@ class Telegram(RPCHandler): "*/fx |all:* `Alias to /forceexit`\n" f"{force_enter_text if self._config.get('force_entry_enable', False) else ''}" "*/delete :* `Instantly delete the given trade in the database`\n" - "*/reload_trade :* `Relade trade from exchange Orders`\n" + "*/reload_trade :* `Reload trade from exchange Orders`\n" "*/cancel_open_order :* `Cancels open orders for trade. " "Only valid when the trade has open orders.`\n" "*/coo |all:* `Alias to /cancel_open_order`\n" @@ -2054,6 +2079,7 @@ class Telegram(RPCHandler): parse_mode=parse_mode, reply_markup=reply_markup, disable_notification=disable_notification, + message_thread_id=self._config["telegram"].get("topic_id"), ) except NetworkError as network_err: # Sometimes the telegram server resets the current connection, @@ -2067,6 +2093,7 @@ class Telegram(RPCHandler): parse_mode=parse_mode, reply_markup=reply_markup, disable_notification=disable_notification, + message_thread_id=self._config["telegram"].get("topic_id"), ) except TelegramError as telegram_err: logger.warning("TelegramError: %s! Giving up on that message.", telegram_err.message) @@ -2112,3 +2139,37 @@ class Telegram(RPCHandler): "Invalid usage of command /marketdir. \n" "Usage: */marketdir [short | long | even | none]*" ) + + async def _tg_info(self, update: Update, context: CallbackContext) -> None: + """ + Intentionally unauthenticated Handler for /tg_info. + Returns information about the current telegram chat - even if chat_id does not + correspond to this chat. + + :param update: message update + :return: None + """ + if not update.message: + return + chat_id = update.message.chat_id + topic_id = update.message.message_thread_id + + msg = f"""Freqtrade Bot Info: + ```json + {{ + "enabled": true, + "token": "********", + "chat_id": "{chat_id}", + {f'"topic_id": "{topic_id}"' if topic_id else ""} + }} + ``` + """ + try: + await context.bot.send_message( + chat_id=chat_id, + text=msg, + parse_mode=ParseMode.MARKDOWN_V2, + message_thread_id=topic_id, + ) + except TelegramError as telegram_err: + logger.warning("TelegramError: %s! Giving up on that message.", telegram_err.message) diff --git a/freqtrade/rpc/webhook.py b/freqtrade/rpc/webhook.py index b1a96a872..eb9aa37c8 100644 --- a/freqtrade/rpc/webhook.py +++ b/freqtrade/rpc/webhook.py @@ -94,8 +94,7 @@ class Webhook(RPCHandler): self._send_msg(payload) except KeyError as exc: logger.exception( - "Problem calling Webhook. Please check your webhook configuration. " - "Exception: %s", + "Problem calling Webhook. Please check your webhook configuration. Exception: %s", exc, ) diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 73d3acfa9..77fe2a84a 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -1160,8 +1160,12 @@ class IStrategy(ABC, HyperStrategyMixin): logger.warning(f"Empty candle (OHLCV) data for pair {pair}") return None, None - latest_date_pd = dataframe["date"].max() - latest = dataframe.loc[dataframe["date"] == latest_date_pd].iloc[-1] + try: + latest_date_pd = dataframe["date"].max() + latest = dataframe.loc[dataframe["date"] == latest_date_pd].iloc[-1] + except Exception as e: + logger.warning(f"Unable to get latest candle (OHLCV) data for pair {pair} - {e}") + return None, None # Explicitly convert to datetime object to ensure the below comparison does not fail latest_date: datetime = latest_date_pd.to_pydatetime() diff --git a/freqtrade/strategy/strategy_wrapper.py b/freqtrade/strategy/strategy_wrapper.py index 9250f54cd..25d44fae7 100644 --- a/freqtrade/strategy/strategy_wrapper.py +++ b/freqtrade/strategy/strategy_wrapper.py @@ -23,9 +23,11 @@ def strategy_safe_wrapper(f: F, message: str = "", default_retval=None, supress_ @wraps(f) def wrapper(*args, **kwargs): try: - if "trade" in kwargs: - # Protect accidental modifications from within the strategy - kwargs["trade"] = deepcopy(kwargs["trade"]) + if not (getattr(f, "__qualname__", "")).startswith("IStrategy."): + # Don't deep-copy if the function is not implemented in the user strategy.`` + if "trade" in kwargs: + # Protect accidental modifications from within the strategy + kwargs["trade"] = deepcopy(kwargs["trade"]) return f(*args, **kwargs) except ValueError as error: logger.warning(f"{message}Strategy caused the following exception: {error}{f}") diff --git a/freqtrade/system/__init__.py b/freqtrade/system/__init__.py index 10a23aa4b..e69e90de8 100644 --- a/freqtrade/system/__init__.py +++ b/freqtrade/system/__init__.py @@ -2,6 +2,7 @@ from freqtrade.system.asyncio_config import asyncio_setup from freqtrade.system.gc_setup import gc_set_threshold +from freqtrade.system.version_info import print_version_info -__all__ = ["asyncio_setup", "gc_set_threshold"] +__all__ = ["asyncio_setup", "gc_set_threshold", "print_version_info"] diff --git a/freqtrade/system/version_info.py b/freqtrade/system/version_info.py new file mode 100644 index 000000000..505992a7f --- /dev/null +++ b/freqtrade/system/version_info.py @@ -0,0 +1,15 @@ +from freqtrade import __version__ + + +def print_version_info(): + """Print version information for freqtrade and its key dependencies.""" + import platform + import sys + + import ccxt + + print(f"Operating System:\t{platform.platform()}") + print(f"Python Version:\t\tPython {sys.version.split(' ')[0]}") + print(f"CCXT Version:\t\t{ccxt.__version__}") + print() + print(f"Freqtrade Version:\tfreqtrade {__version__}") diff --git a/freqtrade/templates/strategy_analysis_example.ipynb b/freqtrade/templates/strategy_analysis_example.ipynb index 2510b38b9..c81a76f72 100644 --- a/freqtrade/templates/strategy_analysis_example.ipynb +++ b/freqtrade/templates/strategy_analysis_example.ipynb @@ -216,7 +216,7 @@ "# Get market change (average change of all pairs from start to end of the backtest period)\n", "print(stats[\"strategy\"][strategy][\"market_change\"])\n", "# Maximum drawdown ()\n", - "print(stats[\"strategy\"][strategy][\"max_drawdown\"])\n", + "print(stats[\"strategy\"][strategy][\"max_drawdown_abs\"])\n", "# Maximum drawdown start and end\n", "print(stats[\"strategy\"][strategy][\"drawdown_start\"])\n", "print(stats[\"strategy\"][strategy][\"drawdown_end\"])\n", diff --git a/freqtrade/util/__init__.py b/freqtrade/util/__init__.py index a0b618b11..5a4857eea 100644 --- a/freqtrade/util/__init__.py +++ b/freqtrade/util/__init__.py @@ -9,6 +9,7 @@ from freqtrade.util.datetime_helpers import ( dt_utc, format_date, format_ms_time, + format_ms_time_det, shorten_date, ) from freqtrade.util.dry_run_wallet import get_dry_run_wallet @@ -36,6 +37,7 @@ __all__ = [ "dt_utc", "format_date", "format_ms_time", + "format_ms_time_det", "get_dry_run_wallet", "FtPrecise", "PeriodicCache", diff --git a/freqtrade/util/datetime_helpers.py b/freqtrade/util/datetime_helpers.py index 255779849..03ee3b6d9 100644 --- a/freqtrade/util/datetime_helpers.py +++ b/freqtrade/util/datetime_helpers.py @@ -107,3 +107,12 @@ def format_ms_time(date: int | float) -> str: : epoch-string in ms """ return dt_from_ts(date).strftime("%Y-%m-%dT%H:%M:%S") + + +def format_ms_time_det(date: int | float) -> str: + """ + convert MS date to readable format - detailed. + : epoch-string in ms + """ + # return dt_from_ts(date).isoformat(timespec="milliseconds") + return dt_from_ts(date).strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] diff --git a/freqtrade/util/progress_tracker.py b/freqtrade/util/progress_tracker.py index 3f46e0b82..cc5c79a18 100644 --- a/freqtrade/util/progress_tracker.py +++ b/freqtrade/util/progress_tracker.py @@ -7,6 +7,7 @@ from rich.progress import ( TimeRemainingColumn, ) +from freqtrade.loggers import error_console from freqtrade.util.rich_progress import CustomProgress @@ -30,5 +31,6 @@ def get_progress_tracker(**kwargs) -> CustomProgress: "•", TimeRemainingColumn(), expand=True, + console=error_console, **kwargs, ) diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index 5a239dadd..c77b27147 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -2,9 +2,8 @@ """Wallet""" import logging -from copy import deepcopy from datetime import datetime, timedelta -from typing import NamedTuple +from typing import Literal, NamedTuple from freqtrade.constants import UNLIMITED_STAKE_AMOUNT, Config, IntOrInf from freqtrade.enums import RunMode, TradingMode @@ -42,7 +41,8 @@ class Wallets: self._wallets: dict[str, Wallet] = {} self._positions: dict[str, PositionWallet] = {} self._start_cap: dict[str, float] = {} - self._stake_currency = config["stake_currency"] + + self._stake_currency = self._exchange.get_proxy_coin() if isinstance(_start_cap := config["dry_run_wallet"], float | int): self._start_cap[self._stake_currency] = _start_cap @@ -178,19 +178,16 @@ class Wallets: def _update_live(self) -> None: balances = self._exchange.get_balances() + _wallets = {} for currency in balances: if isinstance(balances[currency], dict): - self._wallets[currency] = Wallet( + _wallets[currency] = Wallet( currency, balances[currency].get("free", 0), balances[currency].get("used", 0), balances[currency].get("total", 0), ) - # Remove currencies no longer in get_balances output - for currency in deepcopy(self._wallets): - if currency not in balances: - del self._wallets[currency] positions = self._exchange.fetch_positions() _parsed_positions = {} @@ -210,6 +207,7 @@ class Wallets: side=position["side"], ) self._positions = _parsed_positions + self._wallets = _wallets def update(self, require_update: bool = True) -> None: """ @@ -229,8 +227,7 @@ class Wallets: self._update_live() else: self._update_dry() - if not self._is_backtest: - logger.info("Wallets synced.") + self._local_log("Wallets synced.") self._last_wallet_refresh = dt_now() def get_all_balances(self) -> dict[str, Wallet]: @@ -279,7 +276,9 @@ class Wallets: tot_profit = Trade.get_total_closed_profit() open_stakes = Trade.total_open_trades_stakes() available_balance = self.get_free(self._stake_currency) - return available_balance - tot_profit + open_stakes + return (available_balance - tot_profit + open_stakes) * self._config[ + "tradable_balance_ratio" + ] def get_total_stake_amount(self): """ @@ -392,7 +391,10 @@ class Wallets: trade_amount: float | None, ): if not stake_amount: - logger.debug(f"Stake amount is {stake_amount}, ignoring possible trade for {pair}.") + self._local_log( + f"Stake amount is {stake_amount}, ignoring possible trade for {pair}.", + level="debug", + ) return 0 max_allowed_stake = min(max_stake_amount, self.get_available_stake_amount()) @@ -402,34 +404,43 @@ class Wallets: max_allowed_stake = min(max_allowed_stake, max_stake_amount - trade_amount) if min_stake_amount is not None and min_stake_amount > max_allowed_stake: - if not self._is_backtest: - logger.warning( - "Minimum stake amount > available balance. " - f"{min_stake_amount} > {max_allowed_stake}" - ) + self._local_log( + "Minimum stake amount > available balance. " + f"{min_stake_amount} > {max_allowed_stake}", + level="warning", + ) return 0 if min_stake_amount is not None and stake_amount < min_stake_amount: - if not self._is_backtest: - logger.info( - f"Stake amount for pair {pair} is too small " - f"({stake_amount} < {min_stake_amount}), adjusting to {min_stake_amount}." - ) + self._local_log( + f"Stake amount for pair {pair} is too small " + f"({stake_amount} < {min_stake_amount}), adjusting to {min_stake_amount}." + ) if stake_amount * 1.3 < min_stake_amount: # Top-cap stake-amount adjustments to +30%. - if not self._is_backtest: - logger.info( - f"Adjusted stake amount for pair {pair} is more than 30% bigger than " - f"the desired stake amount of ({stake_amount:.8f} * 1.3 = " - f"{stake_amount * 1.3:.8f}) < {min_stake_amount}), ignoring trade." - ) + self._local_log( + f"Adjusted stake amount for pair {pair} is more than 30% bigger than " + f"the desired stake amount of ({stake_amount:.8f} * 1.3 = " + f"{stake_amount * 1.3:.8f}) < {min_stake_amount}), ignoring trade." + ) return 0 stake_amount = min_stake_amount if stake_amount > max_allowed_stake: - if not self._is_backtest: - logger.info( - f"Stake amount for pair {pair} is too big " - f"({stake_amount} > {max_allowed_stake}), adjusting to {max_allowed_stake}." - ) + self._local_log( + f"Stake amount for pair {pair} is too big " + f"({stake_amount} > {max_allowed_stake}), adjusting to {max_allowed_stake}." + ) stake_amount = max_allowed_stake return stake_amount + + def _local_log(self, msg: str, level: Literal["info", "warning", "debug"] = "info") -> None: + """ + Log a message to the local log. + """ + if not self._is_backtest: + if level == "warning": + logger.warning(msg) + elif level == "debug": + logger.debug(msg) + else: + logger.info(msg) diff --git a/ft_client/freqtrade_client/__init__.py b/ft_client/freqtrade_client/__init__.py index d17eec2a7..17ecee98b 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.12-dev" +__version__ = "2025.2-dev" if "dev" in __version__: from pathlib import Path diff --git a/ft_client/freqtrade_client/ft_rest_client.py b/ft_client/freqtrade_client/ft_rest_client.py index 39844b60b..7861e3cd2 100755 --- a/ft_client/freqtrade_client/ft_rest_client.py +++ b/ft_client/freqtrade_client/ft_rest_client.py @@ -23,10 +23,18 @@ PostDataT = dict[str, Any] | list[dict[str, Any]] | None class FtRestClient: def __init__( - self, serverurl, username=None, password=None, *, pool_connections=10, pool_maxsize=10 + self, + serverurl, + username=None, + password=None, + *, + pool_connections=10, + pool_maxsize=10, + timeout=10, ): self._serverurl = serverurl self._session = requests.Session() + self._timeout = timeout # allow configuration of pool adapter = HTTPAdapter(pool_connections=pool_connections, pool_maxsize=pool_maxsize) @@ -50,7 +58,9 @@ class FtRestClient: url = urlunparse((schema, netloc, path, par, query, fragment)) try: - resp = self._session.request(method, url, headers=hd, data=json.dumps(data)) + resp = self._session.request( + method, url, headers=hd, timeout=self._timeout, data=json.dumps(data) + ) # return resp.text return resp.json() except RequestConnectionError: diff --git a/pyproject.toml b/pyproject.toml index cc88c9131..9e877e8eb 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -98,10 +98,6 @@ freqai_rl = [ "sb3-contrib", "tqdm", ] -hdf5 = [ - "tables", - "blosc", -] develop = [ "coveralls", "isort", @@ -129,7 +125,7 @@ jupyter = [ "nbconvert", ] all = [ - "freqtrade[plot,hyperopt,freqai,freqai_rl,hdf5,jupyter]", + "freqtrade[plot,hyperopt,freqai,freqai_rl,jupyter]", ] dev = [ "freqtrade[all,develop]", diff --git a/requirements-dev.txt b/requirements-dev.txt index 34845f03b..e7afc064d 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -7,22 +7,22 @@ -r docs/requirements-docs.txt coveralls==4.0.1 -ruff==0.8.2 -mypy==1.13.0 -pre-commit==4.0.1 +ruff==0.9.5 +mypy==1.15.0 +pre-commit==4.1.0 pytest==8.3.4 -pytest-asyncio==0.24.0 +pytest-asyncio==0.25.3 pytest-cov==6.0.0 pytest-mock==3.14.0 pytest-random-order==1.1.1 pytest-timeout==2.3.1 pytest-xdist==3.6.1 -isort==5.13.2 +isort==6.0.0 # For datetime mocking time-machine==2.16.0 # Convert jupyter notebooks to markdown documents -nbconvert==7.16.4 +nbconvert==7.16.6 # mypy types types-cachetools==5.5.0.20240820 diff --git a/requirements-freqai-rl.txt b/requirements-freqai-rl.txt index ca5c58943..413feec8f 100644 --- a/requirements-freqai-rl.txt +++ b/requirements-freqai-rl.txt @@ -3,9 +3,11 @@ # Required for freqai-rl torch==2.2.2; sys_platform == 'darwin' and platform_machine == 'x86_64' -torch==2.5.1; sys_platform != 'darwin' or platform_machine != 'x86_64' +torch==2.6.0; sys_platform != 'darwin' or platform_machine != 'x86_64' gymnasium==0.29.1 -stable_baselines3==2.4.0 +# SB3 >=2.5.0 depends on torch 2.3.0 - which implies it dropped support x86 macos +stable_baselines3==2.4.1; sys_platform == 'darwin' and platform_machine == 'x86_64' +stable_baselines3==2.5.0; sys_platform != 'darwin' or platform_machine != 'x86_64' sb3_contrib>=2.2.1 # Progress bar for stable-baselines3 and sb3-contrib tqdm==4.67.1 diff --git a/requirements-freqai.txt b/requirements-freqai.txt index 47c69ff64..c978b63cb 100644 --- a/requirements-freqai.txt +++ b/requirements-freqai.txt @@ -3,13 +3,10 @@ -r requirements-plot.txt # Required for freqai -scikit-learn==1.5.2 +scikit-learn==1.6.1 joblib==1.4.2 catboost==1.2.7; 'arm' not in platform_machine -# Pin Matplotlib - it's depended on by catboost -# Temporary downgrade of matplotlib due to https://github.com/matplotlib/matplotlib/issues/28551 -matplotlib==3.9.3 lightgbm==4.5.0 -xgboost==2.1.3 +xgboost==2.1.4 tensorboard==2.18.0 datasieve==0.1.7 diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 7f60c8299..6d193bdc7 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -2,7 +2,7 @@ -r requirements.txt # Required for hyperopt -scipy==1.14.1 -scikit-learn==1.5.2 +scipy==1.15.1 +scikit-learn==1.6.1 ft-scikit-optimize==0.9.2 -filelock==3.16.1 +filelock==3.17.0 diff --git a/requirements-plot.txt b/requirements-plot.txt index a50d56ead..16a162600 100644 --- a/requirements-plot.txt +++ b/requirements-plot.txt @@ -1,4 +1,4 @@ # Include all requirements to run the bot. -r requirements.txt -plotly==5.24.1 +plotly==6.0.0 diff --git a/requirements.txt b/requirements.txt index 85c9bd543..34becb2d3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,61 +4,60 @@ bottleneck==1.4.2 numexpr==2.10.2 pandas-ta==0.3.14b -ccxt==4.4.40 +ccxt==4.4.58 cryptography==42.0.8; platform_machine == 'armv7l' -cryptography==44.0.0; platform_machine != 'armv7l' -aiohttp==3.10.11 -SQLAlchemy==2.0.36 -python-telegram-bot==21.9 +cryptography==44.0.1; platform_machine != 'armv7l' +aiohttp==3.9.5 +SQLAlchemy==2.0.38 +python-telegram-bot==21.10 # can't be hard-pinned due to telegram-bot pinning httpx with ~ httpx>=0.24.1 humanize==4.11.0 -cachetools==5.5.0 +cachetools==5.5.1 requests==2.32.3 -urllib3==2.2.3 +urllib3==2.3.0 jsonschema==4.23.0 -TA-Lib==0.4.34 -technical==1.4.4 +TA-Lib==0.4.38 +technical==1.5.0 tabulate==0.9.0 pycoingecko==3.2.0 -jinja2==3.1.4 -tables==3.10.1 +jinja2==3.1.5 joblib==1.4.2 rich==13.9.4 -pyarrow==18.1.0; platform_machine != 'armv7l' +pyarrow==19.0.0; platform_machine != 'armv7l' # find first, C search in arrays -py_find_1st==1.1.6 +py_find_1st==1.1.7 # Load ticker files 30% faster python-rapidjson==1.20 # Properly format api responses -orjson==3.10.12 +orjson==3.10.15 # Notify systemd sdnotify==0.3.2 # API Server -fastapi==0.115.6 -pydantic==2.10.3 -uvicorn==0.32.1 +fastapi==0.115.8 +pydantic==2.10.6 +uvicorn==0.34.0 pyjwt==2.10.1 aiofiles==24.1.0 -psutil==6.1.0 +psutil==6.1.1 # Building config files interactively -questionary==2.0.1 -prompt-toolkit==3.0.36 +questionary==2.1.0 +prompt-toolkit==3.0.50 # Extensions to datetime library python-dateutil==2.9.0.post0 -pytz==2024.2 +pytz==2025.1 #Futures schedule==1.2.2 #WS Messages -websockets==14.1 -janus==1.1.0 +websockets==14.2 +janus==2.0.0 ast-comments==1.2.2 packaging==24.2 diff --git a/setup.sh b/setup.sh index a9e684d62..0d490a134 100755 --- a/setup.sh +++ b/setup.sh @@ -136,22 +136,6 @@ function install_talib() { cd .. } -function install_mac_newer_python_dependencies() { - - if [ ! $(brew --prefix --installed hdf5 2>/dev/null) ] - then - echo_block "Installing hdf5" - brew install hdf5 - fi - export HDF5_DIR=$(brew --prefix) - - if [ ! $(brew --prefix --installed c-blosc 2>/dev/null) ] - then - echo_block "Installing c-blosc" - brew install c-blosc - fi - export CBLOSC_DIR=$(brew --prefix) -} # Install bot MacOS function install_macos() { @@ -165,10 +149,6 @@ function install_macos() { #Gets number after decimal in python version version=$(egrep -o 3.\[0-9\]+ <<< $PYTHON | sed 's/3.//g') - - if [[ $version -ge 10 ]]; then #Checks if python version >= 3.10 - install_mac_newer_python_dependencies - fi } # Install bot Debian_ubuntu diff --git a/tests/commands/test_commands.py b/tests/commands/test_commands.py index 355b7e20d..65a43b795 100644 --- a/tests/commands/test_commands.py +++ b/tests/commands/test_commands.py @@ -1532,8 +1532,11 @@ def test_hyperopt_list(mocker, capsys, caplog, tmp_path): assert csv_file.is_file() line = csv_file.read_text() assert ( - 'Best,1,2,-1.25%,-1.2222,-0.00125625,,-2.51,"3,930.0 m",0.43662' in line - or "Best,1,2,-1.25%,-1.2222,-0.00125625,,-2.51,2 days 17:30:00,2,0,0.43662" in line + 'Best,1,2,-1.25%,-1.2222,-0.00125625,BTC,-2.51,"3,930.0 m",-0.00125625,23.00%,0.43662' + in line + or "Best,1,2,-1.25%,-1.2222,-0.00125625,BTC,-2.51,2 days 17:30:00,2,0,-0.00125625,23.00%," + "0.43662" + in line ) csv_file.unlink() diff --git a/tests/commands/test_startup_time.py b/tests/commands/test_startup_time.py index 37e2eeea2..08856a636 100644 --- a/tests/commands/test_startup_time.py +++ b/tests/commands/test_startup_time.py @@ -12,6 +12,6 @@ def test_startup_time(): start = time.time() subprocess.run(["freqtrade", "-h"]) elapsed = time.time() - start - assert ( - elapsed < MAXIMUM_STARTUP_TIME - ), "The startup time is too long, try to use lazy import in the command entry function" + assert elapsed < MAXIMUM_STARTUP_TIME, ( + "The startup time is too long, try to use lazy import in the command entry function" + ) diff --git a/tests/conftest.py b/tests/conftest.py index 69ea8878b..ca382f6ae 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -625,7 +625,7 @@ def get_default_conf(testdatadir): "telegram": { "enabled": False, "token": "token", - "chat_id": "0", + "chat_id": "1235", "notification_settings": {}, }, "datadir": Path(testdatadir), @@ -988,6 +988,29 @@ def get_markets(): }, "info": {}, }, + "ETC/BTC": { + "id": "ETCBTC", + "symbol": "ETC/BTC", + "base": "ETC", + "quote": "BTC", + "active": True, + "spot": True, + "swap": False, + "linear": None, + "type": "spot", + "contractSize": None, + "precision": {"base": 8, "quote": 8, "amount": 2, "price": 7}, + "limits": { + "amount": {"min": 0.01, "max": 90000000.0}, + "price": {"min": 1e-07, "max": 1000.0}, + "cost": {"min": 0.0001, "max": 9000000.0}, + "leverage": { + "min": None, + "max": None, + }, + }, + "info": {}, + }, "ETH/USDT": { "id": "USDT-ETH", "symbol": "ETH/USDT", @@ -1756,15 +1779,6 @@ def limit_buy_order_open(): } -@pytest.fixture(scope="function") -def limit_buy_order(limit_buy_order_open): - order = deepcopy(limit_buy_order_open) - order["status"] = "closed" - order["filled"] = order["amount"] - order["remaining"] = 0.0 - return order - - @pytest.fixture def limit_buy_order_old(): return { diff --git a/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py index 9b79983af..1c901bc16 100644 --- a/tests/data/test_btanalysis.py +++ b/tests/data/test_btanalysis.py @@ -1,6 +1,7 @@ from datetime import datetime, timedelta, timezone from pathlib import Path from unittest.mock import MagicMock +from zipfile import ZipFile import pytest from pandas import DataFrame, DateOffset, Timestamp, to_datetime @@ -15,6 +16,7 @@ from freqtrade.data.btanalysis import ( get_latest_hyperopt_file, load_backtest_data, load_backtest_metadata, + load_file_from_zip, load_trades, load_trades_from_db, ) @@ -569,3 +571,22 @@ def test_calculate_max_drawdown_abs(profits, relative, highd, lowdays, result, r assert drawdown.high_value > drawdown.low_value assert drawdown.drawdown_abs == result assert pytest.approx(drawdown.relative_account_drawdown) == result_rel + + +def test_load_file_from_zip(tmp_path): + with pytest.raises(ValueError, match=r"Zip file .* not found\."): + load_file_from_zip(tmp_path / "test.zip", "testfile.txt") + + (tmp_path / "testfile.zip").touch() + with pytest.raises(ValueError, match=r"Bad zip file.*"): + load_file_from_zip(tmp_path / "testfile.zip", "testfile.txt") + + zip_file = tmp_path / "testfile2.zip" + with ZipFile(zip_file, "w") as zipf: + zipf.writestr("testfile.txt", "testfile content") + + content = load_file_from_zip(zip_file, "testfile.txt") + assert content.decode("utf-8") == "testfile content" + + with pytest.raises(ValueError, match=r"File .* not found in zip.*"): + load_file_from_zip(zip_file, "testfile55.txt") diff --git a/tests/data/test_converter.py b/tests/data/test_converter.py index 9c6b7d875..3d6c9adc2 100644 --- a/tests/data/test_converter.py +++ b/tests/data/test_converter.py @@ -120,8 +120,7 @@ def test_ohlcv_fill_up_missing_data(testdatadir, caplog): assert (data.columns == data2.columns).all() assert log_has_re( - f"Missing data fillup for UNITTEST/BTC, 1m: before: " - f"{len(data)} - after: {len(data2)}.*", + f"Missing data fillup for UNITTEST/BTC, 1m: before: {len(data)} - after: {len(data2)}.*", caplog, ) diff --git a/tests/data/test_converter_orderflow.py b/tests/data/test_converter_orderflow.py index 656c1eab3..d5b2e8f83 100644 --- a/tests/data/test_converter_orderflow.py +++ b/tests/data/test_converter_orderflow.py @@ -1,4 +1,3 @@ -import numpy as np import pandas as pd import pytest @@ -6,6 +5,7 @@ from freqtrade.constants import DEFAULT_TRADES_COLUMNS from freqtrade.data.converter import populate_dataframe_with_trades from freqtrade.data.converter.orderflow import ( ORDERFLOW_ADDED_COLUMNS, + stacked_imbalance, timeframe_to_DateOffset, trades_to_volumeprofile_with_total_delta_bid_ask, ) @@ -185,24 +185,24 @@ def test_public_trades_mock_populate_dataframe_with_trades__check_orderflow( assert results["max_delta"] == 17.298 # Assert that stacked imbalances are NaN (not applicable in this test) - assert np.isnan(results["stacked_imbalances_bid"]) - assert np.isnan(results["stacked_imbalances_ask"]) + assert results["stacked_imbalances_bid"] == [] + assert results["stacked_imbalances_ask"] == [] # Repeat assertions for the third from last row results = df.iloc[-2] assert pytest.approx(results["delta"]) == -20.862 assert pytest.approx(results["min_delta"]) == -54.559999 assert 82.842 == results["max_delta"] - assert 234.99 == results["stacked_imbalances_bid"] - assert 234.96 == results["stacked_imbalances_ask"] + assert results["stacked_imbalances_bid"] == [234.97] + assert results["stacked_imbalances_ask"] == [234.94] # Repeat assertions for the last row results = df.iloc[-1] assert pytest.approx(results["delta"]) == -49.302 assert results["min_delta"] == -70.222 assert pytest.approx(results["max_delta"]) == 11.213 - assert np.isnan(results["stacked_imbalances_bid"]) - assert np.isnan(results["stacked_imbalances_ask"]) + assert results["stacked_imbalances_bid"] == [] + assert results["stacked_imbalances_ask"] == [] def test_public_trades_trades_mock_populate_dataframe_with_trades__check_trades( @@ -358,7 +358,8 @@ def test_public_trades_binned_big_sample_list(public_trades_list): assert 197.512 == df["bid_amount"].iloc[0] # total bid amount assert 88.98 == df["ask_amount"].iloc[0] # total ask amount assert 26 == df["ask"].iloc[0] # ask price - assert -108.532 == pytest.approx(df["delta"].iloc[0]) # delta (bid amount - ask amount) + # delta (bid amount - ask amount) + assert -108.532 == pytest.approx(df["delta"].iloc[0]) assert 3 == df["bid"].iloc[-1] # bid price assert 50.659 == df["bid_amount"].iloc[-1] # total bid amount @@ -555,9 +556,9 @@ def test_analyze_with_orderflow( assert col in df2.columns, f"Round2: Column {col} not found in df.columns" if col not in ("stacked_imbalances_bid", "stacked_imbalances_ask"): - assert ( - df2[col].count() == 5 - ), f"Round2: Column {col} has {df2[col].count()} non-NaN values" + assert df2[col].count() == 5, ( + f"Round2: Column {col} has {df2[col].count()} non-NaN values" + ) lastval_trade2 = df2.at[len(df2) - 1, "trades"] assert isinstance(lastval_trade2, list) @@ -567,6 +568,40 @@ def test_analyze_with_orderflow( assert isinstance(lastval_of2, dict) +def test_stacked_imbalances_multiple_prices(): + """Test that stacked imbalances correctly returns multiple price levels when present""" + # Test with empty result + df_no_stacks = pd.DataFrame( + { + "bid_imbalance": [False, False, True, False], + "ask_imbalance": [False, True, False, False], + }, + index=[234.95, 234.96, 234.97, 234.98], + ) + no_stacks = stacked_imbalance(df_no_stacks, "bid", stacked_imbalance_range=2) + assert no_stacks == [] + + # Create a sample DataFrame with known imbalances + df = pd.DataFrame( + { + "bid_imbalance": [True, True, True, False, False, True, True, False, True], + "ask_imbalance": [False, False, True, True, True, False, False, True, True], + }, + index=[234.95, 234.96, 234.97, 234.98, 234.99, 235.00, 235.01, 235.02, 235.03], + ) + # Test bid imbalances (should return prices in ascending order) + bid_prices = stacked_imbalance(df, "bid", stacked_imbalance_range=2) + assert bid_prices == [234.95, 234.96, 235.00] + + # Test ask imbalances (should return prices in descending order) + ask_prices = stacked_imbalance(df, "ask", stacked_imbalance_range=2) + assert ask_prices == [234.97, 234.98, 235.02] + + # Test with higher stacked_imbalance_range + bid_prices_higher = stacked_imbalance(df, "bid", stacked_imbalance_range=3) + assert bid_prices_higher == [234.95] + + def test_timeframe_to_DateOffset(): assert timeframe_to_DateOffset("1s") == pd.DateOffset(seconds=1) assert timeframe_to_DateOffset("1m") == pd.DateOffset(minutes=1) diff --git a/tests/data/test_datahandler.py b/tests/data/test_datahandler.py index e9ffcc7a1..1cff8b54a 100644 --- a/tests/data/test_datahandler.py +++ b/tests/data/test_datahandler.py @@ -12,7 +12,6 @@ from pandas.testing import assert_frame_equal from freqtrade.configuration import TimeRange from freqtrade.constants import AVAILABLE_DATAHANDLERS from freqtrade.data.history.datahandlers.featherdatahandler import FeatherDataHandler -from freqtrade.data.history.datahandlers.hdf5datahandler import HDF5DataHandler from freqtrade.data.history.datahandlers.idatahandler import ( IDataHandler, get_datahandler, @@ -21,6 +20,7 @@ from freqtrade.data.history.datahandlers.idatahandler import ( from freqtrade.data.history.datahandlers.jsondatahandler import JsonDataHandler, JsonGzDataHandler from freqtrade.data.history.datahandlers.parquetdatahandler import ParquetDataHandler from freqtrade.enums import CandleType, TradingMode +from freqtrade.exceptions import OperationalException from tests.conftest import log_has, log_has_re @@ -45,18 +45,12 @@ def test_datahandler_ohlcv_get_pairs(testdatadir): pairs = JsonGzDataHandler.ohlcv_get_pairs(testdatadir, "8m", candle_type=CandleType.SPOT) assert set(pairs) == {"UNITTEST/BTC"} - pairs = HDF5DataHandler.ohlcv_get_pairs(testdatadir, "5m", candle_type=CandleType.SPOT) - assert set(pairs) == {"UNITTEST/BTC"} - pairs = FeatherDataHandler.ohlcv_get_pairs(testdatadir, "1h", candle_type=CandleType.MARK) assert set(pairs) == {"UNITTEST/USDT:USDT", "XRP/USDT:USDT"} pairs = JsonGzDataHandler.ohlcv_get_pairs(testdatadir, "1h", candle_type=CandleType.FUTURES) assert set(pairs) == {"XRP/USDT:USDT"} - pairs = HDF5DataHandler.ohlcv_get_pairs(testdatadir, "1h", candle_type=CandleType.MARK) - assert set(pairs) == {"UNITTEST/USDT:USDT"} - @pytest.mark.parametrize( "filename,pair,timeframe,candletype", @@ -134,8 +128,6 @@ def test_datahandler_ohlcv_get_available_data(testdatadir): paircombs = JsonGzDataHandler.ohlcv_get_available_data(testdatadir, TradingMode.SPOT) assert set(paircombs) == {("UNITTEST/BTC", "8m", CandleType.SPOT)} - paircombs = HDF5DataHandler.ohlcv_get_available_data(testdatadir, TradingMode.SPOT) - assert set(paircombs) == {("UNITTEST/BTC", "5m", CandleType.SPOT)} def test_jsondatahandler_ohlcv_purge(mocker, testdatadir): @@ -321,7 +313,6 @@ def test_datahandler_trades_append(datahandler, testdatadir): "datahandler,expected", [ ("jsongz", {"XRP/ETH", "XRP/OLD"}), - ("hdf5", {"XRP/ETH"}), ("feather", {"XRP/ETH"}), ("parquet", {"XRP/ETH"}), ], @@ -332,32 +323,11 @@ def test_datahandler_trades_get_pairs(testdatadir, datahandler, expected): assert set(pairs) == expected -def test_hdf5datahandler_trades_load(testdatadir): - dh = get_datahandler(testdatadir, "hdf5") - trades = dh.trades_load("XRP/ETH", TradingMode.SPOT) - assert isinstance(trades, DataFrame) - - trades1 = dh.trades_load("UNITTEST/NONEXIST", TradingMode.SPOT) - assert isinstance(trades1, DataFrame) - assert trades1.empty - # data goes from 2019-10-11 - 2019-10-13 - timerange = TimeRange.parse_timerange("20191011-20191012") - - trades2 = dh._trades_load("XRP/ETH", TradingMode.SPOT, timerange) - assert len(trades) > len(trades2) - # Check that ID is None (If it's nan, it's wrong) - assert trades2.iloc[0]["type"] is None - - # unfiltered load has trades before starttime - - assert len(trades.loc[trades["timestamp"] < timerange.startts * 1000]) >= 0 - # filtered list does not have trades before starttime - assert len(trades2.loc[trades2["timestamp"] < timerange.startts * 1000]) == 0 - # unfiltered load has trades after endtime - assert len(trades.loc[trades["timestamp"] > timerange.stopts * 1000]) >= 0 - # filtered list does not have trades after endtime - assert len(trades2.loc[trades2["timestamp"] > timerange.stopts * 1000]) == 0 - # assert len([t for t in trades2 if t[0] > timerange.stopts * 1000]) == 0 +def test_hdf5datahandler_deprecated(testdatadir): + with pytest.raises( + OperationalException, match=r"DEPRECATED: The hdf5 dataformat is deprecated and has.*" + ): + get_datahandler(testdatadir, "hdf5") @pytest.mark.parametrize( @@ -369,52 +339,7 @@ def test_hdf5datahandler_trades_load(testdatadir): ("UNITTEST/USDT:USDT", "1h", "mark", "-mark", "2021-11-16", "2021-11-18"), ], ) -def test_hdf5datahandler_ohlcv_load_and_resave( - testdatadir, tmp_path, pair, timeframe, candle_type, candle_append, startdt, enddt -): - tmpdir2 = tmp_path - if candle_type not in ("", "spot"): - tmpdir2 = tmp_path / "futures" - tmpdir2.mkdir() - dh = get_datahandler(testdatadir, "hdf5") - ohlcv = dh._ohlcv_load(pair, timeframe, None, candle_type=candle_type) - assert isinstance(ohlcv, DataFrame) - assert len(ohlcv) > 0 - - file = tmpdir2 / f"UNITTEST_NEW-{timeframe}{candle_append}.h5" - assert not file.is_file() - - dh1 = get_datahandler(tmp_path, "hdf5") - dh1.ohlcv_store("UNITTEST/NEW", timeframe, ohlcv, candle_type=candle_type) - assert file.is_file() - - assert not ohlcv[ohlcv["date"] < startdt].empty - - timerange = TimeRange.parse_timerange(f"{startdt.replace('-', '')}-{enddt.replace('-', '')}") - - # Call private function to ensure timerange is filtered in hdf5 - ohlcv = dh._ohlcv_load(pair, timeframe, timerange, candle_type=candle_type) - ohlcv1 = dh1._ohlcv_load("UNITTEST/NEW", timeframe, timerange, candle_type=candle_type) - assert len(ohlcv) == len(ohlcv1) - assert ohlcv.equals(ohlcv1) - assert ohlcv[ohlcv["date"] < startdt].empty - assert ohlcv[ohlcv["date"] > enddt].empty - - # Try loading inexisting file - ohlcv = dh.ohlcv_load("UNITTEST/NONEXIST", timeframe, candle_type=candle_type) - assert ohlcv.empty - - -@pytest.mark.parametrize( - "pair,timeframe,candle_type,candle_append,startdt,enddt", - [ - # Data goes from 2018-01-10 - 2018-01-30 - ("UNITTEST/BTC", "5m", "spot", "", "2018-01-15", "2018-01-19"), - # Mark data goes from to 2021-11-15 2021-11-19 - ("UNITTEST/USDT:USDT", "1h", "mark", "-mark", "2021-11-16", "2021-11-18"), - ], -) -@pytest.mark.parametrize("datahandler", ["hdf5", "feather", "parquet"]) +@pytest.mark.parametrize("datahandler", ["feather", "parquet"]) def test_generic_datahandler_ohlcv_load_and_resave( datahandler, mocker, @@ -453,14 +378,7 @@ def test_generic_datahandler_ohlcv_load_and_resave( timerange = TimeRange.parse_timerange(f"{startdt.replace('-', '')}-{enddt.replace('-', '')}") ohlcv = dhbase.ohlcv_load(pair, timeframe, timerange=timerange, candle_type=candle_type) - if datahandler == "hdf5": - ohlcv1 = dh1._ohlcv_load("UNITTEST/NEW", timeframe, timerange, candle_type=candle_type) - if candle_type == "mark": - ohlcv1["volume"] = 0.0 - else: - ohlcv1 = dh1.ohlcv_load( - "UNITTEST/NEW", timeframe, timerange=timerange, candle_type=candle_type - ) + ohlcv1 = dh1.ohlcv_load("UNITTEST/NEW", timeframe, timerange=timerange, candle_type=candle_type) assert len(ohlcv) == len(ohlcv1) assert ohlcv.equals(ohlcv1) @@ -480,30 +398,12 @@ def test_generic_datahandler_ohlcv_load_and_resave( "freqtrade.data.history.datahandlers.parquetdatahandler.read_parquet", side_effect=Exception("Test"), ) - mocker.patch( - "freqtrade.data.history.datahandlers.hdf5datahandler.pd.read_hdf", - side_effect=Exception("Test"), - ) ohlcv_e = dh1.ohlcv_load("UNITTEST/NEW", timeframe, candle_type=candle_type) assert ohlcv_e.empty assert log_has_re("Error loading data from", caplog) -def test_hdf5datahandler_ohlcv_purge(mocker, testdatadir): - mocker.patch.object(Path, "exists", MagicMock(return_value=False)) - unlinkmock = mocker.patch.object(Path, "unlink", MagicMock()) - dh = get_datahandler(testdatadir, "hdf5") - assert not dh.ohlcv_purge("UNITTEST/NONEXIST", "5m", "") - assert not dh.ohlcv_purge("UNITTEST/NONEXIST", "5m", candle_type="mark") - assert unlinkmock.call_count == 0 - - mocker.patch.object(Path, "exists", MagicMock(return_value=True)) - assert dh.ohlcv_purge("UNITTEST/NONEXIST", "5m", "") - assert dh.ohlcv_purge("UNITTEST/NONEXIST", "5m", candle_type="mark") - assert unlinkmock.call_count == 2 - - -@pytest.mark.parametrize("datahandler", ["jsongz", "hdf5", "feather", "parquet"]) +@pytest.mark.parametrize("datahandler", ["jsongz", "feather", "parquet"]) def test_datahandler_trades_load(testdatadir, datahandler): dh = get_datahandler(testdatadir, datahandler) trades = dh.trades_load("XRP/ETH", TradingMode.SPOT) @@ -517,7 +417,7 @@ def test_datahandler_trades_load(testdatadir, datahandler): assert trades1.empty -@pytest.mark.parametrize("datahandler", ["jsongz", "hdf5", "feather", "parquet"]) +@pytest.mark.parametrize("datahandler", ["jsongz", "feather", "parquet"]) def test_datahandler_trades_store(testdatadir, tmp_path, datahandler): dh = get_datahandler(testdatadir, datahandler) trades = dh.trades_load("XRP/ETH", TradingMode.SPOT) @@ -533,7 +433,7 @@ def test_datahandler_trades_store(testdatadir, tmp_path, datahandler): assert len(trades_new) == len(trades) -@pytest.mark.parametrize("datahandler", ["jsongz", "hdf5", "feather", "parquet"]) +@pytest.mark.parametrize("datahandler", ["jsongz", "feather", "parquet"]) def test_datahandler_trades_purge(mocker, testdatadir, datahandler): mocker.patch.object(Path, "exists", MagicMock(return_value=False)) unlinkmock = mocker.patch.object(Path, "unlink", MagicMock()) @@ -557,8 +457,6 @@ def test_datahandler_trades_get_available_data(testdatadir): paircombs = JsonGzDataHandler.trades_get_available_data(testdatadir, TradingMode.SPOT) assert set(paircombs) == {"XRP/ETH", "XRP/OLD"} - paircombs = HDF5DataHandler.trades_get_available_data(testdatadir, TradingMode.SPOT) - assert set(paircombs) == {"XRP/ETH"} def test_datahandler_trades_data_min_max(testdatadir): @@ -589,10 +487,6 @@ def test_gethandlerclass(): assert issubclass(cl, IDataHandler) assert issubclass(cl, JsonDataHandler) - cl = get_datahandlerclass("hdf5") - assert cl == HDF5DataHandler - assert issubclass(cl, IDataHandler) - cl = get_datahandlerclass("feather") assert cl == FeatherDataHandler assert issubclass(cl, IDataHandler) @@ -612,6 +506,3 @@ def test_get_datahandler(testdatadir): assert isinstance(dh, JsonGzDataHandler) dh1 = get_datahandler(testdatadir, "jsongz", dh) assert id(dh1) == id(dh) - - dh = get_datahandler(testdatadir, "hdf5") - assert isinstance(dh, HDF5DataHandler) diff --git a/tests/data/test_dataprovider.py b/tests/data/test_dataprovider.py index 220aafef0..bef894eff 100644 --- a/tests/data/test_dataprovider.py +++ b/tests/data/test_dataprovider.py @@ -92,11 +92,11 @@ def test_historic_trades(mocker, default_conf, trades_history_df): def test_historic_ohlcv_dataformat(mocker, default_conf, ohlcv_history): - hdf5loadmock = MagicMock(return_value=ohlcv_history) + parquetloadmock = MagicMock(return_value=ohlcv_history) featherloadmock = MagicMock(return_value=ohlcv_history) mocker.patch( - "freqtrade.data.history.datahandlers.hdf5datahandler.HDF5DataHandler._ohlcv_load", - hdf5loadmock, + "freqtrade.data.history.datahandlers.parquetdatahandler.ParquetDataHandler._ohlcv_load", + parquetloadmock, ) mocker.patch( "freqtrade.data.history.datahandlers.featherdatahandler.FeatherDataHandler._ohlcv_load", @@ -108,17 +108,17 @@ def test_historic_ohlcv_dataformat(mocker, default_conf, ohlcv_history): dp = DataProvider(default_conf, exchange) data = dp.historic_ohlcv("UNITTEST/BTC", "5m") assert isinstance(data, DataFrame) - hdf5loadmock.assert_not_called() + parquetloadmock.assert_not_called() featherloadmock.assert_called_once() - # Switching to dataformat hdf5 - hdf5loadmock.reset_mock() + # Switching to dataformat parquet + parquetloadmock.reset_mock() featherloadmock.reset_mock() - default_conf["dataformat_ohlcv"] = "hdf5" + default_conf["dataformat_ohlcv"] = "parquet" dp = DataProvider(default_conf, exchange) data = dp.historic_ohlcv("UNITTEST/BTC", "5m") assert isinstance(data, DataFrame) - hdf5loadmock.assert_called_once() + parquetloadmock.assert_called_once() featherloadmock.assert_not_called() diff --git a/tests/data/test_download_data.py b/tests/data/test_download_data.py index ef1d938d3..16a3f5559 100644 --- a/tests/data/test_download_data.py +++ b/tests/data/test_download_data.py @@ -102,3 +102,16 @@ def test_download_data_main_data_invalid(mocker): ) with pytest.raises(OperationalException, match=r"Historic klines not available for .*"): download_data_main(config) + + patch_exchange(mocker, exchange="hyperliquid") + mocker.patch(f"{EXMS}.get_markets", return_value={"ETH/USDC": {}}) + config2 = setup_utils_configuration({"exchange": "hyperliquid"}, RunMode.UTIL_EXCHANGE) + config2.update( + { + "days": 20, + "pairs": ["ETH/USDC", "XRP/USDC"], + "timeframes": ["5m", "1h"], + } + ) + with pytest.raises(OperationalException, match=r"Historic data not available for .*"): + download_data_main(config2) diff --git a/tests/data/test_history.py b/tests/data/test_history.py index 1b865c963..e9af7b226 100644 --- a/tests/data/test_history.py +++ b/tests/data/test_history.py @@ -64,7 +64,7 @@ def test_load_data_30min_timeframe(caplog, testdatadir) -> None: ld = load_pair_history(pair="UNITTEST/BTC", timeframe="30m", datadir=testdatadir) assert isinstance(ld, DataFrame) assert not log_has( - 'Download history data for pair: "UNITTEST/BTC", timeframe: 30m ' "and store in None.", + 'Download history data for pair: "UNITTEST/BTC", timeframe: 30m and store in None.', caplog, ) @@ -86,7 +86,7 @@ def test_load_data_1min_timeframe(ohlcv_history, mocker, caplog, testdatadir) -> load_data(datadir=testdatadir, timeframe="1m", pairs=["UNITTEST/BTC"]) assert file.is_file() assert not log_has( - 'Download history data for pair: "UNITTEST/BTC", interval: 1m ' "and store in None.", caplog + 'Download history data for pair: "UNITTEST/BTC", interval: 1m and store in None.', caplog ) @@ -96,7 +96,7 @@ def test_load_data_mark(ohlcv_history, mocker, caplog, testdatadir) -> None: load_data(datadir=testdatadir, timeframe="1h", pairs=["UNITTEST/BTC"], candle_type="mark") assert file.is_file() assert not log_has( - 'Download history data for pair: "UNITTEST/USDT:USDT", interval: 1m ' "and store in None.", + 'Download history data for pair: "UNITTEST/USDT:USDT", interval: 1m and store in None.', caplog, ) diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py index 3cd668074..fc683e056 100644 --- a/tests/exchange/test_binance.py +++ b/tests/exchange/test_binance.py @@ -45,7 +45,7 @@ def test__get_params_binance(default_conf, mocker, side, order_type, time_in_for ) def test_create_stoploss_order_binance(default_conf, mocker, limitratio, expected, side, trademode): api_mock = MagicMock() - order_id = f"test_prod_buy_{randint(0, 10 ** 6)}" + order_id = f"test_prod_buy_{randint(0, 10**6)}" order_type = "stop_loss_limit" if trademode == TradingMode.SPOT else "stop" api_mock.create_order = MagicMock(return_value={"id": order_id, "info": {"foo": "bar"}}) @@ -1053,3 +1053,6 @@ async def test__async_get_trade_history_id_binance(default_conf_usdt, mocker, fe assert fetch_trades_cal[2][1]["params"][pagination_arg] != "0" assert fetch_trades_cal[3][1]["params"][pagination_arg] != "0" + + # Clean up event loop to avoid warnings + exchange.close() diff --git a/tests/exchange/test_binance_public_data.py b/tests/exchange/test_binance_public_data.py index 6eb5a00bb..655a0c874 100644 --- a/tests/exchange/test_binance_public_data.py +++ b/tests/exchange/test_binance_public_data.py @@ -294,7 +294,7 @@ async def test_get_daily_ohlcv(mocker, testdatadir): "freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get", return_value=MockResponse(spot_path.read_bytes(), 200), ) - df = await get_daily_ohlcv("spot", symbol, timeframe, date, session) + df = await get_daily_ohlcv(symbol, timeframe, CandleType.SPOT, date, session) assert get.call_count == 1 assert df["date"].iloc[0] == first_date assert df["date"].iloc[-1] == last_date @@ -306,7 +306,7 @@ async def test_get_daily_ohlcv(mocker, testdatadir): "freqtrade.exchange.binance_public_data.aiohttp.ClientSession.get", return_value=MockResponse(futures_path.read_bytes(), 200), ) - df = await get_daily_ohlcv("futures/um", symbol, timeframe, date, session) + df = await get_daily_ohlcv(symbol, timeframe, CandleType.FUTURES, date, session) assert get.call_count == 1 assert df["date"].iloc[0] == first_date assert df["date"].iloc[-1] == last_date @@ -316,7 +316,9 @@ async def test_get_daily_ohlcv(mocker, testdatadir): return_value=MockResponse(b"", 404), ) with pytest.raises(Http404): - df = await get_daily_ohlcv("spot", symbol, timeframe, date, session, retry_delay=0) + df = await get_daily_ohlcv( + symbol, timeframe, CandleType.SPOT, date, session, retry_delay=0 + ) assert get.call_count == 1 get = mocker.patch( @@ -325,7 +327,7 @@ async def test_get_daily_ohlcv(mocker, testdatadir): ) mocker.patch("asyncio.sleep") with pytest.raises(BadHttpStatus): - df = await get_daily_ohlcv("spot", symbol, timeframe, date, session) + df = await get_daily_ohlcv(symbol, timeframe, CandleType.SPOT, date, session) assert get.call_count == 4 # 1 + 3 default retries get = mocker.patch( @@ -333,5 +335,5 @@ async def test_get_daily_ohlcv(mocker, testdatadir): return_value=MockResponse(b"nop", 200), ) with pytest.raises(zipfile.BadZipFile): - df = await get_daily_ohlcv("spot", symbol, timeframe, date, session) + df = await get_daily_ohlcv(symbol, timeframe, CandleType.SPOT, date, session) assert get.call_count == 4 # 1 + 3 default retries diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 260dd8f0e..58abb141e 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -645,7 +645,7 @@ def test_reload_markets(default_conf, mocker, caplog, time_machine): # Tried once, failed lam_spy.reset_mock() - # When forceing (bot startup), it should retry 3 times. + # When forcing (bot startup), it should retry 3 times. exchange.reload_markets(force=True) assert lam_spy.call_count == 4 assert exchange.markets == updated_markets @@ -1260,7 +1260,7 @@ def test_create_dry_run_order_market_fill( @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_create_order(default_conf, mocker, side, ordertype, rate, marketprice, exchange_name): api_mock = MagicMock() - order_id = f"test_prod_{side}_{randint(0, 10 ** 6)}" + order_id = f"test_prod_{side}_{randint(0, 10**6)}" api_mock.options = {} if not marketprice else {"createMarketBuyOrderRequiresPrice": True} api_mock.create_order = MagicMock( return_value={"id": order_id, "info": {"foo": "bar"}, "symbol": "XLTCUSDT", "amount": 1} @@ -1339,7 +1339,7 @@ def test_buy_dry_run(default_conf, mocker, exchange_name): @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_buy_prod(default_conf, mocker, exchange_name): api_mock = MagicMock() - order_id = f"test_prod_buy_{randint(0, 10 ** 6)}" + order_id = f"test_prod_buy_{randint(0, 10**6)}" order_type = "market" time_in_force = "gtc" api_mock.options = {} @@ -1460,7 +1460,7 @@ def test_buy_prod(default_conf, mocker, exchange_name): @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_buy_considers_time_in_force(default_conf, mocker, exchange_name): api_mock = MagicMock() - order_id = f"test_prod_buy_{randint(0, 10 ** 6)}" + order_id = f"test_prod_buy_{randint(0, 10**6)}" api_mock.options = {} api_mock.create_order = MagicMock( return_value={"id": order_id, "symbol": "ETH/BTC", "info": {"foo": "bar"}} @@ -1537,7 +1537,7 @@ def test_sell_dry_run(default_conf, mocker): @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_sell_prod(default_conf, mocker, exchange_name): api_mock = MagicMock() - order_id = f"test_prod_sell_{randint(0, 10 ** 6)}" + order_id = f"test_prod_sell_{randint(0, 10**6)}" order_type = "market" api_mock.options = {} api_mock.create_order = MagicMock( @@ -1617,7 +1617,7 @@ def test_sell_prod(default_conf, mocker, exchange_name): @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_sell_considers_time_in_force(default_conf, mocker, exchange_name): api_mock = MagicMock() - order_id = f"test_prod_sell_{randint(0, 10 ** 6)}" + order_id = f"test_prod_sell_{randint(0, 10**6)}" api_mock.create_order = MagicMock( return_value={"id": order_id, "symbol": "ETH/BTC", "info": {"foo": "bar"}} ) @@ -1686,6 +1686,7 @@ def test_get_balances_prod(default_conf, mocker, exchange_name): api_mock.fetch_balance = MagicMock( return_value={"1ST": balance_item, "2ND": balance_item, "3RD": balance_item} ) + api_mock.commonCurrencies = {} default_conf["dry_run"] = False exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange=exchange_name) assert len(exchange.get_balances()) == 3 @@ -2028,6 +2029,7 @@ def test_get_conversion_rate(default_conf_usdt, mocker, exchange_name): mocker.patch(f"{EXMS}.exchange_has", return_value=True) api_mock.fetch_tickers = MagicMock(side_effect=[tick, tick2]) api_mock.fetch_bids_asks = MagicMock(return_value={}) + default_conf_usdt["trading_mode"] = "futures" exchange = get_patched_exchange(mocker, default_conf_usdt, api_mock, exchange=exchange_name) # retrieve original ticker @@ -2045,6 +2047,13 @@ def test_get_conversion_rate(default_conf_usdt, mocker, exchange_name): # Only the call to the "others" market assert api_mock.fetch_tickers.call_count == 1 + if exchange_name == "binance": + # Special binance case of BNFCR matching USDT. + assert exchange.get_conversion_rate("BNFCR", "USDT") is None + assert exchange.get_conversion_rate("BNFCR", "USDC") == 1 + assert exchange.get_conversion_rate("USDT", "BNFCR") is None + assert exchange.get_conversion_rate("USDC", "BNFCR") == 1 + @pytest.mark.parametrize("exchange_name", EXCHANGES) def test_fetch_ticker(default_conf, mocker, exchange_name): @@ -2112,7 +2121,7 @@ def test___now_is_time_to_refresh(default_conf, mocker, exchange_name, time_mach # not refreshed yet assert exchange._now_is_time_to_refresh(pair, "5m", candle_type) is True - last_closed_candle = (start_dt - timedelta(minutes=5)).timestamp() + last_closed_candle = dt_ts(start_dt - timedelta(minutes=5)) exchange._pairs_last_refresh_time[(pair, "5m", candle_type)] = last_closed_candle # next candle not closed yet @@ -2515,7 +2524,7 @@ def test_refresh_latest_ohlcv_cache(mocker, default_conf, candle_type, time_mach assert len(res[pair1]) == 99 assert len(res[pair2]) == 99 assert exchange._klines - assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-2][0] // 1000 + assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-2][0] exchange._api_async.fetch_ohlcv.reset_mock() # Returned from cache @@ -2524,7 +2533,7 @@ def test_refresh_latest_ohlcv_cache(mocker, default_conf, candle_type, time_mach assert len(res) == 2 assert len(res[pair1]) == 99 assert len(res[pair2]) == 99 - assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-2][0] // 1000 + assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-2][0] # Move time 1 candle further but result didn't change yet time_machine.move_to(start + timedelta(hours=101)) @@ -2534,7 +2543,7 @@ def test_refresh_latest_ohlcv_cache(mocker, default_conf, candle_type, time_mach assert len(res[pair1]) == 99 assert len(res[pair2]) == 99 assert res[pair2].at[0, "open"] - assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-2][0] // 1000 + assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-2][0] refresh_pior = exchange._pairs_last_refresh_time[pair1] # New candle on exchange - return 100 candles - but skip one candle so we actually get 2 candles @@ -2552,8 +2561,8 @@ def test_refresh_latest_ohlcv_cache(mocker, default_conf, candle_type, time_mach assert res[pair2].at[0, "open"] assert refresh_pior != exchange._pairs_last_refresh_time[pair1] - assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-2][0] // 1000 - assert exchange._pairs_last_refresh_time[pair2] == ohlcv[-2][0] // 1000 + assert exchange._pairs_last_refresh_time[pair1] == ohlcv[-2][0] + assert exchange._pairs_last_refresh_time[pair2] == ohlcv[-2][0] exchange._api_async.fetch_ohlcv.reset_mock() # Retry same call - from cache @@ -4427,7 +4436,7 @@ def test_ohlcv_candle_limit(default_conf, mocker, exchange_name): pytest.skip("Tested separately for okx") exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name) timeframes = ("1m", "5m", "1h") - expected = exchange._ft_has["ohlcv_candle_limit"] + expected = exchange._ft_has.get("ohlcv_candle_limit", 500) for timeframe in timeframes: # if 'ohlcv_candle_limit_per_timeframe' in exchange._ft_has: # expected = exchange._ft_has['ohlcv_candle_limit_per_timeframe'][timeframe] @@ -4721,7 +4730,7 @@ def test_extract_cost_curr_rate(mocker, default_conf, order, expected) -> None: ], ) def test_calculate_fee_rate(mocker, default_conf, order, expected, unknown_fee_rate) -> None: - mocker.patch(f"{EXMS}.fetch_ticker", return_value={"last": 0.081}) + mocker.patch(f"{EXMS}.get_tickers", return_value={"NEO/BTC": {"last": 0.081}}) if unknown_fee_rate: default_conf["exchange"]["unknown_fee_rate"] = unknown_fee_rate @@ -4898,7 +4907,7 @@ def test_set_margin_mode(mocker, default_conf, margin_mode): ("okx", TradingMode.FUTURES, MarginMode.ISOLATED, False), # * Remove once implemented ("binance", TradingMode.MARGIN, MarginMode.CROSS, True), - ("binance", TradingMode.FUTURES, MarginMode.CROSS, True), + ("binance", TradingMode.FUTURES, MarginMode.CROSS, False), ("kraken", TradingMode.MARGIN, MarginMode.CROSS, True), ("kraken", TradingMode.FUTURES, MarginMode.CROSS, True), ("gate", TradingMode.MARGIN, MarginMode.CROSS, True), @@ -6212,7 +6221,7 @@ def test_get_liquidation_price( ) def test_stoploss_contract_size(mocker, default_conf, contract_size, order_amount): api_mock = MagicMock() - order_id = f"test_prod_buy_{randint(0, 10 ** 6)}" + order_id = f"test_prod_buy_{randint(0, 10**6)}" api_mock.create_order = MagicMock( return_value={ @@ -6250,3 +6259,26 @@ def test_price_to_precision_with_default_conf(default_conf, mocker): prec_price = patched_ex.price_to_precision("XRP/USDT", 1.0000000101) assert prec_price == 1.00000001 assert prec_price == 1.00000001 + + +def test_exchange_features(default_conf, mocker): + conf = copy.deepcopy(default_conf) + exchange = get_patched_exchange(mocker, conf) + exchange._api_async.features = { + "spot": { + "fetchOHLCV": { + "limit": 995, + } + }, + "swap": { + "linear": { + "fetchOHLCV": { + "limit": 997, + } + } + }, + } + assert exchange.features("spot", "fetchOHLCV", "limit", 500) == 995 + assert exchange.features("futures", "fetchOHLCV", "limit", 500) == 997 + # Fall back to default + assert exchange.features("futures", "fetchOHLCV_else", "limit", 601) == 601 diff --git a/tests/exchange/test_exchange_ws.py b/tests/exchange/test_exchange_ws.py index 8b97ae1b8..6dee7f339 100644 --- a/tests/exchange/test_exchange_ws.py +++ b/tests/exchange/test_exchange_ws.py @@ -1,10 +1,15 @@ import asyncio +import logging import threading +from datetime import timedelta from time import sleep from unittest.mock import AsyncMock, MagicMock +from ccxt import NotSupported + from freqtrade.enums import CandleType from freqtrade.exchange.exchange_ws import ExchangeWS +from ft_client.test_client.test_rest_client import log_has_re def test_exchangews_init(mocker): @@ -16,7 +21,7 @@ def test_exchangews_init(mocker): sleep(0.1) assert exchange_ws.config == config - assert exchange_ws.ccxt_object == ccxt_object + assert exchange_ws._ccxt_object == ccxt_object assert exchange_ws._thread.name == "ccxt_ws" assert exchange_ws._background_tasks == set() assert exchange_ws._klines_watching == set() @@ -27,6 +32,23 @@ def test_exchangews_init(mocker): exchange_ws.cleanup() +def test_exchangews_cleanup_error(mocker, caplog): + config = MagicMock() + ccxt_object = MagicMock() + ccxt_object.close = AsyncMock(side_effect=Exception("Test")) + mocker.patch("freqtrade.exchange.exchange_ws.ExchangeWS._start_forever", MagicMock()) + + exchange_ws = ExchangeWS(config, ccxt_object) + patch_eventloop_threading(exchange_ws) + + sleep(0.1) + exchange_ws.reset_connections() + + assert log_has_re("Exception in _cleanup_async", caplog) + + exchange_ws.cleanup() + + def patch_eventloop_threading(exchange): is_init = False @@ -42,11 +64,22 @@ def patch_eventloop_threading(exchange): pass -async def test_exchangews_ohlcv(mocker): +async def test_exchangews_ohlcv(mocker, time_machine, caplog): config = MagicMock() ccxt_object = MagicMock() - ccxt_object.watch_ohlcv = AsyncMock() + caplog.set_level(logging.DEBUG) + + async def sleeper(*args, **kwargs): + # pass + await asyncio.sleep(0.12) + return MagicMock() + + ccxt_object.un_watch_ohlcv_for_symbols = AsyncMock(side_effect=NotSupported) + + ccxt_object.watch_ohlcv = AsyncMock(side_effect=sleeper) ccxt_object.close = AsyncMock() + time_machine.move_to("2024-11-01 01:00:02 +00:00") + mocker.patch("freqtrade.exchange.exchange_ws.ExchangeWS._start_forever", MagicMock()) exchange_ws = ExchangeWS(config, ccxt_object) @@ -56,14 +89,111 @@ async def test_exchangews_ohlcv(mocker): assert exchange_ws._klines_scheduled == set() exchange_ws.schedule_ohlcv("ETH/BTC", "1m", CandleType.SPOT) - await asyncio.sleep(0.5) + exchange_ws.schedule_ohlcv("XRP/BTC", "1m", CandleType.SPOT) + await asyncio.sleep(0.2) - assert exchange_ws._klines_watching == {("ETH/BTC", "1m", CandleType.SPOT)} - assert exchange_ws._klines_scheduled == {("ETH/BTC", "1m", CandleType.SPOT)} + assert exchange_ws._klines_watching == { + ("ETH/BTC", "1m", CandleType.SPOT), + ("XRP/BTC", "1m", CandleType.SPOT), + } + assert exchange_ws._klines_scheduled == { + ("ETH/BTC", "1m", CandleType.SPOT), + ("XRP/BTC", "1m", CandleType.SPOT), + } await asyncio.sleep(0.1) - assert ccxt_object.watch_ohlcv.call_count == 1 - except Exception as e: - print(e) + assert ccxt_object.watch_ohlcv.call_count == 6 + ccxt_object.watch_ohlcv.reset_mock() + + time_machine.shift(timedelta(minutes=5)) + exchange_ws.schedule_ohlcv("ETH/BTC", "1m", CandleType.SPOT) + await asyncio.sleep(1) + assert log_has_re("un_watch_ohlcv_for_symbols not supported: ", caplog) + # XRP/BTC should be cleaned up. + assert exchange_ws._klines_watching == { + ("ETH/BTC", "1m", CandleType.SPOT), + } + + # Cleanup happened. + ccxt_object.un_watch_ohlcv_for_symbols = AsyncMock(side_effect=ValueError) + exchange_ws.schedule_ohlcv("ETH/BTC", "1m", CandleType.SPOT) + assert exchange_ws._klines_watching == { + ("ETH/BTC", "1m", CandleType.SPOT), + } + assert exchange_ws._klines_scheduled == { + ("ETH/BTC", "1m", CandleType.SPOT), + } + finally: # Cleanup exchange_ws.cleanup() + assert log_has_re("Exception in _unwatch_ohlcv", caplog) + + +async def test_exchangews_get_ohlcv(mocker, caplog): + config = MagicMock() + ccxt_object = MagicMock() + ccxt_object.ohlcvs = { + "ETH/USDT": { + "1m": [ + [1635840000000, 100, 200, 300, 400, 500], + [1635840060000, 101, 201, 301, 401, 501], + [1635840120000, 102, 202, 302, 402, 502], + ], + "5m": [ + [1635840000000, 100, 200, 300, 400, 500], + [1635840300000, 105, 201, 301, 401, 501], + [1635840600000, 102, 202, 302, 402, 502], + ], + } + } + mocker.patch("freqtrade.exchange.exchange_ws.ExchangeWS._start_forever", MagicMock()) + + exchange_ws = ExchangeWS(config, ccxt_object) + exchange_ws.klines_last_refresh = { + ("ETH/USDT", "1m", CandleType.SPOT): 1635840120000, + ("ETH/USDT", "5m", CandleType.SPOT): 1635840600000, + } + + # Matching last candle time - drop hint is true + resp = await exchange_ws.get_ohlcv("ETH/USDT", "1m", CandleType.SPOT, 1635840120000) + assert resp[0] == "ETH/USDT" + assert resp[1] == "1m" + assert resp[3] == [ + [1635840000000, 100, 200, 300, 400, 500], + [1635840060000, 101, 201, 301, 401, 501], + [1635840120000, 102, 202, 302, 402, 502], + ] + assert resp[4] is True + + # expected time > last candle time - drop hint is false + resp = await exchange_ws.get_ohlcv("ETH/USDT", "1m", CandleType.SPOT, 1635840180000) + assert resp[0] == "ETH/USDT" + assert resp[1] == "1m" + assert resp[3] == [ + [1635840000000, 100, 200, 300, 400, 500], + [1635840060000, 101, 201, 301, 401, 501], + [1635840120000, 102, 202, 302, 402, 502], + ] + assert resp[4] is False + + # Change "received" times to be before the candle starts. + # This should trigger the "time sync" warning. + exchange_ws.klines_last_refresh = { + ("ETH/USDT", "1m", CandleType.SPOT): 1635840110000, + ("ETH/USDT", "5m", CandleType.SPOT): 1635840600000, + } + msg = r".*Candle date > last refresh.*" + assert not log_has_re(msg, caplog) + resp = await exchange_ws.get_ohlcv("ETH/USDT", "1m", CandleType.SPOT, 1635840120000) + assert resp[0] == "ETH/USDT" + assert resp[1] == "1m" + assert resp[3] == [ + [1635840000000, 100, 200, 300, 400, 500], + [1635840060000, 101, 201, 301, 401, 501], + [1635840120000, 102, 202, 302, 402, 502], + ] + assert resp[4] is True + + assert log_has_re(msg, caplog) + + exchange_ws.cleanup() diff --git a/tests/exchange/test_htx.py b/tests/exchange/test_htx.py index 03099c3de..e32d7e85e 100644 --- a/tests/exchange/test_htx.py +++ b/tests/exchange/test_htx.py @@ -19,7 +19,7 @@ from tests.exchange.test_exchange import ccxt_exceptionhandlers ) def test_create_stoploss_order_htx(default_conf, mocker, limitratio, expected, side): api_mock = MagicMock() - order_id = f"test_prod_buy_{randint(0, 10 ** 6)}" + order_id = f"test_prod_buy_{randint(0, 10**6)}" order_type = "stop-limit" api_mock.create_order = MagicMock(return_value={"id": order_id, "info": {"foo": "bar"}}) diff --git a/tests/exchange/test_kraken.py b/tests/exchange/test_kraken.py index c3a3b1b93..5cfdac15f 100644 --- a/tests/exchange/test_kraken.py +++ b/tests/exchange/test_kraken.py @@ -23,7 +23,7 @@ STOPLOSS_LIMIT_ORDERTYPE = "stop-loss-limit" ) def test_kraken_trading_agreement(default_conf, mocker, order_type, time_in_force, expected_params): api_mock = MagicMock() - order_id = f"test_prod_{order_type}_{randint(0, 10 ** 6)}" + order_id = f"test_prod_{order_type}_{randint(0, 10**6)}" api_mock.options = {} api_mock.create_order = MagicMock( return_value={"id": order_id, "symbol": "ETH/BTC", "info": {"foo": "bar"}} @@ -58,8 +58,10 @@ def test_kraken_trading_agreement(default_conf, mocker, order_type, time_in_forc def test_get_balances_prod_kraken(default_conf, mocker): balance_item = {"free": 0.0, "total": 10.0, "used": 0.0} + kraken = ccxt.kraken() api_mock = MagicMock() + api_mock.commonCurrencies = kraken.commonCurrencies api_mock.fetch_balance = MagicMock( return_value={ "1ST": {"free": 0.0, "total": 0.0, "used": 0.0}, @@ -68,6 +70,8 @@ def test_get_balances_prod_kraken(default_conf, mocker): "3RD": balance_item.copy(), "4TH": balance_item.copy(), "EUR": balance_item.copy(), + "BTC": {"free": 0.0, "total": 0.0, "used": 0.0}, + "XBT.F": balance_item.copy(), "timestamp": 123123, } ) @@ -124,7 +128,7 @@ def test_get_balances_prod_kraken(default_conf, mocker): default_conf["dry_run"] = False exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="kraken") balances = exchange.get_balances() - assert len(balances) == 6 + assert len(balances) == 7 assert balances["1ST"]["free"] == 9.0 assert balances["1ST"]["total"] == 10.0 @@ -145,6 +149,10 @@ def test_get_balances_prod_kraken(default_conf, mocker): assert balances["EUR"]["free"] == 8.0 assert balances["EUR"]["total"] == 10.0 assert balances["EUR"]["used"] == 2.0 + + assert balances["BTC"]["free"] == 10.0 + assert balances["BTC"]["total"] == 10.0 + assert balances["BTC"]["used"] == 0.0 ccxt_exceptionhandlers( mocker, default_conf, api_mock, "kraken", "get_balances", "fetch_balance" ) @@ -160,7 +168,7 @@ def test_get_balances_prod_kraken(default_conf, mocker): ) def test_create_stoploss_order_kraken(default_conf, mocker, ordertype, side, adjustedprice): api_mock = MagicMock() - order_id = f"test_prod_buy_{randint(0, 10 ** 6)}" + order_id = f"test_prod_buy_{randint(0, 10**6)}" api_mock.create_order = MagicMock(return_value={"id": order_id, "info": {"foo": "bar"}}) diff --git a/tests/exchange/test_kucoin.py b/tests/exchange/test_kucoin.py index c2d245927..cfddfa5e9 100644 --- a/tests/exchange/test_kucoin.py +++ b/tests/exchange/test_kucoin.py @@ -20,7 +20,7 @@ from tests.exchange.test_exchange import ccxt_exceptionhandlers ) def test_create_stoploss_order_kucoin(default_conf, mocker, limitratio, expected, side, order_type): api_mock = MagicMock() - order_id = f"test_prod_buy_{randint(0, 10 ** 6)}" + order_id = f"test_prod_buy_{randint(0, 10**6)}" api_mock.create_order = MagicMock(return_value={"id": order_id, "info": {"foo": "bar"}}) default_conf["dry_run"] = False @@ -154,7 +154,7 @@ def test_stoploss_adjust_kucoin(mocker, default_conf): ) def test_kucoin_create_order(default_conf, mocker, side, ordertype, rate): api_mock = MagicMock() - order_id = f"test_prod_{side}_{randint(0, 10 ** 6)}" + order_id = f"test_prod_{side}_{randint(0, 10**6)}" api_mock.create_order = MagicMock( return_value={"id": order_id, "info": {"foo": "bar"}, "symbol": "XRP/USDT", "amount": 1} ) diff --git a/tests/exchange_online/conftest.py b/tests/exchange_online/conftest.py index 867ef1501..8eedcfb01 100644 --- a/tests/exchange_online/conftest.py +++ b/tests/exchange_online/conftest.py @@ -21,6 +21,7 @@ EXCHANGES = { "use_ci_proxy": True, "hasQuoteVolume": True, "timeframe": "1h", + "candle_count": 1000, "futures": True, "futures_pair": "BTC/USDT:USDT", "hasQuoteVolumeFutures": True, @@ -96,6 +97,7 @@ EXCHANGES = { "stake_currency": "USDT", "hasQuoteVolume": True, "timeframe": "1h", + "candle_count": 1000, "futures": False, "skip_ws_tests": True, "sample_order": [ @@ -136,15 +138,34 @@ EXCHANGES = { "stake_currency": "USD", "hasQuoteVolume": True, "timeframe": "1h", + "candle_count": 720, "leverage_tiers_public": False, "leverage_in_spot_market": True, "trades_lookback_hours": 12, + "sample_balances": { + "exchange_response": { + "result": { + "ADA": {"balance": "0.00000000", "hold_trade": "0.00000000"}, + "ADA.F": {"balance": "2.00000000", "hold_trade": "0.00000000"}, + "XBT": {"balance": "0.00060000", "hold_trade": "0.00000000"}, + "XBT.F": {"balance": "0.00100000", "hold_trade": "0.00000000"}, + } + }, + "expected": { + "ADA": {"free": 0.0, "total": 0.0, "used": 0.0}, + "ADA.F": {"free": 2.0, "total": 2.0, "used": 0.0}, + "BTC": {"free": 0.0006, "total": 0.0006, "used": 0.0}, + # XBT.F should be mapped to BTC.F + "XBT.F": {"free": 0.001, "total": 0.001, "used": 0.0}, + }, + }, }, "kucoin": { "pair": "XRP/USDT", "stake_currency": "USDT", "hasQuoteVolume": True, "timeframe": "1h", + "candle_count": 1500, "leverage_tiers_public": False, "leverage_in_spot_market": True, "sample_order": [ @@ -212,6 +233,7 @@ EXCHANGES = { "stake_currency": "USDT", "hasQuoteVolume": True, "timeframe": "1h", + "candle_count": 1000, "futures": True, "futures_pair": "BTC/USDT:USDT", "hasQuoteVolumeFutures": True, @@ -328,6 +350,7 @@ EXCHANGES = { "stake_currency": "USDT", "hasQuoteVolume": True, "timeframe": "1h", + "candle_count": 300, "futures": True, "futures_pair": "BTC/USDT:USDT", "hasQuoteVolumeFutures": False, @@ -341,6 +364,7 @@ EXCHANGES = { "hasQuoteVolume": True, "use_ci_proxy": True, "timeframe": "1h", + "candle_count": 1000, "futures_pair": "BTC/USDT:USDT", "futures": True, "orderbook_max_entries": 50, @@ -381,6 +405,7 @@ EXCHANGES = { "stake_currency": "USDT", "hasQuoteVolume": True, "timeframe": "1h", + "candle_count": 200, "orderbook_max_entries": 50, }, "htx": { @@ -388,13 +413,14 @@ EXCHANGES = { "stake_currency": "BTC", "hasQuoteVolume": True, "timeframe": "1h", - "futures": False, + "candle_count": 1000, }, "bitvavo": { "pair": "BTC/EUR", "stake_currency": "EUR", "hasQuoteVolume": True, "timeframe": "1h", + "candle_count": 1440, "leverage_tiers_public": False, "leverage_in_spot_market": False, }, @@ -403,6 +429,7 @@ EXCHANGES = { "stake_currency": "USDT", "hasQuoteVolume": True, "timeframe": "1h", + "candle_count": 1000, "futures": False, "sample_order": [ { @@ -465,6 +492,7 @@ EXCHANGES = { "hasQuoteVolume": False, "timeframe": "1h", "futures": True, + "candle_count": 5000, "orderbook_max_entries": 20, "futures_pair": "BTC/USDC:USDC", "hasQuoteVolumeFutures": True, diff --git a/tests/exchange_online/test_ccxt_compat.py b/tests/exchange_online/test_ccxt_compat.py index 6ef0bc108..85de9b5ec 100644 --- a/tests/exchange_online/test_ccxt_compat.py +++ b/tests/exchange_online/test_ccxt_compat.py @@ -48,6 +48,22 @@ class TestCCXTExchange: } ) + def test_ohlcv_limit(self, exchange: EXCHANGE_FIXTURE_TYPE): + exch, exchangename = exchange + expected_count = EXCHANGES[exchangename].get("candle_count") + if not expected_count: + pytest.skip("No expected candle count for exchange") + + assert exch.ohlcv_candle_limit("1m", CandleType.SPOT) == expected_count + + def test_ohlcv_limit_futures(self, exchange_futures: EXCHANGE_FIXTURE_TYPE): + exch, exchangename = exchange_futures + expected_count = EXCHANGES[exchangename].get("candle_count") + if not expected_count: + pytest.skip("No expected candle count for exchange") + + assert exch.ohlcv_candle_limit("1m", CandleType.SPOT) == expected_count + def test_load_markets_futures(self, exchange_futures: EXCHANGE_FIXTURE_TYPE): exchange, exchangename = exchange_futures pair = EXCHANGES[exchangename]["pair"] @@ -114,6 +130,19 @@ class TestCCXTExchange: else: pytest.skip(f"No sample Trades available for exchange {exchange_name}") + def test_ccxt_balances_parse(self, exchange: EXCHANGE_FIXTURE_TYPE): + exch, exchange_name = exchange + if balance_response := EXCHANGES[exchange_name].get("sample_balances"): + balances = exch._api.parse_balance(balance_response["exchange_response"]) + expected = balance_response["expected"] + for currency, balance in expected.items(): + assert currency in balances + assert isinstance(balance, dict) + assert balance == balances[currency] + pass + else: + pytest.skip(f"No sample Balances available for exchange {exchange_name}") + def test_ccxt_fetch_tickers(self, exchange: EXCHANGE_FIXTURE_TYPE): exch, exchangename = exchange pair = EXCHANGES[exchangename]["pair"] @@ -262,9 +291,9 @@ class TestCCXTExchange: candles = res[3] candle_count = exchange.ohlcv_candle_limit(timeframe, candle_type, since_ms) * factor candle_count1 = (now.timestamp() * 1000 - since_ms) // timeframe_ms * factor - assert len(candles) >= min( - candle_count, candle_count1 - ), f"{len(candles)} < {candle_count} in {timeframe}, Offset: {offset} {factor}" + assert len(candles) >= min(candle_count, candle_count1), ( + f"{len(candles)} < {candle_count} in {timeframe}, Offset: {offset} {factor}" + ) # Check if first-timeframe is either the start, or start + 1 assert candles[0][0] == since_ms or (since_ms + timeframe_ms) diff --git a/tests/freqtradebot/test_freqtradebot.py b/tests/freqtradebot/test_freqtradebot.py index 937bec8e5..02ffcb2e4 100644 --- a/tests/freqtradebot/test_freqtradebot.py +++ b/tests/freqtradebot/test_freqtradebot.py @@ -374,7 +374,7 @@ def test_total_open_trades_stakes(mocker, default_conf_usdt, ticker_usdt, fee) - def test_create_trade( default_conf_usdt, ticker_usdt, limit_order, fee, mocker, is_short, open_rate ) -> None: - patch_RPCManager(mocker) + send_msg_mock = patch_RPCManager(mocker) patch_exchange(mocker) mocker.patch.multiple( EXMS, @@ -387,6 +387,7 @@ def test_create_trade( whitelist = deepcopy(default_conf_usdt["exchange"]["pair_whitelist"]) freqtrade = FreqtradeBot(default_conf_usdt) patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short) + send_msg_mock.reset_mock() freqtrade.create_trade("ETH/USDT") trade = Trade.session.scalars(select(Trade)).first() @@ -402,6 +403,14 @@ def test_create_trade( limit_order[entry_side(is_short)], "ADA/USDT", entry_side(is_short) ) trade.update_trade(oobj) + assert send_msg_mock.call_count == 1 + entry_msg = send_msg_mock.call_args_list[0][0][0] + assert entry_msg["type"] == RPCMessageType.ENTRY + assert entry_msg["stake_amount"] == trade.stake_amount + assert entry_msg["stake_currency"] == default_conf_usdt["stake_currency"] + assert entry_msg["pair"] == "ETH/USDT" + assert entry_msg["direction"] == ("Short" if is_short else "Long") + assert entry_msg["sub_trade"] is False assert trade.open_rate == open_rate assert trade.amount == 30.0 @@ -693,7 +702,7 @@ def test_process_trade_creation( assert pytest.approx(trade.amount_requested) == 60 / ticker_usdt.return_value[ticker_side] assert log_has( - f'{"Short" if is_short else "Long"} signal found: about create a new trade for ETH/USDT ' + f"{'Short' if is_short else 'Long'} signal found: about create a new trade for ETH/USDT " "with stake_amount: 60.0 ...", caplog, ) @@ -1280,14 +1289,14 @@ def test_exit_positions(mocker, default_conf_usdt, limit_order, is_short, caplog trades = [trade] freqtrade.wallets.update() n = freqtrade.exit_positions(trades) - assert n == 0 + assert n == 1 # Test amount not modified by fee-logic assert not log_has_re(r"Applying fee to amount for Trade .*", caplog) gra = mocker.patch("freqtrade.freqtradebot.FreqtradeBot.get_real_amount", return_value=0.0) # test amount modified by fee-logic n = freqtrade.exit_positions(trades) - assert n == 0 + assert n == 1 assert gra.call_count == 0 @@ -1320,6 +1329,7 @@ def test_exit_positions_exception(mocker, default_conf_usdt, limit_order, caplog ft_price=trade.open_rate, order_id=order_id, ft_is_open=False, + filled=11, ) ) Trade.session.add(trade) @@ -2044,7 +2054,7 @@ def test_adjust_entry_replace_fail( assert len(trades) == 0 assert len(Order.session.scalars(select(Order)).all()) == 0 assert fetch_order_mock.call_count == 4 - assert log_has_re(r"Could not cancel order.*, therefore not replacing\.", caplog) + assert log_has_re(r"Could not fully cancel order.*, therefore not replacing\.", caplog) # Entry adjustment is called assert freqtrade.strategy.adjust_entry_price.call_count == 1 @@ -3971,7 +3981,7 @@ def test_get_real_amount_multi( markets["BNB/ETH"] = markets["ETH/USDT"] freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) mocker.patch(f"{EXMS}.markets", PropertyMock(return_value=markets)) - mocker.patch(f"{EXMS}.fetch_ticker", return_value={"ask": 0.19, "last": 0.2}) + mocker.patch(f"{EXMS}.get_conversion_rate", return_value=0.2) # Amount is reduced by "fee" expected_amount = amount * fee_reduction_amount @@ -5190,6 +5200,13 @@ def test_update_funding_fees( open_exit_order = limit_order_open[exit_side(is_short)] bid = 0.11 enter_rate_mock = MagicMock(return_value=bid) + open_order.update( + { + "status": "closed", + "filled": open_order["amount"], + "remaining": 0, + } + ) enter_mm = MagicMock(return_value=open_order) patch_RPCManager(mocker) patch_exchange(mocker) @@ -5941,13 +5958,13 @@ def test_check_and_call_adjust_trade_position(mocker, default_conf_usdt, fee, ca freqtrade.strategy.adjust_trade_position = MagicMock(return_value=(10, "aaaa")) freqtrade.process_open_trade_positions() assert log_has_re(r"Max adjustment entries for .* has been reached\.", caplog) - assert freqtrade.strategy.adjust_trade_position.call_count == 1 + assert freqtrade.strategy.adjust_trade_position.call_count == 4 caplog.clear() freqtrade.strategy.adjust_trade_position = MagicMock(return_value=(-0.0005, "partial_exit_c")) freqtrade.process_open_trade_positions() assert log_has_re(r"LIMIT_SELL has been fulfilled.*", caplog) - assert freqtrade.strategy.adjust_trade_position.call_count == 1 + assert freqtrade.strategy.adjust_trade_position.call_count == 4 trade = Trade.get_trades(trade_filter=[Trade.id == 5]).first() assert trade.orders[-1].ft_order_tag == "partial_exit_c" assert trade.is_open diff --git a/tests/freqtradebot/test_integration.py b/tests/freqtradebot/test_integration.py index 75cc81fa1..9fc580753 100644 --- a/tests/freqtradebot/test_integration.py +++ b/tests/freqtradebot/test_integration.py @@ -436,7 +436,7 @@ def test_dca_order_adjust(default_conf_usdt, ticker_usdt, leverage, fee, mocker) # Replace new order with diff. order at a lower price freqtrade.strategy.adjust_entry_price = MagicMock(return_value=1.95) - + freqtrade.strategy.adjust_trade_position = MagicMock(return_value=None) freqtrade.process() trade = Trade.get_trades().first() assert len(trade.orders) == 4 @@ -478,10 +478,14 @@ def test_dca_order_adjust(default_conf_usdt, ticker_usdt, leverage, fee, mocker) assert pytest.approx(trade.amount) == 91.689215 * leverage assert pytest.approx(trade.orders[-1].amount) == 91.689215 * leverage assert freqtrade.strategy.adjust_entry_price.call_count == 0 + # Process again, should not adjust entry price freqtrade.process() trade = Trade.get_trades().first() + + assert trade.orders[-2].status == "closed" assert len(trade.orders) == 5 + assert trade.orders[-1].side == trade.exit_side assert trade.orders[-1].status == "open" assert trade.orders[-1].price == 2.02 # Adjust entry price cannot be called - this is an exit order @@ -532,7 +536,7 @@ def test_dca_order_adjust_entry_replace_fails( freqtrade.process() - assert freqtrade.strategy.adjust_trade_position.call_count == 1 + assert freqtrade.strategy.adjust_trade_position.call_count == 2 trades = Trade.session.scalars( select(Trade) .where(Order.ft_is_open.is_(True)) @@ -677,7 +681,11 @@ def test_dca_exiting(default_conf_usdt, ticker_usdt, fee, mocker, caplog, levera assert trade.orders[-1].ft_order_side == "sell" assert pytest.approx(trade.stake_amount) == 40 assert trade.is_open is False - assert log_has_re("Amount to exit is 0.0 due to exchange limits - not exiting.", caplog) + assert log_has_re( + "Wanted to exit of -0.01 amount, but exit amount is now 0.0 due to exchange limits " + "- not exiting.", + caplog, + ) expected_profit = starting_amount - 60 + trade.realized_profit assert pytest.approx(freqtrade.wallets.get_free("USDT")) == expected_profit if spot: @@ -685,3 +693,133 @@ def test_dca_exiting(default_conf_usdt, ticker_usdt, fee, mocker, caplog, levera else: # total won't change in futures mode, only free / used will. assert freqtrade.wallets.get_total("USDT") == starting_amount + trade.realized_profit + + +@pytest.mark.parametrize("leverage", [1, 2]) +@pytest.mark.parametrize("is_short", [False, True]) +def test_dca_handle_similar_open_order( + default_conf_usdt, ticker_usdt, is_short, leverage, fee, mocker, caplog +) -> None: + default_conf_usdt["position_adjustment_enable"] = True + default_conf_usdt["trading_mode"] = "futures" + default_conf_usdt["margin_mode"] = "isolated" + + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) + mocker.patch.multiple( + EXMS, + fetch_ticker=ticker_usdt, + get_fee=fee, + amount_to_precision=lambda s, x, y: y, + price_to_precision=lambda s, x, y: y, + ) + mocker.patch(f"{EXMS}._dry_is_price_crossed", return_value=False) + mocker.patch(f"{EXMS}.get_max_leverage", return_value=10) + mocker.patch(f"{EXMS}.get_funding_fees", return_value=0) + mocker.patch(f"{EXMS}.get_maintenance_ratio_and_amt", return_value=(0, 0)) + + patch_get_signal(freqtrade, enter_short=is_short, enter_long=not is_short) + freqtrade.strategy.custom_entry_price = lambda **kwargs: ticker_usdt["ask"] * 0.96 + freqtrade.strategy.leverage = MagicMock(return_value=leverage) + freqtrade.strategy.custom_exit = MagicMock(return_value=False) + freqtrade.strategy.minimal_roi = {0: 0.2} + + # Create trade and initial entry order + freqtrade.enter_positions() + + assert len(Trade.get_trades().all()) == 1 + trade: Trade = Trade.get_trades().first() + assert len(trade.orders) == 1 + assert trade.orders[-1].side == trade.entry_side + assert trade.orders[-1].status == "open" + + assert trade.has_open_orders + # Process - shouldn't do anything + freqtrade.process() + # Doesn't try to exit, as we're not in a position yet + assert freqtrade.strategy.custom_exit.call_count == 0 + + # Adjust with new price, cancel initial entry order and place new one + freqtrade.strategy.adjust_entry_price = MagicMock(return_value=1.99) + freqtrade.strategy.ft_check_timed_out = MagicMock(return_value=False) + freqtrade.process() + trade = Trade.get_trades().first() + freqtrade.strategy.ft_check_timed_out = MagicMock(return_value=False) + + assert len(trade.orders) == 2 + assert len(trade.open_orders) == 1 + + # Adjust with new amount, should cancel and replace existing order + freqtrade.strategy.adjust_trade_position = MagicMock( + return_value=21 + ) # -(trade.stake_amount * 0.5) + freqtrade.process() + trade = Trade.get_trades().first() + + assert len(trade.orders) == 3 + assert len(trade.open_orders) == 1 + + # Fill entry order + assert freqtrade.strategy.custom_exit.call_count == 0 + + mocker.patch(f"{EXMS}._dry_is_price_crossed", return_value=True) + freqtrade.process() + + trade = Trade.get_trades().first() + assert trade.amount > 0 + + assert freqtrade.strategy.custom_exit.call_count == 1 + freqtrade.strategy.custom_exit.reset_mock() + + # Should Create a new exit order + freqtrade.exchange.amount_to_contract_precision = MagicMock(return_value=2) + freqtrade.strategy.adjust_trade_position = MagicMock(return_value=-2) + + mocker.patch(f"{EXMS}._dry_is_price_crossed", return_value=False) + freqtrade.process() + trade = Trade.get_trades().first() + + assert trade.orders[-2].status == "closed" + assert trade.orders[-1].status == "open" + assert trade.orders[-1].side == trade.exit_side + assert len(trade.orders) == 5 + assert len(trade.open_orders) == 1 + assert freqtrade.strategy.custom_exit.call_count == 1 + freqtrade.strategy.custom_exit.reset_mock() + + # Adjust with new exit amount, should cancel and replace existing exit order + freqtrade.exchange.amount_to_contract_precision = MagicMock(return_value=3) + freqtrade.strategy.adjust_trade_position = MagicMock(return_value=-3) + freqtrade.process() + trade = Trade.get_trades().first() + # Even with open order, trying to exit... + assert freqtrade.strategy.custom_exit.call_count == 1 + freqtrade.strategy.custom_exit.reset_mock() + + assert trade.orders[-2].status == "canceled" + assert len(trade.orders) == 6 + assert len(trade.open_orders) == 1 + + # Adjust with new exit price, should cancel and replace existing exit order + freqtrade.strategy.custom_exit_price = MagicMock(return_value=1.95) + freqtrade.process() + # Even with open order, trying to exit... + assert freqtrade.strategy.custom_exit.call_count == 1 + freqtrade.strategy.custom_exit.reset_mock() + + trade = Trade.get_trades().first() + + assert trade.orders[-2].status == "canceled" + assert len(trade.orders) == 7 + assert len(trade.open_orders) == 1 + similar_msg = r"A similar open order was found for.*" + + assert not log_has_re(similar_msg, caplog) + + # Adjust with same params, should keep existing order as price and amount are similar + freqtrade.strategy.custom_exit_price = MagicMock(return_value=1.95) + freqtrade.process() + trade = Trade.get_trades().first() + assert log_has_re(similar_msg, caplog) + + assert len(trade.orders) == 7 + assert len(trade.open_orders) == 1 diff --git a/tests/optimize/__init__.py b/tests/optimize/__init__.py index dfce6d8c5..3299c6a53 100644 --- a/tests/optimize/__init__.py +++ b/tests/optimize/__init__.py @@ -45,6 +45,7 @@ class BTContainer(NamedTuple): leverage: float = 1.0 timeout: int | None = None adjust_entry_price: float | None = None + adjust_trade_position: list[float] | None = None def _get_frame_time_from_offset(offset): diff --git a/tests/optimize/test_backtest_detail.py b/tests/optimize/test_backtest_detail.py index 63fa1bd96..7ba53a1b3 100644 --- a/tests/optimize/test_backtest_detail.py +++ b/tests/optimize/test_backtest_detail.py @@ -1185,6 +1185,39 @@ tc56 = BTContainer( ) +# Test 57: Custom-entry-price for position adjustment which won't fill +# Causing the negative adjustment to cancel the unfilled order and exit partially +tc57 = BTContainer( + data=[ + # D O H L C V EL XL ES Xs BT + [0, 5000, 5050, 4950, 5000, 6172, 1, 0, 0, 0], + [1, 4598, 5200, 4498, 5000, 6172, 0, 0, 0, 0], + [2, 4900, 5250, 4900, 5100, 6172, 0, 0, 0, 0], # Enhance position, but won't fill + [3, 5100, 5100, 4650, 4750, 6172, 0, 0, 0, 0], + [4, 4750, 4950, 4650, 4750, 6172, 0, 0, 0, 0], + [5, 4750, 4950, 4650, 4750, 6172, 0, 1, 0, 0], + [6, 4750, 4950, 4650, 4750, 6172, 0, 0, 0, 0], + ], + stop_loss=-0.2, + roi={"0": 0.50}, + profit_perc=0.033, + use_exit_signal=True, + timeout=1000, + custom_entry_price=4600, + adjust_trade_position=[ + None, + 0.001, + None, + -0.0001, # Cancels the above unfilled order and exits partially + None, + None, + ], + trades=[ + BTrade(exit_reason=ExitType.EXIT_SIGNAL, open_tick=1, close_tick=6, is_short=False), + ], +) + + TESTS = [ tc0, tc1, @@ -1243,6 +1276,7 @@ TESTS = [ tc54, tc55, tc56, + tc57, ] @@ -1289,7 +1323,13 @@ def test_backtest_results(default_conf, mocker, caplog, data: BTContainer) -> No backtesting.strategy.custom_entry_price = MagicMock(return_value=data.custom_entry_price) if data.custom_exit_price: backtesting.strategy.custom_exit_price = MagicMock(return_value=data.custom_exit_price) - backtesting.strategy.adjust_entry_price = MagicMock(return_value=data.adjust_entry_price) + if data.adjust_trade_position: + backtesting.strategy.position_adjustment_enable = True + backtesting.strategy.adjust_trade_position = MagicMock( + side_effect=data.adjust_trade_position + ) + if data.adjust_entry_price: + backtesting.strategy.adjust_entry_price = MagicMock(return_value=data.adjust_entry_price) backtesting.strategy.use_custom_stoploss = data.use_custom_stoploss backtesting.strategy.leverage = lambda **kwargs: data.leverage @@ -1317,6 +1357,6 @@ def test_backtest_results(default_conf, mocker, caplog, data: BTContainer) -> No assert res.close_date == _get_frame_time_from_offset(trade.close_tick) assert res.is_short == trade.is_short assert len(LocalTrade.bt_trades) == len(data.trades) - assert len(LocalTrade.bt_trades_open) == 0 + assert len(LocalTrade.bt_trades_open) == 0, "Left open trade" backtesting.cleanup() del backtesting diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index 662f8cde1..f14e087af 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -5,7 +5,7 @@ from collections import defaultdict from copy import deepcopy from datetime import datetime, timedelta, timezone from pathlib import Path -from unittest.mock import MagicMock, PropertyMock +from unittest.mock import ANY, MagicMock, PropertyMock import numpy as np import pandas as pd @@ -566,7 +566,9 @@ def test_backtest__enter_trade_futures(default_conf_usdt, fee, mocker) -> None: mocker.patch(f"{EXMS}.get_fee", fee) mocker.patch(f"{EXMS}.get_min_pair_stake_amount", return_value=0.00001) mocker.patch(f"{EXMS}.get_max_pair_stake_amount", return_value=float("inf")) - mocker.patch(f"{EXMS}.price_to_precision", lambda s, x, y, **kwargs: y) + mocker.patch( + "freqtrade.persistence.trade_model.price_to_precision", lambda p, *args, **kwargs: p + ) mocker.patch(f"{EXMS}.get_max_leverage", return_value=100) mocker.patch("freqtrade.optimize.backtesting.price_to_precision", lambda p, *args: p) patch_exchange(mocker) @@ -793,6 +795,7 @@ def test_backtest_one(default_conf, mocker, testdatadir) -> None: "order_filled_timestamp": 1517251200000, "ft_is_entry": True, "ft_order_tag": "", + "cost": ANY, }, { "amount": 0.00957442, @@ -801,6 +804,7 @@ def test_backtest_one(default_conf, mocker, testdatadir) -> None: "order_filled_timestamp": 1517265300000, "ft_is_entry": False, "ft_order_tag": "roi", + "cost": ANY, }, ], [ @@ -811,6 +815,7 @@ def test_backtest_one(default_conf, mocker, testdatadir) -> None: "order_filled_timestamp": 1517283000000, "ft_is_entry": True, "ft_order_tag": "", + "cost": ANY, }, { "amount": 0.0097064, @@ -819,6 +824,7 @@ def test_backtest_one(default_conf, mocker, testdatadir) -> None: "order_filled_timestamp": 1517285400000, "ft_is_entry": False, "ft_order_tag": "roi", + "cost": ANY, }, ], ], @@ -864,6 +870,7 @@ def test_backtest_one_detail(default_conf_usdt, mocker, testdatadir, use_detail) backtesting = Backtesting(default_conf_usdt) backtesting._set_strategy(backtesting.strategylist[0]) backtesting.strategy.populate_entry_trend = advise_entry + backtesting.strategy.ignore_buying_expired_candle_after = 59 backtesting.strategy.custom_entry_price = custom_entry_price pair = "XRP/ETH" # Pick a timerange adapted to the pair we use to test @@ -934,7 +941,7 @@ def test_backtest_one_detail(default_conf_usdt, mocker, testdatadir, use_detail) @pytest.mark.parametrize( "use_detail,exp_funding_fee, exp_ff_updates", [ - (True, -0.018054162, 11), + (True, -0.018054162, 10), (False, -0.01780296, 6), ], ) @@ -996,7 +1003,7 @@ def test_backtest_one_detail_futures( results = result["results"] assert not results.empty # Timeout settings from default_conf = entry: 10, exit: 30 - assert len(results) == (5 if use_detail else 2) + assert len(results) == (4 if use_detail else 2) assert "orders" in results.columns data_pair = processed[pair] @@ -1645,8 +1652,8 @@ def test_backtest_multi_pair_detail( if use_detail: # Backtest loop is called once per candle per pair # Exact numbers depend on trade state - but should be around 3_800 - assert bl_spy.call_count > 1_350 - assert bl_spy.call_count < 1_500 + assert bl_spy.call_count > 1_220 + assert bl_spy.call_count < 1_300 else: assert bl_spy.call_count < 995 @@ -1769,16 +1776,21 @@ def test_backtest_multi_pair_detail_simplified( if use_detail: # Backtest loop is called once per candle per pair - # Exact numbers depend on trade state - but should be around 3_800 - assert bl_spy.call_count > 3_350 - assert bl_spy.call_count < 3_800 + # Exact numbers depend on trade state - but should be around 2_600 + assert bl_spy.call_count > 2_170 + assert bl_spy.call_count < 2_800 + assert len(evaluate_result_multi(results["results"], "1h", 3)) > 0 else: assert bl_spy.call_count < 995 + assert len(evaluate_result_multi(results["results"], "1h", 3)) == 0 # Make sure we have parallel trades assert len(evaluate_result_multi(results["results"], "1h", 2)) > 0 + assert len(evaluate_result_multi(results["results"], "5m", 2)) > 0 # make sure we don't have trades with more than configured max_open_trades - assert len(evaluate_result_multi(results["results"], "1h", 3)) == 0 + # This must evaluate on detail timeframe - as we can have entries within the candle. + assert len(evaluate_result_multi(results["results"], "5m", 3)) == 0 + assert len(evaluate_result_multi(results["results"], "1m", 3)) == 0 # # Cached data correctly removed amounts offset = 1 if tres == 0 else 0 @@ -1797,7 +1809,12 @@ def test_backtest_multi_pair_detail_simplified( "end_date": max_date, } results = backtesting.backtest(**backtest_conf) - assert len(evaluate_result_multi(results["results"], "1h", 1)) == 0 + if use_detail: + assert len(evaluate_result_multi(results["results"], "1h", 1)) > 0 + else: + assert len(evaluate_result_multi(results["results"], "1h", 1)) == 0 + assert len(evaluate_result_multi(results["results"], "5m", 1)) == 0 + assert len(evaluate_result_multi(results["results"], "1m", 1)) == 0 @pytest.mark.parametrize("use_detail", [True, False]) @@ -1894,9 +1911,9 @@ def test_backtest_multi_pair_long_short_switch( if use_detail: # Backtest loop is called once per candle per pair - assert bl_spy.call_count == 1523 + assert bl_spy.call_count == 1511 else: - assert bl_spy.call_count == 479 + assert bl_spy.call_count == 508 # Make sure we have parallel trades assert len(evaluate_result_multi(results["results"], "5m", 0)) > 0 @@ -2596,7 +2613,7 @@ def test_backtest_start_multi_strat_caching( "Parameter -i/--timeframe detected ... Using timeframe: 1m ...", "Parameter --timerange detected: 1510694220-1510700340 ...", f"Using data directory: {testdatadir} ...", - "Loading data from 2017-11-14 20:57:00 " "up to 2017-11-14 22:59:00 (0 days).", + "Loading data from 2017-11-14 20:57:00 up to 2017-11-14 22:59:00 (0 days).", "Parameter --enable-position-stacking detected ...", ] @@ -2658,7 +2675,7 @@ def test_get_backtest_metadata_filename(): # Test with a string file path with no extension filename = "/path/to/backtest_results" - expected = Path("/path/to/backtest_results.meta") + expected = Path("/path/to/backtest_results.meta.json") assert get_backtest_metadata_filename(filename) == expected # Test with a string file path with multiple dots in the name @@ -2670,3 +2687,8 @@ def test_get_backtest_metadata_filename(): filename = "backtest_results.json" expected = Path("backtest_results.meta.json") assert get_backtest_metadata_filename(filename) == expected + # Test with a string file path with no parent directory + + filename = "backtest_results_zip.zip" + expected = Path("backtest_results_zip.meta.json") + assert get_backtest_metadata_filename(filename) == expected diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index 77fb10655..5459f76f5 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -1,7 +1,9 @@ +import json import re from datetime import timedelta from pathlib import Path from shutil import copyfile +from zipfile import ZipFile import joblib import pandas as pd @@ -229,13 +231,14 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmp_path): store_backtest_results(default_conf, stats, "2022_01_01_15_05_13") - # get real Filename (it's btresult-.json) + # get real Filename (it's btresult-.zip) last_fn = get_latest_backtest_filename(filename_last.parent) - assert re.match(r"btresult-.*\.json", last_fn) + assert re.match(r"btresult-.*\.zip", last_fn) filename1 = tmp_path / last_fn assert filename1.is_file() - content = filename1.read_text() + + content = json.dumps(load_backtest_stats(filename1)) assert "max_drawdown_account" in content assert "strategy" in content assert "pairlist" in content @@ -248,19 +251,22 @@ def test_generate_backtest_stats(default_conf, testdatadir, tmp_path): def test_store_backtest_results(testdatadir, mocker): dump_mock = mocker.patch("freqtrade.optimize.optimize_reports.bt_storage.file_dump_json") - + zip_mock = mocker.patch("freqtrade.optimize.optimize_reports.bt_storage.ZipFile") data = {"metadata": {}, "strategy": {}, "strategy_comparison": []} store_backtest_results({"exportfilename": testdatadir}, data, "2022_01_01_15_05_13") - assert dump_mock.call_count == 3 + assert dump_mock.call_count == 2 + assert zip_mock.call_count == 1 assert isinstance(dump_mock.call_args_list[0][0][0], Path) assert str(dump_mock.call_args_list[0][0][0]).startswith(str(testdatadir / "backtest-result")) dump_mock.reset_mock() + zip_mock.reset_mock() filename = testdatadir / "testresult.json" store_backtest_results({"exportfilename": filename}, data, "2022_01_01_15_05_13") - assert dump_mock.call_count == 3 + assert dump_mock.call_count == 2 + assert zip_mock.call_count == 1 assert isinstance(dump_mock.call_args_list[0][0][0], Path) # result will be testdatadir / testresult-.json assert str(dump_mock.call_args_list[0][0][0]).startswith(str(testdatadir / "testresult")) @@ -270,67 +276,33 @@ def test_store_backtest_results_real(tmp_path): data = {"metadata": {}, "strategy": {}, "strategy_comparison": []} store_backtest_results({"exportfilename": tmp_path}, data, "2022_01_01_15_05_13") - assert (tmp_path / "backtest-result-2022_01_01_15_05_13.json").is_file() + zip_file = tmp_path / "backtest-result-2022_01_01_15_05_13.zip" + assert zip_file.is_file() assert (tmp_path / "backtest-result-2022_01_01_15_05_13.meta.json").is_file() assert not (tmp_path / "backtest-result-2022_01_01_15_05_13_market_change.feather").is_file() + with ZipFile(zip_file, "r") as zipf: + assert "backtest-result-2022_01_01_15_05_13.json" in zipf.namelist() + assert "backtest-result-2022_01_01_15_05_13_market_change.feather" not in zipf.namelist() assert (tmp_path / LAST_BT_RESULT_FN).is_file() fn = get_latest_backtest_filename(tmp_path) - assert fn == "backtest-result-2022_01_01_15_05_13.json" + assert fn == "backtest-result-2022_01_01_15_05_13.zip" store_backtest_results( {"exportfilename": tmp_path}, data, "2024_01_01_15_05_25", market_change_data=pd.DataFrame() ) - assert (tmp_path / "backtest-result-2024_01_01_15_05_25.json").is_file() + zip_file = tmp_path / "backtest-result-2024_01_01_15_05_25.zip" + assert zip_file.is_file() assert (tmp_path / "backtest-result-2024_01_01_15_05_25.meta.json").is_file() - assert (tmp_path / "backtest-result-2024_01_01_15_05_25_market_change.feather").is_file() + assert not (tmp_path / "backtest-result-2024_01_01_15_05_25_market_change.feather").is_file() + + with ZipFile(zip_file, "r") as zipf: + assert "backtest-result-2024_01_01_15_05_25.json" in zipf.namelist() + assert "backtest-result-2024_01_01_15_05_25_market_change.feather" in zipf.namelist() assert (tmp_path / LAST_BT_RESULT_FN).is_file() # Last file reference should be updated fn = get_latest_backtest_filename(tmp_path) - assert fn == "backtest-result-2024_01_01_15_05_25.json" - - -def test_store_backtest_candles(tmp_path, mocker): - mocker.patch("freqtrade.optimize.optimize_reports.bt_storage.file_dump_json") - dump_mock = mocker.patch("freqtrade.optimize.optimize_reports.bt_storage.file_dump_joblib") - - candle_dict = {"DefStrat": {"UNITTEST/BTC": pd.DataFrame()}} - bt_results = {"metadata": {}, "strategy": {}, "strategy_comparison": []} - - mock_conf = { - "exportfilename": tmp_path, - "export": "signals", - "runmode": "backtest", - } - - # mock directory exporting - data = { - "signals": candle_dict, - "rejected": {}, - "exited": {}, - } - - store_backtest_results(mock_conf, bt_results, "2022_01_01_15_05_13", analysis_results=data) - - assert dump_mock.call_count == 3 - assert isinstance(dump_mock.call_args_list[0][0][0], Path) - assert str(dump_mock.call_args_list[0][0][0]).endswith("_signals.pkl") - assert str(dump_mock.call_args_list[1][0][0]).endswith("_rejected.pkl") - assert str(dump_mock.call_args_list[2][0][0]).endswith("_exited.pkl") - - dump_mock.reset_mock() - # mock file exporting - filename = Path(tmp_path / "testresult") - mock_conf["exportfilename"] = filename - store_backtest_results(mock_conf, bt_results, "2022_01_01_15_05_13", analysis_results=data) - assert dump_mock.call_count == 3 - assert isinstance(dump_mock.call_args_list[0][0][0], Path) - # result will be tmp_path / testresult-_signals.pkl - assert str(dump_mock.call_args_list[0][0][0]).endswith("_signals.pkl") - assert str(dump_mock.call_args_list[1][0][0]).endswith("_rejected.pkl") - assert str(dump_mock.call_args_list[2][0][0]).endswith("_exited.pkl") - - dump_mock.reset_mock() + assert fn == "backtest-result-2024_01_01_15_05_25.zip" def test_write_read_backtest_candles(tmp_path): @@ -350,9 +322,21 @@ def test_write_read_backtest_candles(tmp_path): "exited": {}, } store_backtest_results(mock_conf, bt_results, sample_date, analysis_results=data) - stored_file = tmp_path / f"backtest-result-{sample_date}_signals.pkl" - with stored_file.open("rb") as scp: - pickled_signal_candles = joblib.load(scp) + stored_file = tmp_path / f"backtest-result-{sample_date}.zip" + signals_pkl = f"backtest-result-{sample_date}_signals.pkl" + rejected_pkl = f"backtest-result-{sample_date}_rejected.pkl" + exited_pkl = f"backtest-result-{sample_date}_exited.pkl" + assert not (tmp_path / signals_pkl).is_file() + assert stored_file.is_file() + + with ZipFile(stored_file, "r") as zipf: + assert signals_pkl in zipf.namelist() + assert rejected_pkl in zipf.namelist() + assert exited_pkl in zipf.namelist() + + # open and read the file + with zipf.open(signals_pkl) as scp: + pickled_signal_candles = joblib.load(scp) assert pickled_signal_candles.keys() == candle_dict.keys() assert pickled_signal_candles["DefStrat"].keys() == pickled_signal_candles["DefStrat"].keys() @@ -366,14 +350,25 @@ def test_write_read_backtest_candles(tmp_path): filename = tmp_path / "testresult" mock_conf["exportfilename"] = filename store_backtest_results(mock_conf, bt_results, sample_date, analysis_results=data) - stored_file = tmp_path / f"testresult-{sample_date}_signals.pkl" - with stored_file.open("rb") as scp: - pickled_signal_candles = joblib.load(scp) + stored_file = tmp_path / f"testresult-{sample_date}.zip" + signals_pkl = f"testresult-{sample_date}_signals.pkl" + rejected_pkl = f"testresult-{sample_date}_rejected.pkl" + exited_pkl = f"testresult-{sample_date}_exited.pkl" + assert not (tmp_path / signals_pkl).is_file() + assert stored_file.is_file() - assert pickled_signal_candles.keys() == candle_dict.keys() - assert pickled_signal_candles["DefStrat"].keys() == pickled_signal_candles["DefStrat"].keys() - assert pickled_signal_candles["DefStrat"]["UNITTEST/BTC"].equals( - pickled_signal_candles["DefStrat"]["UNITTEST/BTC"] + with ZipFile(stored_file, "r") as zipf: + assert signals_pkl in zipf.namelist() + assert rejected_pkl in zipf.namelist() + assert exited_pkl in zipf.namelist() + + with zipf.open(signals_pkl) as scp: + pickled_signal_candles2 = joblib.load(scp) + + assert pickled_signal_candles2.keys() == candle_dict.keys() + assert pickled_signal_candles2["DefStrat"].keys() == pickled_signal_candles2["DefStrat"].keys() + assert pickled_signal_candles2["DefStrat"]["UNITTEST/BTC"].equals( + pickled_signal_candles2["DefStrat"]["UNITTEST/BTC"] ) _clean_test_file(stored_file) diff --git a/tests/persistence/test_persistence.py b/tests/persistence/test_persistence.py index a5e237a45..c30c27244 100644 --- a/tests/persistence/test_persistence.py +++ b/tests/persistence/test_persistence.py @@ -8,6 +8,7 @@ from sqlalchemy import select from freqtrade.constants import CUSTOM_TAG_MAX_LENGTH, DATETIME_PRINT_FORMAT from freqtrade.enums import TradingMode from freqtrade.exceptions import DependencyException +from freqtrade.exchange.exchange_utils import TICK_SIZE from freqtrade.persistence import LocalTrade, Order, Trade, init_db from freqtrade.util import dt_now from tests.conftest import ( @@ -1930,9 +1931,9 @@ def test_get_overall_performance(fee): @pytest.mark.parametrize( "is_short,pair,profit", [ - (True, "ETC/BTC", -0.005), - (False, "XRP/BTC", 0.01), - (None, "XRP/BTC", 0.01), + (True, "XRP/BTC", -0.00018780487), + (False, "ETC/BTC", 0.00003860975), + (None, "XRP/BTC", 0.000025203252), ], ) def test_get_best_pair(fee, is_short, pair, profit): @@ -1941,9 +1942,9 @@ def test_get_best_pair(fee, is_short, pair, profit): create_mock_trades(fee, is_short) res = Trade.get_best_pair() - assert len(res) == 2 + assert len(res) == 4 assert res[0] == pair - assert res[1] == profit + assert pytest.approx(res[1]) == profit @pytest.mark.usefixtures("init_persistence") @@ -1953,9 +1954,9 @@ def test_get_best_pair_lev(fee): create_mock_trades_with_leverage(fee) res = Trade.get_best_pair() - assert len(res) == 2 - assert res[0] == "DOGE/BTC" - assert res[1] == 0.1713156134055116 + assert len(res) == 4 + assert res[0] == "ETC/BTC" + assert pytest.approx(res[1]) == 0.00003860975 @pytest.mark.usefixtures("init_persistence") @@ -2144,7 +2145,6 @@ def test_Trade_object_idem(): "bt_trades_open", "bt_trades_open_pp", "bt_open_open_trade_count", - "bt_open_open_trade_count_candle", "bt_total_profit", "from_json", ) @@ -2833,6 +2833,8 @@ def test_recalc_trade_from_orders_dca(data) -> None: is_short=False, leverage=1.0, trading_mode=TradingMode.SPOT, + price_precision=0.001, + precision_mode_price=TICK_SIZE, ) Trade.session.add(trade) diff --git a/tests/persistence/test_trade_custom_data.py b/tests/persistence/test_trade_custom_data.py index b2971883d..e493276d7 100644 --- a/tests/persistence/test_trade_custom_data.py +++ b/tests/persistence/test_trade_custom_data.py @@ -60,6 +60,7 @@ def test_trade_custom_data(fee, use_db): def test_trade_custom_data_strategy_compat(mocker, default_conf_usdt, fee): mocker.patch(f"{EXMS}.get_rate", return_value=0.50) mocker.patch("freqtrade.freqtradebot.FreqtradeBot.get_real_amount", return_value=None) + mocker.patch("freqtrade.freqtradebot.FreqtradeBot.handle_cancel_exit", return_value=True) default_conf_usdt["minimal_roi"] = {"0": 100} freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) @@ -85,8 +86,9 @@ def test_trade_custom_data_strategy_compat(mocker, default_conf_usdt, fee): trade_after = Trade.get_trades_proxy(pair="ADA/USDT")[0] assert trade_after.get_custom_data("test_str") == "test_value" assert trade_after.get_custom_data("test_int") == 1 - # 2 open pairs eligible for exit - assert ff_spy.call_count == 2 + # 2 trades filled entry, with open exit order + # 1 trade with filled entry order + assert ff_spy.call_count == 3 assert trade_after.exit_reason == "test_value_1" diff --git a/tests/plugins/test_pairlocks.py b/tests/plugins/test_pairlocks.py index 0102079fe..f227818b3 100644 --- a/tests/plugins/test_pairlocks.py +++ b/tests/plugins/test_pairlocks.py @@ -37,6 +37,7 @@ def test_PairLocks(use_db): assert not PairLocks.is_pair_locked(pair) assert not PairLocks.is_pair_locked(pair, side="long") assert PairLocks.is_pair_locked(pair, side="short") + assert len(PairLocks.get_pair_locks(pair)) == 1 # XRP/BTC should not be locked now pair = "XRP/BTC" @@ -47,9 +48,11 @@ def test_PairLocks(use_db): PairLocks.lock_pair(pair, dt_now() + timedelta(minutes=4)) assert PairLocks.is_pair_locked(pair) - # Get both locks from above + # Get all locks from above locks = PairLocks.get_pair_locks(None) - assert len(locks) == 2 + assert len(locks) == 4 + + assert len(PairLocks.get_pair_locks(None, side="*")) == 2 # Unlock original pair pair = "ETH/BTC" diff --git a/tests/plugins/test_percentchangepairlist.py b/tests/plugins/test_percentchangepairlist.py index 6f399fc9f..7c2f16d76 100644 --- a/tests/plugins/test_percentchangepairlist.py +++ b/tests/plugins/test_percentchangepairlist.py @@ -107,7 +107,7 @@ def test_volume_change_pair_list_init_wrong_lookback_period(mocker, rpl_config): with pytest.raises( OperationalException, match=r"ChangeFilter requires lookback_period to not exceed" - r" exchange max request size \(1000\)", + r" exchange max request size \(\d+\)", ): get_patched_freqtradebot(mocker, rpl_config) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index 225c5e364..71eaa3bfd 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -99,6 +99,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: "precision_mode_price": 2, "contract_size": 1, "has_open_orders": False, + "nr_of_successful_entries": ANY, "orders": [ { "amount": 91.07468123, @@ -250,18 +251,23 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: mocker.patch(f"{EXMS}._dry_is_price_crossed", return_value=False) freqtradebot.enter_positions() - result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf["stake_currency"], "USD") + result, headers, fiat_profit_sum, total_sum = rpc._rpc_status_table( + default_conf["stake_currency"], "USD" + ) assert "Since" in headers assert "Pair" in headers assert "now" == result[0][2] assert "ETH/BTC" in result[0][1] - assert "0.00 (0.00)" == result[0][3] + assert "0.00% (0.00)" == result[0][3] assert "0.00" == f"{fiat_profit_sum:.2f}" + assert "0.00" == f"{total_sum:.2f}" mocker.patch(f"{EXMS}._dry_is_price_crossed", return_value=True) freqtradebot.process() - result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf["stake_currency"], "USD") + result, headers, fiat_profit_sum, total_sum = rpc._rpc_status_table( + default_conf["stake_currency"], "USD" + ) assert "Since" in headers assert "Pair" in headers assert "now" == result[0][2] @@ -270,8 +276,11 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: assert "-0.00" == f"{fiat_profit_sum:.2f}" # Test with fiat convert + rpc._config["fiat_display_currency"] = "USD" rpc._fiat_converter = CryptoToFiatConverter({}) - result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf["stake_currency"], "USD") + result, headers, fiat_profit_sum, total_sum = rpc._rpc_status_table( + default_conf["stake_currency"], "USD" + ) assert "Since" in headers assert "Pair" in headers assert len(result[0]) == 4 @@ -279,10 +288,13 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: assert "ETH/BTC" in result[0][1] assert "-0.41% (-0.06)" == result[0][3] assert "-0.06" == f"{fiat_profit_sum:.2f}" + assert "-0.06" == f"{total_sum:.2f}" rpc._config["position_adjustment_enable"] = True rpc._config["max_entry_position_adjustment"] = 3 - result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf["stake_currency"], "USD") + result, headers, fiat_profit_sum, total_sum = rpc._rpc_status_table( + default_conf["stake_currency"], "USD" + ) assert "# Entries" in headers assert len(result[0]) == 5 # 4th column should be 1/4 - as 1 order filled (a total of 4 is possible) @@ -292,7 +304,9 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker) -> None: mocker.patch( f"{EXMS}.get_rate", MagicMock(side_effect=ExchangeError("Pair 'ETH/BTC' not available")) ) - result, headers, fiat_profit_sum = rpc._rpc_status_table(default_conf["stake_currency"], "USD") + result, headers, fiat_profit_sum, total_sum = rpc._rpc_status_table( + default_conf["stake_currency"], "USD" + ) assert "now" == result[0][2] assert "ETH/BTC" in result[0][1] assert "nan%" == result[0][3] @@ -466,8 +480,8 @@ def test_rpc_trade_statistics(default_conf_usdt, ticker, fee, mocker) -> None: assert stats["first_trade_humanized"] == "2 days ago" assert stats["latest_trade_humanized"] == "17 minutes ago" assert stats["avg_duration"] in ("0:17:40") - assert stats["best_pair"] == "XRP/USDT" - assert stats["best_rate"] == 10.0 + assert stats["best_pair"] == "NEO/USDT" + assert stats["best_rate"] == 1.99 # Test non-available pair mocker.patch( @@ -478,8 +492,8 @@ def test_rpc_trade_statistics(default_conf_usdt, ticker, fee, mocker) -> None: assert stats["first_trade_humanized"] == "2 days ago" assert stats["latest_trade_humanized"] == "17 minutes ago" assert stats["avg_duration"] in ("0:17:40") - assert stats["best_pair"] == "XRP/USDT" - assert stats["best_rate"] == 10.0 + assert stats["best_pair"] == "NEO/USDT" + assert stats["best_rate"] == 1.99 assert isnan(stats["profit_all_coin"]) @@ -523,7 +537,9 @@ def test_rpc_balance_handle_error(default_conf, mocker): assert all(currency["currency"] != "ETH" for currency in res["currencies"]) -def test_rpc_balance_handle(default_conf_usdt, mocker, tickers): +@pytest.mark.parametrize("proxy_coin", [None, "BNFCR"]) +@pytest.mark.parametrize("margin_mode", ["isolated", "cross"]) +def test_rpc_balance_handle(default_conf_usdt, mocker, tickers, proxy_coin, margin_mode): mock_balance = { "BTC": { "free": 0.01, @@ -548,6 +564,14 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers): "used": 5.0, }, } + if proxy_coin: + default_conf_usdt["proxy_coin"] = proxy_coin + mock_balance[proxy_coin] = { + "free": 1500.0, + "total": 0.0, + "used": 0.0, + } + mock_pos = [ { "symbol": "ETH/USDT:USDT", @@ -591,6 +615,7 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers): ) default_conf_usdt["dry_run"] = False default_conf_usdt["trading_mode"] = "futures" + default_conf_usdt["margin_mode"] = margin_mode freqtradebot = get_patched_freqtradebot(mocker, default_conf_usdt) patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) @@ -600,21 +625,19 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers): default_conf_usdt["stake_currency"], default_conf_usdt["fiat_display_currency"] ) - assert pytest.approx(result["total"]) == 2824.83464 - assert pytest.approx(result["value"]) == 2824.83464 * 1.2 - assert tickers.call_count == 4 + assert tickers.call_count == 4 if not proxy_coin else 6 assert tickers.call_args_list[0][1]["cached"] is True # Testing futures - so we should get spot tickers assert tickers.call_args_list[-1][1]["market_type"] == "spot" assert "USD" == result["symbol"] - assert result["currencies"] == [ + expected_curr = [ { "currency": "BTC", "free": 0.01, "balance": 0.012, "used": 0.002, "bot_owned": 0, - "est_stake": 103.78464, + "est_stake": 86.4872, "est_stake_bot": 0, "stake": "USDT", "side": "long", @@ -628,7 +651,7 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers): "balance": 5.0, "used": 4.0, "bot_owned": 0, - "est_stake": 2651.05, + "est_stake": 530.21, "est_stake_bot": 0, "stake": "USDT", "side": "long", @@ -678,10 +701,71 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers): "is_position": True, }, ] - assert pytest.approx(result["total_bot"]) == 69.5 - assert pytest.approx(result["total"]) == 2824.83464 # ETH stake is missing. - assert result["starting_capital"] == 50 - assert result["starting_capital_ratio"] == pytest.approx(0.3899999) + if proxy_coin: + if margin_mode == "cross": + # Insert before ETH - as positions are always last. + expected_curr.insert( + len(expected_curr) - 1, + { + "currency": proxy_coin, + "free": 1500.0, + "balance": 0.0, + "used": 0.0, + "bot_owned": 1485.0, + "est_stake": 1500.0, + "est_stake_bot": 1485.0, + "stake": "USDT", + "side": "long", + "position": 0, + "is_bot_managed": True, + "is_position": False, + }, + ) + expected_curr[-3] = { + "currency": "USDT", + "free": 50.0, + "balance": 100.0, + "used": 5.0, + "bot_owned": 0, + "est_stake": 50.0, + "est_stake_bot": 0, + "stake": "USDT", + "side": "long", + "position": 0, + "is_bot_managed": False, + "is_position": False, + } + else: + expected_curr.insert( + len(expected_curr) - 1, + { + "currency": proxy_coin, + "free": 1500.0, + "balance": 0.0, + "used": 0.0, + "bot_owned": 0.0, + "est_stake": 0, + "est_stake_bot": 0, + "stake": "USDT", + "side": "long", + "position": 0, + "is_bot_managed": False, + "is_position": False, + }, + ) + + assert result["currencies"] == expected_curr + if proxy_coin and margin_mode == "cross": + assert pytest.approx(result["total_bot"]) == 1505.0 + assert pytest.approx(result["total"]) == 2186.6972 # ETH stake is missing. + assert result["starting_capital"] == 1500 * default_conf_usdt["tradable_balance_ratio"] + assert result["starting_capital_ratio"] == pytest.approx(0.013468013468013407) + else: + assert pytest.approx(result["total_bot"]) == 69.5 + assert pytest.approx(result["total"]) == 686.6972 # ETH stake is missing. + assert result["starting_capital"] == 50 * default_conf_usdt["tradable_balance_ratio"] + assert result["starting_capital_ratio"] == pytest.approx(0.4040404) + assert pytest.approx(result["value"]) == result["total"] * 1.2 def test_rpc_start(mocker, default_conf) -> None: @@ -908,7 +992,8 @@ def test_performance_handle(default_conf_usdt, ticker, fee, mocker) -> None: assert len(res) == 3 assert res[0]["pair"] == "NEO/USDT" assert res[0]["count"] == 1 - assert res[0]["profit_pct"] == 5.0 + assert res[0]["profit_abs"] == 3.9875 + assert res[0]["profit_pct"] == 1.99 def test_enter_tag_performance_handle(default_conf, ticker, fee, mocker) -> None: @@ -933,14 +1018,14 @@ def test_enter_tag_performance_handle(default_conf, ticker, fee, mocker) -> None assert len(res) == 3 assert res[0]["enter_tag"] == "TEST1" assert res[0]["count"] == 1 - assert res[0]["profit_pct"] == 5.0 + assert res[0]["profit_pct"] == 1.99 res = rpc._rpc_enter_tag_performance(None) assert len(res) == 3 assert res[0]["enter_tag"] == "TEST1" assert res[0]["count"] == 1 - assert res[0]["profit_pct"] == 5.0 + assert res[0]["profit_pct"] == 1.99 def test_enter_tag_performance_handle_2(mocker, default_conf, markets, fee): @@ -956,17 +1041,20 @@ def test_enter_tag_performance_handle_2(mocker, default_conf, markets, fee): assert len(res) == 2 assert res[0]["enter_tag"] == "TEST1" assert res[0]["count"] == 1 - assert pytest.approx(res[0]["profit_pct"]) == 0.5 + assert pytest.approx(res[0]["profit_pct"]) == 0.0 + assert pytest.approx(res[0]["profit_ratio"]) == 0.00003860975 assert res[1]["enter_tag"] == "Other" assert res[1]["count"] == 1 - assert pytest.approx(res[1]["profit_pct"]) == 1.0 + assert pytest.approx(res[1]["profit_pct"]) == 0.0 + assert pytest.approx(res[1]["profit_ratio"]) == 0.00002520325 # Test for a specific pair res = rpc._rpc_enter_tag_performance("ETC/BTC") assert len(res) == 1 assert res[0]["count"] == 1 assert res[0]["enter_tag"] == "TEST1" - assert pytest.approx(res[0]["profit_pct"]) == 0.5 + assert pytest.approx(res[0]["profit_pct"]) == 0.0 + assert pytest.approx(res[0]["profit_ratio"]) == 0.00003860975 def test_exit_reason_performance_handle(default_conf_usdt, ticker, fee, mocker) -> None: @@ -990,7 +1078,7 @@ def test_exit_reason_performance_handle(default_conf_usdt, ticker, fee, mocker) assert len(res) == 3 assert res[0]["exit_reason"] == "exit_signal" assert res[0]["count"] == 1 - assert res[0]["profit_pct"] == 5.0 + assert res[0]["profit_pct"] == 1.99 assert res[1]["exit_reason"] == "roi" assert res[2]["exit_reason"] == "Other" @@ -1009,17 +1097,20 @@ def test_exit_reason_performance_handle_2(mocker, default_conf, markets, fee): assert len(res) == 2 assert res[0]["exit_reason"] == "sell_signal" assert res[0]["count"] == 1 - assert pytest.approx(res[0]["profit_pct"]) == 0.5 + assert pytest.approx(res[0]["profit_pct"]) == 0.0 + assert pytest.approx(res[0]["profit_ratio"]) == 0.00003860975 assert res[1]["exit_reason"] == "roi" assert res[1]["count"] == 1 - assert pytest.approx(res[1]["profit_pct"]) == 1.0 + assert pytest.approx(res[1]["profit_pct"]) == 0.0 + assert pytest.approx(res[1]["profit_ratio"]) == 0.000025203252 # Test for a specific pair res = rpc._rpc_exit_reason_performance("ETC/BTC") assert len(res) == 1 assert res[0]["count"] == 1 assert res[0]["exit_reason"] == "sell_signal" - assert pytest.approx(res[0]["profit_pct"]) == 0.5 + assert pytest.approx(res[0]["profit_pct"]) == 0.0 + assert pytest.approx(res[0]["profit_ratio"]) == 0.00003860975 def test_mix_tag_performance_handle(default_conf, ticker, fee, mocker) -> None: diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 221a8b666..af33cd95c 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -35,6 +35,8 @@ from tests.conftest import ( CURRENT_TEST_STRATEGY, EXMS, create_mock_trades, + create_mock_trades_usdt, + generate_test_data, get_mock_coro, get_patched_freqtradebot, log_has, @@ -219,16 +221,16 @@ def test_api_ws_auth(botclient): bad_token = "bad-ws_token" with pytest.raises(WebSocketDisconnect): - with client.websocket_connect(url(bad_token)) as websocket: - websocket.receive() + with client.websocket_connect(url(bad_token)): + pass good_token = _TEST_WS_TOKEN - with client.websocket_connect(url(good_token)) as websocket: + with client.websocket_connect(url(good_token)): pass jwt_secret = ftbot.config["api_server"].get("jwt_secret_key", "super-secret") jwt_token = create_token({"identity": {"u": "Freqtrade"}}, jwt_secret) - with client.websocket_connect(url(jwt_token)) as websocket: + with client.websocket_connect(url(jwt_token)): pass @@ -284,7 +286,7 @@ def test_api_token_login(botclient): rc = client.get( f"{BASE_URI}/count", headers={ - "Authorization": f'Bearer {rc.json()["access_token"]}', + "Authorization": f"Bearer {rc.json()['access_token']}", "Origin": "http://example.com", }, ) @@ -299,7 +301,7 @@ def test_api_token_refresh(botclient): f"{BASE_URI}/token/refresh", data=None, headers={ - "Authorization": f'Bearer {rc.json()["refresh_token"]}', + "Authorization": f"Bearer {rc.json()['refresh_token']}", "Origin": "http://example.com", }, ) @@ -962,9 +964,10 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): ( True, { - "best_pair": "ETC/BTC", - "best_rate": -0.5, - "best_pair_profit_ratio": -0.005, + "best_pair": "XRP/BTC", + "best_rate": -0.02, + "best_pair_profit_ratio": -0.00018780487, + "best_pair_profit_abs": -0.001155, "profit_all_coin": 15.382312, "profit_all_fiat": 189894.6470718, "profit_all_percent_mean": 49.62, @@ -993,9 +996,10 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): ( False, { - "best_pair": "XRP/BTC", - "best_rate": 1.0, - "best_pair_profit_ratio": 0.01, + "best_pair": "ETC/BTC", + "best_rate": 0.0, + "best_pair_profit_ratio": 0.00003860975, + "best_pair_profit_abs": 0.000584127, "profit_all_coin": -15.46546305, "profit_all_fiat": -190921.14135225, "profit_all_percent_mean": -49.62, @@ -1025,8 +1029,9 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): None, { "best_pair": "XRP/BTC", - "best_rate": 1.0, - "best_pair_profit_ratio": 0.01, + "best_rate": 0.0, + "best_pair_profit_ratio": 0.000025203252, + "best_pair_profit_abs": 0.000155, "profit_all_coin": -14.87167525, "profit_all_fiat": -183590.83096125, "profit_all_percent_mean": 0.13, @@ -1056,6 +1061,7 @@ def test_api_edge_disabled(botclient, mocker, ticker, fee, markets): ) def test_api_profit(botclient, mocker, ticker, fee, markets, is_short, expected): ftbot, client = botclient + ftbot.config["tradable_balance_ratio"] = 1 patch_get_signal(ftbot) mocker.patch.multiple( EXMS, @@ -1078,7 +1084,8 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, is_short, expected) assert rc.json() == { "avg_duration": ANY, "best_pair": expected["best_pair"], - "best_pair_profit_ratio": expected["best_pair_profit_ratio"], + "best_pair_profit_ratio": pytest.approx(expected["best_pair_profit_ratio"]), + "best_pair_profit_abs": expected["best_pair_profit_abs"], "best_rate": expected["best_rate"], "first_trade_date": ANY, "first_trade_humanized": ANY, @@ -1155,59 +1162,35 @@ def test_api_performance(botclient, fee): ftbot, client = botclient patch_get_signal(ftbot) - trade = Trade( - pair="LTC/ETH", - amount=1, - exchange="binance", - stake_amount=1, - open_rate=0.245441, - is_open=False, - fee_close=fee.return_value, - fee_open=fee.return_value, - close_rate=0.265441, - leverage=1.0, - ) - trade.close_profit = trade.calc_profit_ratio(trade.close_rate) - trade.close_profit_abs = trade.calc_profit(trade.close_rate) - Trade.session.add(trade) - - trade = Trade( - pair="XRP/ETH", - amount=5, - stake_amount=1, - exchange="binance", - open_rate=0.412, - is_open=False, - fee_close=fee.return_value, - fee_open=fee.return_value, - close_rate=0.391, - leverage=1.0, - ) - trade.close_profit = trade.calc_profit_ratio(trade.close_rate) - trade.close_profit_abs = trade.calc_profit(trade.close_rate) - - Trade.session.add(trade) - Trade.commit() + create_mock_trades_usdt(fee) rc = client_get(client, f"{BASE_URI}/performance") assert_response(rc) - assert len(rc.json()) == 2 + assert len(rc.json()) == 3 assert rc.json() == [ { "count": 1, - "pair": "LTC/ETH", - "profit": 7.61, - "profit_pct": 7.61, - "profit_ratio": 0.07609203, - "profit_abs": 0.0187228, + "pair": "NEO/USDT", + "profit": 1.99, + "profit_pct": 1.99, + "profit_ratio": 0.0199375, + "profit_abs": 3.9875, }, { "count": 1, - "pair": "XRP/ETH", - "profit": -5.57, - "profit_pct": -5.57, - "profit_ratio": -0.05570419, - "profit_abs": -0.1150375, + "pair": "XRP/USDT", + "profit": 9.47, + "profit_abs": 2.8425, + "profit_pct": 9.47, + "profit_ratio": pytest.approx(0.094749999), + }, + { + "count": 1, + "pair": "LTC/USDT", + "profit": -20.45, + "profit_abs": -4.09, + "profit_pct": -20.45, + "profit_ratio": -0.2045, }, ] @@ -1228,7 +1211,8 @@ def test_api_entries(botclient, fee): resp = response[0] assert resp["enter_tag"] == "TEST1" assert resp["count"] == 1 - assert resp["profit_pct"] == 0.5 + assert resp["profit_pct"] == 0.0 + assert pytest.approx(resp["profit_ratio"]) == 0.000038609756 def test_api_exits(botclient, fee): @@ -1247,7 +1231,8 @@ def test_api_exits(botclient, fee): resp = response[0] assert resp["exit_reason"] == "sell_signal" assert resp["count"] == 1 - assert resp["profit_pct"] == 0.5 + assert resp["profit_pct"] == 0.0 + assert pytest.approx(resp["profit_ratio"]) == 0.000038609756 def test_api_mix_tag(botclient, fee): @@ -1930,6 +1915,15 @@ def test_api_pair_history(botclient, tmp_path, mocker): timeframe = "5m" lfm = mocker.patch("freqtrade.strategy.interface.IStrategy.load_freqAI_model") + # Wrong mode + rc = client_get( + client, + f"{BASE_URI}/pair_history?timeframe={timeframe}" + f"&timerange=20180111-20180112&strategy={CURRENT_TEST_STRATEGY}", + ) + assert_response(rc, 503) + _ftbot.config["runmode"] = RunMode.WEBSERVER + # No pair rc = client_get( client, @@ -2041,6 +2035,87 @@ def test_api_pair_history(botclient, tmp_path, mocker): assert_response(rc, 502) assert rc.json()["detail"] == ("No data for UNITTEST/BTC, 5m in 20200111-20200112 found.") + # No strategy + rc = client_post( + client, + f"{BASE_URI}/pair_history", + data={ + "pair": "UNITTEST/BTC", + "timeframe": timeframe, + "timerange": "20180111-20180112", + # "strategy": CURRENT_TEST_STRATEGY, + "columns": ["rsi", "fastd", "fastk"], + }, + ) + assert_response(rc, 200) + result = rc.json() + assert result["length"] == 289 + assert len(result["data"]) == result["length"] + assert "columns" in result + assert "data" in result + # Result without strategy won't have enter_long assigned. + assert "enter_long" not in result["columns"] + assert result["columns"] == ["date", "open", "high", "low", "close", "volume", "__date_ts"] + + +def test_api_pair_history_live_mode(botclient, tmp_path, mocker): + _ftbot, client = botclient + _ftbot.config["user_data_dir"] = tmp_path + _ftbot.config["runmode"] = RunMode.WEBSERVER + + mocker.patch("freqtrade.strategy.interface.IStrategy.load_freqAI_model") + # no strategy, live data + gho = mocker.patch( + "freqtrade.exchange.binance.Binance.get_historic_ohlcv", + return_value=generate_test_data("1h", 100), + ) + rc = client_post( + client, + f"{BASE_URI}/pair_history", + data={ + "pair": "UNITTEST/BTC", + "timeframe": "1h", + "timerange": "20240101-", + # "strategy": CURRENT_TEST_STRATEGY, + "columns": ["rsi", "fastd", "fastk"], + "live_mode": True, + }, + ) + + assert_response(rc, 200) + result = rc.json() + # 100 candles - as in the generate_test_data call above + assert result["length"] == 100 + assert len(result["data"]) == result["length"] + assert result["columns"] == ["date", "open", "high", "low", "close", "volume", "__date_ts"] + assert gho.call_count == 1 + + gho.reset_mock() + rc = client_post( + client, + f"{BASE_URI}/pair_history", + data={ + "pair": "UNITTEST/BTC", + "timeframe": "1h", + "timerange": "20240101-", + "strategy": CURRENT_TEST_STRATEGY, + "columns": ["rsi", "fastd", "fastk"], + "live_mode": True, + }, + ) + + assert_response(rc, 200) + result = rc.json() + # 80 candles - as in the generate_test_data call above - 20 startup candles + assert result["length"] == 100 - 20 + assert len(result["data"]) == result["length"] + + assert "rsi" in result["columns"] + assert "enter_long" in result["columns"] + assert "fastd" in result["columns"] + assert "date" in result["columns"] + assert gho.call_count == 1 + def test_api_plot_config(botclient, mocker, tmp_path): ftbot, client = botclient @@ -2865,7 +2940,7 @@ def test_api_ws_send_msg(default_conf, mocker, caplog): ApiServer.shutdown() -def test_api_download_data(botclient, mocker, tmp_path, caplog): +def test_api_download_data(botclient, mocker, tmp_path): ftbot, client = botclient rc = client_post(client, f"{BASE_URI}/download_data", data={}) @@ -2934,3 +3009,55 @@ def test_api_download_data(botclient, mocker, tmp_path, caplog): assert response["job_category"] == "download_data" assert response["status"] == "failed" assert response["error"] == "Download error" + + +def test_api_markets_live(botclient): + ftbot, client = botclient + + rc = client_get(client, f"{BASE_URI}/markets") + assert_response(rc, 200) + response = rc.json() + assert "markets" in response + assert len(response["markets"]) >= 0 + assert response["markets"]["XRP/USDT"] == { + "base": "XRP", + "quote": "USDT", + "symbol": "XRP/USDT", + "spot": True, + "swap": False, + } + + assert "BTC/USDT" in response["markets"] + assert "XRP/BTC" in response["markets"] + + rc = client_get( + client, + f"{BASE_URI}/markets?base=XRP", + ) + assert_response(rc, 200) + response = rc.json() + assert "XRP/USDT" in response["markets"] + assert "XRP/BTC" in response["markets"] + + assert "BTC/USDT" not in response["markets"] + + +def test_api_markets_webserver(botclient): + # Ensure webserver exchanges are reset + ApiBG.exchanges = {} + ftbot, client = botclient + # Test in webserver mode + ftbot.config["runmode"] = RunMode.WEBSERVER + + rc = client_get(client, f"{BASE_URI}/markets?exchange=binance") + assert_response(rc, 200) + response = rc.json() + assert "markets" in response + assert len(response["markets"]) >= 0 + assert response["exchange_id"] == "binance" + + rc = client_get(client, f"{BASE_URI}/markets?exchange=hyperliquid") + assert_response(rc, 200) + + assert "hyperliquid_spot" in ApiBG.exchanges + assert "binance_spot" in ApiBG.exchanges diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 845bce37c..1882e09c4 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -67,7 +67,7 @@ def default_conf(default_conf) -> dict: @pytest.fixture def update(): - message = Message(0, datetime.now(timezone.utc), Chat(0, 0)) + message = Message(0, datetime.now(timezone.utc), Chat(1235, 0)) _update = Update(0, message=message) return _update @@ -167,7 +167,7 @@ def test_telegram_init(default_conf, mocker, caplog) -> None: "['stopbuy', 'stopentry'], ['whitelist'], ['blacklist'], " "['bl_delete', 'blacklist_delete'], " "['logs'], ['edge'], ['health'], ['help'], ['version'], ['marketdir'], " - "['order'], ['list_custom_data']]" + "['order'], ['list_custom_data'], ['tg_info']]" ) assert log_has(message_str, caplog) @@ -224,8 +224,8 @@ async def test_authorized_only(default_conf, mocker, caplog, update) -> None: patch_get_signal(bot) await dummy.dummy_handler(update=update, context=MagicMock()) assert dummy.state["called"] is True - assert log_has("Executing handler: dummy_handler for chat_id: 0", caplog) - assert not log_has("Rejected unauthorized message from: 0", caplog) + assert log_has("Executing handler: dummy_handler for chat_id: 1235", caplog) + assert not log_has("Rejected unauthorized message from: 1235", caplog) assert not log_has("Exception occurred within Telegram module", caplog) @@ -918,7 +918,7 @@ async def test_telegram_profit_handle( ) assert "∙ `6.253 USD`" in msg_mock.call_args_list[-1][0][0] - assert "*Best Performing:* `ETH/USDT: 9.45%`" in msg_mock.call_args_list[-1][0][0] + assert "*Best Performing:* `ETH/USDT: 5.685 USDT (9.47%)`" in msg_mock.call_args_list[-1][0][0] assert "*Max Drawdown:*" in msg_mock.call_args_list[-1][0][0] assert "*Profit factor:*" in msg_mock.call_args_list[-1][0][0] assert "*Winrate:*" in msg_mock.call_args_list[-1][0][0] @@ -1087,7 +1087,7 @@ async def test_balance_handle_empty_response_dry(default_conf, update, mocker) - result = msg_mock.call_args_list[0][0][0] assert msg_mock.call_count == 1 assert "*Warning:* Simulated balances in Dry Mode." in result - assert "Starting capital: `1000 BTC`" in result + assert "Starting capital: `990 BTC`" in result async def test_balance_handle_too_large_response(default_conf, update, mocker) -> None: @@ -1591,7 +1591,7 @@ async def test_telegram_performance_handle(default_conf_usdt, update, ticker, fe await telegram._performance(update=update, context=MagicMock()) assert msg_mock.call_count == 1 assert "Performance" in msg_mock.call_args_list[0][0][0] - assert "XRP/USDT\t2.842 USDT (10.00%) (1)" in msg_mock.call_args_list[0][0][0] + assert "XRP/USDT\t2.842 USDT (9.47%) (1)" in msg_mock.call_args_list[0][0][0] async def test_telegram_entry_tag_performance_handle( @@ -1611,7 +1611,7 @@ async def test_telegram_entry_tag_performance_handle( await telegram._enter_tag_performance(update=update, context=context) assert msg_mock.call_count == 1 assert "Entry Tag Performance" in msg_mock.call_args_list[0][0][0] - assert "`TEST1\t3.987 USDT (5.00%) (1)`" in msg_mock.call_args_list[0][0][0] + assert "`TEST1\t3.987 USDT (1.99%) (1)`" in msg_mock.call_args_list[0][0][0] context.args = ["XRP/USDT"] await telegram._enter_tag_performance(update=update, context=context) @@ -1644,7 +1644,7 @@ async def test_telegram_exit_reason_performance_handle( await telegram._exit_reason_performance(update=update, context=context) assert msg_mock.call_count == 1 assert "Exit Reason Performance" in msg_mock.call_args_list[0][0][0] - assert "`roi\t2.842 USDT (10.00%) (1)`" in msg_mock.call_args_list[0][0][0] + assert "`roi\t2.842 USDT (9.47%) (1)`" in msg_mock.call_args_list[0][0][0] context.args = ["XRP/USDT"] await telegram._exit_reason_performance(update=update, context=context) @@ -2876,8 +2876,7 @@ async def test_telegram_list_custom_data(default_conf_usdt, update, ticker, fee, assert msg_mock.call_count == 3 assert "Found custom-data entries: " in msg_mock.call_args_list[0][0][0] assert ( - "*Key:* `test_int`\n*ID:* `1`\n*Trade ID:* `1`\n*Type:* `int`\n" - "*Value:* `1`\n*Create Date:*" + "*Key:* `test_int`\n*ID:* `1`\n*Trade ID:* `1`\n*Type:* `int`\n*Value:* `1`\n*Create Date:*" ) in msg_mock.call_args_list[1][0][0] assert ( "*Key:* `test_dict`\n*ID:* `2`\n*Trade ID:* `1`\n*Type:* `dict`\n" @@ -2967,3 +2966,15 @@ def test_noficiation_settings(default_conf_usdt, mocker): assert loudness({"type": RPCMessageType.EXIT, "exit_reason": "roi"}) == "off" assert loudness({"type": RPCMessageType.EXIT, "exit_reason": "partial_exit"}) == "off" assert loudness({"type": RPCMessageType.EXIT, "exit_reason": "cust_exit112"}) == "off" + + +async def test__tg_info(default_conf_usdt, mocker, update): + (telegram, _, _) = get_telegram_testobject(mocker, default_conf_usdt) + context = AsyncMock() + + await telegram._tg_info(update, context) + + assert context.bot.send_message.call_count == 1 + content = context.bot.send_message.call_args[1]["text"] + assert "Freqtrade Bot Info:\n" in content + assert '"chat_id": "1235"' in content diff --git a/tests/rpc/test_rpc_webhook.py b/tests/rpc/test_rpc_webhook.py index dc33f965f..b60e52177 100644 --- a/tests/rpc/test_rpc_webhook.py +++ b/tests/rpc/test_rpc_webhook.py @@ -383,8 +383,7 @@ def test_exception_send_msg(default_conf, mocker, caplog): } webhook.send_msg(msg) assert log_has( - "Problem calling Webhook. Please check your webhook configuration. " - "Exception: 'DEADBEEF'", + "Problem calling Webhook. Please check your webhook configuration. Exception: 'DEADBEEF'", caplog, ) diff --git a/tests/strategy/test_strategy_safe_wrapper.py b/tests/strategy/test_strategy_safe_wrapper.py index 214f3c9e4..007b3c1a6 100644 --- a/tests/strategy/test_strategy_safe_wrapper.py +++ b/tests/strategy/test_strategy_safe_wrapper.py @@ -3,8 +3,11 @@ import pytest from freqtrade.exceptions import StrategyError from freqtrade.persistence import Trade from freqtrade.strategy.strategy_wrapper import strategy_safe_wrapper +from freqtrade.util.datetime_helpers import dt_now from tests.conftest import create_mock_trades, log_has_re +from .strats.strategy_test_v3 import StrategyTestV3 + @pytest.mark.parametrize( "error", @@ -47,9 +50,14 @@ def test_strategy_safe_wrapper(value): @pytest.mark.usefixtures("init_persistence") -def test_strategy_safe_wrapper_trade_copy(fee): +def test_strategy_safe_wrapper_trade_copy(fee, mocker): create_mock_trades(fee) + import freqtrade.strategy.strategy_wrapper as swm + + deepcopy_mock = mocker.spy(swm, "deepcopy") + trade_ = Trade.get_open_trades()[0] + strat = StrategyTestV3(config={}) def working_method(trade): assert len(trade.orders) > 0 @@ -59,11 +67,27 @@ def test_strategy_safe_wrapper_trade_copy(fee): assert id(trade_) != id(trade) return trade + strat.working_method = working_method + # Don't assert anything before strategy_wrapper. # This ensures that relationship loading works correctly. - ret = strategy_safe_wrapper(working_method, message="DeadBeef")(trade=trade_) + ret = strategy_safe_wrapper(strat.working_method, message="DeadBeef")(trade=trade_) assert isinstance(ret, Trade) assert id(trade_) != id(ret) # Did not modify the original order assert len(trade_.orders) > 0 assert len(ret.orders) == 0 + assert deepcopy_mock.call_count == 1 + deepcopy_mock.reset_mock() + + # Call with non-overridden method - shouldn't deep-copy the trade + ret = strategy_safe_wrapper(strat.custom_entry_price, message="DeadBeef")( + pair="ETH/USDT", + trade=trade_, + current_time=dt_now(), + proposed_rate=0.5, + entry_tag="", + side="long", + ) + + assert deepcopy_mock.call_count == 0 diff --git a/tests/test_arguments.py b/tests/test_arguments.py index 488363685..1d48acb96 100644 --- a/tests/test_arguments.py +++ b/tests/test_arguments.py @@ -25,7 +25,7 @@ def test_parse_args_defaults(mocker) -> None: assert args["config"] == ["config.json"] assert args["strategy_path"] is None assert args["datadir"] is None - assert args["verbosity"] == 0 + assert args["verbosity"] is None def test_parse_args_default_userdatadir(mocker) -> None: @@ -35,17 +35,17 @@ def test_parse_args_default_userdatadir(mocker) -> None: assert args["config"] == [str(Path("user_data/config.json"))] assert args["strategy_path"] is None assert args["datadir"] is None - assert args["verbosity"] == 0 + assert args["verbosity"] is None def test_parse_args_userdatadir(mocker) -> None: mocker.patch.object(Path, "is_file", MagicMock(return_value=True)) - args = Arguments(["trade", "--user-data-dir", "user_data"]).get_parsed_arg() + args = Arguments(["trade", "--user-data-dir", "user_data", "-v"]).get_parsed_arg() # configuration defaults to user_data if that is available. assert args["config"] == [str(Path("user_data/config.json"))] assert args["strategy_path"] is None assert args["datadir"] is None - assert args["verbosity"] == 0 + assert args["verbosity"] == 1 def test_parse_args_config() -> None: @@ -82,8 +82,8 @@ def test_common_scripts_options() -> None: def test_parse_args_version() -> None: - with pytest.raises(SystemExit, match=r"0"): - Arguments(["--version"]).get_parsed_arg() + args = Arguments(["--version"]).get_parsed_arg() + assert args["version_main"] is True def test_parse_args_invalid() -> None: @@ -132,7 +132,7 @@ def test_parse_args_backtesting_custom() -> None: ] call_args = Arguments(args).get_parsed_arg() assert call_args["config"] == ["test_conf.json"] - assert call_args["verbosity"] == 0 + assert call_args["verbosity"] is None assert call_args["command"] == "backtesting" assert call_args["func"] is not None assert call_args["timeframe"] == "1m" @@ -145,7 +145,7 @@ def test_parse_args_hyperopt_custom() -> None: call_args = Arguments(args).get_parsed_arg() assert call_args["config"] == ["test_conf.json"] assert call_args["epochs"] == 20 - assert call_args["verbosity"] == 0 + assert call_args["verbosity"] is None assert call_args["command"] == "hyperopt" assert call_args["spaces"] == ["buy"] assert call_args["func"] is not None diff --git a/tests/test_configuration.py b/tests/test_configuration.py index 3e9df382b..a8ca72d86 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -111,7 +111,7 @@ def test_load_config_file_error_range(default_conf, mocker, caplog) -> None: x = log_config_error_range("somefile", "Parse error at offset 4: Invalid value.") assert isinstance(x, str) - assert x == ' "max_open_trades": 1,\n "stake_currency": "BTC",\n' ' "stake_amount": .001,' + assert x == ' "max_open_trades": 1,\n "stake_currency": "BTC",\n "stake_amount": .001,' x = log_config_error_range("-", "") assert x == "" diff --git a/tests/test_directory_operations.py b/tests/test_directory_operations.py index d64ff9c18..933d38b38 100644 --- a/tests/test_directory_operations.py +++ b/tests/test_directory_operations.py @@ -31,7 +31,7 @@ def test_create_userdata_dir(mocker, tmp_path, caplog) -> None: x = create_userdata_dir(tmp_path / "bar", create_dir=True) assert md.call_count == 10 assert md.call_args[1]["parents"] is False - assert log_has(f'Created user-data directory: {tmp_path / "bar"}', caplog) + assert log_has(f"Created user-data directory: {tmp_path / 'bar'}", caplog) assert isinstance(x, Path) assert str(x) == str(tmp_path / "bar") diff --git a/tests/test_log_setup.py b/tests/test_log_setup.py index 6dc88b79d..d4bc63193 100644 --- a/tests/test_log_setup.py +++ b/tests/test_log_setup.py @@ -6,7 +6,7 @@ import pytest from freqtrade.exceptions import OperationalException from freqtrade.loggers import ( FTBufferingHandler, - FTStdErrStreamHandler, + FtRichHandler, set_loggers, setup_logging, setup_logging_pre, @@ -72,7 +72,7 @@ def test_set_loggers_syslog(): setup_logging(config) assert len(logger.handlers) == 3 assert [x for x in logger.handlers if isinstance(x, logging.handlers.SysLogHandler)] - assert [x for x in logger.handlers if isinstance(x, FTStdErrStreamHandler)] + assert [x for x in logger.handlers if isinstance(x, FtRichHandler)] assert [x for x in logger.handlers if isinstance(x, FTBufferingHandler)] # setting up logging again should NOT cause the loggers to be added a second time. setup_logging(config) @@ -96,7 +96,7 @@ def test_set_loggers_Filehandler(tmp_path): setup_logging(config) assert len(logger.handlers) == 3 assert [x for x in logger.handlers if isinstance(x, logging.handlers.RotatingFileHandler)] - assert [x for x in logger.handlers if isinstance(x, FTStdErrStreamHandler)] + assert [x for x in logger.handlers if isinstance(x, FtRichHandler)] assert [x for x in logger.handlers if isinstance(x, FTBufferingHandler)] # setting up logging again should NOT cause the loggers to be added a second time. setup_logging(config) @@ -145,7 +145,7 @@ def test_set_loggers_journald(mocker): setup_logging(config) assert len(logger.handlers) == 3 assert [x for x in logger.handlers if type(x).__name__ == "JournaldLogHandler"] - assert [x for x in logger.handlers if isinstance(x, FTStdErrStreamHandler)] + assert [x for x in logger.handlers if isinstance(x, FtRichHandler)] # reset handlers to not break pytest logger.handlers = orig_handlers diff --git a/tests/test_main.py b/tests/test_main.py index 3c8150c38..0551def2d 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,5 +1,6 @@ # pragma pylint: disable=missing-docstring +import re from copy import deepcopy from pathlib import Path from unittest.mock import MagicMock, PropertyMock @@ -26,6 +27,14 @@ def test_parse_args_None(caplog) -> None: assert log_has_re(r"Usage of Freqtrade requires a subcommand.*", caplog) +def test_parse_args_version(capsys) -> None: + with pytest.raises(SystemExit): + main(["-V"]) + captured = capsys.readouterr() + assert re.search(r"CCXT Version:\s.*", captured.out, re.MULTILINE) + assert re.search(r"Freqtrade Version:\s+freqtrade\s.*", captured.out, re.MULTILINE) + + def test_parse_args_backtesting(mocker) -> None: """ Test that main() can start backtesting and also ensure we can pass some specific arguments @@ -40,7 +49,7 @@ def test_parse_args_backtesting(mocker) -> None: assert backtesting_mock.call_count == 1 call_args = backtesting_mock.call_args[0][0] assert call_args["config"] == ["config.json"] - assert call_args["verbosity"] == 0 + assert call_args["verbosity"] is None assert call_args["command"] == "backtesting" assert call_args["func"] is not None assert callable(call_args["func"]) @@ -57,7 +66,7 @@ def test_main_start_hyperopt(mocker) -> None: assert hyperopt_mock.call_count == 1 call_args = hyperopt_mock.call_args[0][0] assert call_args["config"] == ["config.json"] - assert call_args["verbosity"] == 0 + assert call_args["verbosity"] is None assert call_args["command"] == "hyperopt" assert call_args["func"] is not None assert callable(call_args["func"]) diff --git a/tests/test_wallets.py b/tests/test_wallets.py index 36211dcab..002e27c18 100644 --- a/tests/test_wallets.py +++ b/tests/test_wallets.py @@ -244,7 +244,7 @@ def test_get_starting_balance( freqtrade = get_patched_freqtradebot(mocker, default_conf) - assert freqtrade.wallets.get_starting_balance() == expected + assert freqtrade.wallets.get_starting_balance() == expected * (1 if available_capital else 0.99) def test_sync_wallet_futures_live(mocker, default_conf): @@ -373,7 +373,10 @@ def test_sync_wallet_dry(mocker, default_conf_usdt, fee): # sum of used and free should be total. assert usdt_bal.total == usdt_bal.free + usdt_bal.used - assert freqtrade.wallets.get_starting_balance() == default_conf_usdt["dry_run_wallet"] + assert ( + freqtrade.wallets.get_starting_balance() + == default_conf_usdt["dry_run_wallet"] * default_conf_usdt["tradable_balance_ratio"] + ) total = freqtrade.wallets.get_total("LTC") free = freqtrade.wallets.get_free("LTC") used = freqtrade.wallets.get_used("LTC") @@ -401,7 +404,10 @@ def test_sync_wallet_futures_dry(mocker, default_conf, fee): assert positions["XRP/BTC"].side == "long" assert positions["LTC/BTC"].side == "short" - assert freqtrade.wallets.get_starting_balance() == default_conf["dry_run_wallet"] + assert ( + freqtrade.wallets.get_starting_balance() + == default_conf["dry_run_wallet"] * default_conf["tradable_balance_ratio"] + ) total = freqtrade.wallets.get_total("BTC") free = freqtrade.wallets.get_free("BTC") used = freqtrade.wallets.get_used("BTC") diff --git a/tests/testdata/UNITTEST_BTC-5m.h5 b/tests/testdata/UNITTEST_BTC-5m.h5 deleted file mode 100644 index 52232af9e..000000000 Binary files a/tests/testdata/UNITTEST_BTC-5m.h5 and /dev/null differ diff --git a/tests/testdata/XRP_ETH-trades.h5 b/tests/testdata/XRP_ETH-trades.h5 deleted file mode 100644 index c13789e2a..000000000 Binary files a/tests/testdata/XRP_ETH-trades.h5 and /dev/null differ diff --git a/tests/testdata/futures/UNITTEST_USDT_USDT-1h-mark.h5 b/tests/testdata/futures/UNITTEST_USDT_USDT-1h-mark.h5 deleted file mode 100644 index e6b128dc1..000000000 Binary files a/tests/testdata/futures/UNITTEST_USDT_USDT-1h-mark.h5 and /dev/null differ diff --git a/tests/util/test_datetime_helpers.py b/tests/util/test_datetime_helpers.py index d17d2ec5a..258c5a0b9 100644 --- a/tests/util/test_datetime_helpers.py +++ b/tests/util/test_datetime_helpers.py @@ -13,6 +13,7 @@ from freqtrade.util import ( dt_utc, format_date, format_ms_time, + format_ms_time_det, shorten_date, ) from freqtrade.util.datetime_helpers import dt_humanize_delta @@ -91,7 +92,7 @@ def test_dt_humanize() -> None: def test_format_ms_time() -> None: # Date 2018-04-10 18:02:01 - date_in_epoch_ms = 1523383321000 + date_in_epoch_ms = 1523383321132 date = format_ms_time(date_in_epoch_ms) assert isinstance(date, str) res = datetime(2018, 4, 10, 18, 2, 1, tzinfo=timezone.utc) @@ -111,3 +112,17 @@ def test_format_date() -> None: date = datetime(2021, 9, 30, 22, 59, 3, 455555, tzinfo=timezone.utc) assert format_date(date) == "2021-09-30 22:59:03" assert format_date(None) == "" + + +def test_format_ms_time_detailed() -> None: + # Date 2018-04-10 18:02:01 + date_in_epoch_ms = 1523383321132 + date = format_ms_time_det(date_in_epoch_ms) + assert isinstance(date, str) + res = datetime(2018, 4, 10, 18, 2, 1, 132145, tzinfo=timezone.utc) + assert date == res.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3] + assert date == "2018-04-10T18:02:01.132" + res = datetime(2017, 12, 13, 8, 2, 1, 512321, tzinfo=timezone.utc) + # Date 2017-12-13 08:02:01 + date_in_epoch_ms = 1513152121512 + assert format_ms_time_det(date_in_epoch_ms) == res.strftime("%Y-%m-%dT%H:%M:%S.%f")[:-3]