diff --git a/.github/dependabot.yml b/.github/dependabot.yml index bd168014b..6e491cd7b 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -1,8 +1,10 @@ version: 2 updates: -- package-ecosystem: docker - cooldown: - default-days: 7 +- package-ecosystem: docker # zizmor: ignore[dependabot-cooldown] Docker does not support cooldowns at the moment. + # Docker does not support cooldowns at the moment. + # https://github.com/dependabot/dependabot-core/issues/14044 + # cooldown: + # default-days: 7 directories: - "/" - "/docker" diff --git a/.github/workflows/binance-lev-tier-update.yml b/.github/workflows/binance-lev-tier-update.yml index cce07573c..e56cc9a4b 100644 --- a/.github/workflows/binance-lev-tier-update.yml +++ b/.github/workflows/binance-lev-tier-update.yml @@ -2,7 +2,7 @@ name: Binance Leverage tiers update on: schedule: - - cron: "25 3 * * 4" + - cron: "25 2 * * 4" # on demand workflow_dispatch: @@ -24,12 +24,8 @@ jobs: with: persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: "3.14" - - - name: Install uv - uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1 + - name: Install uv and Python ๐Ÿ + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: activate-environment: true enable-cache: false @@ -46,7 +42,7 @@ jobs: run: python build_helpers/binance_update_lev_tiers.py - - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 + - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 with: token: ${{ secrets.REPO_SCOPED_TOKEN }} add-paths: freqtrade/exchange/binance_leverage_tiers.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c7a5842db..8bb925792 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -32,13 +32,8 @@ jobs: with: persist-credentials: false - - name: Set up Python ๐Ÿ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: ${{ matrix.python-version }} - - - name: Install uv - uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1 + - name: Install uv and Python ๐Ÿ + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: activate-environment: true enable-cache: true @@ -73,7 +68,7 @@ jobs: run: | pytest --random-order --cov=freqtrade --cov=freqtrade_client --cov-config=.coveragerc - - uses: codecov/codecov-action@671740ac38dd9b0130fbe1cec585b89eea48d3de # v5.5.2 + - uses: codecov/codecov-action@57e3a136b779b570ffcdbf80b3bdc90e7fab3de2 # v6.0.0 if: (runner.os == 'Linux' && matrix.python-version == '3.12' && matrix.os == 'ubuntu-24.04') with: fail_ci_if_error: true @@ -177,13 +172,8 @@ jobs: with: persist-credentials: false - - name: Set up Python ๐Ÿ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 #v6.2.0 - with: - python-version: "3.13" - - - name: Install uv - uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1 + - name: Install uv and Python ๐Ÿ + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: activate-environment: true python-version: "3.13" @@ -201,7 +191,8 @@ jobs: with: persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - name: Set up Python ๐Ÿ + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: "3.13" @@ -219,13 +210,8 @@ jobs: run: | ./tests/test_docs.sh - - name: Set up Python ๐Ÿ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: "3.13" - - - name: Install uv - uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1 + - name: Install uv and Python ๐Ÿ + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: activate-environment: true python-version: "3.13" @@ -256,13 +242,8 @@ jobs: with: persist-credentials: false - - name: Set up Python ๐Ÿ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: "${{ matrix.python-version }}" - - - name: Install uv - uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1 + - name: Install uv and Python ๐Ÿ + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: activate-environment: true enable-cache: true @@ -328,13 +309,8 @@ jobs: with: persist-credentials: false - - name: Set up Python ๐Ÿ - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: "${{ matrix.python-version }}" - - - name: Install uv - uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1 + - name: Install uv and Python ๐Ÿ + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: activate-environment: true python-version: "${{ matrix.python-version }}" @@ -345,7 +321,7 @@ jobs: python -m build --sdist --wheel - name: Upload artifacts ๐Ÿ“ฆ - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: freqtrade-build path: | @@ -357,7 +333,7 @@ jobs: python -m build --sdist --wheel ft_client - name: Upload artifacts ๐Ÿ“ฆ - uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0 + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: name: freqtrade-client-build path: | @@ -388,7 +364,7 @@ jobs: merge-multiple: true - name: Publish to PyPI (Test) - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 with: repository-url: https://test.pypi.org/legacy/ @@ -417,7 +393,7 @@ jobs: merge-multiple: true - name: Publish to PyPI - uses: pypa/gh-action-pypi-publish@ed0c53931b1dc9bd32cbe73a98c7f6766f8a527e # v1.13.0 + uses: pypa/gh-action-pypi-publish@cef221092ed1bacb1cc03d23a2d87d1d172e277b # v1.14.0 docker-build: diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 8b9d114b6..741799baa 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -26,15 +26,15 @@ jobs: with: persist-credentials: true - - name: Set up Python - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 + - name: Install uv and Python ๐Ÿ + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: - python-version: '3.12' + activate-environment: true + python-version: '3.13' - name: Install dependencies run: | - python -m pip install --upgrade pip - pip install -r docs/requirements-docs.txt + uv pip install -r docs/requirements-docs.txt - name: Fetch gh-pages branch run: | diff --git a/.github/workflows/devcontainer-build.yml b/.github/workflows/devcontainer-build.yml index 3cbc8ba6c..05d152f00 100644 --- a/.github/workflows/devcontainer-build.yml +++ b/.github/workflows/devcontainer-build.yml @@ -31,13 +31,13 @@ jobs: with: persist-credentials: false - name: Login to GitHub Container Registry - uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Pre-build dev container image - uses: devcontainers/ci@8bf61b26e9c3a98f69cb6ce2f88d24ff59b785c6 # v0.3.19 + uses: devcontainers/ci@b63b30de439b47a52267f241112c5b453b673db5 # v0.3.1900000449 with: subFolder: .github imageName: ghcr.io/${{ github.repository }}-devcontainer diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index 7dce79b6a..90cc7c629 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -59,7 +59,7 @@ jobs: uses: ./.github/actions/docker-tags - name: Login to Docker Hub - uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} @@ -183,13 +183,13 @@ jobs: uses: ./.github/actions/docker-tags - name: Login to Docker Hub - uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 with: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Login to github - uses: docker/login-action@b45d80f862d83dbcd57f89517bcf500b2ab88fb2 # v4.0.0 + uses: docker/login-action@4907a6ddec9925e35a0a9e82d7399ccc52663121 # v4.1.0 with: registry: ghcr.io username: ${{ github.actor }} diff --git a/.github/workflows/pre-commit-update.yml b/.github/workflows/pre-commit-update.yml index 723a47d2a..5bea4c0e6 100644 --- a/.github/workflows/pre-commit-update.yml +++ b/.github/workflows/pre-commit-update.yml @@ -25,12 +25,8 @@ jobs: with: persist-credentials: false - - uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 - with: - python-version: "3.13" - - - name: Install uv - uses: astral-sh/setup-uv@5a095e7a2014a4212f075830d4f7277575a9d098 # v7.3.1 + - name: Install uv and Python ๐Ÿ + uses: astral-sh/setup-uv@08807647e7069bb48b6ef5acd8ec9567f424441b # v8.1.0 with: activate-environment: true python-version: "3.13" @@ -41,7 +37,7 @@ jobs: - name: Run auto-update run: pre-commit autoupdate - - uses: peter-evans/create-pull-request@c0f553fe549906ede9cf27b5156039d195d2ece0 # v8.1.0 + - uses: peter-evans/create-pull-request@5f6978faf089d4d20b00c7766989d076bb2fc7f1 # v8.1.1 with: token: ${{ secrets.REPO_SCOPED_TOKEN }} add-paths: .pre-commit-config.yaml diff --git a/.github/workflows/zizmor_action.yml b/.github/workflows/zizmor_action.yml index aa6738715..a36439856 100644 --- a/.github/workflows/zizmor_action.yml +++ b/.github/workflows/zizmor_action.yml @@ -31,4 +31,4 @@ jobs: persist-credentials: false - name: Run zizmor ๐ŸŒˆ - uses: zizmorcore/zizmor-action@71321a20a9ded102f6e9ce5718a2fcec2c4f70d8 # v0.5.2 + uses: zizmorcore/zizmor-action@b1d7e1fb5de872772f31590499237e7cce841e8e # v0.5.3 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5f5490799..dbd3be2c3 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -15,23 +15,23 @@ repos: - repo: https://github.com/pre-commit/mirrors-mypy - rev: "v1.19.1" + rev: "v1.20.2" hooks: - id: mypy exclude: build_helpers additional_dependencies: - - types-cachetools==6.2.0.20251022 + - types-cachetools==6.2.0.20260408 - types-filelock==3.2.7 - - types-requests==2.32.4.20260107 - - types-tabulate==0.10.0.20260308 - - types-python-dateutil==2.9.0.20260305 - - scipy-stubs==1.17.1.2 - - SQLAlchemy==2.0.48 + - types-requests==2.33.0.20260408 + - types-tabulate==0.10.0.20260408 + - types-python-dateutil==2.9.0.20260408 + - scipy-stubs==1.17.1.4 + - SQLAlchemy==2.0.49 # stages: [push] - repo: https://github.com/charliermarsh/ruff-pre-commit # Ruff version. - rev: 'v0.15.7' + rev: 'v0.15.12' hooks: - id: ruff - id: ruff-format @@ -70,6 +70,6 @@ repos: # Ensure github actions remain safe - repo: https://github.com/woodruffw/zizmor-pre-commit - rev: v1.23.1 + rev: v1.24.1 hooks: - id: zizmor diff --git a/Dockerfile b/Dockerfile index ade667989..7c09e18a6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.13.12-slim-trixie AS base +FROM python:3.14.3-slim-trixie AS base # Setup env ENV LANG=C.UTF-8 diff --git a/build_helpers/create_command_partials.py b/build_helpers/create_command_partials.py index 3f5bad52e..fe7212993 100644 --- a/build_helpers/create_command_partials.py +++ b/build_helpers/create_command_partials.py @@ -87,7 +87,7 @@ def extract_command_partials(): help_output = _get_help_output(subparser) _write_partial_file(f"docs/commands/{command}.md", help_output) else: - print(f" Warning: subcommand '{command}' not found in parser") + print(f" Warning: subcommand '{command}' not found in parser") # freqtrade-client still uses subprocess as requested print("Running for freqtrade-client") diff --git a/build_helpers/schema.json b/build_helpers/schema.json index 8bf56b2a1..5d07b4ad1 100644 --- a/build_helpers/schema.json +++ b/build_helpers/schema.json @@ -283,6 +283,10 @@ "month" ] }, + "skip_wallet_history_migration": { + "description": "Disable wallet history migration.", + "type": "boolean" + }, "hyperopt_path": { "description": "Specify additional lookup path for Hyperopt Loss functions.", "type": "string" diff --git a/docker/Dockerfile.armhf b/docker/Dockerfile.armhf index d654400eb..920bf82e0 100644 --- a/docker/Dockerfile.armhf +++ b/docker/Dockerfile.armhf @@ -1,4 +1,4 @@ -FROM python:3.11.14-slim-bookworm AS base +FROM python:3.11.15-slim-bookworm AS base # Setup env ENV LANG=C.UTF-8 diff --git a/docs/backtesting.md b/docs/backtesting.md index 6b86d9635..00776ed66 100644 --- a/docs/backtesting.md +++ b/docs/backtesting.md @@ -160,117 +160,131 @@ The most important in the backtesting is to understand the result. A backtesting result will look like that: ``` - BACKTESTING REPORT -โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“ -โ”ƒ Pair โ”ƒ Trades โ”ƒ Avg Profit % โ”ƒ Tot Profit USDT โ”ƒ Tot Profit % โ”ƒ Avg Duration โ”ƒ Win Draw Loss Win% โ”ƒ -โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ -โ”‚ LTC/USDT:USDT โ”‚ 16 โ”‚ 1.0 โ”‚ 56.176 โ”‚ 5.62 โ”‚ 16:16:00 โ”‚ 16 0 0 100 โ”‚ -โ”‚ ETC/USDT:USDT โ”‚ 12 โ”‚ 0.72 โ”‚ 30.936 โ”‚ 3.09 โ”‚ 9:55:00 โ”‚ 11 0 1 91.7 โ”‚ -โ”‚ ETH/USDT:USDT โ”‚ 8 โ”‚ 0.66 โ”‚ 17.864 โ”‚ 1.79 โ”‚ 1 day, 13:55:00 โ”‚ 7 0 1 87.5 โ”‚ -โ”‚ XLM/USDT:USDT โ”‚ 10 โ”‚ 0.31 โ”‚ 11.054 โ”‚ 1.11 โ”‚ 12:08:00 โ”‚ 9 0 1 90.0 โ”‚ -โ”‚ BTC/USDT:USDT โ”‚ 8 โ”‚ 0.21 โ”‚ 7.289 โ”‚ 0.73 โ”‚ 3 days, 1:24:00 โ”‚ 6 0 2 75.0 โ”‚ -โ”‚ XRP/USDT:USDT โ”‚ 9 โ”‚ -0.14 โ”‚ -7.261 โ”‚ -0.73 โ”‚ 21:18:00 โ”‚ 8 0 1 88.9 โ”‚ -โ”‚ DOT/USDT:USDT โ”‚ 6 โ”‚ -0.4 โ”‚ -9.187 โ”‚ -0.92 โ”‚ 5:35:00 โ”‚ 4 0 2 66.7 โ”‚ -โ”‚ ADA/USDT:USDT โ”‚ 8 โ”‚ -1.76 โ”‚ -52.098 โ”‚ -5.21 โ”‚ 11:38:00 โ”‚ 6 0 2 75.0 โ”‚ -โ”‚ TOTAL โ”‚ 77 โ”‚ 0.22 โ”‚ 54.774 โ”‚ 5.48 โ”‚ 22:12:00 โ”‚ 67 0 10 87.0 โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - LEFT OPEN TRADES REPORT -โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“ -โ”ƒ Pair โ”ƒ Trades โ”ƒ Avg Profit % โ”ƒ Tot Profit USDT โ”ƒ Tot Profit % โ”ƒ Avg Duration โ”ƒ Win Draw Loss Win% โ”ƒ -โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ -โ”‚ BTC/USDT:USDT โ”‚ 1 โ”‚ -4.14 โ”‚ -9.930 โ”‚ -0.99 โ”‚ 17 days, 8:00:00 โ”‚ 0 0 1 0 โ”‚ -โ”‚ ETC/USDT:USDT โ”‚ 1 โ”‚ -4.24 โ”‚ -15.365 โ”‚ -1.54 โ”‚ 10:40:00 โ”‚ 0 0 1 0 โ”‚ -โ”‚ DOT/USDT:USDT โ”‚ 1 โ”‚ -5.29 โ”‚ -19.125 โ”‚ -1.91 โ”‚ 11:30:00 โ”‚ 0 0 1 0 โ”‚ -โ”‚ TOTAL โ”‚ 3 โ”‚ -4.56 โ”‚ -44.420 โ”‚ -4.44 โ”‚ 6 days, 2:03:00 โ”‚ 0 0 3 0 โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - ENTER TAG STATS -โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“ -โ”ƒ Enter Tag โ”ƒ Entries โ”ƒ Avg Profit % โ”ƒ Tot Profit USDT โ”ƒ Tot Profit % โ”ƒ Avg Duration โ”ƒ Win Draw Loss Win% โ”ƒ -โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ -โ”‚ OTHER โ”‚ 77 โ”‚ 0.22 โ”‚ 54.774 โ”‚ 5.48 โ”‚ 22:12:00 โ”‚ 67 0 10 87.0 โ”‚ -โ”‚ TOTAL โ”‚ 77 โ”‚ 0.22 โ”‚ 54.774 โ”‚ 5.48 โ”‚ 22:12:00 โ”‚ 67 0 10 87.0 โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - EXIT REASON STATS -โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“ -โ”ƒ Exit Reason โ”ƒ Exits โ”ƒ Avg Profit % โ”ƒ Tot Profit USDT โ”ƒ Tot Profit % โ”ƒ Avg Duration โ”ƒ Win Draw Loss Win% โ”ƒ -โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ -โ”‚ roi โ”‚ 67 โ”‚ 1.05 โ”‚ 242.179 โ”‚ 24.22 โ”‚ 15:49:00 โ”‚ 67 0 0 100 โ”‚ -โ”‚ exit_signal โ”‚ 4 โ”‚ -2.23 โ”‚ -31.217 โ”‚ -3.12 โ”‚ 1 day, 8:38:00 โ”‚ 0 0 4 0 โ”‚ -โ”‚ force_exit โ”‚ 3 โ”‚ -4.56 โ”‚ -44.420 โ”‚ -4.44 โ”‚ 6 days, 2:03:00 โ”‚ 0 0 3 0 โ”‚ -โ”‚ stop_loss โ”‚ 3 โ”‚ -10.14 โ”‚ -111.768 โ”‚ -11.18 โ”‚ 1 day, 3:05:00 โ”‚ 0 0 3 0 โ”‚ -โ”‚ TOTAL โ”‚ 77 โ”‚ 0.22 โ”‚ 54.774 โ”‚ 5.48 โ”‚ 22:12:00 โ”‚ 67 0 10 87.0 โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - MIXED TAG STATS -โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“ -โ”ƒ Enter Tag โ”ƒ Exit Reason โ”ƒ Trades โ”ƒ Avg Profit % โ”ƒ Tot Profit USDT โ”ƒ Tot Profit % โ”ƒ Avg Duration โ”ƒ Win Draw Loss Win% โ”ƒ -โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ -โ”‚ โ”‚ roi โ”‚ 67 โ”‚ 1.05 โ”‚ 242.179 โ”‚ 24.22 โ”‚ 15:49:00 โ”‚ 67 0 0 100 โ”‚ -โ”‚ โ”‚ exit_signal โ”‚ 4 โ”‚ -2.23 โ”‚ -31.217 โ”‚ -3.12 โ”‚ 1 day, 8:38:00 โ”‚ 0 0 4 0 โ”‚ -โ”‚ โ”‚ force_exit โ”‚ 3 โ”‚ -4.56 โ”‚ -44.420 โ”‚ -4.44 โ”‚ 6 days, 2:03:00 โ”‚ 0 0 3 0 โ”‚ -โ”‚ โ”‚ stop_loss โ”‚ 3 โ”‚ -10.14 โ”‚ -111.768 โ”‚ -11.18 โ”‚ 1 day, 3:05:00 โ”‚ 0 0 3 0 โ”‚ -โ”‚ TOTAL โ”‚ โ”‚ 77 โ”‚ 0.22 โ”‚ 54.774 โ”‚ 5.48 โ”‚ 22:12:00 โ”‚ 67 0 10 87.0 โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ - SUMMARY METRICS -โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“ -โ”ƒ Metric โ”ƒ Value โ”ƒ -โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ -โ”‚ Backtesting from โ”‚ 2025-07-01 00:00:00 โ”‚ -โ”‚ Backtesting to โ”‚ 2025-08-01 00:00:00 โ”‚ -โ”‚ Trading Mode โ”‚ Isolated Futures โ”‚ -โ”‚ Max open trades โ”‚ 3 โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ Total/Daily Avg Trades โ”‚ 77 / 2.48 โ”‚ -โ”‚ Starting balance โ”‚ 1000 USDT โ”‚ -โ”‚ Final balance โ”‚ 1054.774 USDT โ”‚ -โ”‚ Absolute profit โ”‚ 54.774 USDT โ”‚ -โ”‚ Total profit % โ”‚ 5.48% โ”‚ -โ”‚ CAGR % โ”‚ 87.36% โ”‚ -โ”‚ Sortino โ”‚ 2.48 โ”‚ -โ”‚ Sharpe โ”‚ 3.75 โ”‚ -โ”‚ Calmar โ”‚ 40.99 โ”‚ -โ”‚ SQN โ”‚ 0.69 โ”‚ -โ”‚ Profit factor โ”‚ 1.29 โ”‚ -โ”‚ Expectancy (Ratio) โ”‚ 0.71 (0.04) โ”‚ -โ”‚ Avg. daily profit โ”‚ 1.767 USDT โ”‚ -โ”‚ Avg. stake amount โ”‚ 345.016 USDT โ”‚ -โ”‚ Total trade volume โ”‚ 53316.954 USDT โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ Long / Short trades โ”‚ 67 / 10 โ”‚ -โ”‚ Long / Short profit % โ”‚ 8.94% / -3.47% โ”‚ -โ”‚ Long / Short profit USDT โ”‚ 89.425 / -34.651 โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ Best Pair โ”‚ LTC/USDT:USDT 5.62% โ”‚ -โ”‚ Worst Pair โ”‚ ADA/USDT:USDT -5.21% โ”‚ -โ”‚ Best trade โ”‚ ETC/USDT:USDT 2.00% โ”‚ -โ”‚ Worst trade โ”‚ ADA/USDT:USDT -10.17% โ”‚ -โ”‚ Best day โ”‚ 26.91 USDT โ”‚ -โ”‚ Worst day โ”‚ -47.741 USDT โ”‚ -โ”‚ Days win/draw/lose โ”‚ 20 / 6 / 5 โ”‚ -โ”‚ Min/Max/Avg. Duration Winners โ”‚ 0d 00:35 / 5d 18:15 / 0d 15:49 โ”‚ -โ”‚ Min/Max/Avg. Duration Losers โ”‚ 0d 10:40 / 17d 08:00 / 2d 17:00 โ”‚ -โ”‚ Max Consecutive Wins / Loss โ”‚ 36 / 3 โ”‚ -โ”‚ Rejected Entry signals โ”‚ 258 โ”‚ -โ”‚ Entry/Exit Timeouts โ”‚ 0 / 0 โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ Min balance โ”‚ 1003.168 USDT โ”‚ -โ”‚ Max balance โ”‚ 1149.421 USDT โ”‚ -โ”‚ Max % of account underwater โ”‚ 8.23% โ”‚ -โ”‚ Absolute drawdown โ”‚ 94.647 USDT (8.23%) โ”‚ -โ”‚ Drawdown duration โ”‚ 9 days 08:50:00 โ”‚ -โ”‚ Profit at drawdown start โ”‚ 149.421 USDT โ”‚ -โ”‚ Profit at drawdown end โ”‚ 54.774 USDT โ”‚ -โ”‚ Drawdown start โ”‚ 2025-07-22 15:10:00 โ”‚ -โ”‚ Drawdown end โ”‚ 2025-08-01 00:00:00 โ”‚ -โ”‚ Market change โ”‚ 30.51% โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + BACKTESTING REPORT +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“ +โ”ƒ Pair โ”ƒ Trades โ”ƒ Avg Profit % โ”ƒ Tot Profit โ”ƒ Tot Profit % โ”ƒ Avg Duration โ”ƒ Win Draw Loss Win% โ”ƒ +โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ +โ”‚ LTC/USDT:USDT โ”‚ 16 โ”‚ 1.01 โ”‚ 56.882 โ”‚ 5.69 โ”‚ 16:16:00 โ”‚ 16 0 0 100 โ”‚ +โ”‚ ETC/USDT:USDT โ”‚ 12 โ”‚ 0.73 โ”‚ 31.513 โ”‚ 3.15 โ”‚ 9:55:00 โ”‚ 11 0 1 91.7 โ”‚ +โ”‚ ETH/USDT:USDT โ”‚ 8 โ”‚ 0.69 โ”‚ 18.659 โ”‚ 1.87 โ”‚ 1 day, 13:55:00 โ”‚ 7 0 1 87.5 โ”‚ +โ”‚ XLM/USDT:USDT โ”‚ 10 โ”‚ 0.3 โ”‚ 10.694 โ”‚ 1.07 โ”‚ 12:08:00 โ”‚ 9 0 1 90.0 โ”‚ +โ”‚ BTC/USDT:USDT โ”‚ 8 โ”‚ 0.22 โ”‚ 7.502 โ”‚ 0.75 โ”‚ 3 days, 1:24:00 โ”‚ 6 0 2 75.0 โ”‚ +โ”‚ XRP/USDT:USDT โ”‚ 9 โ”‚ -0.13 โ”‚ -6.837 โ”‚ -0.68 โ”‚ 21:18:00 โ”‚ 8 0 1 88.9 โ”‚ +โ”‚ DOT/USDT:USDT โ”‚ 6 โ”‚ -0.39 โ”‚ -9.169 โ”‚ -0.92 โ”‚ 5:35:00 โ”‚ 4 0 2 66.7 โ”‚ +โ”‚ ADA/USDT:USDT โ”‚ 8 โ”‚ -1.75 โ”‚ -52.089 โ”‚ -5.21 โ”‚ 11:38:00 โ”‚ 6 0 2 75.0 โ”‚ +โ”‚ TOTAL โ”‚ 77 โ”‚ 0.23 โ”‚ 57.157 โ”‚ 5.72 โ”‚ 22:12:00 โ”‚ 67 0 10 87.0 โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + LEFT OPEN TRADES REPORT +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“ +โ”ƒ Pair โ”ƒ Trades โ”ƒ Avg Profit % โ”ƒ Tot Profit โ”ƒ Tot Profit % โ”ƒ Avg Duration โ”ƒ Win Draw Loss Win% โ”ƒ +โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ +โ”‚ BTC/USDT:USDT โ”‚ 1 โ”‚ -4.14 โ”‚ -9.930 โ”‚ -0.99 โ”‚ 17 days, 8:00:00 โ”‚ 0 0 1 0 โ”‚ +โ”‚ ETC/USDT:USDT โ”‚ 1 โ”‚ -4.24 โ”‚ -15.365 โ”‚ -1.54 โ”‚ 10:40:00 โ”‚ 0 0 1 0 โ”‚ +โ”‚ DOT/USDT:USDT โ”‚ 1 โ”‚ -5.29 โ”‚ -19.166 โ”‚ -1.92 โ”‚ 11:30:00 โ”‚ 0 0 1 0 โ”‚ +โ”‚ TOTAL โ”‚ 3 โ”‚ -4.56 โ”‚ -44.461 โ”‚ -4.45 โ”‚ 6 days, 2:03:00 โ”‚ 0 0 3 0 โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + ENTER TAG STATS +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“ +โ”ƒ Enter Tag โ”ƒ Entries โ”ƒ Avg Profit % โ”ƒ Tot Profit โ”ƒ Tot Profit % โ”ƒ Avg Duration โ”ƒ Win Draw Loss Win% โ”ƒ +โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ +โ”‚ OTHER โ”‚ 77 โ”‚ 0.23 โ”‚ 57.157 โ”‚ 5.72 โ”‚ 22:12:00 โ”‚ 67 0 10 87.0 โ”‚ +โ”‚ TOTAL โ”‚ 77 โ”‚ 0.23 โ”‚ 57.157 โ”‚ 5.72 โ”‚ 22:12:00 โ”‚ 67 0 10 87.0 โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + EXIT REASON STATS +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“ +โ”ƒ Exit Reason โ”ƒ Exits โ”ƒ Avg Profit % โ”ƒ Tot Profit โ”ƒ Tot Profit % โ”ƒ Avg Duration โ”ƒ Win Draw Loss Win% โ”ƒ +โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ +โ”‚ roi โ”‚ 67 โ”‚ 1.06 โ”‚ 245.117 โ”‚ 24.51 โ”‚ 15:49:00 โ”‚ 67 0 0 100 โ”‚ +โ”‚ exit_signal โ”‚ 4 โ”‚ -2.23 โ”‚ -31.226 โ”‚ -3.12 โ”‚ 1 day, 8:38:00 โ”‚ 0 0 4 0 โ”‚ +โ”‚ force_exit โ”‚ 3 โ”‚ -4.56 โ”‚ -44.461 โ”‚ -4.45 โ”‚ 6 days, 2:03:00 โ”‚ 0 0 3 0 โ”‚ +โ”‚ stop_loss โ”‚ 3 โ”‚ -10.14 โ”‚ -112.273 โ”‚ -11.23 โ”‚ 1 day, 3:05:00 โ”‚ 0 0 3 0 โ”‚ +โ”‚ TOTAL โ”‚ 77 โ”‚ 0.23 โ”‚ 57.157 โ”‚ 5.72 โ”‚ 22:12:00 โ”‚ 67 0 10 87.0 โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + MIXED TAG STATS +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“ +โ”ƒ Enter Tag โ”ƒ Exit Reason โ”ƒ Trades โ”ƒ Avg Profit % โ”ƒ Tot Profit โ”ƒ Tot Profit % โ”ƒ Avg Duration โ”ƒ Win Draw Loss Win% โ”ƒ +โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ +โ”‚ โ”‚ roi โ”‚ 67 โ”‚ 1.06 โ”‚ 245.117 โ”‚ 24.51 โ”‚ 15:49:00 โ”‚ 67 0 0 100 โ”‚ +โ”‚ โ”‚ exit_signal โ”‚ 4 โ”‚ -2.23 โ”‚ -31.226 โ”‚ -3.12 โ”‚ 1 day, 8:38:00 โ”‚ 0 0 4 0 โ”‚ +โ”‚ โ”‚ force_exit โ”‚ 3 โ”‚ -4.56 โ”‚ -44.461 โ”‚ -4.45 โ”‚ 6 days, 2:03:00 โ”‚ 0 0 3 0 โ”‚ +โ”‚ โ”‚ stop_loss โ”‚ 3 โ”‚ -10.14 โ”‚ -112.273 โ”‚ -11.23 โ”‚ 1 day, 3:05:00 โ”‚ 0 0 3 0 โ”‚ +โ”‚ TOTAL โ”‚ โ”‚ 77 โ”‚ 0.23 โ”‚ 57.157 โ”‚ 5.72 โ”‚ 22:12:00 โ”‚ 67 0 10 87.0 โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + SUMMARY METRICS +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“ +โ”ƒ Metric โ”ƒ Value โ”ƒ +โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ +โ”‚ Backtesting from โ”‚ 2025-07-01 00:00:00 โ”‚ +โ”‚ Backtesting to โ”‚ 2025-08-01 00:00:00 โ”‚ +โ”‚ Trading Mode โ”‚ Isolated Futures โ”‚ +โ”‚ Max open trades โ”‚ 3 โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ Total/Daily Avg Trades โ”‚ 77 / 2.48 โ”‚ +โ”‚ Starting balance โ”‚ 1000 USDT โ”‚ +โ”‚ Final balance โ”‚ 1057.157 USDT โ”‚ +โ”‚ Absolute profit โ”‚ 57.157 USDT โ”‚ +โ”‚ Total profit % โ”‚ 5.72% โ”‚ +โ”‚ CAGR % โ”‚ 92.41% โ”‚ +โ”‚ Sharpe (closed trades) โ”‚ 3.89 โ”‚ +โ”‚ Sortino (closed trades) โ”‚ 2.57 โ”‚ +โ”‚ Calmar (closed trades) โ”‚ 43.03 โ”‚ +โ”‚ SQN โ”‚ 0.71 โ”‚ +โ”‚ Profit factor โ”‚ 1.30 โ”‚ +โ”‚ Expectancy (Ratio) โ”‚ 0.74 (0.04) โ”‚ +โ”‚ Avg. daily profit โ”‚ 1.844 USDT โ”‚ +โ”‚ Avg. stake amount โ”‚ 345.478 USDT โ”‚ +โ”‚ Market change โ”‚ 30.51% โ”‚ +โ”‚ Total trade volume โ”‚ 53390.788 USDT โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ Long / Short trades โ”‚ 67 / 10 โ”‚ +โ”‚ Long / Short profit % โ”‚ 9.19% / -3.48% โ”‚ +โ”‚ Long / Short profit USDT โ”‚ 91.940 / -34.783 โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ Best Pair โ”‚ LTC/USDT:USDT 5.69% โ”‚ +โ”‚ Worst Pair โ”‚ ADA/USDT:USDT -5.21% โ”‚ +โ”‚ Best trade โ”‚ XRP/USDT:USDT 2.00% โ”‚ +โ”‚ Worst trade โ”‚ ADA/USDT:USDT -10.17% โ”‚ +โ”‚ Best day โ”‚ 27.031 USDT โ”‚ +โ”‚ Worst day โ”‚ -47.826 USDT โ”‚ +โ”‚ Days win/draw/lose โ”‚ 20 / 6 / 5 โ”‚ +โ”‚ Min/Max/Avg. Duration Winners โ”‚ 0d 00:35 / 5d 18:15 / 0d 15:49 โ”‚ +โ”‚ Min/Max/Avg. Duration Losers โ”‚ 0d 10:40 / 17d 08:00 / 2d 17:00 โ”‚ +โ”‚ Max Consecutive Wins / Loss โ”‚ 36 / 3 โ”‚ +โ”‚ Rejected Entry signals โ”‚ 258 โ”‚ +โ”‚ Entry/Exit Timeouts โ”‚ 0 / 0 โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ Min/Max balance (closed trades) โ”‚ 1003.205 USDT / 1151.425 USDT โ”‚ +โ”‚ Max % of account underwater โ”‚ 8.19% โ”‚ +โ”‚ Absolute drawdown โ”‚ 94.268 USDT (8.19%) โ”‚ +โ”‚ Drawdown duration โ”‚ 9 days 08:50:00 โ”‚ +โ”‚ Profit at drawdown start โ”‚ 151.425 USDT โ”‚ +โ”‚ Profit at drawdown end โ”‚ 57.157 USDT โ”‚ +โ”‚ Drawdown start โ”‚ 2025-07-22 15:10:00 โ”‚ +โ”‚ Drawdown end โ”‚ 2025-08-01 00:00:00 โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ Wallet based Metrics โ”‚ โ”‚ +โ”‚ Min/Max balance (wallet balance) โ”‚ 1000 USDT / 1151.425 USDT โ”‚ +โ”‚ Min/Max balance dates (wallet balance) โ”‚ 2025-07-01 00:05:00 / 2025-07-22 15:15:00 โ”‚ +โ”‚ Max % of account underwater (balance) โ”‚ 5.01% โ”‚ +โ”‚ Absolute drawdown (wallet balance) โ”‚ 54.76 USDT (4.76%) โ”‚ +โ”‚ Drawdown duration โ”‚ 7 days 20:35:00 โ”‚ +โ”‚ Profit at drawdown start โ”‚ 151.425 USDT โ”‚ +โ”‚ Profit at drawdown end โ”‚ 96.664 USDT โ”‚ +โ”‚ Drawdown start โ”‚ 2025-07-22 15:15:00 โ”‚ +โ”‚ Drawdown end โ”‚ 2025-07-30 11:50:00 โ”‚ +โ”‚ Sharpe (daily wallet balance) โ”‚ 4.42 โ”‚ +โ”‚ Sortino (daily wallet balance) โ”‚ 4.35 โ”‚ +โ”‚ Calmar (daily wallet balance) โ”‚ 136.07 โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ Backtested 2025-07-01 00:00:00 -> 2025-08-01 00:00:00 | Max open trades : 3 - STRATEGY SUMMARY -โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“ -โ”ƒ Strategy โ”ƒ Trades โ”ƒ Avg Profit % โ”ƒ Tot Profit USDT โ”ƒ Tot Profit % โ”ƒ Avg Duration โ”ƒ Win Draw Loss Win% โ”ƒ Drawdown โ”ƒ -โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ -โ”‚ SampleStrategy โ”‚ 77 โ”‚ 0.22 โ”‚ 54.774 โ”‚ 5.48 โ”‚ 22:12:00 โ”‚ 67 0 10 87.0 โ”‚ 94.647 USDT 8.23% โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + STRATEGY SUMMARY +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“ +โ”ƒ Strategy โ”ƒ Trades โ”ƒ Avg Profit % โ”ƒ Tot Profit โ”ƒ Tot Profit % โ”ƒ Avg Duration โ”ƒ Win Draw Loss Win% โ”ƒ Drawdown โ”ƒ +โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ +โ”‚ SampleStrategy โ”‚ 77 โ”‚ 0.23 โ”‚ 57.157 โ”‚ 5.72 โ”‚ 22:12:00 โ”‚ 67 0 10 87.0 โ”‚ 94.268 8.19% โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + ``` ### Backtesting report table @@ -329,54 +343,72 @@ The last element of the backtest report is the summary metrics table. It contains key metrics about the performance of your strategy on backtesting data. ``` -โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“ -โ”ƒ Metric โ”ƒ Value โ”ƒ -โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ -โ”‚ Backtesting from โ”‚ 2025-07-01 00:00:00 โ”‚ -โ”‚ Backtesting to โ”‚ 2025-08-01 00:00:00 โ”‚ -โ”‚ Trading Mode โ”‚ Isolated Futures โ”‚ -โ”‚ Max open trades โ”‚ 3 โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ Total/Daily Avg Trades โ”‚ 72 / 2.32 โ”‚ -โ”‚ Starting balance โ”‚ 1000 USDT โ”‚ -โ”‚ Final balance โ”‚ 1106.734 USDT โ”‚ -โ”‚ Absolute profit โ”‚ 106.734 USDT โ”‚ -โ”‚ Total profit % โ”‚ 10.67% โ”‚ -โ”‚ CAGR % โ”‚ 230.04% โ”‚ -โ”‚ Sortino โ”‚ 4.99 โ”‚ -โ”‚ Sharpe โ”‚ 8.00 โ”‚ -โ”‚ Calmar โ”‚ 77.76 โ”‚ -โ”‚ SQN โ”‚ 1.52 โ”‚ -โ”‚ Profit factor โ”‚ 1.79 โ”‚ -โ”‚ Expectancy (Ratio) โ”‚ 1.48 (0.07) โ”‚ -โ”‚ Avg. daily profit โ”‚ 3.443 USDT โ”‚ -โ”‚ Avg. stake amount โ”‚ 363.133 USDT โ”‚ -โ”‚ Total trade volume โ”‚ 52466.174 USDT โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ Best Pair โ”‚ LTC/USDT:USDT 4.48% โ”‚ -โ”‚ Worst Pair โ”‚ ADA/USDT:USDT -1.78% โ”‚ -โ”‚ Best trade โ”‚ ETC/USDT:USDT 2.00% โ”‚ -โ”‚ Worst trade โ”‚ ADA/USDT:USDT -10.17% โ”‚ -โ”‚ Best day โ”‚ 23.535 USDT โ”‚ -โ”‚ Worst day โ”‚ -49.813 USDT โ”‚ -โ”‚ Days win/draw/lose โ”‚ 21 / 6 / 4 โ”‚ -โ”‚ Min/Max/Avg. Duration Winners โ”‚ 0d 00:35 / 5d 18:15 / 0d 15:30 โ”‚ -โ”‚ Min/Max/Avg. Duration Losers โ”‚ 0d 12:00 / 17d 08:00 / 3d 23:28 โ”‚ -โ”‚ Max Consecutive Wins / Loss โ”‚ 58 / 4 โ”‚ -โ”‚ Rejected Entry signals โ”‚ 254 โ”‚ -โ”‚ Entry/Exit Timeouts โ”‚ 0 / 0 โ”‚ -โ”‚ โ”‚ โ”‚ -โ”‚ Min balance โ”‚ 1003.168 USDT โ”‚ -โ”‚ Max balance โ”‚ 1209 USDT โ”‚ -โ”‚ Max % of account underwater โ”‚ 8.46% โ”‚ -โ”‚ Absolute drawdown โ”‚ 102.266 USDT (8.46%) โ”‚ -โ”‚ Drawdown duration โ”‚ 9 days 08:50:00 โ”‚ -โ”‚ Profit at drawdown start โ”‚ 209 USDT โ”‚ -โ”‚ Profit at drawdown end โ”‚ 106.734 USDT โ”‚ -โ”‚ Drawdown start โ”‚ 2025-07-22 15:10:00 โ”‚ -โ”‚ Drawdown end โ”‚ 2025-08-01 00:00:00 โ”‚ -โ”‚ Market change โ”‚ 30.51% โ”‚ -โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + SUMMARY METRICS +โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ณโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”“ +โ”ƒ Metric โ”ƒ Value โ”ƒ +โ”กโ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ•‡โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”โ”ฉ +โ”‚ Backtesting from โ”‚ 2025-07-01 00:00:00 โ”‚ +โ”‚ Backtesting to โ”‚ 2025-08-01 00:00:00 โ”‚ +โ”‚ Trading Mode โ”‚ Isolated Futures โ”‚ +โ”‚ Max open trades โ”‚ 3 โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ Total/Daily Avg Trades โ”‚ 77 / 2.48 โ”‚ +โ”‚ Starting balance โ”‚ 1000 USDT โ”‚ +โ”‚ Final balance โ”‚ 1057.157 USDT โ”‚ +โ”‚ Absolute profit โ”‚ 57.157 USDT โ”‚ +โ”‚ Total profit % โ”‚ 5.72% โ”‚ +โ”‚ CAGR % โ”‚ 92.41% โ”‚ +โ”‚ Sharpe (closed trades) โ”‚ 3.89 โ”‚ +โ”‚ Sortino (closed trades) โ”‚ 2.57 โ”‚ +โ”‚ Calmar (closed trades) โ”‚ 43.03 โ”‚ +โ”‚ SQN โ”‚ 0.71 โ”‚ +โ”‚ Profit factor โ”‚ 1.30 โ”‚ +โ”‚ Expectancy (Ratio) โ”‚ 0.74 (0.04) โ”‚ +โ”‚ Avg. daily profit โ”‚ 1.844 USDT โ”‚ +โ”‚ Avg. stake amount โ”‚ 345.478 USDT โ”‚ +โ”‚ Market change โ”‚ 30.51% โ”‚ +โ”‚ Total trade volume โ”‚ 53390.788 USDT โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ Long / Short trades โ”‚ 67 / 10 โ”‚ +โ”‚ Long / Short profit % โ”‚ 9.19% / -3.48% โ”‚ +โ”‚ Long / Short profit USDT โ”‚ 91.940 / -34.783 โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ Best Pair โ”‚ LTC/USDT:USDT 5.69% โ”‚ +โ”‚ Worst Pair โ”‚ ADA/USDT:USDT -5.21% โ”‚ +โ”‚ Best trade โ”‚ XRP/USDT:USDT 2.00% โ”‚ +โ”‚ Worst trade โ”‚ ADA/USDT:USDT -10.17% โ”‚ +โ”‚ Best day โ”‚ 27.031 USDT โ”‚ +โ”‚ Worst day โ”‚ -47.826 USDT โ”‚ +โ”‚ Days win/draw/lose โ”‚ 20 / 6 / 5 โ”‚ +โ”‚ Min/Max/Avg. Duration Winners โ”‚ 0d 00:35 / 5d 18:15 / 0d 15:49 โ”‚ +โ”‚ Min/Max/Avg. Duration Losers โ”‚ 0d 10:40 / 17d 08:00 / 2d 17:00 โ”‚ +โ”‚ Max Consecutive Wins / Loss โ”‚ 36 / 3 โ”‚ +โ”‚ Rejected Entry signals โ”‚ 258 โ”‚ +โ”‚ Entry/Exit Timeouts โ”‚ 0 / 0 โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ Min/Max balance (closed trades) โ”‚ 1003.205 USDT / 1151.425 USDT โ”‚ +โ”‚ Max % of account underwater โ”‚ 8.19% โ”‚ +โ”‚ Absolute drawdown โ”‚ 94.268 USDT (8.19%) โ”‚ +โ”‚ Drawdown duration โ”‚ 9 days 08:50:00 โ”‚ +โ”‚ Profit at drawdown start โ”‚ 151.425 USDT โ”‚ +โ”‚ Profit at drawdown end โ”‚ 57.157 USDT โ”‚ +โ”‚ Drawdown start โ”‚ 2025-07-22 15:10:00 โ”‚ +โ”‚ Drawdown end โ”‚ 2025-08-01 00:00:00 โ”‚ +โ”‚ โ”‚ โ”‚ +โ”‚ Wallet based Metrics โ”‚ โ”‚ +โ”‚ Min/Max balance (wallet balance) โ”‚ 1000 USDT / 1151.425 USDT โ”‚ +โ”‚ Min/Max balance dates (wallet balance) โ”‚ 2025-07-01 00:05:00 / 2025-07-22 15:15:00 โ”‚ +โ”‚ Max % of account underwater (balance) โ”‚ 5.01% โ”‚ +โ”‚ Absolute drawdown (wallet balance) โ”‚ 54.76 USDT (4.76%) โ”‚ +โ”‚ Drawdown duration โ”‚ 7 days 20:35:00 โ”‚ +โ”‚ Profit at drawdown start โ”‚ 151.425 USDT โ”‚ +โ”‚ Profit at drawdown end โ”‚ 96.664 USDT โ”‚ +โ”‚ Drawdown start โ”‚ 2025-07-22 15:15:00 โ”‚ +โ”‚ Drawdown end โ”‚ 2025-07-30 11:50:00 โ”‚ +โ”‚ Sharpe (daily wallet balance) โ”‚ 4.42 โ”‚ +โ”‚ Sortino (daily wallet balance) โ”‚ 4.35 โ”‚ +โ”‚ Calmar (daily wallet balance) โ”‚ 136.07 โ”‚ +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ดโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ ``` - `Backtesting from` / `Backtesting to`: Backtesting range (usually defined with the `--timerange` option). @@ -388,14 +420,15 @@ It contains key metrics about the performance of your strategy on backtesting da - `Absolute profit`: Profit made in stake currency. - `Total profit %`: Total profit. Aligned to the `TOTAL` row's `Tot Profit %` from the first table. Calculated as `(End capital โˆ’ Starting capital) / Starting capital`. - `CAGR %`: Compound annual growth rate. -- `Sortino`: Annualized Sortino ratio. -- `Sharpe`: Annualized Sharpe ratio. -- `Calmar`: Annualized Calmar ratio. +- `Sharpe (closed trades)`: Annualized Sharpe ratio including only closed trades (ignoring open trades with profits or losses). +- `Sortino (closed trades)`: Annualized Sortino ratio including only closed trades (ignoring open trades with profits or losses). +- `Calmar (closed trades)`: Annualized Calmar ratio including only closed trades (ignoring open trades with profits or losses). - `SQN`: System Quality Number (SQN) - by Van Tharp. - `Profit factor`: Sum of the profits of all winning trades divided by the sum of the losses of all losing trades. - `Expectancy (Ratio)`: Expectancy ratio, which is the average profit or loss per trade. A negative expectancy ratio means that your strategy is not profitable. - `Avg. daily profit`: Average profit per day, calculated as `(Total Profit / Backtest Days)`. - `Avg. stake amount`: Average stake amount, either `stake_amount` or the average when using dynamic stake amount. +- `Market change`: Change of the market during the backtest period. Calculated as the average of all pairs' changes from the first to the last candle using the "close" column. - `Total trade volume`: Volume generated on the exchange to reach the above profit. - `Long / Short trades`: Split long/short trade counts (only shown when short trades were made). - `Long / Short profit %`: Profit percentage for long and short trades (only shown when short trades were made). @@ -409,13 +442,21 @@ It contains key metrics about the performance of your strategy on backtesting da - `Max Consecutive Wins / Loss`: Maximum consecutive wins/losses in a row. - `Rejected Entry signals`: Trade entry signals that could not be acted upon due to `max_open_trades` being reached. - `Entry/Exit Timeouts`: Entry/exit orders which did not fill (only applicable if custom pricing is used). -- `Min balance` / `Max balance`: Lowest and Highest Wallet balance during the backtest period. +- `Min/Max balance (closed trades)`: Lowest and Highest Wallet balance during the backtest period based on closed trades trades. - `Max % of account underwater`: Maximum percentage your account has decreased from the top since the simulation started. Calculated as the maximum of `(Max Balance - Current Balance) / (Max Balance)`. - `Absolute drawdown`: Maximum absolute drawdown experienced, including percentage relative to the account calculated as `(Absolute Drawdown) / (DrawdownHigh + startingBalance)`.. +- `Absolute drawdown (wallet balance)`: Maximum absolute drawdown experienced based on the unrealized balance, including percentage relative to the account calculated as `(Absolute Drawdown) / (DrawdownHigh + startingBalance)`. - `Drawdown duration`: Duration of the largest drawdown period. - `Profit at drawdown start` / `Profit at drawdown end`: Profit at the beginning and end of the largest drawdown period. - `Drawdown start` / `Drawdown end`: Start and end datetime for the largest drawdown (can also be visualized via the `plot-dataframe` sub-command). -- `Market change`: Change of the market during the backtest period. Calculated as the average of all pairs' changes from the first to the last candle using the "close" column. +- `Min/Max balance (wallet balance)`: Lowest and Highest Wallet balance during the backtest period - including capital tied in open trades. +- `Min/Max balance dates (wallet balance)`: Dates when the minimum and maximum unrealized balance occurred. +- `Sharpe (wallet balance)` Annualized Sharpe ratio calculation including unrealized profits. +- `Sortino (wallet balance)` Annualized Sortino ratio calculation including unrealized profits. +- `Calmar (wallet balance)` Annualized Calmar ratio calculation including unrealized profits. + +!!! Tip "Wallet based Metrics" + The metrics under the "Wallet based Metrics" section are calculated based on the unrealized balance, which includes the capital tied in open trades. This provides a more comprehensive view of the strategy's performance, as it accounts for both realized and unrealized profits and losses. ### Daily / Weekly / Monthly / Yearly breakdown diff --git a/docs/exchanges.md b/docs/exchanges.md index 14edc11c6..7f1fee3f6 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -345,6 +345,15 @@ API Keys for live futures trading must have the following permissions: We do strongly recommend to limit all API keys to the IP you're going to use it from. +### Bybit Demo Mode + +Bybit has a [demo mode](https://learn.bybit.com/en/bybit-guide/how-to-use-bybit-demo-trading) - which can be activated by setting `exchange.demo_trading` to `true` in the configuration. +Bybit uses live markets to simulate your trades (without market impact) - making it work very similar to freqtrade's dry-run mode. + +You'll need to use separate API keys for demo trading, which you can create on bybit's demo page. + +Demo mode is incompatible with dry-run. + ## Bitmart Bitmart requires the API key Memo (the name you give the API key) to go along with the exchange key and secret. @@ -429,31 +438,50 @@ Hyperliquid handles deposits and withdrawals on the Arbitrum One chain, a Layer * Create a different software wallet, only transfer the funds you want to trade with to that wallet, and use that wallet to trade on Hyperliquid. * If you have funds you don't want to use for trading (after making a profit for example), transfer them back to your hardware wallet. -### Hyperliquid Vault / Subaccount -Hyperliquid allows you to create either a vault or a subaccount. -To use these with Freqtrade, you will need to use the following configuration pattern: +!!! Warning "Vaults and Subaccounts" + You can only use either a vault or a subaccount - not both at the same time. + +### Hyperliquid Subaccount + +Hyperliquid allows you to create subaccounts with sufficient previous trading volume. +To use subaccounts with Freqtrade, you will need to use the following configuration pattern: ``` json "exchange": { "name": "hyperliquid", - "walletAddress": "your_master_wallet_address", // Your master wallet address (not the API wallet address and not the vault/subaccount address). + "walletAddress": "your_master_wallet_address", // Your master wallet address (not the API wallet or vault address - but not subaccount address). "privateKey": "your_api_private_key", // API wallet private key (see https://app.hyperliquid.xyz/API). You'll only need the private key. "ccxt_config": { "options": { - "vaultAddress": "your_vault_address", // Optional, only if you want to use a vault ... - "subAccountAddress": "your_subaccount_address" // OR optional, only if you want to use a subaccount + "subAccountAddress": "your_subaccount_address" // Required if you want to use a subaccount. } }, // ... } ``` -Your balance and trades will now be used from your vault / subaccount - and no longer from your main account. +Your balance and trades will now be used from your subaccount - and no longer from your main account. -!!! Note - You can only use either a vault or a subaccount - not both at the same time. +### Hyperliquid Vault +Hyperliquid allows you to create vaults. To use vaults with Freqtrade, you will need to use the following configuration pattern: + +``` json +"exchange": { + "name": "hyperliquid", + "walletAddress": "your_vault_address", // Your vault wallet address (Must also be added below in the ccxt_config.options.vaultAddress field) + "privateKey": "your_api_private_key", // API wallet private key (see https://app.hyperliquid.xyz/API). You'll only need the private key. + "ccxt_config": { + "options": { + "vaultAddress": "your_vault_address", // Optional, only if you want to use a vault ... (vault address must also be added to walletAdress) + } + }, + // ... +} +``` + +Your balance and trades will now be used from your vault - and no longer from your main account. ### Historic Hyperliquid data diff --git a/docs/faq.md b/docs/faq.md index aea0030ad..55af35d42 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -2,7 +2,7 @@ ## Supported Markets -Freqtrade supports spot trading, as well as futures trading for some selected exchanges. Please refer to the [documentation start page](index.md#supported-futures-exchanges-experimental) for an up-to-date list of supported exchanges. +Freqtrade supports spot trading, as well as futures trading for some selected exchanges. Please refer to the [documentation start page](index.md#supported-futures-exchanges) for an up-to-date list of supported exchanges. ### Can my bot open short positions? @@ -14,7 +14,7 @@ In spot markets, you can in some cases use leveraged spot tokens, which reflect ### Can my bot trade options or futures? -Futures trading is supported for selected exchanges. Please refer to the [documentation start page](index.md#supported-futures-exchanges-experimental) for an up-to-date list of supported exchanges. +Futures trading is supported for selected exchanges. Please refer to the [documentation start page](index.md#supported-futures-exchanges) for an up-to-date list of supported exchanges. ## Beginner Tips & Tricks diff --git a/docs/freq-ui.md b/docs/freq-ui.md index ff9758888..d561810f7 100644 --- a/docs/freq-ui.md +++ b/docs/freq-ui.md @@ -46,6 +46,23 @@ On this page, you can also interact with the bot by starting and stopping it and ![FreqUI - trade view](assets/freqUI-trade-pane-dark.png#only-dark) ![FreqUI - trade view](assets/freqUI-trade-pane-light.png#only-light) +### Dashboard + +The dashboard view provides an overview of the bot's performance and status. +If multiple bots are connected, the dashboard will show an overview of all connected bots, allowing you to easily switch between them or show just a subset of available bots. + +#### Wallet Balance + +New in freqtrade 2026.4: This shows the balance of the bot over time. + +Compared to the "cumulative Profit" chart, this chart will show the actual balance of the bot over time, including unrealized profit and losses, as well as deposits and withdrawals. + +Historic data has re-populated based on available exchange data - however is assumed to be best-effort and may not be 100% accurate. +More specifically, it won't cover deposits and withdrawals, and will assume a starting balance of current balance - profit/losses. + +For clarity - a "Capture start" marker line is shown on the chart, which indicates the point at which the migration to the new wallet balance tracking system happened. +Only beyond this point, the wallet balance is expected to be accurate. + ### Plot Configurator FreqUI Plots can be configured either via a `plot_config` configuration object in the strategy (which can be loaded via "from strategy" button) or via the UI. diff --git a/docs/plotting.md b/docs/plotting.md index ae480e78f..599a71dbc 100644 --- a/docs/plotting.md +++ b/docs/plotting.md @@ -111,10 +111,10 @@ It also allows multiple subplots to display both MACD and RSI at the same time. Plot type can be configured using `type` key. Possible types are: -* `scatter` corresponding to `plotly.graph_objects.Scatter` class (default). -* `bar` corresponding to `plotly.graph_objects.Bar` class. +* `scatter` corresponding a scatter plot. +* `bar` corresponding to a bar plot. -Extra parameters to `plotly.graph_objects.*` constructor can be specified in `plotly` dict. +Extra parameters to `plotly.graph_objects.*` constructor can be specified in `plotly` dict - these are only supported when using plotly as plotting library and will be ignored when using freq-ui. Sample configuration with inline comments explaining the process: @@ -163,7 +163,7 @@ def plot_config(self): ``` ??? Note "As attribute (former method)" - Assigning plot_config is also possible as Attribute (this used to be the default way). + Assigning `plot_config` is also possible as Attribute (this used to be the default way). This has the disadvantage that strategy parameters are not available, preventing certain configurations from working. ``` python diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index 5267c55df..fbdb3701e 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,7 +1,7 @@ markdown==3.10.2 mkdocs==1.6.1 -mkdocs-material==9.7.5 +mkdocs-material==9.7.6 mdx_truly_sane_lists==1.3 -pymdown-extensions==10.21 +pymdown-extensions==10.21.2 jinja2==3.1.6 -mike==2.1.4 +mike==2.2.0 diff --git a/docs/stoploss.md b/docs/stoploss.md index e42a885c8..9a0bbb9e8 100644 --- a/docs/stoploss.md +++ b/docs/stoploss.md @@ -39,6 +39,22 @@ The Order-type will be ignored if only one mode is available. In that case, the bot will fallback to using the `emergency_exit` order type to place a market order as placing the stoploss order failed. Freqtrade currently does not implement a limitation to avoid this situation, so please ensure your stoploss values are within reasonable limits for your exchange or disable stoploss on exchange. +### Which order type is used for stoploss on exchange? + +The order type used for stoploss on exchange is determined by the `stoploss` value and the exchange capabilities. +If your selected exchange supports both stop-limit and stop-market orders, then the `stoploss` value will determine which order type is used for stoploss on exchange. +If your exchange only supports one of the two order types, you must configure your `stoploss` value accordingly, otherwise the bot will fail to start. + +### Which order type should i use for stoploss on exchange? + +If we translate the two stoploss order types into human words - they would be something like this: + +* **stoploss-market** -> "when stop triggers, get me the hell out of here at whatever price". +* **stoploss-limit** -> "when stop triggers, place a limit order x% below the stoploss price. I accept a loss of "stoploss + 1%" at worst - but if price jumps further - i accept to wait for price to get back down to me, potentially resulting in a much bigger loss than "stoploss + 1%". + +As a consequence, we recommend using stoploss-market orders whenever possible, as the main point of a stoploss is to get you out of a position when the market is crashing, and in such situations, you'll want to exit the position immediately at the best available price, rather than risking a limit order not getting filled and potentially incurring even greater losses. +The choice is ultimately up to you, but please be aware of the risk of using stoploss-limit orders, especially in volatile markets. + ### stoploss_on_exchange and stoploss_on_exchange_limit_ratio Enable or Disable stop loss on exchange. diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index 34ed56bbc..beadda2b4 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,6 +1,6 @@ """Freqtrade bot""" -__version__ = "2026.3" +__version__ = "2026.4" if "dev" in __version__: from pathlib import Path diff --git a/freqtrade/commands/db_commands.py b/freqtrade/commands/db_commands.py index d5acf8f14..384af6e2f 100644 --- a/freqtrade/commands/db_commands.py +++ b/freqtrade/commands/db_commands.py @@ -8,15 +8,10 @@ logger = logging.getLogger(__name__) def start_convert_db(args: dict[str, Any]) -> None: - from sqlalchemy import func, select - from sqlalchemy.orm import make_transient from freqtrade.configuration.config_setup import setup_utils_configuration - from freqtrade.persistence import Order, Trade, init_db - from freqtrade.persistence.custom_data import _CustomData - from freqtrade.persistence.key_value_store import _KeyValueStoreModel - from freqtrade.persistence.migrations import set_sequence_ids - from freqtrade.persistence.pairlock import PairLock + from freqtrade.persistence import Trade, init_db + from freqtrade.persistence.db_migration import migrate_db config = setup_utils_configuration(args, RunMode.UTIL_NO_EXCHANGE) @@ -24,56 +19,4 @@ def start_convert_db(args: dict[str, Any]) -> None: session_target = Trade.session init_db(config["db_url_from"]) logger.info("Starting db migration.") - - trade_count = 0 - pairlock_count = 0 - kv_count = 0 - custom_data_count = 0 - for trade in Trade.get_trades(): - trade_count += 1 - make_transient(trade) - for o in trade.orders: - make_transient(o) - - session_target.add(trade) - - session_target.commit() - - for pairlock in PairLock.get_all_locks(): - pairlock_count += 1 - make_transient(pairlock) - session_target.add(pairlock) - session_target.commit() - - for kv in _KeyValueStoreModel.session.scalars(select(_KeyValueStoreModel)): - kv_count += 1 - make_transient(kv) - session_target.add(kv) - session_target.commit() - - for cd in _CustomData.session.scalars(select(_CustomData)): - custom_data_count += 1 - make_transient(cd) - session_target.add(cd) - session_target.commit() - - # Update sequences - max_trade_id = session_target.scalar(select(func.max(Trade.id))) - max_order_id = session_target.scalar(select(func.max(Order.id))) - max_pairlock_id = session_target.scalar(select(func.max(PairLock.id))) - max_kv_id = session_target.scalar(select(func.max(_KeyValueStoreModel.id))) - max_custom_data_id = session_target.scalar(select(func.max(_CustomData.id))) - - set_sequence_ids( - session_target.get_bind(), - trade_id=(max_trade_id or 0) + 1, - order_id=(max_order_id or 0) + 1, - pairlock_id=(max_pairlock_id or 0) + 1, - kv_id=(max_kv_id or 0) + 1, - custom_data_id=(max_custom_data_id or 0) + 1, - ) - - logger.info( - f"Migrated {trade_count} Trades, {pairlock_count} Pairlocks, " - f"{kv_count} Key-Value pairs, and {custom_data_count} Custom Data entries." - ) + migrate_db(session_target) diff --git a/freqtrade/config_schema/config_schema.py b/freqtrade/config_schema/config_schema.py index fc2c42441..7fc885662 100644 --- a/freqtrade/config_schema/config_schema.py +++ b/freqtrade/config_schema/config_schema.py @@ -236,6 +236,10 @@ CONF_SCHEMA = { "type": "string", "enum": BACKTEST_CACHE_AGE, }, + "skip_wallet_history_migration": { + "description": "Disable wallet history migration.", + "type": "boolean", + }, # Hyperopt "hyperopt_path": { "description": "Specify additional lookup path for Hyperopt Loss functions.", diff --git a/freqtrade/configuration/config_validation.py b/freqtrade/configuration/config_validation.py index 9e9f0ada9..1c22c2bdd 100644 --- a/freqtrade/configuration/config_validation.py +++ b/freqtrade/configuration/config_validation.py @@ -92,6 +92,7 @@ def validate_config_consistency(conf: dict[str, Any], *, preliminary: bool = Fal _validate_consumers(conf) validate_migrated_strategy_settings(conf) _validate_orderflow(conf) + _validate_demo_trading(conf) # validate configuration before returning logger.info("Validating configuration ...") @@ -413,6 +414,11 @@ def _validate_orderflow(conf: dict[str, Any]) -> None: ) +def _validate_demo_trading(conf: dict[str, Any]) -> None: + if conf.get("exchange", {}).get("demo_trading", False) and conf.get("dry_run", False): + raise ConfigurationError("Demo trading cannot be used together with dry_run.") + + def _strategy_settings(conf: dict[str, Any]) -> None: process_deprecated_setting(conf, None, "use_sell_signal", None, "use_exit_signal") process_deprecated_setting(conf, None, "sell_profit_only", None, "exit_profit_only") diff --git a/freqtrade/data/btanalysis/__init__.py b/freqtrade/data/btanalysis/__init__.py index 2253a45ae..bd8b55df8 100644 --- a/freqtrade/data/btanalysis/__init__.py +++ b/freqtrade/data/btanalysis/__init__.py @@ -7,6 +7,7 @@ from .bt_fileutils import ( get_backtest_market_change, get_backtest_result, get_backtest_resultlist, + get_backtest_wallet_change, get_latest_backtest_filename, get_latest_hyperopt_file, get_latest_hyperopt_filename, diff --git a/freqtrade/data/btanalysis/bt_fileutils.py b/freqtrade/data/btanalysis/bt_fileutils.py index e1c0ea64c..9328ba428 100644 --- a/freqtrade/data/btanalysis/bt_fileutils.py +++ b/freqtrade/data/btanalysis/bt_fileutils.py @@ -10,7 +10,6 @@ from io import BytesIO, StringIO from pathlib import Path from typing import Any, Literal -import numpy as np import pandas as pd from freqtrade.constants import LAST_BT_RESULT_FN @@ -308,10 +307,31 @@ def get_backtest_market_change(filename: Path, include_ts: bool = True) -> pd.Da else: df = pd.read_feather(filename) if include_ts: - df.loc[:, "__date_ts"] = df.loc[:, "date"].astype(np.int64) // 1000 // 1000 + df.loc[:, "__date_ts"] = df.loc[:, "date"].dt.as_unit("ms").astype("int64") return df +def get_backtest_wallet_change(filename: Path, strategy_name: str) -> pd.DataFrame | None: + """ + Read backtest wallet change file. + :param filename: Path to the backtest result zip file + :param strategy_name: Name of the strategy to load + :return: DataFrame with wallet change data + """ + if filename.suffix != ".zip": + return None + + try: + data = load_file_from_zip(filename, f"{filename.stem}_{strategy_name}_wallet.feather") + df = pd.read_feather(BytesIO(data)) + + df.loc[:, "__date_ts"] = df.loc[:, "date"].dt.as_unit("ms").astype("int64") + return df + except ValueError: + pass + return None + + def find_existing_backtest_stats( dirname: Path | str, run_ids: dict[str, str], min_backtest_date: datetime | None = None ) -> dict[str, Any]: @@ -503,13 +523,16 @@ def load_backtest_analysis_data( return None -def trade_list_to_dataframe(trades: list[Trade] | list[LocalTrade]) -> pd.DataFrame: +def trade_list_to_dataframe( + trades: list[Trade] | list[LocalTrade], *, minified: bool = True +) -> pd.DataFrame: """ Convert list of Trade objects to pandas Dataframe :param trades: List of trade objects + :param minified: Whether to use minified version of trade JSON :return: Dataframe with BT_DATA_COLUMNS """ - df = pd.DataFrame.from_records([t.to_json(True) for t in trades], columns=BT_DATA_COLUMNS) + df = pd.DataFrame.from_records([t.to_json(minified) for t in trades], columns=BT_DATA_COLUMNS) if len(df) > 0: df["close_date"] = pd.to_datetime(df["close_timestamp"], unit="ms", utc=True) df["open_date"] = pd.to_datetime(df["open_timestamp"], unit="ms", utc=True) diff --git a/freqtrade/data/btanalysis/trade_parallelism.py b/freqtrade/data/btanalysis/trade_parallelism.py index eabdcf08a..8ed9702e0 100644 --- a/freqtrade/data/btanalysis/trade_parallelism.py +++ b/freqtrade/data/btanalysis/trade_parallelism.py @@ -1,9 +1,15 @@ import logging +from datetime import datetime import numpy as np import pandas as pd from freqtrade.constants import IntOrInf +from freqtrade.exchange import ( + timeframe_to_prev_date, + timeframe_to_resample_freq, +) +from freqtrade.util import dt_from_ts logger = logging.getLogger(__name__) @@ -58,3 +64,95 @@ def evaluate_result_multi( """ df_final = analyze_trade_parallelism(trades, timeframe) return df_final[df_final["open_trades"] > max_open_trades] + + +def balance_distribution_over_time( + trades: pd.DataFrame, + min_date: datetime, + max_date: datetime, + timeframe: str, + stake_currency: str, + start_balance: float, + pairlist: list[str], +) -> pd.DataFrame: + """ + Return a dataframe with stake_currency and the pairlist as columns + Each column will contain the amount of the currency at the given time + Columns added are: + - stake_currency: amount of stake currency + - : amount of base currency in the pair + - _leverage: leverage used for the pair at the time (NaN if no open trade) + - _is_short: 1 if the open trade is short, 0 if long (NaN if no open trade) + - _collateral: amount of stake currency used as collateral for open trades + :param trades: Trades Dataframe - can be loaded from backtest, or created + via trade_list_to_dataframe + :param timeframe: Frequency to use for the resulting dataframe + :param min_date: start date + :param max_date: End date (will be rounded down to timeframe) + :param stake_currency: The stake currency + :param start_balance: Starting balance in stake currency + :param pairlist: List of trading pairs to include in the dataframe + Can be obtained via trade_df["pair"].unique() + For pairs without trades, the column will be all zeros + :return: Dataframe with balance distribution over time + """ + min_date_res = timeframe_to_prev_date(timeframe, min_date) + max_date_res = timeframe_to_prev_date(timeframe, max_date) + index = pd.date_range(min_date_res, max_date_res, freq=timeframe_to_resample_freq(timeframe)) + pairs_lev = [f"{pair}_leverage" for pair in pairlist] + pairs_is_short = [f"{pair}_is_short" for pair in pairlist] + pairs_collateral = [f"{pair}_collateral" for pair in pairlist] + pairs_lev += pairs_is_short + + df = pd.DataFrame( + index=index, columns=[stake_currency] + pairlist + pairs_lev + pairs_collateral, dtype=float + ) + # Initialize variables to starting values + df[stake_currency] = float(start_balance) + df[pairlist + pairs_collateral] = 0.0 + df[pairs_lev] = np.nan + + for trade in trades.sort_values(by=["open_date"]).itertuples(): + pair = trade.pair + end_date = trade.close_date if trade.close_date is not pd.NaT else None + # Exclude open orders - these won't have order_filled_timestamp set. + df.loc[trade.open_date : end_date, f"{pair}_leverage"] = trade.leverage + df.loc[trade.open_date : end_date, f"{pair}_is_short"] = 1 if trade.is_short else 0 + orders = [o for o in trade.orders if o["order_filled_timestamp"]] + current_position = 0 + current_collateral = 0 + for order in sorted(orders, key=lambda x: x["order_filled_timestamp"]): + filled_at = pd.Timestamp(dt_from_ts(order["order_filled_timestamp"])) + real_amount = order.get("filled", order["amount"]) + stake = order["safe_price"] * real_amount + stake_no_lev = stake / trade.leverage + if order["ft_is_entry"]: + # Entry order: lock collateral and pay fee + # For both long and short: balance decreases by collateral + fee + fee_open = stake * trade.fee_open + current_position += real_amount + current_collateral += stake_no_lev + df.loc[filled_at:end_date, pair] += real_amount + df.loc[filled_at:end_date, f"{pair}_collateral"] += stake_no_lev + df.loc[filled_at:, stake_currency] -= stake_no_lev + fee_open + else: + # Exit order: release collateral and realize profit/loss + fee_close = stake * trade.fee_close + if trade.is_short: + # For SHORT + df.loc[filled_at:, stake_currency] += ( + current_collateral * (1 + trade.leverage) - stake + ) - fee_close + else: + # For LONG + df.loc[filled_at:, stake_currency] += ( + stake - current_collateral * (trade.leverage - 1) - fee_close + ) + df.loc[filled_at:end_date, pair] -= real_amount + df.loc[filled_at:end_date, f"{pair}_collateral"] -= stake_no_lev + current_position -= real_amount + current_collateral -= stake_no_lev + + # Round to avoid floating point issues + df = df.round(14) + return df diff --git a/freqtrade/data/history/datahandlers/featherdatahandler.py b/freqtrade/data/history/datahandlers/featherdatahandler.py index ef293d6b2..23d0b2d76 100644 --- a/freqtrade/data/history/datahandlers/featherdatahandler.py +++ b/freqtrade/data/history/datahandlers/featherdatahandler.py @@ -1,6 +1,6 @@ import logging -from pandas import DataFrame, read_feather, to_datetime +from pandas import DataFrame, read_feather from pyarrow import dataset from freqtrade.configuration import TimeRange @@ -71,7 +71,7 @@ class FeatherDataHandler(IDataHandler): "volume": "float", } ) - pairdata["date"] = to_datetime(pairdata["date"], unit="ms", utc=True) + pairdata["date"] = pairdata["date"].dt.as_unit("ms") return pairdata except Exception as e: logger.exception( diff --git a/freqtrade/data/history/datahandlers/jsondatahandler.py b/freqtrade/data/history/datahandlers/jsondatahandler.py index 1a33b3e2f..a2bd3f6db 100644 --- a/freqtrade/data/history/datahandlers/jsondatahandler.py +++ b/freqtrade/data/history/datahandlers/jsondatahandler.py @@ -1,6 +1,5 @@ import logging -import numpy as np from pandas import DataFrame, read_json, to_datetime from freqtrade import misc @@ -35,8 +34,8 @@ class JsonDataHandler(IDataHandler): filename = self._pair_data_filename(self._datadir, pair, timeframe, candle_type) self.create_dir_if_needed(filename) _data = data.copy() - # Convert date to int - _data["date"] = _data["date"].astype(np.int64) // 1000 // 1000 + # Convert date to int (milliseconds) + _data["date"] = _data["date"].dt.as_unit("ms").astype("int64") # Reset index, select only appropriate columns and save as json _data.reset_index(drop=True).loc[:, self._columns].to_json( @@ -81,7 +80,7 @@ class JsonDataHandler(IDataHandler): "volume": "float", } ) - pairdata["date"] = to_datetime(pairdata["date"], unit="ms", utc=True) + pairdata["date"] = to_datetime(pairdata["date"], unit="ms", utc=True).dt.as_unit("ms") return pairdata def ohlcv_append( @@ -105,6 +104,9 @@ class JsonDataHandler(IDataHandler): :param trading_mode: Trading mode to use (used to determine the filename) """ filename = self._pair_trades_filename(self._datadir, pair, trading_mode) + # Convert StringDtype columns to object to avoid NaN serialization issues + for col in data.select_dtypes(include="string").columns: + data[col] = data[col].astype(object).where(data[col].notna(), other=None) trades = data.values.tolist() misc.file_dump_json(filename, trades, is_zip=self._use_zip) diff --git a/freqtrade/data/history/datahandlers/parquetdatahandler.py b/freqtrade/data/history/datahandlers/parquetdatahandler.py index 1813f9991..7a5cb39f1 100644 --- a/freqtrade/data/history/datahandlers/parquetdatahandler.py +++ b/freqtrade/data/history/datahandlers/parquetdatahandler.py @@ -1,6 +1,6 @@ import logging -from pandas import DataFrame, read_parquet, to_datetime +from pandas import DataFrame, read_parquet from freqtrade.configuration import TimeRange from freqtrade.constants import DEFAULT_DATAFRAME_COLUMNS, DEFAULT_TRADES_COLUMNS @@ -68,7 +68,7 @@ class ParquetDataHandler(IDataHandler): "volume": "float", } ) - pairdata["date"] = to_datetime(pairdata["date"], unit="ms", utc=True) + pairdata["date"] = pairdata["date"].dt.as_unit("ms") return pairdata except Exception as e: logger.exception( diff --git a/freqtrade/data/metrics.py b/freqtrade/data/metrics.py index a9d3963d2..0d7c65bab 100644 --- a/freqtrade/data/metrics.py +++ b/freqtrade/data/metrics.py @@ -140,7 +140,7 @@ def _calc_drawdown_series( max_drawdown_df["drawdown_relative"] = (max_balance - cumulative_balance) / max_balance else: # NOTE: This is not completely accurate, - # but might good enough if starting_balance is not available + # but will be good enough if starting_balance is not available max_drawdown_df["drawdown_relative"] = ( max_drawdown_df["high_value"] - max_drawdown_df["cumulative"] ) / max_drawdown_df["high_value"] @@ -333,6 +333,72 @@ def calculate_expectancy(trades: pd.DataFrame) -> tuple[float, float]: return expectancy, expectancy_ratio +def _calculate_annualized_ratio( + expected_returns_mean: float, + denominator: float, + annualization_factor: int = 365, +) -> float: + """ + Helper function to calculate annualized ratios like Sharpe and Sortino. + :param expected_returns_mean: Mean of the returns (expected returns) + :param denominator: Denominator of the ratio (e.g. standard deviation for Sharpe) + :param annualization_factor: Factor to annualize the ratio (default is 365 for daily returns) + :return: Annualized ratio, or -100.0 if denominator is zero or NaN to indicate this is + not optimal. + """ + if denominator != 0 and not np.isnan(denominator): + return float(expected_returns_mean / denominator * np.sqrt(annualization_factor)) + + # Define high (negative) ratio to be clear that this is NOT optimal. + return -100.0 + + +def _calculate_daily_returns_from_balance( + balance_history: pd.DataFrame, + date_col: str, + balance_col: str, +) -> pd.Series: + wallet = _prepare_balance_history(balance_history, date_col, balance_col) + if len(wallet) == 0: + return pd.DataFrame(columns=[date_col, balance_col]) + + # Sample balance to daily end-of-day values to normalize variable snapshot frequency. + daily_balance = ( + wallet.set_index(date_col)[balance_col].resample("1D").last().dropna().rename(balance_col) + ) + daily_balance = daily_balance.reset_index() + + if len(daily_balance) < 2: + return pd.Series(dtype=float) + + return daily_balance[balance_col].pct_change().dropna() + + +def _prepare_balance_history( + balance_history: pd.DataFrame, + date_col: str, + balance_col: str, +) -> pd.DataFrame: + """ + Prepare balance history for calculations by filtering out rows with + missing date or balance values. + """ + if ( + len(balance_history) == 0 + or date_col not in balance_history + or balance_col not in balance_history + ): + return pd.DataFrame(columns=[date_col, balance_col]) + + wallet = balance_history.loc[:, [date_col, balance_col]].copy() + wallet = wallet.dropna(subset=[date_col, balance_col]).sort_values(date_col) + + if len(wallet) == 0: + return pd.DataFrame(columns=[date_col, balance_col]) + + return wallet + + def calculate_sortino( trades: pd.DataFrame, min_date: datetime | None, @@ -354,14 +420,31 @@ def calculate_sortino( down_stdev = np.std(trades.loc[trades["profit_abs"] < 0, "profit_abs"] / starting_balance) - if down_stdev != 0 and not np.isnan(down_stdev): - sortino_ratio = expected_returns_mean / down_stdev * np.sqrt(365) - else: - # Define high (negative) sortino ratio to be clear that this is NOT optimal. - sortino_ratio = -100 + return _calculate_annualized_ratio(expected_returns_mean, down_stdev) - # print(expected_returns_mean, down_stdev, sortino_ratio) - return sortino_ratio + +def calculate_sortino_from_balance( + balance_history: pd.DataFrame, + date_col: str = "date", + balance_col: str = "total_quote", +) -> float: + """ + Calculate sortino ratio from historical balance snapshots. + + :param balance_history: DataFrame containing at least date and balance columns + :param date_col: Column containing timestamps + :param balance_col: Column containing historical balance values + :return: sortino + """ + daily_returns = _calculate_daily_returns_from_balance(balance_history, date_col, balance_col) + + if len(daily_returns) == 0: + return 0.0 + + expected_returns_mean = daily_returns.mean() + downside_returns = daily_returns[daily_returns < 0] + down_stdev = downside_returns.std(ddof=0) + return _calculate_annualized_ratio(expected_returns_mean, down_stdev) def calculate_sharpe( @@ -384,14 +467,67 @@ def calculate_sharpe( expected_returns_mean = total_profit.sum() / days_period up_stdev = np.std(total_profit) - if up_stdev != 0: - sharp_ratio = expected_returns_mean / up_stdev * np.sqrt(365) - else: - # Define high (negative) sharpe ratio to be clear that this is NOT optimal. - sharp_ratio = -100 + return _calculate_annualized_ratio(expected_returns_mean, up_stdev) - # print(expected_returns_mean, up_stdev, sharp_ratio) - return sharp_ratio + +def calculate_sharpe_from_balance( + balance_history: pd.DataFrame, + date_col: str = "date", + balance_col: str = "total_quote", +) -> float: + """ + Calculate sharpe ratio from historical balance snapshots. + + :param balance_history: DataFrame containing at least date and balance columns + :param date_col: Column containing timestamps + :param balance_col: Column containing historical balance values + :return: sharpe + """ + daily_returns = _calculate_daily_returns_from_balance(balance_history, date_col, balance_col) + + if len(daily_returns) == 0: + return 0.0 + + expected_returns_mean = daily_returns.mean() + up_stdev = daily_returns.std(ddof=0) + return _calculate_annualized_ratio(expected_returns_mean, up_stdev) + + +def calculate_max_drawdown_from_balance( + balance_history: pd.DataFrame, + date_col: str = "date", + balance_col: str = "total_quote", + relative: bool = False, +) -> DrawDownResult: + """ + Calculate max drawdown from historical balance snapshots. + + :param balance_history: DataFrame containing at least date and balance columns + :param date_col: Column containing timestamps + :param balance_col: Column containing historical balance values + :param relative: If True, use relative drawdown for max calculation instead of absolute + :return: DrawDownResult object + :raise: ValueError if balance-history dataframe was found empty. + """ + wallet = _prepare_balance_history( + balance_history=balance_history, + date_col=date_col, + balance_col=balance_col, + ) + + if len(wallet) < 2: + raise ValueError("Balance-history dataframe empty.") + + starting_balance = float(wallet[balance_col].iloc[0]) + wallet.loc[:, "total_balance"] = wallet[balance_col].diff().fillna(0.0) + + return calculate_max_drawdown( + wallet, + date_col=date_col, + value_col="total_balance", + starting_balance=starting_balance, + relative=relative, + ) def calculate_calmar( @@ -401,12 +537,12 @@ def calculate_calmar( starting_balance: float, ) -> float: """ - Calculate calmar + Calculate calmar from trades data. :param trades: DataFrame containing trades (requires columns close_date and profit_abs) :return: calmar """ if (len(trades) == 0) or (min_date is None) or (max_date is None) or (min_date == max_date): - return 0 + return 0.0 total_profit = trades["profit_abs"].sum() / starting_balance days_period = max(1, (max_date - min_date).days) @@ -422,16 +558,51 @@ def calculate_calmar( ) max_drawdown = drawdown.relative_account_drawdown except ValueError: - max_drawdown = 0 + return 0.0 - if max_drawdown != 0: - calmar_ratio = expected_returns_mean / max_drawdown * math.sqrt(365) - else: - # Define high (negative) calmar ratio to be clear that this is NOT optimal. - calmar_ratio = -100 + return _calculate_annualized_ratio(expected_returns_mean, max_drawdown) - # print(expected_returns_mean, max_drawdown, calmar_ratio) - return calmar_ratio + +def calculate_calmar_from_balance( + balance_history: pd.DataFrame, + date_col: str = "date", + balance_col: str = "total_quote", +) -> float: + """ + Calculate calmar ratio from historical balance snapshots. + + :param balance_history: DataFrame containing at least date and balance columns + :param date_col: Column containing timestamps + :param balance_col: Column containing historical balance values + :return: calmar + """ + wallet = _prepare_balance_history( + balance_history=balance_history, + date_col=date_col, + balance_col=balance_col, + ) + + if len(wallet) < 2: + return 0.0 + + starting_balance = float(wallet[balance_col].iloc[0]) + final_balance = float(wallet[balance_col].iloc[-1]) + days_period = max(1, (wallet[date_col].iloc[-1] - wallet[date_col].iloc[0]).days) + + total_profit = (final_balance - starting_balance) / starting_balance + expected_returns_mean = total_profit / days_period * 100 + + try: + drawdown = calculate_max_drawdown_from_balance( + wallet, + date_col=date_col, + balance_col=balance_col, + ) + max_drawdown = drawdown.relative_account_drawdown + except ValueError: + return 0.0 + + return _calculate_annualized_ratio(expected_returns_mean, max_drawdown) def calculate_sqn(trades: pd.DataFrame, starting_balance: float) -> float: diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 6fab1afe0..36779bcaf 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -46,6 +46,10 @@ class Binance(Exchange): "l2_limit_range": [5, 10, 20, 50, 100, 500, 1000], "ws_enabled": True, "has_delisting": True, + # Demo trading + # https://www.binance.com/en/support/faq/detail/9be58f73e5e14338809e3b705b9687dd + # Intentionally Disabled as it's a separate market - not a simulated live market. + "supports_demo_trading": False, } _ft_has_futures: FtHas = { "ohlcv_candle_limit": 499, diff --git a/freqtrade/exchange/binance_leverage_tiers.json b/freqtrade/exchange/binance_leverage_tiers.json index da0d66a97..1f92dc082 100644 --- a/freqtrade/exchange/binance_leverage_tiers.json +++ b/freqtrade/exchange/binance_leverage_tiers.json @@ -5,13 +5,13 @@ "symbol": "0G/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 20000.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, "maxLeverage": 75.0, "info": { "bracket": 1, "initialLeverage": 75, - "notionalCap": 20000, + "notionalCap": 5000, "notionalFloor": 0, "maintMarginRatio": 0.01, "cum": 0.0 @@ -21,136 +21,170 @@ "tier": 2.0, "symbol": "0G/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 200000.0, + "minNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": 2, "initialLeverage": 50, - "notionalCap": 200000, - "notionalFloor": 20000, + "notionalCap": 10000, + "notionalFloor": 5000, "maintMarginRatio": 0.015, - "cum": 100.0 + "cum": 25.0 } }, { "tier": 3.0, "symbol": "0G/USDT:USDT", "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 1000000.0, + "minNotional": 10000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": 3, "initialLeverage": 25, - "notionalCap": 1000000, - "notionalFloor": 200000, + "notionalCap": 50000, + "notionalFloor": 10000, "maintMarginRatio": 0.02, - "cum": 1100.0 + "cum": 75.0 } }, { "tier": 4.0, "symbol": "0G/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": 4, "initialLeverage": 20, - "notionalCap": 2000000, - "notionalFloor": 1000000, + "notionalCap": 100000, + "notionalFloor": 50000, "maintMarginRatio": 0.025, - "cum": 6100.0 + "cum": 325.0 } }, { "tier": 5.0, "symbol": "0G/USDT:USDT", "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 5000000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 100000.0, + "maxNotional": 175000.0, + "maintenanceMarginRate": 0.0333, + "maxLeverage": 15.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 5000000, - "notionalFloor": 2000000, - "maintMarginRatio": 0.05, - "cum": 56100.0 + "initialLeverage": 15, + "notionalCap": 175000, + "notionalFloor": 100000, + "maintMarginRatio": 0.0333, + "cum": 1155.0 } }, { "tier": 6.0, "symbol": "0G/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 175000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 7500000, - "notionalFloor": 5000000, - "maintMarginRatio": 0.1, - "cum": 306100.0 + "initialLeverage": 10, + "notionalCap": 250000, + "notionalFloor": 175000, + "maintMarginRatio": 0.05, + "cum": 4077.5 } }, { "tier": 7.0, "symbol": "0G/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 10000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 250000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 7, - "initialLeverage": 4, - "notionalCap": 10000000, - "notionalFloor": 7500000, - "maintMarginRatio": 0.125, - "cum": 493600.0 + "initialLeverage": 5, + "notionalCap": 750000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1, + "cum": 16577.5 } }, { "tier": 8.0, "symbol": "0G/USDT:USDT", "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 12500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 8, - "initialLeverage": 2, - "notionalCap": 12500000, - "notionalFloor": 10000000, - "maintMarginRatio": 0.25, - "cum": 1743600.0 + "initialLeverage": 4, + "notionalCap": 1500000, + "notionalFloor": 750000, + "maintMarginRatio": 0.125, + "cum": 35327.5 } }, { "tier": 9.0, "symbol": "0G/USDT:USDT", "currency": "USDT", - "minNotional": 12500000.0, - "maxNotional": 15000000.0, + "minNotional": 1500000.0, + "maxNotional": 4500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 9, + "initialLeverage": 3, + "notionalCap": 4500000, + "notionalFloor": 1500000, + "maintMarginRatio": 0.1667, + "cum": 97877.5 + } + }, + { + "tier": 10.0, + "symbol": "0G/USDT:USDT", + "currency": "USDT", + "minNotional": 4500000.0, + "maxNotional": 7500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 10, + "initialLeverage": 2, + "notionalCap": 7500000, + "notionalFloor": 4500000, + "maintMarginRatio": 0.25, + "cum": 472727.5 + } + }, + { + "tier": 11.0, + "symbol": "0G/USDT:USDT", + "currency": "USDT", + "minNotional": 7500000.0, + "maxNotional": 12500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 9, + "bracket": 11, "initialLeverage": 1, - "notionalCap": 15000000, - "notionalFloor": 12500000, + "notionalCap": 12500000, + "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 4868600.0 + "cum": 2347727.5 } } ], @@ -3687,6 +3721,110 @@ } } ], + "AAPL/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "AAPL/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 1, + "initialLeverage": 10, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.05, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "AAPL/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 2, + "initialLeverage": 5, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.1, + "cum": 250.0 + } + }, + { + "tier": 3.0, + "symbol": "AAPL/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 3, + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 10000, + "maintMarginRatio": 0.125, + "cum": 500.0 + } + }, + { + "tier": 4.0, + "symbol": "AAPL/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 4, + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 4670.0 + } + }, + { + "tier": 5.0, + "symbol": "AAPL/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 5, + "initialLeverage": 2, + "notionalCap": 8000000, + "notionalFloor": 500000, + "maintMarginRatio": 0.25, + "cum": 46320.0 + } + }, + { + "tier": 6.0, + "symbol": "AAPL/USDT:USDT", + "currency": "USDT", + "minNotional": 8000000.0, + "maxNotional": 15000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 6, + "initialLeverage": 1, + "notionalCap": 15000000, + "notionalFloor": 8000000, + "maintMarginRatio": 0.5, + "cum": 2046320.0 + } + } + ], "AAVE/USDC:USDC": [ { "tier": 1.0, @@ -4004,14 +4142,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 1, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.025, "cum": 0.0 } }, @@ -4021,15 +4159,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 2, - "initialLeverage": 20, + "initialLeverage": 10, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.025, - "cum": 50.0 + "maintMarginRatio": 0.05, + "cum": 125.0 } }, { @@ -4037,50 +4175,50 @@ "symbol": "ACE/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 3, - "initialLeverage": 10, - "notionalCap": 20000, + "initialLeverage": 5, + "notionalCap": 60000, "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 300.0 + "maintMarginRatio": 0.1, + "cum": 625.0 } }, { "tier": 4.0, "symbol": "ACE/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 60000.0, + "maxNotional": 70000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 4, - "initialLeverage": 5, - "notionalCap": 50000, - "notionalFloor": 20000, - "maintMarginRatio": 0.1, - "cum": 1300.0 + "initialLeverage": 4, + "notionalCap": 70000, + "notionalFloor": 60000, + "maintMarginRatio": 0.125, + "cum": 2125.0 } }, { "tier": 5.0, "symbol": "ACE/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, + "minNotional": 70000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 5, - "initialLeverage": 4, + "initialLeverage": 3, "notionalCap": 250000, - "notionalFloor": 50000, - "maintMarginRatio": 0.125, - "cum": 2550.0 + "notionalFloor": 70000, + "maintMarginRatio": 0.1667, + "cum": 5044.0 } }, { @@ -4089,15 +4227,15 @@ "currency": "USDT", "minNotional": 250000.0, "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": 6, - "initialLeverage": 3, + "initialLeverage": 2, "notionalCap": 500000, "notionalFloor": 250000, - "maintMarginRatio": 0.1667, - "cum": 12975.0 + "maintMarginRatio": 0.25, + "cum": 25869.0 } }, { @@ -4105,33 +4243,16 @@ "symbol": "ACE/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 7, - "initialLeverage": 2, - "notionalCap": 7500000, - "notionalFloor": 500000, - "maintMarginRatio": 0.25, - "cum": 54625.0 - } - }, - { - "tier": 8.0, - "symbol": "ACE/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 8, + "bracket": 7, "initialLeverage": 1, - "notionalCap": 12500000, - "notionalFloor": 7500000, + "notionalCap": 800000, + "notionalFloor": 500000, "maintMarginRatio": 0.5, - "cum": 1929625.0 + "cum": 150869.0 } } ], @@ -5848,15 +5969,15 @@ "symbol": "AI/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 1, - "initialLeverage": 25, - "notionalCap": 5000, + "initialLeverage": 10, + "notionalCap": 10000, "notionalFloor": 0, - "maintMarginRatio": 0.02, + "maintMarginRatio": 0.05, "cum": 0.0 } }, @@ -5864,55 +5985,21 @@ "tier": 2.0, "symbol": "AI/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, - "info": { - "bracket": 2, - "initialLeverage": 20, - "notionalCap": 10000, - "notionalFloor": 5000, - "maintMarginRatio": 0.025, - "cum": 25.0 - } - }, - { - "tier": 3.0, - "symbol": "AI/USDT:USDT", - "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, - "info": { - "bracket": 3, - "initialLeverage": 10, - "notionalCap": 20000, - "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 275.0 - } - }, - { - "tier": 4.0, - "symbol": "AI/USDT:USDT", - "currency": "USDT", - "minNotional": 20000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": 4, + "bracket": 2, "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 20000, + "notionalFloor": 10000, "maintMarginRatio": 0.1, - "cum": 1275.0 + "cum": 500.0 } }, { - "tier": 5.0, + "tier": 3.0, "symbol": "AI/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, @@ -5920,16 +6007,16 @@ "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": 5, + "bracket": 3, "initialLeverage": 4, "notionalCap": 100000, "notionalFloor": 50000, "maintMarginRatio": 0.125, - "cum": 2525.0 + "cum": 1750.0 } }, { - "tier": 6.0, + "tier": 4.0, "symbol": "AI/USDT:USDT", "currency": "USDT", "minNotional": 100000.0, @@ -5937,46 +6024,46 @@ "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { - "bracket": 6, + "bracket": 4, "initialLeverage": 3, "notionalCap": 250000, "notionalFloor": 100000, "maintMarginRatio": 0.1667, - "cum": 6695.0 + "cum": 5920.0 } }, { - "tier": 7.0, + "tier": 5.0, "symbol": "AI/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 2500000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 7, + "bracket": 5, "initialLeverage": 2, - "notionalCap": 2500000, + "notionalCap": 500000, "notionalFloor": 250000, "maintMarginRatio": 0.25, - "cum": 27520.0 + "cum": 26745.0 } }, { - "tier": 8.0, + "tier": 6.0, "symbol": "AI/USDT:USDT", "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 500000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 8, + "bracket": 6, "initialLeverage": 1, - "notionalCap": 5000000, - "notionalFloor": 2500000, + "notionalCap": 800000, + "notionalFloor": 500000, "maintMarginRatio": 0.5, - "cum": 652520.0 + "cum": 151745.0 } } ], @@ -6239,6 +6326,127 @@ } } ], + "AIGENSYN/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "AIGENSYN/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": 1, + "initialLeverage": 20, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.025, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "AIGENSYN/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 2, + "initialLeverage": 10, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.05, + "cum": 125.0 + } + }, + { + "tier": 3.0, + "symbol": "AIGENSYN/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 3, + "initialLeverage": 5, + "notionalCap": 50000, + "notionalFloor": 10000, + "maintMarginRatio": 0.1, + "cum": 625.0 + } + }, + { + "tier": 4.0, + "symbol": "AIGENSYN/USDT:USDT", + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 4, + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 50000, + "maintMarginRatio": 0.125, + "cum": 1875.0 + } + }, + { + "tier": 5.0, + "symbol": "AIGENSYN/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 5, + "initialLeverage": 3, + "notionalCap": 250000, + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 6045.0 + } + }, + { + "tier": 6.0, + "symbol": "AIGENSYN/USDT:USDT", + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 6, + "initialLeverage": 2, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.25, + "cum": 26870.0 + } + }, + { + "tier": 7.0, + "symbol": "AIGENSYN/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 800000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 7, + "initialLeverage": 1, + "notionalCap": 800000, + "notionalFloor": 500000, + "maintMarginRatio": 0.5, + "cum": 151870.0 + } + } + ], "AIN/USDT:USDT": [ { "tier": 1.0, @@ -10022,15 +10230,15 @@ "symbol": "ARIA/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 6000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 1, - "initialLeverage": 50, - "notionalCap": 5000, + "initialLeverage": 10, + "notionalCap": 6000, "notionalFloor": 0, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.05, "cum": 0.0 } }, @@ -10038,119 +10246,85 @@ "tier": 2.0, "symbol": "ARIA/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 6000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 2, - "initialLeverage": 20, - "notionalCap": 10000, - "notionalFloor": 5000, - "maintMarginRatio": 0.025, - "cum": 50.0 + "initialLeverage": 5, + "notionalCap": 50000, + "notionalFloor": 6000, + "maintMarginRatio": 0.1, + "cum": 300.0 } }, { "tier": 3.0, "symbol": "ARIA/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 3, - "initialLeverage": 10, - "notionalCap": 20000, - "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 300.0 + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 50000, + "maintMarginRatio": 0.125, + "cum": 1550.0 } }, { "tier": 4.0, "symbol": "ARIA/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 100000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 4, - "initialLeverage": 5, - "notionalCap": 50000, - "notionalFloor": 20000, - "maintMarginRatio": 0.1, - "cum": 1300.0 + "initialLeverage": 3, + "notionalCap": 300000, + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 5720.0 } }, { "tier": 5.0, "symbol": "ARIA/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 300000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": 5, - "initialLeverage": 4, - "notionalCap": 250000, - "notionalFloor": 50000, - "maintMarginRatio": 0.125, - "cum": 2550.0 + "initialLeverage": 2, + "notionalCap": 1000000, + "notionalFloor": 300000, + "maintMarginRatio": 0.25, + "cum": 30710.0 } }, { "tier": 6.0, "symbol": "ARIA/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 6, - "initialLeverage": 3, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.1667, - "cum": 12975.0 - } - }, - { - "tier": 7.0, - "symbol": "ARIA/USDT:USDT", - "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 7, - "initialLeverage": 2, - "notionalCap": 7500000, - "notionalFloor": 500000, - "maintMarginRatio": 0.25, - "cum": 54625.0 - } - }, - { - "tier": 8.0, - "symbol": "ARIA/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 1000000.0, + "maxNotional": 2000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 8, + "bracket": 6, "initialLeverage": 1, - "notionalCap": 12500000, - "notionalFloor": 7500000, + "notionalCap": 2000000, + "notionalFloor": 1000000, "maintMarginRatio": 0.5, - "cum": 1929625.0 + "cum": 280710.0 } } ], @@ -10299,14 +10473,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -10316,15 +10490,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, - "cum": 25.0 + "maintMarginRatio": 0.025, + "cum": 50.0 } }, { @@ -10332,33 +10506,33 @@ "symbol": "ARKM/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 25000, + "initialLeverage": 10, + "notionalCap": 20000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, - "cum": 75.0 + "maintMarginRatio": 0.05, + "cum": 300.0 } }, { "tier": 4.0, "symbol": "ARKM/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 20000.0, "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 4, - "initialLeverage": 20, + "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 25000, - "maintMarginRatio": 0.025, - "cum": 200.0 + "notionalFloor": 20000, + "maintMarginRatio": 0.1, + "cum": 1300.0 } }, { @@ -10366,88 +10540,54 @@ "symbol": "ARKM/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 125000, + "initialLeverage": 4, + "notionalCap": 250000, "notionalFloor": 50000, - "maintMarginRatio": 0.05, - "cum": 1450.0 + "maintMarginRatio": 0.125, + "cum": 2550.0 } }, { "tier": 6.0, "symbol": "ARKM/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 250000, - "notionalFloor": 125000, - "maintMarginRatio": 0.1, - "cum": 7700.0 + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 12975.0 } }, { "tier": 7.0, "symbol": "ARKM/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 7, - "initialLeverage": 4, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.125, - "cum": 13950.0 - } - }, - { - "tier": 8.0, - "symbol": "ARKM/USDT:USDT", - "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 8, - "initialLeverage": 3, - "notionalCap": 1000000, - "notionalFloor": 500000, - "maintMarginRatio": 0.1667, - "cum": 34800.0 - } - }, - { - "tier": 9.0, - "symbol": "ARKM/USDT:USDT", - "currency": "USDT", - "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 9, + "bracket": 7, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 1000000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 118100.0 + "cum": 54625.0 } }, { - "tier": 10.0, + "tier": 8.0, "symbol": "ARKM/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -10455,12 +10595,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 1993100.0 + "cum": 1929625.0 } } ], @@ -11298,15 +11438,15 @@ "symbol": "ATH/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, - "notionalCap": 5000, + "initialLeverage": 50, + "notionalCap": 10000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -11314,38 +11454,21 @@ "tier": 2.0, "symbol": "ATH/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": "ATH/USDT:USDT", - "currency": "USDT", "minNotional": 10000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { - "bracket": 3, + "bracket": 2, "initialLeverage": 25, "notionalCap": 50000, "notionalFloor": 10000, "maintMarginRatio": 0.02, - "cum": 75.0 + "cum": 50.0 } }, { - "tier": 4.0, + "tier": 3.0, "symbol": "ATH/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, @@ -11353,50 +11476,50 @@ "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { - "bracket": 4, + "bracket": 3, "initialLeverage": 20, "notionalCap": 100000, "notionalFloor": 50000, "maintMarginRatio": 0.025, - "cum": 325.0 + "cum": 300.0 + } + }, + { + "tier": 4.0, + "symbol": "ATH/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 125000.0, + "maintenanceMarginRate": 0.0333, + "maxLeverage": 15.0, + "info": { + "bracket": 4, + "initialLeverage": 15, + "notionalCap": 125000, + "notionalFloor": 100000, + "maintMarginRatio": 0.0333, + "cum": 1130.0 } }, { "tier": 5.0, "symbol": "ATH/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 175000.0, - "maintenanceMarginRate": 0.0333, - "maxLeverage": 15.0, - "info": { - "bracket": 5, - "initialLeverage": 15, - "notionalCap": 175000, - "notionalFloor": 100000, - "maintMarginRatio": 0.0333, - "cum": 1155.0 - } - }, - { - "tier": 6.0, - "symbol": "ATH/USDT:USDT", - "currency": "USDT", - "minNotional": 175000.0, + "minNotional": 125000.0, "maxNotional": 250000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": 6, + "bracket": 5, "initialLeverage": 10, "notionalCap": 250000, - "notionalFloor": 175000, + "notionalFloor": 125000, "maintMarginRatio": 0.05, - "cum": 4077.5 + "cum": 3217.5 } }, { - "tier": 7.0, + "tier": 6.0, "symbol": "ATH/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, @@ -11404,16 +11527,16 @@ "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": 7, + "bracket": 6, "initialLeverage": 5, "notionalCap": 750000, "notionalFloor": 250000, "maintMarginRatio": 0.1, - "cum": 16577.5 + "cum": 15717.5 } }, { - "tier": 8.0, + "tier": 7.0, "symbol": "ATH/USDT:USDT", "currency": "USDT", "minNotional": 750000.0, @@ -11421,63 +11544,63 @@ "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": 8, + "bracket": 7, "initialLeverage": 4, "notionalCap": 1500000, "notionalFloor": 750000, "maintMarginRatio": 0.125, - "cum": 35327.5 + "cum": 34467.5 + } + }, + { + "tier": 8.0, + "symbol": "ATH/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 2500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 8, + "initialLeverage": 3, + "notionalCap": 2500000, + "notionalFloor": 1500000, + "maintMarginRatio": 0.1667, + "cum": 97017.5 } }, { "tier": 9.0, "symbol": "ATH/USDT:USDT", "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 4500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": 9, - "initialLeverage": 3, - "notionalCap": 4500000, - "notionalFloor": 1500000, - "maintMarginRatio": 0.1667, - "cum": 97877.5 + "initialLeverage": 2, + "notionalCap": 5000000, + "notionalFloor": 2500000, + "maintMarginRatio": 0.25, + "cum": 305267.5 } }, { "tier": 10.0, "symbol": "ATH/USDT:USDT", "currency": "USDT", - "minNotional": 4500000.0, + "minNotional": 5000000.0, "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 10, - "initialLeverage": 2, - "notionalCap": 7500000, - "notionalFloor": 4500000, - "maintMarginRatio": 0.25, - "cum": 472727.5 - } - }, - { - "tier": 11.0, - "symbol": "ATH/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 11, + "bracket": 10, "initialLeverage": 1, - "notionalCap": 12500000, - "notionalFloor": 7500000, + "notionalCap": 7500000, + "notionalFloor": 5000000, "maintMarginRatio": 0.5, - "cum": 2347727.5 + "cum": 1555267.5 } } ], @@ -11660,14 +11783,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -11677,15 +11800,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, - "cum": 25.0 + "maintMarginRatio": 0.025, + "cum": 50.0 } }, { @@ -11693,33 +11816,33 @@ "symbol": "AUCTION/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 25000, + "initialLeverage": 10, + "notionalCap": 20000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, - "cum": 75.0 + "maintMarginRatio": 0.05, + "cum": 300.0 } }, { "tier": 4.0, "symbol": "AUCTION/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 20000.0, "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 4, - "initialLeverage": 20, + "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 25000, - "maintMarginRatio": 0.025, - "cum": 200.0 + "notionalFloor": 20000, + "maintMarginRatio": 0.1, + "cum": 1300.0 } }, { @@ -11727,88 +11850,54 @@ "symbol": "AUCTION/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 125000, + "initialLeverage": 4, + "notionalCap": 250000, "notionalFloor": 50000, - "maintMarginRatio": 0.05, - "cum": 1450.0 + "maintMarginRatio": 0.125, + "cum": 2550.0 } }, { "tier": 6.0, "symbol": "AUCTION/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 250000, - "notionalFloor": 125000, - "maintMarginRatio": 0.1, - "cum": 7700.0 + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 12975.0 } }, { "tier": 7.0, "symbol": "AUCTION/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 7, - "initialLeverage": 4, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.125, - "cum": 13950.0 - } - }, - { - "tier": 8.0, - "symbol": "AUCTION/USDT:USDT", - "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 8, - "initialLeverage": 3, - "notionalCap": 1000000, - "notionalFloor": 500000, - "maintMarginRatio": 0.1667, - "cum": 34800.0 - } - }, - { - "tier": 9.0, - "symbol": "AUCTION/USDT:USDT", - "currency": "USDT", - "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 9, + "bracket": 7, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 1000000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 118100.0 + "cum": 54625.0 } }, { - "tier": 10.0, + "tier": 8.0, "symbol": "AUCTION/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -11816,12 +11905,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 1993100.0 + "cum": 1929625.0 } } ], @@ -12428,6 +12517,110 @@ } } ], + "AVGO/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "AVGO/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 1, + "initialLeverage": 10, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.05, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "AVGO/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 2, + "initialLeverage": 5, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.1, + "cum": 250.0 + } + }, + { + "tier": 3.0, + "symbol": "AVGO/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 3, + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 10000, + "maintMarginRatio": 0.125, + "cum": 500.0 + } + }, + { + "tier": 4.0, + "symbol": "AVGO/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 4, + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 4670.0 + } + }, + { + "tier": 5.0, + "symbol": "AVGO/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 5, + "initialLeverage": 2, + "notionalCap": 8000000, + "notionalFloor": 500000, + "maintMarginRatio": 0.25, + "cum": 46320.0 + } + }, + { + "tier": 6.0, + "symbol": "AVGO/USDT:USDT", + "currency": "USDT", + "minNotional": 8000000.0, + "maxNotional": 15000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 6, + "initialLeverage": 1, + "notionalCap": 15000000, + "notionalFloor": 8000000, + "maintMarginRatio": 0.5, + "cum": 2046320.0 + } + } + ], "AVNT/USDT:USDT": [ { "tier": 1.0, @@ -13519,15 +13712,15 @@ "symbol": "B3/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 1, - "initialLeverage": 25, - "notionalCap": 5000, + "initialLeverage": 10, + "notionalCap": 10000, "notionalFloor": 0, - "maintMarginRatio": 0.02, + "maintMarginRatio": 0.05, "cum": 0.0 } }, @@ -13535,55 +13728,21 @@ "tier": 2.0, "symbol": "B3/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, - "info": { - "bracket": 2, - "initialLeverage": 20, - "notionalCap": 10000, - "notionalFloor": 5000, - "maintMarginRatio": 0.025, - "cum": 25.0 - } - }, - { - "tier": 3.0, - "symbol": "B3/USDT:USDT", - "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, - "info": { - "bracket": 3, - "initialLeverage": 10, - "notionalCap": 20000, - "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 275.0 - } - }, - { - "tier": 4.0, - "symbol": "B3/USDT:USDT", - "currency": "USDT", - "minNotional": 20000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": 4, + "bracket": 2, "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 20000, + "notionalFloor": 10000, "maintMarginRatio": 0.1, - "cum": 1275.0 + "cum": 500.0 } }, { - "tier": 5.0, + "tier": 3.0, "symbol": "B3/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, @@ -13591,16 +13750,16 @@ "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": 5, + "bracket": 3, "initialLeverage": 4, "notionalCap": 100000, "notionalFloor": 50000, "maintMarginRatio": 0.125, - "cum": 2525.0 + "cum": 1750.0 } }, { - "tier": 6.0, + "tier": 4.0, "symbol": "B3/USDT:USDT", "currency": "USDT", "minNotional": 100000.0, @@ -13608,46 +13767,150 @@ "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { - "bracket": 6, + "bracket": 4, "initialLeverage": 3, "notionalCap": 250000, "notionalFloor": 100000, "maintMarginRatio": 0.1667, - "cum": 6695.0 + "cum": 5920.0 } }, { - "tier": 7.0, + "tier": 5.0, "symbol": "B3/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 2500000.0, + "maxNotional": 300000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 7, + "bracket": 5, "initialLeverage": 2, - "notionalCap": 2500000, + "notionalCap": 300000, "notionalFloor": 250000, "maintMarginRatio": 0.25, - "cum": 27520.0 + "cum": 26745.0 } }, { - "tier": 8.0, + "tier": 6.0, "symbol": "B3/USDT:USDT", "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 300000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 8, + "bracket": 6, "initialLeverage": 1, - "notionalCap": 5000000, - "notionalFloor": 2500000, + "notionalCap": 500000, + "notionalFloor": 300000, "maintMarginRatio": 0.5, - "cum": 652520.0 + "cum": 101745.0 + } + } + ], + "BABA/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "BABA/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 1, + "initialLeverage": 10, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.05, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "BABA/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 2, + "initialLeverage": 5, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.1, + "cum": 250.0 + } + }, + { + "tier": 3.0, + "symbol": "BABA/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 3, + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 10000, + "maintMarginRatio": 0.125, + "cum": 500.0 + } + }, + { + "tier": 4.0, + "symbol": "BABA/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 4, + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 4670.0 + } + }, + { + "tier": 5.0, + "symbol": "BABA/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 5, + "initialLeverage": 2, + "notionalCap": 8000000, + "notionalFloor": 500000, + "maintMarginRatio": 0.25, + "cum": 46320.0 + } + }, + { + "tier": 6.0, + "symbol": "BABA/USDT:USDT", + "currency": "USDT", + "minNotional": 8000000.0, + "maxNotional": 15000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 6, + "initialLeverage": 1, + "notionalCap": 15000000, + "notionalFloor": 8000000, + "maintMarginRatio": 0.5, + "cum": 2046320.0 } } ], @@ -15152,6 +15415,161 @@ } } ], + "BASED/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "BASED/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": 1, + "initialLeverage": 50, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.015, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "BASED/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": 2, + "initialLeverage": 25, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.02, + "cum": 25.0 + } + }, + { + "tier": 3.0, + "symbol": "BASED/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": 3, + "initialLeverage": 20, + "notionalCap": 25000, + "notionalFloor": 10000, + "maintMarginRatio": 0.025, + "cum": 75.0 + } + }, + { + "tier": 4.0, + "symbol": "BASED/USDT:USDT", + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 62500.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 4, + "initialLeverage": 10, + "notionalCap": 62500, + "notionalFloor": 25000, + "maintMarginRatio": 0.05, + "cum": 700.0 + } + }, + { + "tier": 5.0, + "symbol": "BASED/USDT:USDT", + "currency": "USDT", + "minNotional": 62500.0, + "maxNotional": 125000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 5, + "initialLeverage": 5, + "notionalCap": 125000, + "notionalFloor": 62500, + "maintMarginRatio": 0.1, + "cum": 3825.0 + } + }, + { + "tier": 6.0, + "symbol": "BASED/USDT:USDT", + "currency": "USDT", + "minNotional": 125000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 6, + "initialLeverage": 4, + "notionalCap": 250000, + "notionalFloor": 125000, + "maintMarginRatio": 0.125, + "cum": 6950.0 + } + }, + { + "tier": 7.0, + "symbol": "BASED/USDT:USDT", + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 7, + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 17375.0 + } + }, + { + "tier": 8.0, + "symbol": "BASED/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 7500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 8, + "initialLeverage": 2, + "notionalCap": 7500000, + "notionalFloor": 500000, + "maintMarginRatio": 0.25, + "cum": 59025.0 + } + }, + { + "tier": 9.0, + "symbol": "BASED/USDT:USDT", + "currency": "USDT", + "minNotional": 7500000.0, + "maxNotional": 12500000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 9, + "initialLeverage": 1, + "notionalCap": 12500000, + "notionalFloor": 7500000, + "maintMarginRatio": 0.5, + "cum": 1934025.0 + } + } + ], "BAT/USDT:USDT": [ { "tier": 1.0, @@ -15806,110 +16224,6 @@ } } ], - "BDXN/USDT:USDT": [ - { - "tier": 1.0, - "symbol": "BDXN/USDT:USDT", - "currency": "USDT", - "minNotional": 0.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.09, - "maxLeverage": 10.0, - "info": { - "bracket": 1, - "initialLeverage": 10, - "notionalCap": 10000, - "notionalFloor": 0, - "maintMarginRatio": 0.09, - "cum": 0.0 - } - }, - { - "tier": 2.0, - "symbol": "BDXN/USDT:USDT", - "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, - "info": { - "bracket": 2, - "initialLeverage": 5, - "notionalCap": 50000, - "notionalFloor": 10000, - "maintMarginRatio": 0.1, - "cum": 100.0 - } - }, - { - "tier": 3.0, - "symbol": "BDXN/USDT:USDT", - "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 3, - "initialLeverage": 4, - "notionalCap": 100000, - "notionalFloor": 50000, - "maintMarginRatio": 0.125, - "cum": 1350.0 - } - }, - { - "tier": 4.0, - "symbol": "BDXN/USDT:USDT", - "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 4, - "initialLeverage": 3, - "notionalCap": 250000, - "notionalFloor": 100000, - "maintMarginRatio": 0.1667, - "cum": 5520.0 - } - }, - { - "tier": 5.0, - "symbol": "BDXN/USDT:USDT", - "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 5, - "initialLeverage": 2, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.25, - "cum": 26345.0 - } - }, - { - "tier": 6.0, - "symbol": "BDXN/USDT:USDT", - "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 800000.0, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1.0, - "info": { - "bracket": 6, - "initialLeverage": 1, - "notionalCap": 800000, - "notionalFloor": 500000, - "maintMarginRatio": 0.5, - "cum": 151345.0 - } - } - ], "BEAMX/USDT:USDT": [ { "tier": 1.0, @@ -16330,13 +16644,13 @@ "symbol": "BERA/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 50000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": 3, "initialLeverage": 25, - "notionalCap": 50000, + "notionalCap": 25000, "notionalFloor": 10000, "maintMarginRatio": 0.02, "cum": 75.0 @@ -16346,51 +16660,51 @@ "tier": 4.0, "symbol": "BERA/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, + "minNotional": 25000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": 4, "initialLeverage": 20, - "notionalCap": 100000, - "notionalFloor": 50000, + "notionalCap": 50000, + "notionalFloor": 25000, "maintMarginRatio": 0.025, - "cum": 325.0 + "cum": 200.0 } }, { "tier": 5.0, "symbol": "BERA/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 175000.0, - "maintenanceMarginRate": 0.0333, - "maxLeverage": 15.0, + "minNotional": 50000.0, + "maxNotional": 125000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 5, - "initialLeverage": 15, - "notionalCap": 175000, - "notionalFloor": 100000, - "maintMarginRatio": 0.0333, - "cum": 1155.0 + "initialLeverage": 10, + "notionalCap": 125000, + "notionalFloor": 50000, + "maintMarginRatio": 0.05, + "cum": 1450.0 } }, { "tier": 6.0, "symbol": "BERA/USDT:USDT", "currency": "USDT", - "minNotional": 175000.0, + "minNotional": 125000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 6, - "initialLeverage": 10, + "initialLeverage": 5, "notionalCap": 250000, - "notionalFloor": 175000, - "maintMarginRatio": 0.05, - "cum": 4077.5 + "notionalFloor": 125000, + "maintMarginRatio": 0.1, + "cum": 7700.0 } }, { @@ -16398,71 +16712,54 @@ "symbol": "BERA/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 750000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 7, - "initialLeverage": 5, - "notionalCap": 750000, + "initialLeverage": 4, + "notionalCap": 500000, "notionalFloor": 250000, - "maintMarginRatio": 0.1, - "cum": 16577.5 + "maintMarginRatio": 0.125, + "cum": 13950.0 } }, { "tier": 8.0, "symbol": "BERA/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 8, - "initialLeverage": 4, - "notionalCap": 1500000, - "notionalFloor": 750000, - "maintMarginRatio": 0.125, - "cum": 35327.5 + "initialLeverage": 3, + "notionalCap": 1000000, + "notionalFloor": 500000, + "maintMarginRatio": 0.1667, + "cum": 34800.0 } }, { "tier": 9.0, "symbol": "BERA/USDT:USDT", "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 4500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 9, - "initialLeverage": 3, - "notionalCap": 4500000, - "notionalFloor": 1500000, - "maintMarginRatio": 0.1667, - "cum": 97877.5 - } - }, - { - "tier": 10.0, - "symbol": "BERA/USDT:USDT", - "currency": "USDT", - "minNotional": 4500000.0, + "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 10, + "bracket": 9, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 4500000, + "notionalFloor": 1000000, "maintMarginRatio": 0.25, - "cum": 472727.5 + "cum": 118100.0 } }, { - "tier": 11.0, + "tier": 10.0, "symbol": "BERA/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -16470,12 +16767,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 11, + "bracket": 10, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 2347727.5 + "cum": 1993100.0 } } ], @@ -16745,14 +17042,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -16762,15 +17059,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, - "cum": 25.0 + "maintMarginRatio": 0.025, + "cum": 50.0 } }, { @@ -16778,33 +17075,33 @@ "symbol": "BIGTIME/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 25000, + "initialLeverage": 10, + "notionalCap": 20000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, - "cum": 75.0 + "maintMarginRatio": 0.05, + "cum": 300.0 } }, { "tier": 4.0, "symbol": "BIGTIME/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 20000.0, "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 4, - "initialLeverage": 20, + "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 25000, - "maintMarginRatio": 0.025, - "cum": 200.0 + "notionalFloor": 20000, + "maintMarginRatio": 0.1, + "cum": 1300.0 } }, { @@ -16812,88 +17109,54 @@ "symbol": "BIGTIME/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 125000, + "initialLeverage": 4, + "notionalCap": 250000, "notionalFloor": 50000, - "maintMarginRatio": 0.05, - "cum": 1450.0 + "maintMarginRatio": 0.125, + "cum": 2550.0 } }, { "tier": 6.0, "symbol": "BIGTIME/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 250000, - "notionalFloor": 125000, - "maintMarginRatio": 0.1, - "cum": 7700.0 + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 12975.0 } }, { "tier": 7.0, "symbol": "BIGTIME/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 7, - "initialLeverage": 4, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.125, - "cum": 13950.0 - } - }, - { - "tier": 8.0, - "symbol": "BIGTIME/USDT:USDT", - "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 8, - "initialLeverage": 3, - "notionalCap": 1000000, - "notionalFloor": 500000, - "maintMarginRatio": 0.1667, - "cum": 34800.0 - } - }, - { - "tier": 9.0, - "symbol": "BIGTIME/USDT:USDT", - "currency": "USDT", - "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 9, + "bracket": 7, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 1000000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 118100.0 + "cum": 54625.0 } }, { - "tier": 10.0, + "tier": 8.0, "symbol": "BIGTIME/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -16901,12 +17164,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 1993100.0 + "cum": 1929625.0 } } ], @@ -17089,14 +17352,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -17106,14 +17369,14 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 25, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.02, "cum": 25.0 } }, @@ -17122,15 +17385,15 @@ "symbol": "BIO/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 50000, + "initialLeverage": 20, + "notionalCap": 25000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, + "maintMarginRatio": 0.025, "cum": 75.0 } }, @@ -17138,51 +17401,51 @@ "tier": 4.0, "symbol": "BIO/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 25000.0, + "maxNotional": 62500.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 4, - "initialLeverage": 20, - "notionalCap": 100000, - "notionalFloor": 50000, - "maintMarginRatio": 0.025, - "cum": 325.0 + "initialLeverage": 10, + "notionalCap": 62500, + "notionalFloor": 25000, + "maintMarginRatio": 0.05, + "cum": 700.0 } }, { "tier": 5.0, "symbol": "BIO/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 175000.0, - "maintenanceMarginRate": 0.0333, - "maxLeverage": 15.0, + "minNotional": 62500.0, + "maxNotional": 125000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 5, - "initialLeverage": 15, - "notionalCap": 175000, - "notionalFloor": 100000, - "maintMarginRatio": 0.0333, - "cum": 1155.0 + "initialLeverage": 5, + "notionalCap": 125000, + "notionalFloor": 62500, + "maintMarginRatio": 0.1, + "cum": 3825.0 } }, { "tier": 6.0, "symbol": "BIO/USDT:USDT", "currency": "USDT", - "minNotional": 175000.0, + "minNotional": 125000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 6, - "initialLeverage": 10, + "initialLeverage": 4, "notionalCap": 250000, - "notionalFloor": 175000, - "maintMarginRatio": 0.05, - "cum": 4077.5 + "notionalFloor": 125000, + "maintMarginRatio": 0.125, + "cum": 6950.0 } }, { @@ -17190,71 +17453,37 @@ "symbol": "BIO/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 750000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 7, - "initialLeverage": 5, - "notionalCap": 750000, + "initialLeverage": 3, + "notionalCap": 500000, "notionalFloor": 250000, - "maintMarginRatio": 0.1, - "cum": 16577.5 + "maintMarginRatio": 0.1667, + "cum": 17375.0 } }, { "tier": 8.0, "symbol": "BIO/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 8, - "initialLeverage": 4, - "notionalCap": 1500000, - "notionalFloor": 750000, - "maintMarginRatio": 0.125, - "cum": 35327.5 - } - }, - { - "tier": 9.0, - "symbol": "BIO/USDT:USDT", - "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 4500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 9, - "initialLeverage": 3, - "notionalCap": 4500000, - "notionalFloor": 1500000, - "maintMarginRatio": 0.1667, - "cum": 97877.5 - } - }, - { - "tier": 10.0, - "symbol": "BIO/USDT:USDT", - "currency": "USDT", - "minNotional": 4500000.0, + "minNotional": 500000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 4500000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 472727.5 + "cum": 59025.0 } }, { - "tier": 11.0, + "tier": 9.0, "symbol": "BIO/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -17262,12 +17491,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 11, + "bracket": 9, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 2347727.5 + "cum": 1934025.0 } } ], @@ -18657,15 +18886,15 @@ "symbol": "BOB/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 1, - "initialLeverage": 20, - "notionalCap": 5000, + "initialLeverage": 10, + "notionalCap": 10000, "notionalFloor": 0, - "maintMarginRatio": 0.025, + "maintMarginRatio": 0.05, "cum": 0.0 } }, @@ -18673,38 +18902,21 @@ "tier": 2.0, "symbol": "BOB/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, - "info": { - "bracket": 2, - "initialLeverage": 10, - "notionalCap": 10000, - "notionalFloor": 5000, - "maintMarginRatio": 0.05, - "cum": 125.0 - } - }, - { - "tier": 3.0, - "symbol": "BOB/USDT:USDT", - "currency": "USDT", "minNotional": 10000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": 3, + "bracket": 2, "initialLeverage": 5, "notionalCap": 50000, "notionalFloor": 10000, "maintMarginRatio": 0.1, - "cum": 625.0 + "cum": 500.0 } }, { - "tier": 4.0, + "tier": 3.0, "symbol": "BOB/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, @@ -18712,16 +18924,16 @@ "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": 4, + "bracket": 3, "initialLeverage": 4, "notionalCap": 100000, "notionalFloor": 50000, "maintMarginRatio": 0.125, - "cum": 1875.0 + "cum": 1750.0 } }, { - "tier": 5.0, + "tier": 4.0, "symbol": "BOB/USDT:USDT", "currency": "USDT", "minNotional": 100000.0, @@ -18729,46 +18941,46 @@ "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { - "bracket": 5, + "bracket": 4, "initialLeverage": 3, "notionalCap": 250000, "notionalFloor": 100000, "maintMarginRatio": 0.1667, - "cum": 6045.0 + "cum": 5920.0 + } + }, + { + "tier": 5.0, + "symbol": "BOB/USDT:USDT", + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 5, + "initialLeverage": 2, + "notionalCap": 400000, + "notionalFloor": 250000, + "maintMarginRatio": 0.25, + "cum": 26745.0 } }, { "tier": 6.0, "symbol": "BOB/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, + "minNotional": 400000.0, "maxNotional": 500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 6, - "initialLeverage": 2, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.25, - "cum": 26870.0 - } - }, - { - "tier": 7.0, - "symbol": "BOB/USDT:USDT", - "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 800000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 7, + "bracket": 6, "initialLeverage": 1, - "notionalCap": 800000, - "notionalFloor": 500000, + "notionalCap": 500000, + "notionalFloor": 400000, "maintMarginRatio": 0.5, - "cum": 151870.0 + "cum": 126745.0 } } ], @@ -18951,14 +19163,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -18968,15 +19180,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, - "cum": 25.0 + "maintMarginRatio": 0.025, + "cum": 50.0 } }, { @@ -18984,33 +19196,33 @@ "symbol": "BOME/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 25000, + "initialLeverage": 10, + "notionalCap": 20000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, - "cum": 75.0 + "maintMarginRatio": 0.05, + "cum": 300.0 } }, { "tier": 4.0, "symbol": "BOME/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 20000.0, "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 4, - "initialLeverage": 20, + "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 25000, - "maintMarginRatio": 0.025, - "cum": 200.0 + "notionalFloor": 20000, + "maintMarginRatio": 0.1, + "cum": 1300.0 } }, { @@ -19018,88 +19230,54 @@ "symbol": "BOME/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 125000, + "initialLeverage": 4, + "notionalCap": 250000, "notionalFloor": 50000, - "maintMarginRatio": 0.05, - "cum": 1450.0 + "maintMarginRatio": 0.125, + "cum": 2550.0 } }, { "tier": 6.0, "symbol": "BOME/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 250000, - "notionalFloor": 125000, - "maintMarginRatio": 0.1, - "cum": 7700.0 + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 12975.0 } }, { "tier": 7.0, "symbol": "BOME/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 7, - "initialLeverage": 4, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.125, - "cum": 13950.0 - } - }, - { - "tier": 8.0, - "symbol": "BOME/USDT:USDT", - "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 8, - "initialLeverage": 3, - "notionalCap": 1000000, - "notionalFloor": 500000, - "maintMarginRatio": 0.1667, - "cum": 34800.0 - } - }, - { - "tier": 9.0, - "symbol": "BOME/USDT:USDT", - "currency": "USDT", - "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 9, + "bracket": 7, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 1000000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 118100.0 + "cum": 54625.0 } }, { - "tier": 10.0, + "tier": 8.0, "symbol": "BOME/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -19107,12 +19285,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 1993100.0 + "cum": 1929625.0 } } ], @@ -20736,144 +20914,6 @@ } } ], - "BTC/USDT:USDT-260327": [ - { - "tier": 1.0, - "symbol": "BTC/USDT:USDT-260327", - "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-260327", - "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-260327", - "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-260327", - "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-260327", - "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-260327", - "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-260327", - "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-260327", - "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-260626": [ { "tier": 1.0, @@ -21012,6 +21052,144 @@ } } ], + "BTC/USDT:USDT-260925": [ + { + "tier": 1.0, + "symbol": "BTC/USDT:USDT-260925", + "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-260925", + "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-260925", + "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-260925", + "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-260925", + "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-260925", + "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-260925", + "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-260925", + "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, @@ -21428,6 +21606,178 @@ } } ], + "BZ/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "BZ/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.005, + "maxLeverage": 100.0, + "info": { + "bracket": 1, + "initialLeverage": 100, + "notionalCap": 50000, + "notionalFloor": 0, + "maintMarginRatio": 0.005, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "BZ/USDT:USDT", + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.0065, + "maxLeverage": 75.0, + "info": { + "bracket": 2, + "initialLeverage": 75, + "notionalCap": 400000, + "notionalFloor": 50000, + "maintMarginRatio": 0.0065, + "cum": 75.0 + } + }, + { + "tier": 3.0, + "symbol": "BZ/USDT:USDT", + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": 3, + "initialLeverage": 50, + "notionalCap": 1000000, + "notionalFloor": 400000, + "maintMarginRatio": 0.01, + "cum": 1475.0 + } + }, + { + "tier": 4.0, + "symbol": "BZ/USDT:USDT", + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": 4, + "initialLeverage": 25, + "notionalCap": 4000000, + "notionalFloor": 1000000, + "maintMarginRatio": 0.02, + "cum": 11475.0 + } + }, + { + "tier": 5.0, + "symbol": "BZ/USDT:USDT", + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": 5, + "initialLeverage": 20, + "notionalCap": 8000000, + "notionalFloor": 4000000, + "maintMarginRatio": 0.025, + "cum": 31475.0 + } + }, + { + "tier": 6.0, + "symbol": "BZ/USDT:USDT", + "currency": "USDT", + "minNotional": 8000000.0, + "maxNotional": 40000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 6, + "initialLeverage": 10, + "notionalCap": 40000000, + "notionalFloor": 8000000, + "maintMarginRatio": 0.05, + "cum": 231475.0 + } + }, + { + "tier": 7.0, + "symbol": "BZ/USDT:USDT", + "currency": "USDT", + "minNotional": 40000000.0, + "maxNotional": 80000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 7, + "initialLeverage": 5, + "notionalCap": 80000000, + "notionalFloor": 40000000, + "maintMarginRatio": 0.1, + "cum": 2231475.0 + } + }, + { + "tier": 8.0, + "symbol": "BZ/USDT:USDT", + "currency": "USDT", + "minNotional": 80000000.0, + "maxNotional": 100000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 8, + "initialLeverage": 4, + "notionalCap": 100000000, + "notionalFloor": 80000000, + "maintMarginRatio": 0.125, + "cum": 4231475.0 + } + }, + { + "tier": 9.0, + "symbol": "BZ/USDT:USDT", + "currency": "USDT", + "minNotional": 100000000.0, + "maxNotional": 200000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 9, + "initialLeverage": 2, + "notionalCap": 200000000, + "notionalFloor": 100000000, + "maintMarginRatio": 0.25, + "cum": 16731475.0 + } + }, + { + "tier": 10.0, + "symbol": "BZ/USDT:USDT", + "currency": "USDT", + "minNotional": 200000000.0, + "maxNotional": 400000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 10, + "initialLeverage": 1, + "notionalCap": 400000000, + "notionalFloor": 200000000, + "maintMarginRatio": 0.5, + "cum": 66731475.0 + } + } + ], "C/USDT:USDT": [ { "tier": 1.0, @@ -23530,6 +23880,161 @@ } } ], + "CHIP/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "CHIP/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": 1, + "initialLeverage": 50, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.015, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "CHIP/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": 2, + "initialLeverage": 25, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.02, + "cum": 25.0 + } + }, + { + "tier": 3.0, + "symbol": "CHIP/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": 3, + "initialLeverage": 20, + "notionalCap": 25000, + "notionalFloor": 10000, + "maintMarginRatio": 0.025, + "cum": 75.0 + } + }, + { + "tier": 4.0, + "symbol": "CHIP/USDT:USDT", + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 62500.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 4, + "initialLeverage": 10, + "notionalCap": 62500, + "notionalFloor": 25000, + "maintMarginRatio": 0.05, + "cum": 700.0 + } + }, + { + "tier": 5.0, + "symbol": "CHIP/USDT:USDT", + "currency": "USDT", + "minNotional": 62500.0, + "maxNotional": 125000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 5, + "initialLeverage": 5, + "notionalCap": 125000, + "notionalFloor": 62500, + "maintMarginRatio": 0.1, + "cum": 3825.0 + } + }, + { + "tier": 6.0, + "symbol": "CHIP/USDT:USDT", + "currency": "USDT", + "minNotional": 125000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 6, + "initialLeverage": 4, + "notionalCap": 250000, + "notionalFloor": 125000, + "maintMarginRatio": 0.125, + "cum": 6950.0 + } + }, + { + "tier": 7.0, + "symbol": "CHIP/USDT:USDT", + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 7, + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 17375.0 + } + }, + { + "tier": 8.0, + "symbol": "CHIP/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 7500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 8, + "initialLeverage": 2, + "notionalCap": 7500000, + "notionalFloor": 500000, + "maintMarginRatio": 0.25, + "cum": 59025.0 + } + }, + { + "tier": 9.0, + "symbol": "CHIP/USDT:USDT", + "currency": "USDT", + "minNotional": 7500000.0, + "maxNotional": 12500000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 9, + "initialLeverage": 1, + "notionalCap": 12500000, + "notionalFloor": 7500000, + "maintMarginRatio": 0.5, + "cum": 1934025.0 + } + } + ], "CHR/USDT:USDT": [ { "tier": 1.0, @@ -24012,6 +24517,178 @@ } } ], + "CL/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "CL/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.005, + "maxLeverage": 100.0, + "info": { + "bracket": 1, + "initialLeverage": 100, + "notionalCap": 50000, + "notionalFloor": 0, + "maintMarginRatio": 0.005, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "CL/USDT:USDT", + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.0065, + "maxLeverage": 75.0, + "info": { + "bracket": 2, + "initialLeverage": 75, + "notionalCap": 400000, + "notionalFloor": 50000, + "maintMarginRatio": 0.0065, + "cum": 75.0 + } + }, + { + "tier": 3.0, + "symbol": "CL/USDT:USDT", + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": 3, + "initialLeverage": 50, + "notionalCap": 1000000, + "notionalFloor": 400000, + "maintMarginRatio": 0.01, + "cum": 1475.0 + } + }, + { + "tier": 4.0, + "symbol": "CL/USDT:USDT", + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": 4, + "initialLeverage": 25, + "notionalCap": 4000000, + "notionalFloor": 1000000, + "maintMarginRatio": 0.02, + "cum": 11475.0 + } + }, + { + "tier": 5.0, + "symbol": "CL/USDT:USDT", + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": 5, + "initialLeverage": 20, + "notionalCap": 8000000, + "notionalFloor": 4000000, + "maintMarginRatio": 0.025, + "cum": 31475.0 + } + }, + { + "tier": 6.0, + "symbol": "CL/USDT:USDT", + "currency": "USDT", + "minNotional": 8000000.0, + "maxNotional": 40000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 6, + "initialLeverage": 10, + "notionalCap": 40000000, + "notionalFloor": 8000000, + "maintMarginRatio": 0.05, + "cum": 231475.0 + } + }, + { + "tier": 7.0, + "symbol": "CL/USDT:USDT", + "currency": "USDT", + "minNotional": 40000000.0, + "maxNotional": 80000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 7, + "initialLeverage": 5, + "notionalCap": 80000000, + "notionalFloor": 40000000, + "maintMarginRatio": 0.1, + "cum": 2231475.0 + } + }, + { + "tier": 8.0, + "symbol": "CL/USDT:USDT", + "currency": "USDT", + "minNotional": 80000000.0, + "maxNotional": 100000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 8, + "initialLeverage": 4, + "notionalCap": 100000000, + "notionalFloor": 80000000, + "maintMarginRatio": 0.125, + "cum": 4231475.0 + } + }, + { + "tier": 9.0, + "symbol": "CL/USDT:USDT", + "currency": "USDT", + "minNotional": 100000000.0, + "maxNotional": 200000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 9, + "initialLeverage": 2, + "notionalCap": 200000000, + "notionalFloor": 100000000, + "maintMarginRatio": 0.25, + "cum": 16731475.0 + } + }, + { + "tier": 10.0, + "symbol": "CL/USDT:USDT", + "currency": "USDT", + "minNotional": 200000000.0, + "maxNotional": 400000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 10, + "initialLeverage": 1, + "notionalCap": 400000000, + "notionalFloor": 200000000, + "maintMarginRatio": 0.5, + "cum": 66731475.0 + } + } + ], "CLANKER/USDT:USDT": [ { "tier": 1.0, @@ -25004,14 +25681,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 1, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.025, "cum": 0.0 } }, @@ -25021,15 +25698,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 2, - "initialLeverage": 20, + "initialLeverage": 10, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.025, - "cum": 50.0 + "maintMarginRatio": 0.05, + "cum": 125.0 } }, { @@ -25037,101 +25714,84 @@ "symbol": "COOKIE/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 3, - "initialLeverage": 10, - "notionalCap": 20000, + "initialLeverage": 5, + "notionalCap": 60000, "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 300.0 + "maintMarginRatio": 0.1, + "cum": 625.0 } }, { "tier": 4.0, "symbol": "COOKIE/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 60000.0, + "maxNotional": 70000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 4, - "initialLeverage": 5, - "notionalCap": 50000, - "notionalFloor": 20000, - "maintMarginRatio": 0.1, - "cum": 1300.0 + "initialLeverage": 4, + "notionalCap": 70000, + "notionalFloor": 60000, + "maintMarginRatio": 0.125, + "cum": 2125.0 } }, { "tier": 5.0, "symbol": "COOKIE/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 70000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 5, - "initialLeverage": 4, - "notionalCap": 100000, - "notionalFloor": 50000, - "maintMarginRatio": 0.125, - "cum": 2550.0 + "initialLeverage": 3, + "notionalCap": 250000, + "notionalFloor": 70000, + "maintMarginRatio": 0.1667, + "cum": 5044.0 } }, { "tier": 6.0, "symbol": "COOKIE/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": 6, - "initialLeverage": 3, - "notionalCap": 250000, - "notionalFloor": 100000, - "maintMarginRatio": 0.1667, - "cum": 6720.0 + "initialLeverage": 2, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.25, + "cum": 25869.0 } }, { "tier": 7.0, "symbol": "COOKIE/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 2500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 7, - "initialLeverage": 2, - "notionalCap": 2500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.25, - "cum": 27545.0 - } - }, - { - "tier": 8.0, - "symbol": "COOKIE/USDT:USDT", - "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 500000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 8, + "bracket": 7, "initialLeverage": 1, - "notionalCap": 5000000, - "notionalFloor": 2500000, + "notionalCap": 800000, + "notionalFloor": 500000, "maintMarginRatio": 0.5, - "cum": 652545.0 + "cum": 150869.0 } } ], @@ -27417,13 +28077,13 @@ "symbol": "DAM/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 2500000.0, + "maxNotional": 400000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": 5, "initialLeverage": 2, - "notionalCap": 2500000, + "notionalCap": 400000, "notionalFloor": 250000, "maintMarginRatio": 0.25, "cum": 26745.0 @@ -27433,17 +28093,17 @@ "tier": 6.0, "symbol": "DAM/USDT:USDT", "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 400000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": 6, "initialLeverage": 1, - "notionalCap": 5000000, - "notionalFloor": 2500000, + "notionalCap": 500000, + "notionalFloor": 400000, "maintMarginRatio": 0.5, - "cum": 651745.0 + "cum": 126745.0 } } ], @@ -28056,15 +28716,15 @@ "symbol": "DEGEN/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 1, - "initialLeverage": 50, - "notionalCap": 5000, + "initialLeverage": 10, + "notionalCap": 10000, "notionalFloor": 0, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.05, "cum": 0.0 } }, @@ -28072,119 +28732,85 @@ "tier": 2.0, "symbol": "DEGEN/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 10000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 2, - "initialLeverage": 20, - "notionalCap": 10000, - "notionalFloor": 5000, - "maintMarginRatio": 0.025, - "cum": 50.0 + "initialLeverage": 5, + "notionalCap": 50000, + "notionalFloor": 10000, + "maintMarginRatio": 0.1, + "cum": 500.0 } }, { "tier": 3.0, "symbol": "DEGEN/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 3, - "initialLeverage": 10, - "notionalCap": 20000, - "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 300.0 + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 50000, + "maintMarginRatio": 0.125, + "cum": 1750.0 } }, { "tier": 4.0, "symbol": "DEGEN/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 4, - "initialLeverage": 5, - "notionalCap": 50000, - "notionalFloor": 20000, - "maintMarginRatio": 0.1, - "cum": 1300.0 + "initialLeverage": 3, + "notionalCap": 250000, + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 5920.0 } }, { "tier": 5.0, "symbol": "DEGEN/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 250000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": 5, - "initialLeverage": 4, - "notionalCap": 250000, - "notionalFloor": 50000, - "maintMarginRatio": 0.125, - "cum": 2550.0 + "initialLeverage": 2, + "notionalCap": 400000, + "notionalFloor": 250000, + "maintMarginRatio": 0.25, + "cum": 26745.0 } }, { "tier": 6.0, "symbol": "DEGEN/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, + "minNotional": 400000.0, "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 6, - "initialLeverage": 3, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.1667, - "cum": 12975.0 - } - }, - { - "tier": 7.0, - "symbol": "DEGEN/USDT:USDT", - "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 7, - "initialLeverage": 2, - "notionalCap": 7500000, - "notionalFloor": 500000, - "maintMarginRatio": 0.25, - "cum": 54625.0 - } - }, - { - "tier": 8.0, - "symbol": "DEGEN/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 8, + "bracket": 6, "initialLeverage": 1, - "notionalCap": 12500000, - "notionalFloor": 7500000, + "notionalCap": 500000, + "notionalFloor": 400000, "maintMarginRatio": 0.5, - "cum": 1929625.0 + "cum": 126745.0 } } ], @@ -28245,13 +28871,13 @@ "symbol": "DEGO/USDT:USDT", "currency": "USDT", "minNotional": 70000.0, - "maxNotional": 700000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { "bracket": 4, "initialLeverage": 3, - "notionalCap": 700000, + "notionalCap": 200000, "notionalFloor": 70000, "maintMarginRatio": 0.1667, "cum": 4919.0 @@ -28261,34 +28887,34 @@ "tier": 5.0, "symbol": "DEGO/USDT:USDT", "currency": "USDT", - "minNotional": 700000.0, - "maxNotional": 2500000.0, + "minNotional": 200000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": 5, "initialLeverage": 2, - "notionalCap": 2500000, - "notionalFloor": 700000, + "notionalCap": 500000, + "notionalFloor": 200000, "maintMarginRatio": 0.25, - "cum": 63229.0 + "cum": 21579.0 } }, { "tier": 6.0, "symbol": "DEGO/USDT:USDT", "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 500000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": 6, "initialLeverage": 1, - "notionalCap": 5000000, - "notionalFloor": 2500000, + "notionalCap": 800000, + "notionalFloor": 500000, "maintMarginRatio": 0.5, - "cum": 688229.0 + "cum": 146579.0 } } ], @@ -28298,15 +28924,15 @@ "symbol": "DENT/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 15000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 1, - "initialLeverage": 50, - "notionalCap": 5000, + "initialLeverage": 10, + "notionalCap": 15000, "notionalFloor": 0, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.05, "cum": 0.0 } }, @@ -28314,136 +28940,85 @@ "tier": 2.0, "symbol": "DENT/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "minNotional": 15000.0, + "maxNotional": 80000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 2, - "initialLeverage": 25, - "notionalCap": 10000, - "notionalFloor": 5000, - "maintMarginRatio": 0.02, - "cum": 25.0 + "initialLeverage": 5, + "notionalCap": 80000, + "notionalFloor": 15000, + "maintMarginRatio": 0.1, + "cum": 750.0 } }, { "tier": 3.0, "symbol": "DENT/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 80000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 3, - "initialLeverage": 20, - "notionalCap": 25000, - "notionalFloor": 10000, - "maintMarginRatio": 0.025, - "cum": 75.0 + "initialLeverage": 4, + "notionalCap": 200000, + "notionalFloor": 80000, + "maintMarginRatio": 0.125, + "cum": 2750.0 } }, { "tier": 4.0, "symbol": "DENT/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 62500.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 200000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 4, - "initialLeverage": 10, - "notionalCap": 62500, - "notionalFloor": 25000, - "maintMarginRatio": 0.05, - "cum": 700.0 + "initialLeverage": 3, + "notionalCap": 300000, + "notionalFloor": 200000, + "maintMarginRatio": 0.1667, + "cum": 11090.0 } }, { "tier": 5.0, "symbol": "DENT/USDT:USDT", "currency": "USDT", - "minNotional": 62500.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 300000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": 5, - "initialLeverage": 5, - "notionalCap": 125000, - "notionalFloor": 62500, - "maintMarginRatio": 0.1, - "cum": 3825.0 + "initialLeverage": 2, + "notionalCap": 500000, + "notionalFloor": 300000, + "maintMarginRatio": 0.25, + "cum": 36080.0 } }, { "tier": 6.0, "symbol": "DENT/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 6, - "initialLeverage": 4, - "notionalCap": 250000, - "notionalFloor": 125000, - "maintMarginRatio": 0.125, - "cum": 6950.0 - } - }, - { - "tier": 7.0, - "symbol": "DENT/USDT:USDT", - "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 7, - "initialLeverage": 3, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.1667, - "cum": 17375.0 - } - }, - { - "tier": 8.0, - "symbol": "DENT/USDT:USDT", - "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 8, - "initialLeverage": 2, - "notionalCap": 7500000, - "notionalFloor": 500000, - "maintMarginRatio": 0.25, - "cum": 59025.0 - } - }, - { - "tier": 9.0, - "symbol": "DENT/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 9, + "bracket": 6, "initialLeverage": 1, - "notionalCap": 12500000, - "notionalFloor": 7500000, + "notionalCap": 800000, + "notionalFloor": 500000, "maintMarginRatio": 0.5, - "cum": 1934025.0 + "cum": 161080.0 } } ], @@ -30161,14 +30736,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.04, + "maxLeverage": 20.0, "info": { "bracket": 1, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.04, "cum": 0.0 } }, @@ -30178,14 +30753,14 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 2, - "initialLeverage": 20, + "initialLeverage": 10, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.025, + "maintMarginRatio": 0.05, "cum": 50.0 } }, @@ -30194,32 +30769,32 @@ "symbol": "DRIFT/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 3, - "initialLeverage": 10, - "notionalCap": 20000, + "initialLeverage": 5, + "notionalCap": 30000, "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 300.0 + "maintMarginRatio": 0.1, + "cum": 550.0 } }, { "tier": 4.0, "symbol": "DRIFT/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 30000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 4, - "initialLeverage": 5, - "notionalCap": 50000, - "notionalFloor": 20000, - "maintMarginRatio": 0.1, + "initialLeverage": 4, + "notionalCap": 200000, + "notionalFloor": 30000, + "maintMarginRatio": 0.125, "cum": 1300.0 } }, @@ -30227,68 +30802,51 @@ "tier": 5.0, "symbol": "DRIFT/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 200000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 5, - "initialLeverage": 4, - "notionalCap": 250000, - "notionalFloor": 50000, - "maintMarginRatio": 0.125, - "cum": 2550.0 + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 200000, + "maintMarginRatio": 0.1667, + "cum": 9640.0 } }, { "tier": 6.0, "symbol": "DRIFT/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "minNotional": 500000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": 6, - "initialLeverage": 3, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.1667, - "cum": 12975.0 + "initialLeverage": 2, + "notionalCap": 600000, + "notionalFloor": 500000, + "maintMarginRatio": 0.25, + "cum": 51290.0 } }, { "tier": 7.0, "symbol": "DRIFT/USDT:USDT", "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 7, - "initialLeverage": 2, - "notionalCap": 7500000, - "notionalFloor": 500000, - "maintMarginRatio": 0.25, - "cum": 54625.0 - } - }, - { - "tier": 8.0, - "symbol": "DRIFT/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 600000.0, + "maxNotional": 650000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 8, + "bracket": 7, "initialLeverage": 1, - "notionalCap": 12500000, - "notionalFloor": 7500000, + "notionalCap": 650000, + "notionalFloor": 600000, "maintMarginRatio": 0.5, - "cum": 1929625.0 + "cum": 201290.0 } } ], @@ -30902,14 +31460,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 5, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.1, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -30919,15 +31477,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": 2, - "initialLeverage": 4, + "initialLeverage": 25, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.125, - "cum": 125.0 + "maintMarginRatio": 0.02, + "cum": 25.0 } }, { @@ -30935,50 +31493,118 @@ "symbol": "EDGE/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 30000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 3, - "initialLeverage": 3, - "notionalCap": 30000, + "initialLeverage": 20, + "notionalCap": 25000, "notionalFloor": 10000, - "maintMarginRatio": 0.1667, - "cum": 542.0 + "maintMarginRatio": 0.025, + "cum": 75.0 } }, { "tier": 4.0, "symbol": "EDGE/USDT:USDT", "currency": "USDT", - "minNotional": 30000.0, - "maxNotional": 80000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, + "minNotional": 25000.0, + "maxNotional": 62500.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 4, - "initialLeverage": 2, - "notionalCap": 80000, - "notionalFloor": 30000, - "maintMarginRatio": 0.25, - "cum": 3041.0 + "initialLeverage": 10, + "notionalCap": 62500, + "notionalFloor": 25000, + "maintMarginRatio": 0.05, + "cum": 700.0 } }, { "tier": 5.0, "symbol": "EDGE/USDT:USDT", "currency": "USDT", - "minNotional": 80000.0, - "maxNotional": 200000.0, + "minNotional": 62500.0, + "maxNotional": 125000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 5, + "initialLeverage": 5, + "notionalCap": 125000, + "notionalFloor": 62500, + "maintMarginRatio": 0.1, + "cum": 3825.0 + } + }, + { + "tier": 6.0, + "symbol": "EDGE/USDT:USDT", + "currency": "USDT", + "minNotional": 125000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 6, + "initialLeverage": 4, + "notionalCap": 250000, + "notionalFloor": 125000, + "maintMarginRatio": 0.125, + "cum": 6950.0 + } + }, + { + "tier": 7.0, + "symbol": "EDGE/USDT:USDT", + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 7, + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 17375.0 + } + }, + { + "tier": 8.0, + "symbol": "EDGE/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 7500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 8, + "initialLeverage": 2, + "notionalCap": 7500000, + "notionalFloor": 500000, + "maintMarginRatio": 0.25, + "cum": 59025.0 + } + }, + { + "tier": 9.0, + "symbol": "EDGE/USDT:USDT", + "currency": "USDT", + "minNotional": 7500000.0, + "maxNotional": 12500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 5, + "bracket": 9, "initialLeverage": 1, - "notionalCap": 200000, - "notionalFloor": 80000, + "notionalCap": 12500000, + "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 23041.0 + "cum": 1934025.0 } } ], @@ -32609,14 +33235,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -32626,14 +33252,14 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 25, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.02, "cum": 25.0 } }, @@ -32642,15 +33268,15 @@ "symbol": "ERA/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 50000, + "initialLeverage": 20, + "notionalCap": 25000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, + "maintMarginRatio": 0.025, "cum": 75.0 } }, @@ -32658,51 +33284,51 @@ "tier": 4.0, "symbol": "ERA/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 25000.0, + "maxNotional": 62500.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 4, - "initialLeverage": 20, - "notionalCap": 100000, - "notionalFloor": 50000, - "maintMarginRatio": 0.025, - "cum": 325.0 + "initialLeverage": 10, + "notionalCap": 62500, + "notionalFloor": 25000, + "maintMarginRatio": 0.05, + "cum": 700.0 } }, { "tier": 5.0, "symbol": "ERA/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 175000.0, - "maintenanceMarginRate": 0.0333, - "maxLeverage": 15.0, + "minNotional": 62500.0, + "maxNotional": 125000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 5, - "initialLeverage": 15, - "notionalCap": 175000, - "notionalFloor": 100000, - "maintMarginRatio": 0.0333, - "cum": 1155.0 + "initialLeverage": 5, + "notionalCap": 125000, + "notionalFloor": 62500, + "maintMarginRatio": 0.1, + "cum": 3825.0 } }, { "tier": 6.0, "symbol": "ERA/USDT:USDT", "currency": "USDT", - "minNotional": 175000.0, + "minNotional": 125000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 6, - "initialLeverage": 10, + "initialLeverage": 4, "notionalCap": 250000, - "notionalFloor": 175000, - "maintMarginRatio": 0.05, - "cum": 4077.5 + "notionalFloor": 125000, + "maintMarginRatio": 0.125, + "cum": 6950.0 } }, { @@ -32710,71 +33336,37 @@ "symbol": "ERA/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 750000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 7, - "initialLeverage": 5, - "notionalCap": 750000, + "initialLeverage": 3, + "notionalCap": 500000, "notionalFloor": 250000, - "maintMarginRatio": 0.1, - "cum": 16577.5 + "maintMarginRatio": 0.1667, + "cum": 17375.0 } }, { "tier": 8.0, "symbol": "ERA/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 8, - "initialLeverage": 4, - "notionalCap": 1500000, - "notionalFloor": 750000, - "maintMarginRatio": 0.125, - "cum": 35327.5 - } - }, - { - "tier": 9.0, - "symbol": "ERA/USDT:USDT", - "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 4500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 9, - "initialLeverage": 3, - "notionalCap": 4500000, - "notionalFloor": 1500000, - "maintMarginRatio": 0.1667, - "cum": 97877.5 - } - }, - { - "tier": 10.0, - "symbol": "ERA/USDT:USDT", - "currency": "USDT", - "minNotional": 4500000.0, + "minNotional": 500000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 4500000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 472727.5 + "cum": 59025.0 } }, { - "tier": 11.0, + "tier": 9.0, "symbol": "ERA/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -32782,12 +33374,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 11, + "bracket": 9, "initialLeverage": 1, "notionalCap": 12000000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 2347727.5 + "cum": 1934025.0 } } ], @@ -33806,144 +34398,6 @@ } } ], - "ETH/USDT:USDT-260327": [ - { - "tier": 1.0, - "symbol": "ETH/USDT:USDT-260327", - "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-260327", - "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-260327", - "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-260327", - "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-260327", - "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-260327", - "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-260327", - "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-260327", - "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-260626": [ { "tier": 1.0, @@ -34082,6 +34536,144 @@ } } ], + "ETH/USDT:USDT-260925": [ + { + "tier": 1.0, + "symbol": "ETH/USDT:USDT-260925", + "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-260925", + "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-260925", + "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-260925", + "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-260925", + "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-260925", + "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-260925", + "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-260925", + "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, @@ -34847,14 +35439,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 1, - "initialLeverage": 10, + "initialLeverage": 20, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.05, + "maintMarginRatio": 0.025, "cum": 0.0 } }, @@ -34864,15 +35456,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maintenanceMarginRate": 0.0333, + "maxLeverage": 15.0, "info": { "bracket": 2, - "initialLeverage": 5, + "initialLeverage": 15, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.1, - "cum": 250.0 + "maintMarginRatio": 0.0333, + "cum": 41.5 } }, { @@ -34880,37 +35472,71 @@ "symbol": "EWJ/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 3, - "initialLeverage": 4, - "notionalCap": 100000, + "initialLeverage": 10, + "notionalCap": 25000, "notionalFloor": 10000, - "maintMarginRatio": 0.125, - "cum": 500.0 + "maintMarginRatio": 0.05, + "cum": 208.5 } }, { "tier": 4.0, "symbol": "EWJ/USDT:USDT", "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 4, + "initialLeverage": 5, + "notionalCap": 50000, + "notionalFloor": 25000, + "maintMarginRatio": 0.1, + "cum": 1458.5 + } + }, + { + "tier": 5.0, + "symbol": "EWJ/USDT:USDT", + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 5, + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 50000, + "maintMarginRatio": 0.125, + "cum": 2708.5 + } + }, + { + "tier": 6.0, + "symbol": "EWJ/USDT:USDT", + "currency": "USDT", "minNotional": 100000.0, "maxNotional": 500000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { - "bracket": 4, + "bracket": 6, "initialLeverage": 3, "notionalCap": 500000, "notionalFloor": 100000, "maintMarginRatio": 0.1667, - "cum": 4670.0 + "cum": 6878.5 } }, { - "tier": 5.0, + "tier": 7.0, "symbol": "EWJ/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, @@ -34918,16 +35544,16 @@ "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 5, + "bracket": 7, "initialLeverage": 2, "notionalCap": 8000000, "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 46320.0 + "cum": 48528.5 } }, { - "tier": 6.0, + "tier": 8.0, "symbol": "EWJ/USDT:USDT", "currency": "USDT", "minNotional": 8000000.0, @@ -34935,12 +35561,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 6, + "bracket": 8, "initialLeverage": 1, "notionalCap": 15000000, "notionalFloor": 8000000, "maintMarginRatio": 0.5, - "cum": 2046320.0 + "cum": 2048528.5 } } ], @@ -34951,14 +35577,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 1, - "initialLeverage": 10, + "initialLeverage": 20, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.05, + "maintMarginRatio": 0.025, "cum": 0.0 } }, @@ -34968,15 +35594,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maintenanceMarginRate": 0.0333, + "maxLeverage": 15.0, "info": { "bracket": 2, - "initialLeverage": 5, + "initialLeverage": 15, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.1, - "cum": 250.0 + "maintMarginRatio": 0.0333, + "cum": 41.5 } }, { @@ -34984,37 +35610,71 @@ "symbol": "EWY/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 3, - "initialLeverage": 4, - "notionalCap": 100000, + "initialLeverage": 10, + "notionalCap": 25000, "notionalFloor": 10000, - "maintMarginRatio": 0.125, - "cum": 500.0 + "maintMarginRatio": 0.05, + "cum": 208.5 } }, { "tier": 4.0, "symbol": "EWY/USDT:USDT", "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 4, + "initialLeverage": 5, + "notionalCap": 50000, + "notionalFloor": 25000, + "maintMarginRatio": 0.1, + "cum": 1458.5 + } + }, + { + "tier": 5.0, + "symbol": "EWY/USDT:USDT", + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 5, + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 50000, + "maintMarginRatio": 0.125, + "cum": 2708.5 + } + }, + { + "tier": 6.0, + "symbol": "EWY/USDT:USDT", + "currency": "USDT", "minNotional": 100000.0, "maxNotional": 500000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { - "bracket": 4, + "bracket": 6, "initialLeverage": 3, "notionalCap": 500000, "notionalFloor": 100000, "maintMarginRatio": 0.1667, - "cum": 4670.0 + "cum": 6878.5 } }, { - "tier": 5.0, + "tier": 7.0, "symbol": "EWY/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, @@ -35022,16 +35682,16 @@ "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 5, + "bracket": 7, "initialLeverage": 2, "notionalCap": 8000000, "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 46320.0 + "cum": 48528.5 } }, { - "tier": 6.0, + "tier": 8.0, "symbol": "EWY/USDT:USDT", "currency": "USDT", "minNotional": 8000000.0, @@ -35039,12 +35699,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 6, + "bracket": 8, "initialLeverage": 1, "notionalCap": 15000000, "notionalFloor": 8000000, "maintMarginRatio": 0.5, - "cum": 2046320.0 + "cum": 2048528.5 } } ], @@ -35570,13 +36230,13 @@ "symbol": "FF/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 20000.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, "maxLeverage": 75.0, "info": { "bracket": 1, "initialLeverage": 75, - "notionalCap": 20000, + "notionalCap": 5000, "notionalFloor": 0, "maintMarginRatio": 0.01, "cum": 0.0 @@ -35586,136 +36246,170 @@ "tier": 2.0, "symbol": "FF/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 200000.0, + "minNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": 2, "initialLeverage": 50, - "notionalCap": 200000, - "notionalFloor": 20000, + "notionalCap": 10000, + "notionalFloor": 5000, "maintMarginRatio": 0.015, - "cum": 100.0 + "cum": 25.0 } }, { "tier": 3.0, "symbol": "FF/USDT:USDT", "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 1000000.0, + "minNotional": 10000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": 3, "initialLeverage": 25, - "notionalCap": 1000000, - "notionalFloor": 200000, + "notionalCap": 50000, + "notionalFloor": 10000, "maintMarginRatio": 0.02, - "cum": 1100.0 + "cum": 75.0 } }, { "tier": 4.0, "symbol": "FF/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": 4, "initialLeverage": 20, - "notionalCap": 2000000, - "notionalFloor": 1000000, + "notionalCap": 100000, + "notionalFloor": 50000, "maintMarginRatio": 0.025, - "cum": 6100.0 + "cum": 325.0 } }, { "tier": 5.0, "symbol": "FF/USDT:USDT", "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 5000000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 100000.0, + "maxNotional": 175000.0, + "maintenanceMarginRate": 0.0333, + "maxLeverage": 15.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 5000000, - "notionalFloor": 2000000, - "maintMarginRatio": 0.05, - "cum": 56100.0 + "initialLeverage": 15, + "notionalCap": 175000, + "notionalFloor": 100000, + "maintMarginRatio": 0.0333, + "cum": 1155.0 } }, { "tier": 6.0, "symbol": "FF/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 175000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 7500000, - "notionalFloor": 5000000, - "maintMarginRatio": 0.1, - "cum": 306100.0 + "initialLeverage": 10, + "notionalCap": 250000, + "notionalFloor": 175000, + "maintMarginRatio": 0.05, + "cum": 4077.5 } }, { "tier": 7.0, "symbol": "FF/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 10000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 250000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 7, - "initialLeverage": 4, - "notionalCap": 10000000, - "notionalFloor": 7500000, - "maintMarginRatio": 0.125, - "cum": 493600.0 + "initialLeverage": 5, + "notionalCap": 750000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1, + "cum": 16577.5 } }, { "tier": 8.0, "symbol": "FF/USDT:USDT", "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 12500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 8, - "initialLeverage": 2, - "notionalCap": 12500000, - "notionalFloor": 10000000, - "maintMarginRatio": 0.25, - "cum": 1743600.0 + "initialLeverage": 4, + "notionalCap": 1500000, + "notionalFloor": 750000, + "maintMarginRatio": 0.125, + "cum": 35327.5 } }, { "tier": 9.0, "symbol": "FF/USDT:USDT", "currency": "USDT", - "minNotional": 12500000.0, - "maxNotional": 15000000.0, + "minNotional": 1500000.0, + "maxNotional": 4500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 9, + "initialLeverage": 3, + "notionalCap": 4500000, + "notionalFloor": 1500000, + "maintMarginRatio": 0.1667, + "cum": 97877.5 + } + }, + { + "tier": 10.0, + "symbol": "FF/USDT:USDT", + "currency": "USDT", + "minNotional": 4500000.0, + "maxNotional": 7500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 10, + "initialLeverage": 2, + "notionalCap": 7500000, + "notionalFloor": 4500000, + "maintMarginRatio": 0.25, + "cum": 472727.5 + } + }, + { + "tier": 11.0, + "symbol": "FF/USDT:USDT", + "currency": "USDT", + "minNotional": 7500000.0, + "maxNotional": 12500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 9, + "bracket": 11, "initialLeverage": 1, - "notionalCap": 15000000, - "notionalFloor": 12500000, + "notionalCap": 12500000, + "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 4868600.0 + "cum": 2347727.5 } } ], @@ -36500,13 +37194,13 @@ "symbol": "FIO/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 2500000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": 5, "initialLeverage": 2, - "notionalCap": 2500000, + "notionalCap": 500000, "notionalFloor": 250000, "maintMarginRatio": 0.25, "cum": 26745.0 @@ -36516,17 +37210,17 @@ "tier": 6.0, "symbol": "FIO/USDT:USDT", "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 500000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": 6, "initialLeverage": 1, - "notionalCap": 5000000, - "notionalFloor": 2500000, + "notionalCap": 800000, + "notionalFloor": 500000, "maintMarginRatio": 0.5, - "cum": 651745.0 + "cum": 151745.0 } } ], @@ -38364,13 +39058,13 @@ "symbol": "FUN/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 2500000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": 6, "initialLeverage": 2, - "notionalCap": 2500000, + "notionalCap": 500000, "notionalFloor": 250000, "maintMarginRatio": 0.25, "cum": 27120.0 @@ -38380,17 +39074,17 @@ "tier": 7.0, "symbol": "FUN/USDT:USDT", "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 500000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": 7, "initialLeverage": 1, - "notionalCap": 5000000, - "notionalFloor": 2500000, + "notionalCap": 800000, + "notionalFloor": 500000, "maintMarginRatio": 0.5, - "cum": 652120.0 + "cum": 152120.0 } } ], @@ -39135,6 +39829,144 @@ } } ], + "GENIUS/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "GENIUS/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": 1, + "initialLeverage": 50, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.015, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "GENIUS/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": 2, + "initialLeverage": 20, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.025, + "cum": 50.0 + } + }, + { + "tier": 3.0, + "symbol": "GENIUS/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 3, + "initialLeverage": 10, + "notionalCap": 20000, + "notionalFloor": 10000, + "maintMarginRatio": 0.05, + "cum": 300.0 + } + }, + { + "tier": 4.0, + "symbol": "GENIUS/USDT:USDT", + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 4, + "initialLeverage": 5, + "notionalCap": 50000, + "notionalFloor": 20000, + "maintMarginRatio": 0.1, + "cum": 1300.0 + } + }, + { + "tier": 5.0, + "symbol": "GENIUS/USDT:USDT", + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 5, + "initialLeverage": 4, + "notionalCap": 250000, + "notionalFloor": 50000, + "maintMarginRatio": 0.125, + "cum": 2550.0 + } + }, + { + "tier": 6.0, + "symbol": "GENIUS/USDT:USDT", + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 6, + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 12975.0 + } + }, + { + "tier": 7.0, + "symbol": "GENIUS/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 7500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 7, + "initialLeverage": 2, + "notionalCap": 7500000, + "notionalFloor": 500000, + "maintMarginRatio": 0.25, + "cum": 54625.0 + } + }, + { + "tier": 8.0, + "symbol": "GENIUS/USDT:USDT", + "currency": "USDT", + "minNotional": 7500000.0, + "maxNotional": 12500000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 8, + "initialLeverage": 1, + "notionalCap": 12500000, + "notionalFloor": 7500000, + "maintMarginRatio": 0.5, + "cum": 1929625.0 + } + } + ], "GHST/USDT:USDT": [ { "tier": 1.0, @@ -39643,14 +40475,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -39660,15 +40492,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, - "cum": 25.0 + "maintMarginRatio": 0.025, + "cum": 50.0 } }, { @@ -39676,33 +40508,33 @@ "symbol": "GMT/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 25000, + "initialLeverage": 10, + "notionalCap": 20000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, - "cum": 75.0 + "maintMarginRatio": 0.05, + "cum": 300.0 } }, { "tier": 4.0, "symbol": "GMT/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 20000.0, "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 4, - "initialLeverage": 20, + "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 25000, - "maintMarginRatio": 0.025, - "cum": 200.0 + "notionalFloor": 20000, + "maintMarginRatio": 0.1, + "cum": 1300.0 } }, { @@ -39710,88 +40542,54 @@ "symbol": "GMT/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 125000, + "initialLeverage": 4, + "notionalCap": 250000, "notionalFloor": 50000, - "maintMarginRatio": 0.05, - "cum": 1450.0 + "maintMarginRatio": 0.125, + "cum": 2550.0 } }, { "tier": 6.0, "symbol": "GMT/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 250000, - "notionalFloor": 125000, - "maintMarginRatio": 0.1, - "cum": 7700.0 + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 12975.0 } }, { "tier": 7.0, "symbol": "GMT/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 7, - "initialLeverage": 4, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.125, - "cum": 13950.0 - } - }, - { - "tier": 8.0, - "symbol": "GMT/USDT:USDT", - "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 8, - "initialLeverage": 3, - "notionalCap": 1000000, - "notionalFloor": 500000, - "maintMarginRatio": 0.1667, - "cum": 34800.0 - } - }, - { - "tier": 9.0, - "symbol": "GMT/USDT:USDT", - "currency": "USDT", - "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 9, + "bracket": 7, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 1000000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 118100.0 + "cum": 54625.0 } }, { - "tier": 10.0, + "tier": 8.0, "symbol": "GMT/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -39799,12 +40597,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 1993100.0 + "cum": 1929625.0 } } ], @@ -42507,14 +43305,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 1, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.025, "cum": 0.0 } }, @@ -42524,15 +43322,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 2, - "initialLeverage": 20, + "initialLeverage": 10, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.025, - "cum": 50.0 + "maintMarginRatio": 0.05, + "cum": 125.0 } }, { @@ -42540,50 +43338,50 @@ "symbol": "HIGH/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 3, - "initialLeverage": 10, - "notionalCap": 20000, + "initialLeverage": 5, + "notionalCap": 60000, "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 300.0 + "maintMarginRatio": 0.1, + "cum": 625.0 } }, { "tier": 4.0, "symbol": "HIGH/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 60000.0, + "maxNotional": 70000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 4, - "initialLeverage": 5, - "notionalCap": 50000, - "notionalFloor": 20000, - "maintMarginRatio": 0.1, - "cum": 1300.0 + "initialLeverage": 4, + "notionalCap": 70000, + "notionalFloor": 60000, + "maintMarginRatio": 0.125, + "cum": 2125.0 } }, { "tier": 5.0, "symbol": "HIGH/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, + "minNotional": 70000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 5, - "initialLeverage": 4, + "initialLeverage": 3, "notionalCap": 250000, - "notionalFloor": 50000, - "maintMarginRatio": 0.125, - "cum": 2550.0 + "notionalFloor": 70000, + "maintMarginRatio": 0.1667, + "cum": 5044.0 } }, { @@ -42592,15 +43390,15 @@ "currency": "USDT", "minNotional": 250000.0, "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": 6, - "initialLeverage": 3, + "initialLeverage": 2, "notionalCap": 500000, "notionalFloor": 250000, - "maintMarginRatio": 0.1667, - "cum": 12975.0 + "maintMarginRatio": 0.25, + "cum": 25869.0 } }, { @@ -42608,33 +43406,16 @@ "symbol": "HIGH/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 7, - "initialLeverage": 2, - "notionalCap": 7500000, - "notionalFloor": 500000, - "maintMarginRatio": 0.25, - "cum": 54625.0 - } - }, - { - "tier": 8.0, - "symbol": "HIGH/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 8, + "bracket": 7, "initialLeverage": 1, - "notionalCap": 12500000, - "notionalFloor": 7500000, + "notionalCap": 800000, + "notionalFloor": 500000, "maintMarginRatio": 0.5, - "cum": 1929625.0 + "cum": 150869.0 } } ], @@ -42695,13 +43476,13 @@ "symbol": "HIPPO/USDT:USDT", "currency": "USDT", "minNotional": 100000.0, - "maxNotional": 1000000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { "bracket": 4, "initialLeverage": 3, - "notionalCap": 1000000, + "notionalCap": 500000, "notionalFloor": 100000, "maintMarginRatio": 0.1667, "cum": 5920.0 @@ -42711,34 +43492,34 @@ "tier": 5.0, "symbol": "HIPPO/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2500000.0, + "minNotional": 500000.0, + "maxNotional": 600000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": 5, "initialLeverage": 2, - "notionalCap": 2500000, - "notionalFloor": 1000000, + "notionalCap": 600000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 89220.0 + "cum": 47570.0 } }, { "tier": 6.0, "symbol": "HIPPO/USDT:USDT", "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 600000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": 6, "initialLeverage": 1, - "notionalCap": 5000000, - "notionalFloor": 2500000, + "notionalCap": 800000, + "notionalFloor": 600000, "maintMarginRatio": 0.5, - "cum": 714220.0 + "cum": 197570.0 } } ], @@ -43025,14 +43806,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -43042,15 +43823,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, - "cum": 25.0 + "maintMarginRatio": 0.025, + "cum": 50.0 } }, { @@ -43058,33 +43839,33 @@ "symbol": "HOLO/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 25000, + "initialLeverage": 10, + "notionalCap": 20000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, - "cum": 75.0 + "maintMarginRatio": 0.05, + "cum": 300.0 } }, { "tier": 4.0, "symbol": "HOLO/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 20000.0, "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 4, - "initialLeverage": 20, + "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 25000, - "maintMarginRatio": 0.025, - "cum": 200.0 + "notionalFloor": 20000, + "maintMarginRatio": 0.1, + "cum": 1300.0 } }, { @@ -43092,88 +43873,54 @@ "symbol": "HOLO/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 125000, + "initialLeverage": 4, + "notionalCap": 250000, "notionalFloor": 50000, - "maintMarginRatio": 0.05, - "cum": 1450.0 + "maintMarginRatio": 0.125, + "cum": 2550.0 } }, { "tier": 6.0, "symbol": "HOLO/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 250000, - "notionalFloor": 125000, - "maintMarginRatio": 0.1, - "cum": 7700.0 + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 12975.0 } }, { "tier": 7.0, "symbol": "HOLO/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 7, - "initialLeverage": 4, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.125, - "cum": 13950.0 - } - }, - { - "tier": 8.0, - "symbol": "HOLO/USDT:USDT", - "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 8, - "initialLeverage": 3, - "notionalCap": 1000000, - "notionalFloor": 500000, - "maintMarginRatio": 0.1667, - "cum": 34800.0 - } - }, - { - "tier": 9.0, - "symbol": "HOLO/USDT:USDT", - "currency": "USDT", - "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 9, + "bracket": 7, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 1000000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 118100.0 + "cum": 54625.0 } }, { - "tier": 10.0, + "tier": 8.0, "symbol": "HOLO/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -43181,12 +43928,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 1993100.0 + "cum": 1929625.0 } } ], @@ -46127,14 +46874,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -46144,15 +46891,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, - "cum": 25.0 + "maintMarginRatio": 0.025, + "cum": 50.0 } }, { @@ -46160,33 +46907,33 @@ "symbol": "IO/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 25000, + "initialLeverage": 10, + "notionalCap": 20000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, - "cum": 75.0 + "maintMarginRatio": 0.05, + "cum": 300.0 } }, { "tier": 4.0, "symbol": "IO/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 20000.0, "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 4, - "initialLeverage": 20, + "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 25000, - "maintMarginRatio": 0.025, - "cum": 200.0 + "notionalFloor": 20000, + "maintMarginRatio": 0.1, + "cum": 1300.0 } }, { @@ -46194,88 +46941,54 @@ "symbol": "IO/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 125000, + "initialLeverage": 4, + "notionalCap": 250000, "notionalFloor": 50000, - "maintMarginRatio": 0.05, - "cum": 1450.0 + "maintMarginRatio": 0.125, + "cum": 2550.0 } }, { "tier": 6.0, "symbol": "IO/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 250000, - "notionalFloor": 125000, - "maintMarginRatio": 0.1, - "cum": 7700.0 + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 12975.0 } }, { "tier": 7.0, "symbol": "IO/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 7, - "initialLeverage": 4, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.125, - "cum": 13950.0 - } - }, - { - "tier": 8.0, - "symbol": "IO/USDT:USDT", - "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 8, - "initialLeverage": 3, - "notionalCap": 1000000, - "notionalFloor": 500000, - "maintMarginRatio": 0.1667, - "cum": 34800.0 - } - }, - { - "tier": 9.0, - "symbol": "IO/USDT:USDT", - "currency": "USDT", - "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 9, + "bracket": 7, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 1000000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 118100.0 + "cum": 54625.0 } }, { - "tier": 10.0, + "tier": 8.0, "symbol": "IO/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -46283,12 +46996,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 1993100.0 + "cum": 1929625.0 } } ], @@ -47141,15 +47854,15 @@ "symbol": "IR/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 40.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 1, - "initialLeverage": 40, - "notionalCap": 5000, + "initialLeverage": 10, + "notionalCap": 10000, "notionalFloor": 0, - "maintMarginRatio": 0.02, + "maintMarginRatio": 0.05, "cum": 0.0 } }, @@ -47157,55 +47870,21 @@ "tier": 2.0, "symbol": "IR/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, - "info": { - "bracket": 2, - "initialLeverage": 20, - "notionalCap": 10000, - "notionalFloor": 5000, - "maintMarginRatio": 0.025, - "cum": 25.0 - } - }, - { - "tier": 3.0, - "symbol": "IR/USDT:USDT", - "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, - "info": { - "bracket": 3, - "initialLeverage": 10, - "notionalCap": 20000, - "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 275.0 - } - }, - { - "tier": 4.0, - "symbol": "IR/USDT:USDT", - "currency": "USDT", - "minNotional": 20000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": 4, + "bracket": 2, "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 20000, + "notionalFloor": 10000, "maintMarginRatio": 0.1, - "cum": 1275.0 + "cum": 500.0 } }, { - "tier": 5.0, + "tier": 3.0, "symbol": "IR/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, @@ -47213,16 +47892,16 @@ "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": 5, + "bracket": 3, "initialLeverage": 4, "notionalCap": 100000, "notionalFloor": 50000, "maintMarginRatio": 0.125, - "cum": 2525.0 + "cum": 1750.0 } }, { - "tier": 6.0, + "tier": 4.0, "symbol": "IR/USDT:USDT", "currency": "USDT", "minNotional": 100000.0, @@ -47230,46 +47909,46 @@ "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { - "bracket": 6, + "bracket": 4, "initialLeverage": 3, "notionalCap": 250000, "notionalFloor": 100000, "maintMarginRatio": 0.1667, - "cum": 6695.0 + "cum": 5920.0 } }, { - "tier": 7.0, + "tier": 5.0, "symbol": "IR/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 2500000.0, + "maxNotional": 400000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 7, + "bracket": 5, "initialLeverage": 2, - "notionalCap": 2500000, + "notionalCap": 400000, "notionalFloor": 250000, "maintMarginRatio": 0.25, - "cum": 27520.0 + "cum": 26745.0 } }, { - "tier": 8.0, + "tier": 6.0, "symbol": "IR/USDT:USDT", "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 400000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 8, + "bracket": 6, "initialLeverage": 1, - "notionalCap": 5000000, - "notionalFloor": 2500000, + "notionalCap": 500000, + "notionalFloor": 400000, "maintMarginRatio": 0.5, - "cum": 652520.0 + "cum": 126745.0 } } ], @@ -48880,13 +49559,13 @@ "symbol": "KAITO/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 50000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": 3, "initialLeverage": 25, - "notionalCap": 50000, + "notionalCap": 25000, "notionalFloor": 10000, "maintMarginRatio": 0.02, "cum": 75.0 @@ -48896,51 +49575,51 @@ "tier": 4.0, "symbol": "KAITO/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, + "minNotional": 25000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": 4, "initialLeverage": 20, - "notionalCap": 100000, - "notionalFloor": 50000, + "notionalCap": 50000, + "notionalFloor": 25000, "maintMarginRatio": 0.025, - "cum": 325.0 + "cum": 200.0 } }, { "tier": 5.0, "symbol": "KAITO/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 175000.0, - "maintenanceMarginRate": 0.0333, - "maxLeverage": 15.0, + "minNotional": 50000.0, + "maxNotional": 125000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 5, - "initialLeverage": 15, - "notionalCap": 175000, - "notionalFloor": 100000, - "maintMarginRatio": 0.0333, - "cum": 1155.0 + "initialLeverage": 10, + "notionalCap": 125000, + "notionalFloor": 50000, + "maintMarginRatio": 0.05, + "cum": 1450.0 } }, { "tier": 6.0, "symbol": "KAITO/USDT:USDT", "currency": "USDT", - "minNotional": 175000.0, + "minNotional": 125000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 6, - "initialLeverage": 10, + "initialLeverage": 5, "notionalCap": 250000, - "notionalFloor": 175000, - "maintMarginRatio": 0.05, - "cum": 4077.5 + "notionalFloor": 125000, + "maintMarginRatio": 0.1, + "cum": 7700.0 } }, { @@ -48948,71 +49627,54 @@ "symbol": "KAITO/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 750000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 7, - "initialLeverage": 5, - "notionalCap": 750000, + "initialLeverage": 4, + "notionalCap": 500000, "notionalFloor": 250000, - "maintMarginRatio": 0.1, - "cum": 16577.5 + "maintMarginRatio": 0.125, + "cum": 13950.0 } }, { "tier": 8.0, "symbol": "KAITO/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 8, - "initialLeverage": 4, - "notionalCap": 1500000, - "notionalFloor": 750000, - "maintMarginRatio": 0.125, - "cum": 35327.5 + "initialLeverage": 3, + "notionalCap": 1000000, + "notionalFloor": 500000, + "maintMarginRatio": 0.1667, + "cum": 34800.0 } }, { "tier": 9.0, "symbol": "KAITO/USDT:USDT", "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 4500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 9, - "initialLeverage": 3, - "notionalCap": 4500000, - "notionalFloor": 1500000, - "maintMarginRatio": 0.1667, - "cum": 97877.5 - } - }, - { - "tier": 10.0, - "symbol": "KAITO/USDT:USDT", - "currency": "USDT", - "minNotional": 4500000.0, + "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 10, + "bracket": 9, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 4500000, + "notionalFloor": 1000000, "maintMarginRatio": 0.25, - "cum": 472727.5 + "cum": 118100.0 } }, { - "tier": 11.0, + "tier": 10.0, "symbol": "KAITO/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -49020,12 +49682,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 11, + "bracket": 10, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 2347727.5 + "cum": 1993100.0 } } ], @@ -51106,14 +51768,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -51123,15 +51785,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, - "cum": 25.0 + "maintMarginRatio": 0.025, + "cum": 50.0 } }, { @@ -51139,33 +51801,33 @@ "symbol": "LAYER/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 25000, + "initialLeverage": 10, + "notionalCap": 20000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, - "cum": 75.0 + "maintMarginRatio": 0.05, + "cum": 300.0 } }, { "tier": 4.0, "symbol": "LAYER/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 20000.0, "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 4, - "initialLeverage": 20, + "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 25000, - "maintMarginRatio": 0.025, - "cum": 200.0 + "notionalFloor": 20000, + "maintMarginRatio": 0.1, + "cum": 1300.0 } }, { @@ -51173,88 +51835,54 @@ "symbol": "LAYER/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 125000, + "initialLeverage": 4, + "notionalCap": 250000, "notionalFloor": 50000, - "maintMarginRatio": 0.05, - "cum": 1450.0 + "maintMarginRatio": 0.125, + "cum": 2550.0 } }, { "tier": 6.0, "symbol": "LAYER/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 250000, - "notionalFloor": 125000, - "maintMarginRatio": 0.1, - "cum": 7700.0 + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 12975.0 } }, { "tier": 7.0, "symbol": "LAYER/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 7, - "initialLeverage": 4, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.125, - "cum": 13950.0 - } - }, - { - "tier": 8.0, - "symbol": "LAYER/USDT:USDT", - "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 8, - "initialLeverage": 3, - "notionalCap": 1000000, - "notionalFloor": 500000, - "maintMarginRatio": 0.1667, - "cum": 34800.0 - } - }, - { - "tier": 9.0, - "symbol": "LAYER/USDT:USDT", - "currency": "USDT", - "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 9, + "bracket": 7, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 1000000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 118100.0 + "cum": 54625.0 } }, { - "tier": 10.0, + "tier": 8.0, "symbol": "LAYER/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -51262,12 +51890,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 1993100.0 + "cum": 1929625.0 } } ], @@ -51812,15 +52440,15 @@ "symbol": "LINEA/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 7500.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, - "notionalCap": 7500, + "initialLeverage": 50, + "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -51828,170 +52456,136 @@ "tier": 2.0, "symbol": "LINEA/USDT:USDT", "currency": "USDT", - "minNotional": 7500.0, - "maxNotional": 15000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": 2, - "initialLeverage": 50, - "notionalCap": 15000, - "notionalFloor": 7500, - "maintMarginRatio": 0.015, - "cum": 37.5 + "initialLeverage": 25, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.02, + "cum": 25.0 } }, { "tier": 3.0, "symbol": "LINEA/USDT:USDT", "currency": "USDT", - "minNotional": 15000.0, - "maxNotional": 75000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "minNotional": 10000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 75000, - "notionalFloor": 15000, - "maintMarginRatio": 0.02, - "cum": 112.5 + "initialLeverage": 20, + "notionalCap": 25000, + "notionalFloor": 10000, + "maintMarginRatio": 0.025, + "cum": 75.0 } }, { "tier": 4.0, "symbol": "LINEA/USDT:USDT", "currency": "USDT", - "minNotional": 75000.0, - "maxNotional": 200000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 25000.0, + "maxNotional": 62500.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 4, - "initialLeverage": 20, - "notionalCap": 200000, - "notionalFloor": 75000, - "maintMarginRatio": 0.025, - "cum": 487.5 + "initialLeverage": 10, + "notionalCap": 62500, + "notionalFloor": 25000, + "maintMarginRatio": 0.05, + "cum": 700.0 } }, { "tier": 5.0, "symbol": "LINEA/USDT:USDT", "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 350000.0, - "maintenanceMarginRate": 0.0333, - "maxLeverage": 15.0, + "minNotional": 62500.0, + "maxNotional": 125000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 5, - "initialLeverage": 15, - "notionalCap": 350000, - "notionalFloor": 200000, - "maintMarginRatio": 0.0333, - "cum": 2147.5 + "initialLeverage": 5, + "notionalCap": 125000, + "notionalFloor": 62500, + "maintMarginRatio": 0.1, + "cum": 3825.0 } }, { "tier": 6.0, "symbol": "LINEA/USDT:USDT", "currency": "USDT", - "minNotional": 350000.0, - "maxNotional": 750000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 125000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 6, - "initialLeverage": 10, - "notionalCap": 750000, - "notionalFloor": 350000, - "maintMarginRatio": 0.05, - "cum": 7992.5 + "initialLeverage": 4, + "notionalCap": 250000, + "notionalFloor": 125000, + "maintMarginRatio": 0.125, + "cum": 6950.0 } }, { "tier": 7.0, "symbol": "LINEA/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 3000000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 7, - "initialLeverage": 5, - "notionalCap": 3000000, - "notionalFloor": 750000, - "maintMarginRatio": 0.1, - "cum": 45492.5 + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 17375.0 } }, { "tier": 8.0, "symbol": "LINEA/USDT:USDT", "currency": "USDT", - "minNotional": 3000000.0, - "maxNotional": 4500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 500000.0, + "maxNotional": 7500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": 8, - "initialLeverage": 4, - "notionalCap": 4500000, - "notionalFloor": 3000000, - "maintMarginRatio": 0.125, - "cum": 120492.5 + "initialLeverage": 2, + "notionalCap": 7500000, + "notionalFloor": 500000, + "maintMarginRatio": 0.25, + "cum": 59025.0 } }, { "tier": 9.0, "symbol": "LINEA/USDT:USDT", "currency": "USDT", - "minNotional": 4500000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 9, - "initialLeverage": 3, - "notionalCap": 7500000, - "notionalFloor": 4500000, - "maintMarginRatio": 0.1667, - "cum": 308142.5 - } - }, - { - "tier": 10.0, - "symbol": "LINEA/USDT:USDT", - "currency": "USDT", "minNotional": 7500000.0, - "maxNotional": 12000000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 10, - "initialLeverage": 2, - "notionalCap": 12000000, - "notionalFloor": 7500000, - "maintMarginRatio": 0.25, - "cum": 932892.5 - } - }, - { - "tier": 11.0, - "symbol": "LINEA/USDT:USDT", - "currency": "USDT", - "minNotional": 12000000.0, - "maxNotional": 18000000.0, + "maxNotional": 12500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 11, + "bracket": 9, "initialLeverage": 1, - "notionalCap": 18000000, - "notionalFloor": 12000000, + "notionalCap": 12500000, + "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 3932892.5 + "cum": 1934025.0 } } ], @@ -54241,13 +54835,13 @@ "symbol": "M/USDT:USDT", "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 20000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": 2, "initialLeverage": 20, - "notionalCap": 20000, + "notionalCap": 10000, "notionalFloor": 5000, "maintMarginRatio": 0.025, "cum": 50.0 @@ -54257,58 +54851,58 @@ "tier": 3.0, "symbol": "M/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 50000.0, + "minNotional": 10000.0, + "maxNotional": 20000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": 3, "initialLeverage": 10, - "notionalCap": 50000, - "notionalFloor": 20000, + "notionalCap": 20000, + "notionalFloor": 10000, "maintMarginRatio": 0.05, - "cum": 550.0 + "cum": 300.0 } }, { "tier": 4.0, "symbol": "M/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 125000.0, + "minNotional": 20000.0, + "maxNotional": 80000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": 4, "initialLeverage": 5, - "notionalCap": 125000, - "notionalFloor": 50000, + "notionalCap": 80000, + "notionalFloor": 20000, "maintMarginRatio": 0.1, - "cum": 3050.0 + "cum": 1300.0 } }, { "tier": 5.0, "symbol": "M/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, + "minNotional": 80000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": 5, "initialLeverage": 4, - "notionalCap": 250000, - "notionalFloor": 125000, + "notionalCap": 200000, + "notionalFloor": 80000, "maintMarginRatio": 0.125, - "cum": 6175.0 + "cum": 3300.0 } }, { "tier": 6.0, "symbol": "M/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, + "minNotional": 200000.0, "maxNotional": 500000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, @@ -54316,9 +54910,9 @@ "bracket": 6, "initialLeverage": 3, "notionalCap": 500000, - "notionalFloor": 250000, + "notionalFloor": 200000, "maintMarginRatio": 0.1667, - "cum": 16600.0 + "cum": 11640.0 } }, { @@ -54335,7 +54929,7 @@ "notionalCap": 2500000, "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 58250.0 + "cum": 53290.0 } }, { @@ -54352,7 +54946,7 @@ "notionalCap": 3000000, "notionalFloor": 2500000, "maintMarginRatio": 0.5, - "cum": 683250.0 + "cum": 678290.0 } } ], @@ -56140,14 +56734,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -56157,15 +56751,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, - "cum": 25.0 + "maintMarginRatio": 0.025, + "cum": 50.0 } }, { @@ -56173,33 +56767,33 @@ "symbol": "MEME/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 25000, + "initialLeverage": 10, + "notionalCap": 20000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, - "cum": 75.0 + "maintMarginRatio": 0.05, + "cum": 300.0 } }, { "tier": 4.0, "symbol": "MEME/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 20000.0, "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 4, - "initialLeverage": 20, + "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 25000, - "maintMarginRatio": 0.025, - "cum": 200.0 + "notionalFloor": 20000, + "maintMarginRatio": 0.1, + "cum": 1300.0 } }, { @@ -56207,88 +56801,54 @@ "symbol": "MEME/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 125000, + "initialLeverage": 4, + "notionalCap": 250000, "notionalFloor": 50000, - "maintMarginRatio": 0.05, - "cum": 1450.0 + "maintMarginRatio": 0.125, + "cum": 2550.0 } }, { "tier": 6.0, "symbol": "MEME/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 250000, - "notionalFloor": 125000, - "maintMarginRatio": 0.1, - "cum": 7700.0 + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 12975.0 } }, { "tier": 7.0, "symbol": "MEME/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 7, - "initialLeverage": 4, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.125, - "cum": 13950.0 - } - }, - { - "tier": 8.0, - "symbol": "MEME/USDT:USDT", - "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 8, - "initialLeverage": 3, - "notionalCap": 1000000, - "notionalFloor": 500000, - "maintMarginRatio": 0.1667, - "cum": 34800.0 - } - }, - { - "tier": 9.0, - "symbol": "MEME/USDT:USDT", - "currency": "USDT", - "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 9, + "bracket": 7, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 1000000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 118100.0 + "cum": 54625.0 } }, { - "tier": 10.0, + "tier": 8.0, "symbol": "MEME/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -56296,12 +56856,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 1993100.0 + "cum": 1929625.0 } } ], @@ -56433,14 +56993,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -56450,15 +57010,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, - "cum": 25.0 + "maintMarginRatio": 0.025, + "cum": 50.0 } }, { @@ -56466,33 +57026,33 @@ "symbol": "MERL/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 25000, + "initialLeverage": 10, + "notionalCap": 20000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, - "cum": 75.0 + "maintMarginRatio": 0.05, + "cum": 300.0 } }, { "tier": 4.0, "symbol": "MERL/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 20000.0, "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 4, - "initialLeverage": 20, + "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 25000, - "maintMarginRatio": 0.025, - "cum": 200.0 + "notionalFloor": 20000, + "maintMarginRatio": 0.1, + "cum": 1300.0 } }, { @@ -56500,88 +57060,54 @@ "symbol": "MERL/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 125000, + "initialLeverage": 4, + "notionalCap": 250000, "notionalFloor": 50000, - "maintMarginRatio": 0.05, - "cum": 1450.0 + "maintMarginRatio": 0.125, + "cum": 2550.0 } }, { "tier": 6.0, "symbol": "MERL/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 250000, - "notionalFloor": 125000, - "maintMarginRatio": 0.1, - "cum": 7700.0 + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 12975.0 } }, { "tier": 7.0, "symbol": "MERL/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 7, - "initialLeverage": 4, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.125, - "cum": 13950.0 - } - }, - { - "tier": 8.0, - "symbol": "MERL/USDT:USDT", - "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 8, - "initialLeverage": 3, - "notionalCap": 1000000, - "notionalFloor": 500000, - "maintMarginRatio": 0.1667, - "cum": 34800.0 - } - }, - { - "tier": 9.0, - "symbol": "MERL/USDT:USDT", - "currency": "USDT", - "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 9, + "bracket": 7, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 1000000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 118100.0 + "cum": 54625.0 } }, { - "tier": 10.0, + "tier": 8.0, "symbol": "MERL/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -56589,12 +57115,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 1993100.0 + "cum": 1929625.0 } } ], @@ -57881,14 +58407,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 1, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.025, "cum": 0.0 } }, @@ -57898,15 +58424,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 2, - "initialLeverage": 20, + "initialLeverage": 10, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.025, - "cum": 50.0 + "maintMarginRatio": 0.05, + "cum": 125.0 } }, { @@ -57914,50 +58440,50 @@ "symbol": "MLN/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 3, - "initialLeverage": 10, - "notionalCap": 20000, + "initialLeverage": 5, + "notionalCap": 60000, "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 300.0 + "maintMarginRatio": 0.1, + "cum": 625.0 } }, { "tier": 4.0, "symbol": "MLN/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 60000.0, + "maxNotional": 70000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 4, - "initialLeverage": 5, - "notionalCap": 50000, - "notionalFloor": 20000, - "maintMarginRatio": 0.1, - "cum": 1300.0 + "initialLeverage": 4, + "notionalCap": 70000, + "notionalFloor": 60000, + "maintMarginRatio": 0.125, + "cum": 2125.0 } }, { "tier": 5.0, "symbol": "MLN/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, + "minNotional": 70000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 5, - "initialLeverage": 4, + "initialLeverage": 3, "notionalCap": 250000, - "notionalFloor": 50000, - "maintMarginRatio": 0.125, - "cum": 2550.0 + "notionalFloor": 70000, + "maintMarginRatio": 0.1667, + "cum": 5044.0 } }, { @@ -57966,15 +58492,15 @@ "currency": "USDT", "minNotional": 250000.0, "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": 6, - "initialLeverage": 3, + "initialLeverage": 2, "notionalCap": 500000, "notionalFloor": 250000, - "maintMarginRatio": 0.1667, - "cum": 12975.0 + "maintMarginRatio": 0.25, + "cum": 25869.0 } }, { @@ -57982,33 +58508,16 @@ "symbol": "MLN/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 7, - "initialLeverage": 2, - "notionalCap": 7500000, - "notionalFloor": 500000, - "maintMarginRatio": 0.25, - "cum": 54625.0 - } - }, - { - "tier": 8.0, - "symbol": "MLN/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 8, + "bracket": 7, "initialLeverage": 1, - "notionalCap": 12500000, - "notionalFloor": 7500000, + "notionalCap": 800000, + "notionalFloor": 500000, "maintMarginRatio": 0.5, - "cum": 1929625.0 + "cum": 150869.0 } } ], @@ -58535,14 +59044,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -58552,14 +59061,14 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 25, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.02, "cum": 25.0 } }, @@ -58568,15 +59077,15 @@ "symbol": "MOODENG/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 50000, + "initialLeverage": 20, + "notionalCap": 25000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, + "maintMarginRatio": 0.025, "cum": 75.0 } }, @@ -58584,51 +59093,51 @@ "tier": 4.0, "symbol": "MOODENG/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 25000.0, + "maxNotional": 62500.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 4, - "initialLeverage": 20, - "notionalCap": 100000, - "notionalFloor": 50000, - "maintMarginRatio": 0.025, - "cum": 325.0 + "initialLeverage": 10, + "notionalCap": 62500, + "notionalFloor": 25000, + "maintMarginRatio": 0.05, + "cum": 700.0 } }, { "tier": 5.0, "symbol": "MOODENG/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 175000.0, - "maintenanceMarginRate": 0.0333, - "maxLeverage": 15.0, + "minNotional": 62500.0, + "maxNotional": 125000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 5, - "initialLeverage": 15, - "notionalCap": 175000, - "notionalFloor": 100000, - "maintMarginRatio": 0.0333, - "cum": 1155.0 + "initialLeverage": 5, + "notionalCap": 125000, + "notionalFloor": 62500, + "maintMarginRatio": 0.1, + "cum": 3825.0 } }, { "tier": 6.0, "symbol": "MOODENG/USDT:USDT", "currency": "USDT", - "minNotional": 175000.0, + "minNotional": 125000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 6, - "initialLeverage": 10, + "initialLeverage": 4, "notionalCap": 250000, - "notionalFloor": 175000, - "maintMarginRatio": 0.05, - "cum": 4077.5 + "notionalFloor": 125000, + "maintMarginRatio": 0.125, + "cum": 6950.0 } }, { @@ -58636,71 +59145,37 @@ "symbol": "MOODENG/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 750000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 7, - "initialLeverage": 5, - "notionalCap": 750000, + "initialLeverage": 3, + "notionalCap": 500000, "notionalFloor": 250000, - "maintMarginRatio": 0.1, - "cum": 16577.5 + "maintMarginRatio": 0.1667, + "cum": 17375.0 } }, { "tier": 8.0, "symbol": "MOODENG/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 8, - "initialLeverage": 4, - "notionalCap": 1500000, - "notionalFloor": 750000, - "maintMarginRatio": 0.125, - "cum": 35327.5 - } - }, - { - "tier": 9.0, - "symbol": "MOODENG/USDT:USDT", - "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 4500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 9, - "initialLeverage": 3, - "notionalCap": 4500000, - "notionalFloor": 1500000, - "maintMarginRatio": 0.1667, - "cum": 97877.5 - } - }, - { - "tier": 10.0, - "symbol": "MOODENG/USDT:USDT", - "currency": "USDT", - "minNotional": 4500000.0, + "minNotional": 500000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 4500000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 472727.5 + "cum": 59025.0 } }, { - "tier": 11.0, + "tier": 9.0, "symbol": "MOODENG/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -58708,12 +59183,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 11, + "bracket": 9, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 2347727.5 + "cum": 1934025.0 } } ], @@ -59199,6 +59674,110 @@ } } ], + "MSFT/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "MSFT/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 1, + "initialLeverage": 10, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.05, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "MSFT/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 2, + "initialLeverage": 5, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.1, + "cum": 250.0 + } + }, + { + "tier": 3.0, + "symbol": "MSFT/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 3, + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 10000, + "maintMarginRatio": 0.125, + "cum": 500.0 + } + }, + { + "tier": 4.0, + "symbol": "MSFT/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 4, + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 4670.0 + } + }, + { + "tier": 5.0, + "symbol": "MSFT/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 5, + "initialLeverage": 2, + "notionalCap": 8000000, + "notionalFloor": 500000, + "maintMarginRatio": 0.25, + "cum": 46320.0 + } + }, + { + "tier": 6.0, + "symbol": "MSFT/USDT:USDT", + "currency": "USDT", + "minNotional": 8000000.0, + "maxNotional": 15000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 6, + "initialLeverage": 1, + "notionalCap": 15000000, + "notionalFloor": 8000000, + "maintMarginRatio": 0.5, + "cum": 2046320.0 + } + } + ], "MSTR/USDT:USDT": [ { "tier": 1.0, @@ -59441,6 +60020,110 @@ } } ], + "MU/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "MU/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 1, + "initialLeverage": 10, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.05, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "MU/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 2, + "initialLeverage": 5, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.1, + "cum": 250.0 + } + }, + { + "tier": 3.0, + "symbol": "MU/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 3, + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 10000, + "maintMarginRatio": 0.125, + "cum": 500.0 + } + }, + { + "tier": 4.0, + "symbol": "MU/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 4, + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 4670.0 + } + }, + { + "tier": 5.0, + "symbol": "MU/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 5, + "initialLeverage": 2, + "notionalCap": 8000000, + "notionalFloor": 500000, + "maintMarginRatio": 0.25, + "cum": 46320.0 + } + }, + { + "tier": 6.0, + "symbol": "MU/USDT:USDT", + "currency": "USDT", + "minNotional": 8000000.0, + "maxNotional": 15000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 6, + "initialLeverage": 1, + "notionalCap": 15000000, + "notionalFloor": 8000000, + "maintMarginRatio": 0.5, + "cum": 2046320.0 + } + } + ], "MUBARAK/USDT:USDT": [ { "tier": 1.0, @@ -59925,6 +60608,178 @@ } } ], + "NATGAS/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "NATGAS/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.005, + "maxLeverage": 100.0, + "info": { + "bracket": 1, + "initialLeverage": 100, + "notionalCap": 50000, + "notionalFloor": 0, + "maintMarginRatio": 0.005, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "NATGAS/USDT:USDT", + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.0065, + "maxLeverage": 75.0, + "info": { + "bracket": 2, + "initialLeverage": 75, + "notionalCap": 400000, + "notionalFloor": 50000, + "maintMarginRatio": 0.0065, + "cum": 75.0 + } + }, + { + "tier": 3.0, + "symbol": "NATGAS/USDT:USDT", + "currency": "USDT", + "minNotional": 400000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, + "info": { + "bracket": 3, + "initialLeverage": 50, + "notionalCap": 1000000, + "notionalFloor": 400000, + "maintMarginRatio": 0.01, + "cum": 1475.0 + } + }, + { + "tier": 4.0, + "symbol": "NATGAS/USDT:USDT", + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": 4, + "initialLeverage": 25, + "notionalCap": 4000000, + "notionalFloor": 1000000, + "maintMarginRatio": 0.02, + "cum": 11475.0 + } + }, + { + "tier": 5.0, + "symbol": "NATGAS/USDT:USDT", + "currency": "USDT", + "minNotional": 4000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": 5, + "initialLeverage": 20, + "notionalCap": 8000000, + "notionalFloor": 4000000, + "maintMarginRatio": 0.025, + "cum": 31475.0 + } + }, + { + "tier": 6.0, + "symbol": "NATGAS/USDT:USDT", + "currency": "USDT", + "minNotional": 8000000.0, + "maxNotional": 40000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 6, + "initialLeverage": 10, + "notionalCap": 40000000, + "notionalFloor": 8000000, + "maintMarginRatio": 0.05, + "cum": 231475.0 + } + }, + { + "tier": 7.0, + "symbol": "NATGAS/USDT:USDT", + "currency": "USDT", + "minNotional": 40000000.0, + "maxNotional": 80000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 7, + "initialLeverage": 5, + "notionalCap": 80000000, + "notionalFloor": 40000000, + "maintMarginRatio": 0.1, + "cum": 2231475.0 + } + }, + { + "tier": 8.0, + "symbol": "NATGAS/USDT:USDT", + "currency": "USDT", + "minNotional": 80000000.0, + "maxNotional": 100000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 8, + "initialLeverage": 4, + "notionalCap": 100000000, + "notionalFloor": 80000000, + "maintMarginRatio": 0.125, + "cum": 4231475.0 + } + }, + { + "tier": 9.0, + "symbol": "NATGAS/USDT:USDT", + "currency": "USDT", + "minNotional": 100000000.0, + "maxNotional": 200000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 9, + "initialLeverage": 2, + "notionalCap": 200000000, + "notionalFloor": 100000000, + "maintMarginRatio": 0.25, + "cum": 16731475.0 + } + }, + { + "tier": 10.0, + "symbol": "NATGAS/USDT:USDT", + "currency": "USDT", + "minNotional": 200000000.0, + "maxNotional": 400000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 10, + "initialLeverage": 1, + "notionalCap": 400000000, + "notionalFloor": 200000000, + "maintMarginRatio": 0.5, + "cum": 66731475.0 + } + } + ], "NEAR/USDC:USDC": [ { "tier": 1.0, @@ -60225,14 +61080,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -60242,15 +61097,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, - "cum": 25.0 + "maintMarginRatio": 0.025, + "cum": 50.0 } }, { @@ -60258,33 +61113,33 @@ "symbol": "NEIRO/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 25000, + "initialLeverage": 10, + "notionalCap": 20000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, - "cum": 75.0 + "maintMarginRatio": 0.05, + "cum": 300.0 } }, { "tier": 4.0, "symbol": "NEIRO/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 20000.0, "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 4, - "initialLeverage": 20, + "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 25000, - "maintMarginRatio": 0.025, - "cum": 200.0 + "notionalFloor": 20000, + "maintMarginRatio": 0.1, + "cum": 1300.0 } }, { @@ -60292,88 +61147,54 @@ "symbol": "NEIRO/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 125000, + "initialLeverage": 4, + "notionalCap": 250000, "notionalFloor": 50000, - "maintMarginRatio": 0.05, - "cum": 1450.0 + "maintMarginRatio": 0.125, + "cum": 2550.0 } }, { "tier": 6.0, "symbol": "NEIRO/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 250000, - "notionalFloor": 125000, - "maintMarginRatio": 0.1, - "cum": 7700.0 + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 12975.0 } }, { "tier": 7.0, "symbol": "NEIRO/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 7, - "initialLeverage": 4, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.125, - "cum": 13950.0 - } - }, - { - "tier": 8.0, - "symbol": "NEIRO/USDT:USDT", - "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 8, - "initialLeverage": 3, - "notionalCap": 1000000, - "notionalFloor": 500000, - "maintMarginRatio": 0.1667, - "cum": 34800.0 - } - }, - { - "tier": 9.0, - "symbol": "NEIRO/USDT:USDT", - "currency": "USDT", - "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 9, + "bracket": 7, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 1000000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 118100.0 + "cum": 54625.0 } }, { - "tier": 10.0, + "tier": 8.0, "symbol": "NEIRO/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -60381,12 +61202,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 1993100.0 + "cum": 1929625.0 } } ], @@ -61534,333 +62355,6 @@ "symbol": "NMR/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, - "info": { - "bracket": 3, - "initialLeverage": 25, - "notionalCap": 50000, - "notionalFloor": 10000, - "maintMarginRatio": 0.02, - "cum": 75.0 - } - }, - { - "tier": 4.0, - "symbol": "NMR/USDT:USDT", - "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, - "info": { - "bracket": 4, - "initialLeverage": 20, - "notionalCap": 100000, - "notionalFloor": 50000, - "maintMarginRatio": 0.025, - "cum": 325.0 - } - }, - { - "tier": 5.0, - "symbol": "NMR/USDT:USDT", - "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 175000.0, - "maintenanceMarginRate": 0.0333, - "maxLeverage": 15.0, - "info": { - "bracket": 5, - "initialLeverage": 15, - "notionalCap": 175000, - "notionalFloor": 100000, - "maintMarginRatio": 0.0333, - "cum": 1155.0 - } - }, - { - "tier": 6.0, - "symbol": "NMR/USDT:USDT", - "currency": "USDT", - "minNotional": 175000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, - "info": { - "bracket": 6, - "initialLeverage": 10, - "notionalCap": 250000, - "notionalFloor": 175000, - "maintMarginRatio": 0.05, - "cum": 4077.5 - } - }, - { - "tier": 7.0, - "symbol": "NMR/USDT:USDT", - "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 750000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, - "info": { - "bracket": 7, - "initialLeverage": 5, - "notionalCap": 750000, - "notionalFloor": 250000, - "maintMarginRatio": 0.1, - "cum": 16577.5 - } - }, - { - "tier": 8.0, - "symbol": "NMR/USDT:USDT", - "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 8, - "initialLeverage": 4, - "notionalCap": 1500000, - "notionalFloor": 750000, - "maintMarginRatio": 0.125, - "cum": 35327.5 - } - }, - { - "tier": 9.0, - "symbol": "NMR/USDT:USDT", - "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 4500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 9, - "initialLeverage": 3, - "notionalCap": 4500000, - "notionalFloor": 1500000, - "maintMarginRatio": 0.1667, - "cum": 97877.5 - } - }, - { - "tier": 10.0, - "symbol": "NMR/USDT:USDT", - "currency": "USDT", - "minNotional": 4500000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 10, - "initialLeverage": 2, - "notionalCap": 7500000, - "notionalFloor": 4500000, - "maintMarginRatio": 0.25, - "cum": 472727.5 - } - }, - { - "tier": 11.0, - "symbol": "NMR/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1.0, - "info": { - "bracket": 11, - "initialLeverage": 1, - "notionalCap": 12500000, - "notionalFloor": 7500000, - "maintMarginRatio": 0.5, - "cum": 2347727.5 - } - } - ], - "NOM/USDT:USDT": [ - { - "tier": 1.0, - "symbol": "NOM/USDT:USDT", - "currency": "USDT", - "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, - "info": { - "bracket": 1, - "initialLeverage": 50, - "notionalCap": 5000, - "notionalFloor": 0, - "maintMarginRatio": 0.015, - "cum": 0.0 - } - }, - { - "tier": 2.0, - "symbol": "NOM/USDT:USDT", - "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, - "info": { - "bracket": 2, - "initialLeverage": 20, - "notionalCap": 10000, - "notionalFloor": 5000, - "maintMarginRatio": 0.025, - "cum": 50.0 - } - }, - { - "tier": 3.0, - "symbol": "NOM/USDT:USDT", - "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, - "info": { - "bracket": 3, - "initialLeverage": 10, - "notionalCap": 20000, - "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 300.0 - } - }, - { - "tier": 4.0, - "symbol": "NOM/USDT:USDT", - "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, - "info": { - "bracket": 4, - "initialLeverage": 5, - "notionalCap": 50000, - "notionalFloor": 20000, - "maintMarginRatio": 0.1, - "cum": 1300.0 - } - }, - { - "tier": 5.0, - "symbol": "NOM/USDT:USDT", - "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 5, - "initialLeverage": 4, - "notionalCap": 250000, - "notionalFloor": 50000, - "maintMarginRatio": 0.125, - "cum": 2550.0 - } - }, - { - "tier": 6.0, - "symbol": "NOM/USDT:USDT", - "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 6, - "initialLeverage": 3, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.1667, - "cum": 12975.0 - } - }, - { - "tier": 7.0, - "symbol": "NOM/USDT:USDT", - "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 7, - "initialLeverage": 2, - "notionalCap": 7500000, - "notionalFloor": 500000, - "maintMarginRatio": 0.25, - "cum": 54625.0 - } - }, - { - "tier": 8.0, - "symbol": "NOM/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, - "maintenanceMarginRate": 0.5, - "maxLeverage": 1.0, - "info": { - "bracket": 8, - "initialLeverage": 1, - "notionalCap": 12500000, - "notionalFloor": 7500000, - "maintMarginRatio": 0.5, - "cum": 1929625.0 - } - } - ], - "NOT/USDT:USDT": [ - { - "tier": 1.0, - "symbol": "NOT/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": "NOT/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": "NOT/USDT:USDT", - "currency": "USDT", - "minNotional": 10000.0, "maxNotional": 25000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, @@ -61875,7 +62369,7 @@ }, { "tier": 4.0, - "symbol": "NOT/USDT:USDT", + "symbol": "NMR/USDT:USDT", "currency": "USDT", "minNotional": 25000.0, "maxNotional": 50000.0, @@ -61892,7 +62386,7 @@ }, { "tier": 5.0, - "symbol": "NOT/USDT:USDT", + "symbol": "NMR/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, "maxNotional": 125000.0, @@ -61909,7 +62403,7 @@ }, { "tier": 6.0, - "symbol": "NOT/USDT:USDT", + "symbol": "NMR/USDT:USDT", "currency": "USDT", "minNotional": 125000.0, "maxNotional": 250000.0, @@ -61926,7 +62420,7 @@ }, { "tier": 7.0, - "symbol": "NOT/USDT:USDT", + "symbol": "NMR/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, "maxNotional": 500000.0, @@ -61943,7 +62437,7 @@ }, { "tier": 8.0, - "symbol": "NOT/USDT:USDT", + "symbol": "NMR/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, "maxNotional": 1000000.0, @@ -61960,7 +62454,7 @@ }, { "tier": 9.0, - "symbol": "NOT/USDT:USDT", + "symbol": "NMR/USDT:USDT", "currency": "USDT", "minNotional": 1000000.0, "maxNotional": 7500000.0, @@ -61977,7 +62471,7 @@ }, { "tier": 10.0, - "symbol": "NOT/USDT:USDT", + "symbol": "NMR/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, "maxNotional": 12500000.0, @@ -61993,6 +62487,265 @@ } } ], + "NOM/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "NOM/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": 1, + "initialLeverage": 20, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.025, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "NOM/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 2, + "initialLeverage": 10, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.05, + "cum": 125.0 + } + }, + { + "tier": 3.0, + "symbol": "NOM/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 3, + "initialLeverage": 5, + "notionalCap": 60000, + "notionalFloor": 10000, + "maintMarginRatio": 0.1, + "cum": 625.0 + } + }, + { + "tier": 4.0, + "symbol": "NOM/USDT:USDT", + "currency": "USDT", + "minNotional": 60000.0, + "maxNotional": 70000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 4, + "initialLeverage": 4, + "notionalCap": 70000, + "notionalFloor": 60000, + "maintMarginRatio": 0.125, + "cum": 2125.0 + } + }, + { + "tier": 5.0, + "symbol": "NOM/USDT:USDT", + "currency": "USDT", + "minNotional": 70000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 5, + "initialLeverage": 3, + "notionalCap": 250000, + "notionalFloor": 70000, + "maintMarginRatio": 0.1667, + "cum": 5044.0 + } + }, + { + "tier": 6.0, + "symbol": "NOM/USDT:USDT", + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 6, + "initialLeverage": 2, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.25, + "cum": 25869.0 + } + }, + { + "tier": 7.0, + "symbol": "NOM/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 800000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 7, + "initialLeverage": 1, + "notionalCap": 800000, + "notionalFloor": 500000, + "maintMarginRatio": 0.5, + "cum": 150869.0 + } + } + ], + "NOT/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "NOT/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, + "info": { + "bracket": 1, + "initialLeverage": 50, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.015, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "NOT/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": 2, + "initialLeverage": 20, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.025, + "cum": 50.0 + } + }, + { + "tier": 3.0, + "symbol": "NOT/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 3, + "initialLeverage": 10, + "notionalCap": 20000, + "notionalFloor": 10000, + "maintMarginRatio": 0.05, + "cum": 300.0 + } + }, + { + "tier": 4.0, + "symbol": "NOT/USDT:USDT", + "currency": "USDT", + "minNotional": 20000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 4, + "initialLeverage": 5, + "notionalCap": 50000, + "notionalFloor": 20000, + "maintMarginRatio": 0.1, + "cum": 1300.0 + } + }, + { + "tier": 5.0, + "symbol": "NOT/USDT:USDT", + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 5, + "initialLeverage": 4, + "notionalCap": 250000, + "notionalFloor": 50000, + "maintMarginRatio": 0.125, + "cum": 2550.0 + } + }, + { + "tier": 6.0, + "symbol": "NOT/USDT:USDT", + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 6, + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 12975.0 + } + }, + { + "tier": 7.0, + "symbol": "NOT/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 7500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 7, + "initialLeverage": 2, + "notionalCap": 7500000, + "notionalFloor": 500000, + "maintMarginRatio": 0.25, + "cum": 54625.0 + } + }, + { + "tier": 8.0, + "symbol": "NOT/USDT:USDT", + "currency": "USDT", + "minNotional": 7500000.0, + "maxNotional": 12500000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 8, + "initialLeverage": 1, + "notionalCap": 12500000, + "notionalFloor": 7500000, + "maintMarginRatio": 0.5, + "cum": 1929625.0 + } + } + ], "NTRN/USDT:USDT": [ { "tier": 1.0, @@ -62984,15 +63737,15 @@ "symbol": "OL/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 1, - "initialLeverage": 20, - "notionalCap": 5000, + "initialLeverage": 10, + "notionalCap": 10000, "notionalFloor": 0, - "maintMarginRatio": 0.025, + "maintMarginRatio": 0.05, "cum": 0.0 } }, @@ -63000,38 +63753,21 @@ "tier": 2.0, "symbol": "OL/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, - "info": { - "bracket": 2, - "initialLeverage": 10, - "notionalCap": 10000, - "notionalFloor": 5000, - "maintMarginRatio": 0.05, - "cum": 125.0 - } - }, - { - "tier": 3.0, - "symbol": "OL/USDT:USDT", - "currency": "USDT", "minNotional": 10000.0, "maxNotional": 60000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": 3, + "bracket": 2, "initialLeverage": 5, "notionalCap": 60000, "notionalFloor": 10000, "maintMarginRatio": 0.1, - "cum": 625.0 + "cum": 500.0 } }, { - "tier": 4.0, + "tier": 3.0, "symbol": "OL/USDT:USDT", "currency": "USDT", "minNotional": 60000.0, @@ -63039,16 +63775,16 @@ "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": 4, + "bracket": 3, "initialLeverage": 4, "notionalCap": 70000, "notionalFloor": 60000, "maintMarginRatio": 0.125, - "cum": 2125.0 + "cum": 2000.0 } }, { - "tier": 5.0, + "tier": 4.0, "symbol": "OL/USDT:USDT", "currency": "USDT", "minNotional": 70000.0, @@ -63056,16 +63792,16 @@ "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { - "bracket": 5, + "bracket": 4, "initialLeverage": 3, "notionalCap": 250000, "notionalFloor": 70000, "maintMarginRatio": 0.1667, - "cum": 5044.0 + "cum": 4919.0 } }, { - "tier": 6.0, + "tier": 5.0, "symbol": "OL/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, @@ -63073,16 +63809,16 @@ "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 6, + "bracket": 5, "initialLeverage": 2, "notionalCap": 500000, "notionalFloor": 250000, "maintMarginRatio": 0.25, - "cum": 25869.0 + "cum": 25744.0 } }, { - "tier": 7.0, + "tier": 6.0, "symbol": "OL/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, @@ -63090,12 +63826,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 7, + "bracket": 6, "initialLeverage": 1, "notionalCap": 800000, "notionalFloor": 500000, "maintMarginRatio": 0.5, - "cum": 150869.0 + "cum": 150744.0 } } ], @@ -64649,6 +65385,127 @@ } } ], + "OPG/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "OPG/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": 1, + "initialLeverage": 20, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.025, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "OPG/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 2, + "initialLeverage": 10, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.05, + "cum": 125.0 + } + }, + { + "tier": 3.0, + "symbol": "OPG/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 3, + "initialLeverage": 5, + "notionalCap": 50000, + "notionalFloor": 10000, + "maintMarginRatio": 0.1, + "cum": 625.0 + } + }, + { + "tier": 4.0, + "symbol": "OPG/USDT:USDT", + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 4, + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 50000, + "maintMarginRatio": 0.125, + "cum": 1875.0 + } + }, + { + "tier": 5.0, + "symbol": "OPG/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 5, + "initialLeverage": 3, + "notionalCap": 250000, + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 6045.0 + } + }, + { + "tier": 6.0, + "symbol": "OPG/USDT:USDT", + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 6, + "initialLeverage": 2, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.25, + "cum": 26870.0 + } + }, + { + "tier": 7.0, + "symbol": "OPG/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 800000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 7, + "initialLeverage": 1, + "notionalCap": 800000, + "notionalFloor": 500000, + "maintMarginRatio": 0.5, + "cum": 151870.0 + } + } + ], "OPN/USDT:USDT": [ { "tier": 1.0, @@ -65586,14 +66443,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.035, + "maxLeverage": 20.0, "info": { "bracket": 1, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.035, "cum": 0.0 } }, @@ -65602,67 +66459,67 @@ "symbol": "OXT/USDT:USDT", "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 15000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 2, - "initialLeverage": 20, - "notionalCap": 10000, + "initialLeverage": 10, + "notionalCap": 15000, "notionalFloor": 5000, - "maintMarginRatio": 0.025, - "cum": 50.0 + "maintMarginRatio": 0.05, + "cum": 75.0 } }, { "tier": 3.0, "symbol": "OXT/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 15000.0, + "maxNotional": 30000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 3, - "initialLeverage": 10, - "notionalCap": 20000, - "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 300.0 + "initialLeverage": 5, + "notionalCap": 30000, + "notionalFloor": 15000, + "maintMarginRatio": 0.1, + "cum": 825.0 } }, { "tier": 4.0, "symbol": "OXT/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 30000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 4, - "initialLeverage": 5, - "notionalCap": 50000, - "notionalFloor": 20000, - "maintMarginRatio": 0.1, - "cum": 1300.0 + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 30000, + "maintMarginRatio": 0.125, + "cum": 1575.0 } }, { "tier": 5.0, "symbol": "OXT/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, + "minNotional": 100000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 5, - "initialLeverage": 4, + "initialLeverage": 3, "notionalCap": 250000, - "notionalFloor": 50000, - "maintMarginRatio": 0.125, - "cum": 2550.0 + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 5745.0 } }, { @@ -65671,15 +66528,15 @@ "currency": "USDT", "minNotional": 250000.0, "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": 6, - "initialLeverage": 3, + "initialLeverage": 2, "notionalCap": 500000, "notionalFloor": 250000, - "maintMarginRatio": 0.1667, - "cum": 12975.0 + "maintMarginRatio": 0.25, + "cum": 26570.0 } }, { @@ -65687,33 +66544,16 @@ "symbol": "OXT/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 7, - "initialLeverage": 2, - "notionalCap": 7500000, - "notionalFloor": 500000, - "maintMarginRatio": 0.25, - "cum": 54625.0 - } - }, - { - "tier": 8.0, - "symbol": "OXT/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 8, + "bracket": 7, "initialLeverage": 1, - "notionalCap": 12500000, - "notionalFloor": 7500000, + "notionalCap": 800000, + "notionalFloor": 500000, "maintMarginRatio": 0.5, - "cum": 1929625.0 + "cum": 151570.0 } } ], @@ -68206,14 +69046,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -68223,15 +69063,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, - "cum": 25.0 + "maintMarginRatio": 0.025, + "cum": 50.0 } }, { @@ -68239,33 +69079,33 @@ "symbol": "PNUT/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 25000, + "initialLeverage": 10, + "notionalCap": 20000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, - "cum": 75.0 + "maintMarginRatio": 0.05, + "cum": 300.0 } }, { "tier": 4.0, "symbol": "PNUT/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 20000.0, "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 4, - "initialLeverage": 20, + "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 25000, - "maintMarginRatio": 0.025, - "cum": 200.0 + "notionalFloor": 20000, + "maintMarginRatio": 0.1, + "cum": 1300.0 } }, { @@ -68273,88 +69113,54 @@ "symbol": "PNUT/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 125000, + "initialLeverage": 4, + "notionalCap": 250000, "notionalFloor": 50000, - "maintMarginRatio": 0.05, - "cum": 1450.0 + "maintMarginRatio": 0.125, + "cum": 2550.0 } }, { "tier": 6.0, "symbol": "PNUT/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 250000, - "notionalFloor": 125000, - "maintMarginRatio": 0.1, - "cum": 7700.0 + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 12975.0 } }, { "tier": 7.0, "symbol": "PNUT/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 7, - "initialLeverage": 4, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.125, - "cum": 13950.0 - } - }, - { - "tier": 8.0, - "symbol": "PNUT/USDT:USDT", - "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 4500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 8, - "initialLeverage": 3, - "notionalCap": 4500000, - "notionalFloor": 500000, - "maintMarginRatio": 0.1667, - "cum": 34800.0 - } - }, - { - "tier": 9.0, - "symbol": "PNUT/USDT:USDT", - "currency": "USDT", - "minNotional": 4500000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 9, + "bracket": 7, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 4500000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 409650.0 + "cum": 54625.0 } }, { - "tier": 10.0, + "tier": 8.0, "symbol": "PNUT/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -68362,12 +69168,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 2284650.0 + "cum": 1929625.0 } } ], @@ -69475,6 +70281,127 @@ } } ], + "PRL/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "PRL/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": 1, + "initialLeverage": 20, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.025, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "PRL/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 2, + "initialLeverage": 10, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.05, + "cum": 125.0 + } + }, + { + "tier": 3.0, + "symbol": "PRL/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 3, + "initialLeverage": 5, + "notionalCap": 50000, + "notionalFloor": 10000, + "maintMarginRatio": 0.1, + "cum": 625.0 + } + }, + { + "tier": 4.0, + "symbol": "PRL/USDT:USDT", + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 4, + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 50000, + "maintMarginRatio": 0.125, + "cum": 1875.0 + } + }, + { + "tier": 5.0, + "symbol": "PRL/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 5, + "initialLeverage": 3, + "notionalCap": 250000, + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 6045.0 + } + }, + { + "tier": 6.0, + "symbol": "PRL/USDT:USDT", + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 6, + "initialLeverage": 2, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.25, + "cum": 26870.0 + } + }, + { + "tier": 7.0, + "symbol": "PRL/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 800000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 7, + "initialLeverage": 1, + "notionalCap": 800000, + "notionalFloor": 500000, + "maintMarginRatio": 0.5, + "cum": 151870.0 + } + } + ], "PROM/USDT:USDT": [ { "tier": 1.0, @@ -69808,13 +70735,13 @@ "symbol": "PROVE/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 50000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": 3, "initialLeverage": 25, - "notionalCap": 50000, + "notionalCap": 25000, "notionalFloor": 10000, "maintMarginRatio": 0.02, "cum": 75.0 @@ -69824,51 +70751,51 @@ "tier": 4.0, "symbol": "PROVE/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, + "minNotional": 25000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": 4, "initialLeverage": 20, - "notionalCap": 100000, - "notionalFloor": 50000, + "notionalCap": 50000, + "notionalFloor": 25000, "maintMarginRatio": 0.025, - "cum": 325.0 + "cum": 200.0 } }, { "tier": 5.0, "symbol": "PROVE/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 175000.0, - "maintenanceMarginRate": 0.0333, - "maxLeverage": 15.0, + "minNotional": 50000.0, + "maxNotional": 125000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 5, - "initialLeverage": 15, - "notionalCap": 175000, - "notionalFloor": 100000, - "maintMarginRatio": 0.0333, - "cum": 1155.0 + "initialLeverage": 10, + "notionalCap": 125000, + "notionalFloor": 50000, + "maintMarginRatio": 0.05, + "cum": 1450.0 } }, { "tier": 6.0, "symbol": "PROVE/USDT:USDT", "currency": "USDT", - "minNotional": 175000.0, + "minNotional": 125000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 6, - "initialLeverage": 10, + "initialLeverage": 5, "notionalCap": 250000, - "notionalFloor": 175000, - "maintMarginRatio": 0.05, - "cum": 4077.5 + "notionalFloor": 125000, + "maintMarginRatio": 0.1, + "cum": 7700.0 } }, { @@ -69876,71 +70803,54 @@ "symbol": "PROVE/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 750000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 7, - "initialLeverage": 5, - "notionalCap": 750000, + "initialLeverage": 4, + "notionalCap": 500000, "notionalFloor": 250000, - "maintMarginRatio": 0.1, - "cum": 16577.5 + "maintMarginRatio": 0.125, + "cum": 13950.0 } }, { "tier": 8.0, "symbol": "PROVE/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 8, - "initialLeverage": 4, - "notionalCap": 1500000, - "notionalFloor": 750000, - "maintMarginRatio": 0.125, - "cum": 35327.5 + "initialLeverage": 3, + "notionalCap": 1000000, + "notionalFloor": 500000, + "maintMarginRatio": 0.1667, + "cum": 34800.0 } }, { "tier": 9.0, "symbol": "PROVE/USDT:USDT", "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 4500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 9, - "initialLeverage": 3, - "notionalCap": 4500000, - "notionalFloor": 1500000, - "maintMarginRatio": 0.1667, - "cum": 97877.5 - } - }, - { - "tier": 10.0, - "symbol": "PROVE/USDT:USDT", - "currency": "USDT", - "minNotional": 4500000.0, + "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 10, + "bracket": 9, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 4500000, + "notionalFloor": 1000000, "maintMarginRatio": 0.25, - "cum": 472727.5 + "cum": 118100.0 } }, { - "tier": 11.0, + "tier": 10.0, "symbol": "PROVE/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -69948,12 +70858,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 11, + "bracket": 10, "initialLeverage": 1, "notionalCap": 12000000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 2347727.5 + "cum": 1993100.0 } } ], @@ -70084,15 +70994,15 @@ "symbol": "PUFFER/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 1, - "initialLeverage": 25, - "notionalCap": 5000, + "initialLeverage": 10, + "notionalCap": 10000, "notionalFloor": 0, - "maintMarginRatio": 0.02, + "maintMarginRatio": 0.05, "cum": 0.0 } }, @@ -70100,119 +71010,85 @@ "tier": 2.0, "symbol": "PUFFER/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 10000.0, + "maxNotional": 40000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 2, - "initialLeverage": 20, - "notionalCap": 10000, - "notionalFloor": 5000, - "maintMarginRatio": 0.025, - "cum": 25.0 + "initialLeverage": 5, + "notionalCap": 40000, + "notionalFloor": 10000, + "maintMarginRatio": 0.1, + "cum": 500.0 } }, { "tier": 3.0, "symbol": "PUFFER/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 40000.0, + "maxNotional": 80000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 3, - "initialLeverage": 10, - "notionalCap": 25000, - "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 275.0 + "initialLeverage": 4, + "notionalCap": 80000, + "notionalFloor": 40000, + "maintMarginRatio": 0.125, + "cum": 1500.0 } }, { "tier": 4.0, "symbol": "PUFFER/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 80000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 4, - "initialLeverage": 5, - "notionalCap": 50000, - "notionalFloor": 25000, - "maintMarginRatio": 0.1, - "cum": 1525.0 + "initialLeverage": 3, + "notionalCap": 250000, + "notionalFloor": 80000, + "maintMarginRatio": 0.1667, + "cum": 4836.0 } }, { "tier": 5.0, "symbol": "PUFFER/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": 5, - "initialLeverage": 4, - "notionalCap": 100000, - "notionalFloor": 50000, - "maintMarginRatio": 0.125, - "cum": 2775.0 + "initialLeverage": 2, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.25, + "cum": 25661.0 } }, { "tier": 6.0, "symbol": "PUFFER/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 6, - "initialLeverage": 3, - "notionalCap": 250000, - "notionalFloor": 100000, - "maintMarginRatio": 0.1667, - "cum": 6945.0 - } - }, - { - "tier": 7.0, - "symbol": "PUFFER/USDT:USDT", - "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 2500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 7, - "initialLeverage": 2, - "notionalCap": 2500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.25, - "cum": 27770.0 - } - }, - { - "tier": 8.0, - "symbol": "PUFFER/USDT:USDT", - "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 500000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 8, + "bracket": 6, "initialLeverage": 1, - "notionalCap": 5000000, - "notionalFloor": 2500000, + "notionalCap": 800000, + "notionalFloor": 500000, "maintMarginRatio": 0.5, - "cum": 652770.0 + "cum": 150661.0 } } ], @@ -71095,6 +71971,144 @@ } } ], + "QQQ/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "QQQ/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": 1, + "initialLeverage": 20, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.025, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "QQQ/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.0333, + "maxLeverage": 15.0, + "info": { + "bracket": 2, + "initialLeverage": 15, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.0333, + "cum": 41.5 + } + }, + { + "tier": 3.0, + "symbol": "QQQ/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 3, + "initialLeverage": 10, + "notionalCap": 25000, + "notionalFloor": 10000, + "maintMarginRatio": 0.05, + "cum": 208.5 + } + }, + { + "tier": 4.0, + "symbol": "QQQ/USDT:USDT", + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 4, + "initialLeverage": 5, + "notionalCap": 50000, + "notionalFloor": 25000, + "maintMarginRatio": 0.1, + "cum": 1458.5 + } + }, + { + "tier": 5.0, + "symbol": "QQQ/USDT:USDT", + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 5, + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 50000, + "maintMarginRatio": 0.125, + "cum": 2708.5 + } + }, + { + "tier": 6.0, + "symbol": "QQQ/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 6, + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 6878.5 + } + }, + { + "tier": 7.0, + "symbol": "QQQ/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 7, + "initialLeverage": 2, + "notionalCap": 8000000, + "notionalFloor": 500000, + "maintMarginRatio": 0.25, + "cum": 48528.5 + } + }, + { + "tier": 8.0, + "symbol": "QQQ/USDT:USDT", + "currency": "USDT", + "minNotional": 8000000.0, + "maxNotional": 15000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 8, + "initialLeverage": 1, + "notionalCap": 15000000, + "notionalFloor": 8000000, + "maintMarginRatio": 0.5, + "cum": 2048528.5 + } + } + ], "QTUM/USDT:USDT": [ { "tier": 1.0, @@ -71637,14 +72651,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.025, + "maintenanceMarginRate": 0.04, "maxLeverage": 20.0, "info": { "bracket": 1, "initialLeverage": 20, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.025, + "maintMarginRatio": 0.04, "cum": 0.0 } }, @@ -71662,7 +72676,7 @@ "notionalCap": 10000, "notionalFloor": 5000, "maintMarginRatio": 0.05, - "cum": 125.0 + "cum": 50.0 } }, { @@ -71679,7 +72693,7 @@ "notionalCap": 50000, "notionalFloor": 10000, "maintMarginRatio": 0.1, - "cum": 625.0 + "cum": 550.0 } }, { @@ -71696,7 +72710,7 @@ "notionalCap": 100000, "notionalFloor": 50000, "maintMarginRatio": 0.125, - "cum": 1875.0 + "cum": 1800.0 } }, { @@ -71713,7 +72727,7 @@ "notionalCap": 250000, "notionalFloor": 100000, "maintMarginRatio": 0.1667, - "cum": 6045.0 + "cum": 5970.0 } }, { @@ -71730,7 +72744,7 @@ "notionalCap": 500000, "notionalFloor": 250000, "maintMarginRatio": 0.25, - "cum": 26870.0 + "cum": 26795.0 } }, { @@ -71747,7 +72761,7 @@ "notionalCap": 800000, "notionalFloor": 500000, "maintMarginRatio": 0.5, - "cum": 151870.0 + "cum": 151795.0 } } ], @@ -73706,15 +74720,15 @@ "symbol": "RLS/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 1, - "initialLeverage": 20, - "notionalCap": 5000, + "initialLeverage": 10, + "notionalCap": 10000, "notionalFloor": 0, - "maintMarginRatio": 0.025, + "maintMarginRatio": 0.05, "cum": 0.0 } }, @@ -73722,38 +74736,21 @@ "tier": 2.0, "symbol": "RLS/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, - "info": { - "bracket": 2, - "initialLeverage": 10, - "notionalCap": 10000, - "notionalFloor": 5000, - "maintMarginRatio": 0.05, - "cum": 125.0 - } - }, - { - "tier": 3.0, - "symbol": "RLS/USDT:USDT", - "currency": "USDT", "minNotional": 10000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": 3, + "bracket": 2, "initialLeverage": 5, "notionalCap": 50000, "notionalFloor": 10000, "maintMarginRatio": 0.1, - "cum": 625.0 + "cum": 500.0 } }, { - "tier": 4.0, + "tier": 3.0, "symbol": "RLS/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, @@ -73761,16 +74758,16 @@ "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": 4, + "bracket": 3, "initialLeverage": 4, "notionalCap": 100000, "notionalFloor": 50000, "maintMarginRatio": 0.125, - "cum": 1875.0 + "cum": 1750.0 } }, { - "tier": 5.0, + "tier": 4.0, "symbol": "RLS/USDT:USDT", "currency": "USDT", "minNotional": 100000.0, @@ -73778,16 +74775,16 @@ "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { - "bracket": 5, + "bracket": 4, "initialLeverage": 3, "notionalCap": 250000, "notionalFloor": 100000, "maintMarginRatio": 0.1667, - "cum": 6045.0 + "cum": 5920.0 } }, { - "tier": 6.0, + "tier": 5.0, "symbol": "RLS/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, @@ -73795,16 +74792,16 @@ "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 6, + "bracket": 5, "initialLeverage": 2, "notionalCap": 500000, "notionalFloor": 250000, "maintMarginRatio": 0.25, - "cum": 26870.0 + "cum": 26745.0 } }, { - "tier": 7.0, + "tier": 6.0, "symbol": "RLS/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, @@ -73812,12 +74809,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 7, + "bracket": 6, "initialLeverage": 1, "notionalCap": 800000, "notionalFloor": 500000, "maintMarginRatio": 0.5, - "cum": 151870.0 + "cum": 151745.0 } } ], @@ -77170,14 +78167,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -77186,15 +78183,15 @@ "symbol": "SIGN/USDT:USDT", "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": 2, - "initialLeverage": 50, - "notionalCap": 10000, + "initialLeverage": 25, + "notionalCap": 25000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.02, "cum": 25.0 } }, @@ -77202,38 +78199,21 @@ "tier": 3.0, "symbol": "SIGN/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, - "info": { - "bracket": 3, - "initialLeverage": 25, - "notionalCap": 25000, - "notionalFloor": 10000, - "maintMarginRatio": 0.02, - "cum": 75.0 - } - }, - { - "tier": 4.0, - "symbol": "SIGN/USDT:USDT", - "currency": "USDT", "minNotional": 25000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { - "bracket": 4, + "bracket": 3, "initialLeverage": 20, "notionalCap": 50000, "notionalFloor": 25000, "maintMarginRatio": 0.025, - "cum": 200.0 + "cum": 150.0 } }, { - "tier": 5.0, + "tier": 4.0, "symbol": "SIGN/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, @@ -77241,16 +78221,16 @@ "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": 5, + "bracket": 4, "initialLeverage": 10, "notionalCap": 125000, "notionalFloor": 50000, "maintMarginRatio": 0.05, - "cum": 1450.0 + "cum": 1400.0 } }, { - "tier": 6.0, + "tier": 5.0, "symbol": "SIGN/USDT:USDT", "currency": "USDT", "minNotional": 125000.0, @@ -77258,16 +78238,16 @@ "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": 6, + "bracket": 5, "initialLeverage": 5, "notionalCap": 250000, "notionalFloor": 125000, "maintMarginRatio": 0.1, - "cum": 7700.0 + "cum": 7650.0 } }, { - "tier": 7.0, + "tier": 6.0, "symbol": "SIGN/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, @@ -77275,16 +78255,16 @@ "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": 7, + "bracket": 6, "initialLeverage": 4, "notionalCap": 500000, "notionalFloor": 250000, "maintMarginRatio": 0.125, - "cum": 13950.0 + "cum": 13900.0 } }, { - "tier": 8.0, + "tier": 7.0, "symbol": "SIGN/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, @@ -77292,46 +78272,46 @@ "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { - "bracket": 8, + "bracket": 7, "initialLeverage": 3, "notionalCap": 1000000, "notionalFloor": 500000, "maintMarginRatio": 0.1667, - "cum": 34800.0 + "cum": 34750.0 + } + }, + { + "tier": 8.0, + "symbol": "SIGN/USDT:USDT", + "currency": "USDT", + "minNotional": 1000000.0, + "maxNotional": 2000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 8, + "initialLeverage": 2, + "notionalCap": 2000000, + "notionalFloor": 1000000, + "maintMarginRatio": 0.25, + "cum": 118050.0 } }, { "tier": 9.0, "symbol": "SIGN/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 9, - "initialLeverage": 2, - "notionalCap": 7500000, - "notionalFloor": 1000000, - "maintMarginRatio": 0.25, - "cum": 118100.0 - } - }, - { - "tier": 10.0, - "symbol": "SIGN/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 2000000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 10, + "bracket": 9, "initialLeverage": 1, - "notionalCap": 12500000, - "notionalFloor": 7500000, + "notionalCap": 3000000, + "notionalFloor": 2000000, "maintMarginRatio": 0.5, - "cum": 1993100.0 + "cum": 618050.0 } } ], @@ -78371,6 +79351,110 @@ } } ], + "SNDK/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "SNDK/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 1, + "initialLeverage": 10, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.05, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "SNDK/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 2, + "initialLeverage": 5, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.1, + "cum": 250.0 + } + }, + { + "tier": 3.0, + "symbol": "SNDK/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 3, + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 10000, + "maintMarginRatio": 0.125, + "cum": 500.0 + } + }, + { + "tier": 4.0, + "symbol": "SNDK/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 4, + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 4670.0 + } + }, + { + "tier": 5.0, + "symbol": "SNDK/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 5, + "initialLeverage": 2, + "notionalCap": 8000000, + "notionalFloor": 500000, + "maintMarginRatio": 0.25, + "cum": 46320.0 + } + }, + { + "tier": 6.0, + "symbol": "SNDK/USDT:USDT", + "currency": "USDT", + "minNotional": 8000000.0, + "maxNotional": 15000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 6, + "initialLeverage": 1, + "notionalCap": 15000000, + "notionalFloor": 8000000, + "maintMarginRatio": 0.5, + "cum": 2046320.0 + } + } + ], "SNT/USDT:USDT": [ { "tier": 1.0, @@ -78532,13 +79616,13 @@ "symbol": "SNX/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 50000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": 3, "initialLeverage": 25, - "notionalCap": 50000, + "notionalCap": 25000, "notionalFloor": 10000, "maintMarginRatio": 0.02, "cum": 75.0 @@ -78548,51 +79632,51 @@ "tier": 4.0, "symbol": "SNX/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, + "minNotional": 25000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": 4, "initialLeverage": 20, - "notionalCap": 100000, - "notionalFloor": 50000, + "notionalCap": 50000, + "notionalFloor": 25000, "maintMarginRatio": 0.025, - "cum": 325.0 + "cum": 200.0 } }, { "tier": 5.0, "symbol": "SNX/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 175000.0, - "maintenanceMarginRate": 0.0333, - "maxLeverage": 15.0, + "minNotional": 50000.0, + "maxNotional": 125000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 5, - "initialLeverage": 15, - "notionalCap": 175000, - "notionalFloor": 100000, - "maintMarginRatio": 0.0333, - "cum": 1155.0 + "initialLeverage": 10, + "notionalCap": 125000, + "notionalFloor": 50000, + "maintMarginRatio": 0.05, + "cum": 1450.0 } }, { "tier": 6.0, "symbol": "SNX/USDT:USDT", "currency": "USDT", - "minNotional": 175000.0, + "minNotional": 125000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 6, - "initialLeverage": 10, + "initialLeverage": 5, "notionalCap": 250000, - "notionalFloor": 175000, - "maintMarginRatio": 0.05, - "cum": 4077.5 + "notionalFloor": 125000, + "maintMarginRatio": 0.1, + "cum": 7700.0 } }, { @@ -78600,71 +79684,54 @@ "symbol": "SNX/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 750000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 7, - "initialLeverage": 5, - "notionalCap": 750000, + "initialLeverage": 4, + "notionalCap": 500000, "notionalFloor": 250000, - "maintMarginRatio": 0.1, - "cum": 16577.5 + "maintMarginRatio": 0.125, + "cum": 13950.0 } }, { "tier": 8.0, "symbol": "SNX/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 8, - "initialLeverage": 4, - "notionalCap": 1500000, - "notionalFloor": 750000, - "maintMarginRatio": 0.125, - "cum": 35327.5 + "initialLeverage": 3, + "notionalCap": 1000000, + "notionalFloor": 500000, + "maintMarginRatio": 0.1667, + "cum": 34800.0 } }, { "tier": 9.0, "symbol": "SNX/USDT:USDT", "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 4500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 9, - "initialLeverage": 3, - "notionalCap": 4500000, - "notionalFloor": 1500000, - "maintMarginRatio": 0.1667, - "cum": 97877.5 - } - }, - { - "tier": 10.0, - "symbol": "SNX/USDT:USDT", - "currency": "USDT", - "minNotional": 4500000.0, + "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 10, + "bracket": 9, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 4500000, + "notionalFloor": 1000000, "maintMarginRatio": 0.25, - "cum": 472727.5 + "cum": 118100.0 } }, { - "tier": 11.0, + "tier": 10.0, "symbol": "SNX/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -78672,12 +79739,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 11, + "bracket": 10, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 2347727.5 + "cum": 1993100.0 } } ], @@ -79170,14 +80237,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -79187,15 +80254,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, - "cum": 25.0 + "maintMarginRatio": 0.025, + "cum": 50.0 } }, { @@ -79203,33 +80270,33 @@ "symbol": "SOMI/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 25000, + "initialLeverage": 10, + "notionalCap": 20000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, - "cum": 75.0 + "maintMarginRatio": 0.05, + "cum": 300.0 } }, { "tier": 4.0, "symbol": "SOMI/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 20000.0, "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 4, - "initialLeverage": 20, + "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 25000, - "maintMarginRatio": 0.025, - "cum": 200.0 + "notionalFloor": 20000, + "maintMarginRatio": 0.1, + "cum": 1300.0 } }, { @@ -79237,88 +80304,54 @@ "symbol": "SOMI/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 125000, + "initialLeverage": 4, + "notionalCap": 250000, "notionalFloor": 50000, - "maintMarginRatio": 0.05, - "cum": 1450.0 + "maintMarginRatio": 0.125, + "cum": 2550.0 } }, { "tier": 6.0, "symbol": "SOMI/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 250000, - "notionalFloor": 125000, - "maintMarginRatio": 0.1, - "cum": 7700.0 + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1667, + "cum": 12975.0 } }, { "tier": 7.0, "symbol": "SOMI/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 7, - "initialLeverage": 4, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.125, - "cum": 13950.0 - } - }, - { - "tier": 8.0, - "symbol": "SOMI/USDT:USDT", - "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 8, - "initialLeverage": 3, - "notionalCap": 1000000, - "notionalFloor": 500000, - "maintMarginRatio": 0.1667, - "cum": 34800.0 - } - }, - { - "tier": 9.0, - "symbol": "SOMI/USDT:USDT", - "currency": "USDT", - "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 9, + "bracket": 7, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 1000000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 118100.0 + "cum": 54625.0 } }, { - "tier": 10.0, + "tier": 8.0, "symbol": "SOMI/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -79326,12 +80359,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 1, "notionalCap": 12500000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 1993100.0 + "cum": 1929625.0 } } ], @@ -80015,14 +81048,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -80032,14 +81065,14 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 25, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.02, "cum": 25.0 } }, @@ -80048,15 +81081,15 @@ "symbol": "SPK/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 50000, + "initialLeverage": 20, + "notionalCap": 25000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, + "maintMarginRatio": 0.025, "cum": 75.0 } }, @@ -80064,51 +81097,51 @@ "tier": 4.0, "symbol": "SPK/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 25000.0, + "maxNotional": 62500.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 4, - "initialLeverage": 20, - "notionalCap": 100000, - "notionalFloor": 50000, - "maintMarginRatio": 0.025, - "cum": 325.0 + "initialLeverage": 10, + "notionalCap": 62500, + "notionalFloor": 25000, + "maintMarginRatio": 0.05, + "cum": 700.0 } }, { "tier": 5.0, "symbol": "SPK/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 175000.0, - "maintenanceMarginRate": 0.0333, - "maxLeverage": 15.0, + "minNotional": 62500.0, + "maxNotional": 125000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 5, - "initialLeverage": 15, - "notionalCap": 175000, - "notionalFloor": 100000, - "maintMarginRatio": 0.0333, - "cum": 1155.0 + "initialLeverage": 5, + "notionalCap": 125000, + "notionalFloor": 62500, + "maintMarginRatio": 0.1, + "cum": 3825.0 } }, { "tier": 6.0, "symbol": "SPK/USDT:USDT", "currency": "USDT", - "minNotional": 175000.0, + "minNotional": 125000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 6, - "initialLeverage": 10, + "initialLeverage": 4, "notionalCap": 250000, - "notionalFloor": 175000, - "maintMarginRatio": 0.05, - "cum": 4077.5 + "notionalFloor": 125000, + "maintMarginRatio": 0.125, + "cum": 6950.0 } }, { @@ -80116,71 +81149,37 @@ "symbol": "SPK/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 750000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 7, - "initialLeverage": 5, - "notionalCap": 750000, + "initialLeverage": 3, + "notionalCap": 500000, "notionalFloor": 250000, - "maintMarginRatio": 0.1, - "cum": 16577.5 + "maintMarginRatio": 0.1667, + "cum": 17375.0 } }, { "tier": 8.0, "symbol": "SPK/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 8, - "initialLeverage": 4, - "notionalCap": 1500000, - "notionalFloor": 750000, - "maintMarginRatio": 0.125, - "cum": 35327.5 - } - }, - { - "tier": 9.0, - "symbol": "SPK/USDT:USDT", - "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 4500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 9, - "initialLeverage": 3, - "notionalCap": 4500000, - "notionalFloor": 1500000, - "maintMarginRatio": 0.1667, - "cum": 97877.5 - } - }, - { - "tier": 10.0, - "symbol": "SPK/USDT:USDT", - "currency": "USDT", - "minNotional": 4500000.0, + "minNotional": 500000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 4500000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 472727.5 + "cum": 59025.0 } }, { - "tier": 11.0, + "tier": 9.0, "symbol": "SPK/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -80188,12 +81187,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 11, + "bracket": 9, "initialLeverage": 1, "notionalCap": 12000000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 2347727.5 + "cum": 1934025.0 } } ], @@ -80507,6 +81506,144 @@ } } ], + "SPY/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "SPY/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": 1, + "initialLeverage": 20, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.025, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "SPY/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.0333, + "maxLeverage": 15.0, + "info": { + "bracket": 2, + "initialLeverage": 15, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.0333, + "cum": 41.5 + } + }, + { + "tier": 3.0, + "symbol": "SPY/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 3, + "initialLeverage": 10, + "notionalCap": 25000, + "notionalFloor": 10000, + "maintMarginRatio": 0.05, + "cum": 208.5 + } + }, + { + "tier": 4.0, + "symbol": "SPY/USDT:USDT", + "currency": "USDT", + "minNotional": 25000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 4, + "initialLeverage": 5, + "notionalCap": 50000, + "notionalFloor": 25000, + "maintMarginRatio": 0.1, + "cum": 1458.5 + } + }, + { + "tier": 5.0, + "symbol": "SPY/USDT:USDT", + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 5, + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 50000, + "maintMarginRatio": 0.125, + "cum": 2708.5 + } + }, + { + "tier": 6.0, + "symbol": "SPY/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 6, + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 6878.5 + } + }, + { + "tier": 7.0, + "symbol": "SPY/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 7, + "initialLeverage": 2, + "notionalCap": 8000000, + "notionalFloor": 500000, + "maintMarginRatio": 0.25, + "cum": 48528.5 + } + }, + { + "tier": 8.0, + "symbol": "SPY/USDT:USDT", + "currency": "USDT", + "minNotional": 8000000.0, + "maxNotional": 15000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 8, + "initialLeverage": 1, + "notionalCap": 15000000, + "notionalFloor": 8000000, + "maintMarginRatio": 0.5, + "cum": 2048528.5 + } + } + ], "SQD/USDT:USDT": [ { "tier": 1.0, @@ -83063,13 +84200,13 @@ "symbol": "SUSHI/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 50000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": 3, "initialLeverage": 25, - "notionalCap": 50000, + "notionalCap": 25000, "notionalFloor": 10000, "maintMarginRatio": 0.02, "cum": 75.0 @@ -83079,51 +84216,51 @@ "tier": 4.0, "symbol": "SUSHI/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, + "minNotional": 25000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": 4, "initialLeverage": 20, - "notionalCap": 100000, - "notionalFloor": 50000, + "notionalCap": 50000, + "notionalFloor": 25000, "maintMarginRatio": 0.025, - "cum": 325.0 + "cum": 200.0 } }, { "tier": 5.0, "symbol": "SUSHI/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 175000.0, - "maintenanceMarginRate": 0.0333, - "maxLeverage": 15.0, + "minNotional": 50000.0, + "maxNotional": 125000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 5, - "initialLeverage": 15, - "notionalCap": 175000, - "notionalFloor": 100000, - "maintMarginRatio": 0.0333, - "cum": 1155.0 + "initialLeverage": 10, + "notionalCap": 125000, + "notionalFloor": 50000, + "maintMarginRatio": 0.05, + "cum": 1450.0 } }, { "tier": 6.0, "symbol": "SUSHI/USDT:USDT", "currency": "USDT", - "minNotional": 175000.0, + "minNotional": 125000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 6, - "initialLeverage": 10, + "initialLeverage": 5, "notionalCap": 250000, - "notionalFloor": 175000, - "maintMarginRatio": 0.05, - "cum": 4077.5 + "notionalFloor": 125000, + "maintMarginRatio": 0.1, + "cum": 7700.0 } }, { @@ -83131,71 +84268,54 @@ "symbol": "SUSHI/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 750000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 7, - "initialLeverage": 5, - "notionalCap": 750000, + "initialLeverage": 4, + "notionalCap": 500000, "notionalFloor": 250000, - "maintMarginRatio": 0.1, - "cum": 16577.5 + "maintMarginRatio": 0.125, + "cum": 13950.0 } }, { "tier": 8.0, "symbol": "SUSHI/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 8, - "initialLeverage": 4, - "notionalCap": 1500000, - "notionalFloor": 750000, - "maintMarginRatio": 0.125, - "cum": 35327.5 + "initialLeverage": 3, + "notionalCap": 1000000, + "notionalFloor": 500000, + "maintMarginRatio": 0.1667, + "cum": 34800.0 } }, { "tier": 9.0, "symbol": "SUSHI/USDT:USDT", "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 4500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 9, - "initialLeverage": 3, - "notionalCap": 4500000, - "notionalFloor": 1500000, - "maintMarginRatio": 0.1667, - "cum": 97877.5 - } - }, - { - "tier": 10.0, - "symbol": "SUSHI/USDT:USDT", - "currency": "USDT", - "minNotional": 4500000.0, + "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 10, + "bracket": 9, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 4500000, + "notionalFloor": 1000000, "maintMarginRatio": 0.25, - "cum": 472727.5 + "cum": 118100.0 } }, { - "tier": 11.0, + "tier": 10.0, "symbol": "SUSHI/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -83203,12 +84323,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 11, + "bracket": 10, "initialLeverage": 1, "notionalCap": 12000000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 2347727.5 + "cum": 1993100.0 } } ], @@ -86533,14 +87653,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 40.0, + "maintenanceMarginRate": 0.04, + "maxLeverage": 20.0, "info": { "bracket": 1, - "initialLeverage": 40, + "initialLeverage": 20, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.02, + "maintMarginRatio": 0.04, "cum": 0.0 } }, @@ -86550,15 +87670,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 2, - "initialLeverage": 20, + "initialLeverage": 10, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.025, - "cum": 25.0 + "maintMarginRatio": 0.05, + "cum": 50.0 } }, { @@ -86566,37 +87686,20 @@ "symbol": "TRADOOR/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, - "info": { - "bracket": 3, - "initialLeverage": 10, - "notionalCap": 20000, - "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 275.0 - } - }, - { - "tier": 4.0, - "symbol": "TRADOOR/USDT:USDT", - "currency": "USDT", - "minNotional": 20000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": 4, + "bracket": 3, "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 20000, + "notionalFloor": 10000, "maintMarginRatio": 0.1, - "cum": 1275.0 + "cum": 550.0 } }, { - "tier": 5.0, + "tier": 4.0, "symbol": "TRADOOR/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, @@ -86604,63 +87707,63 @@ "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": 5, + "bracket": 4, "initialLeverage": 4, "notionalCap": 100000, "notionalFloor": 50000, "maintMarginRatio": 0.125, - "cum": 2525.0 + "cum": 1800.0 + } + }, + { + "tier": 5.0, + "symbol": "TRADOOR/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 5, + "initialLeverage": 3, + "notionalCap": 200000, + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 5970.0 } }, { "tier": 6.0, "symbol": "TRADOOR/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "minNotional": 200000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": 6, - "initialLeverage": 3, - "notionalCap": 250000, - "notionalFloor": 100000, - "maintMarginRatio": 0.1667, - "cum": 6695.0 + "initialLeverage": 2, + "notionalCap": 1000000, + "notionalFloor": 200000, + "maintMarginRatio": 0.25, + "cum": 22630.0 } }, { "tier": 7.0, "symbol": "TRADOOR/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 2500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 7, - "initialLeverage": 2, - "notionalCap": 2500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.25, - "cum": 27520.0 - } - }, - { - "tier": 8.0, - "symbol": "TRADOOR/USDT:USDT", - "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 1000000.0, + "maxNotional": 1200000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 8, + "bracket": 7, "initialLeverage": 1, - "notionalCap": 5000000, - "notionalFloor": 2500000, + "notionalCap": 1200000, + "notionalFloor": 1000000, "maintMarginRatio": 0.5, - "cum": 652520.0 + "cum": 272630.0 } } ], @@ -86671,14 +87774,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maintenanceMarginRate": 0.015, + "maxLeverage": 50.0, "info": { "bracket": 1, - "initialLeverage": 75, + "initialLeverage": 50, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.01, + "maintMarginRatio": 0.015, "cum": 0.0 } }, @@ -86688,14 +87791,14 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": 2, - "initialLeverage": 50, + "initialLeverage": 25, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.02, "cum": 25.0 } }, @@ -86704,15 +87807,15 @@ "symbol": "TRB/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maxNotional": 25000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 3, - "initialLeverage": 25, - "notionalCap": 50000, + "initialLeverage": 20, + "notionalCap": 25000, "notionalFloor": 10000, - "maintMarginRatio": 0.02, + "maintMarginRatio": 0.025, "cum": 75.0 } }, @@ -86720,51 +87823,51 @@ "tier": 4.0, "symbol": "TRB/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 25000.0, + "maxNotional": 62500.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 4, - "initialLeverage": 20, - "notionalCap": 100000, - "notionalFloor": 50000, - "maintMarginRatio": 0.025, - "cum": 325.0 + "initialLeverage": 10, + "notionalCap": 62500, + "notionalFloor": 25000, + "maintMarginRatio": 0.05, + "cum": 700.0 } }, { "tier": 5.0, "symbol": "TRB/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 175000.0, - "maintenanceMarginRate": 0.0333, - "maxLeverage": 15.0, + "minNotional": 62500.0, + "maxNotional": 125000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 5, - "initialLeverage": 15, - "notionalCap": 175000, - "notionalFloor": 100000, - "maintMarginRatio": 0.0333, - "cum": 1155.0 + "initialLeverage": 5, + "notionalCap": 125000, + "notionalFloor": 62500, + "maintMarginRatio": 0.1, + "cum": 3825.0 } }, { "tier": 6.0, "symbol": "TRB/USDT:USDT", "currency": "USDT", - "minNotional": 175000.0, + "minNotional": 125000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 6, - "initialLeverage": 10, + "initialLeverage": 4, "notionalCap": 250000, - "notionalFloor": 175000, - "maintMarginRatio": 0.05, - "cum": 4077.5 + "notionalFloor": 125000, + "maintMarginRatio": 0.125, + "cum": 6950.0 } }, { @@ -86772,71 +87875,37 @@ "symbol": "TRB/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 750000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 7, - "initialLeverage": 5, - "notionalCap": 750000, + "initialLeverage": 3, + "notionalCap": 500000, "notionalFloor": 250000, - "maintMarginRatio": 0.1, - "cum": 16577.5 + "maintMarginRatio": 0.1667, + "cum": 17375.0 } }, { "tier": 8.0, "symbol": "TRB/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 1500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 8, - "initialLeverage": 4, - "notionalCap": 1500000, - "notionalFloor": 750000, - "maintMarginRatio": 0.125, - "cum": 35327.5 - } - }, - { - "tier": 9.0, - "symbol": "TRB/USDT:USDT", - "currency": "USDT", - "minNotional": 1500000.0, - "maxNotional": 4500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 9, - "initialLeverage": 3, - "notionalCap": 4500000, - "notionalFloor": 1500000, - "maintMarginRatio": 0.1667, - "cum": 97877.5 - } - }, - { - "tier": 10.0, - "symbol": "TRB/USDT:USDT", - "currency": "USDT", - "minNotional": 4500000.0, + "minNotional": 500000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 10, + "bracket": 8, "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 4500000, + "notionalFloor": 500000, "maintMarginRatio": 0.25, - "cum": 472727.5 + "cum": 59025.0 } }, { - "tier": 11.0, + "tier": 9.0, "symbol": "TRB/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -86844,12 +87913,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 11, + "bracket": 9, "initialLeverage": 1, "notionalCap": 12000000, "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 2347727.5 + "cum": 1934025.0 } } ], @@ -88284,6 +89353,110 @@ } } ], + "TSM/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "TSM/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 1, + "initialLeverage": 10, + "notionalCap": 5000, + "notionalFloor": 0, + "maintMarginRatio": 0.05, + "cum": 0.0 + } + }, + { + "tier": 2.0, + "symbol": "TSM/USDT:USDT", + "currency": "USDT", + "minNotional": 5000.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 2, + "initialLeverage": 5, + "notionalCap": 10000, + "notionalFloor": 5000, + "maintMarginRatio": 0.1, + "cum": 250.0 + } + }, + { + "tier": 3.0, + "symbol": "TSM/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 3, + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 10000, + "maintMarginRatio": 0.125, + "cum": 500.0 + } + }, + { + "tier": 4.0, + "symbol": "TSM/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 4, + "initialLeverage": 3, + "notionalCap": 500000, + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 4670.0 + } + }, + { + "tier": 5.0, + "symbol": "TSM/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 5, + "initialLeverage": 2, + "notionalCap": 8000000, + "notionalFloor": 500000, + "maintMarginRatio": 0.25, + "cum": 46320.0 + } + }, + { + "tier": 6.0, + "symbol": "TSM/USDT:USDT", + "currency": "USDT", + "minNotional": 8000000.0, + "maxNotional": 15000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 6, + "initialLeverage": 1, + "notionalCap": 15000000, + "notionalFloor": 8000000, + "maintMarginRatio": 0.5, + "cum": 2046320.0 + } + } + ], "TST/USDT:USDT": [ { "tier": 1.0, @@ -91741,14 +92914,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 40.0, + "maintenanceMarginRate": 0.045, + "maxLeverage": 20.0, "info": { "bracket": 1, - "initialLeverage": 40, + "initialLeverage": 20, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.02, + "maintMarginRatio": 0.045, "cum": 0.0 } }, @@ -91758,14 +92931,14 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 2, - "initialLeverage": 20, + "initialLeverage": 10, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.025, + "maintMarginRatio": 0.05, "cum": 25.0 } }, @@ -91774,37 +92947,20 @@ "symbol": "VINE/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, - "info": { - "bracket": 3, - "initialLeverage": 10, - "notionalCap": 20000, - "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 275.0 - } - }, - { - "tier": 4.0, - "symbol": "VINE/USDT:USDT", - "currency": "USDT", - "minNotional": 20000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": 4, + "bracket": 3, "initialLeverage": 5, "notionalCap": 50000, - "notionalFloor": 20000, + "notionalFloor": 10000, "maintMarginRatio": 0.1, - "cum": 1275.0 + "cum": 525.0 } }, { - "tier": 5.0, + "tier": 4.0, "symbol": "VINE/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, @@ -91812,16 +92968,16 @@ "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": 5, + "bracket": 4, "initialLeverage": 4, "notionalCap": 100000, "notionalFloor": 50000, "maintMarginRatio": 0.125, - "cum": 2525.0 + "cum": 1775.0 } }, { - "tier": 6.0, + "tier": 5.0, "symbol": "VINE/USDT:USDT", "currency": "USDT", "minNotional": 100000.0, @@ -91829,46 +92985,46 @@ "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { - "bracket": 6, + "bracket": 5, "initialLeverage": 3, "notionalCap": 250000, "notionalFloor": 100000, "maintMarginRatio": 0.1667, - "cum": 6695.0 + "cum": 5945.0 + } + }, + { + "tier": 6.0, + "symbol": "VINE/USDT:USDT", + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 6, + "initialLeverage": 2, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.25, + "cum": 26770.0 } }, { "tier": 7.0, "symbol": "VINE/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 2500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 7, - "initialLeverage": 2, - "notionalCap": 2500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.25, - "cum": 27520.0 - } - }, - { - "tier": 8.0, - "symbol": "VINE/USDT:USDT", - "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 500000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 8, + "bracket": 7, "initialLeverage": 1, - "notionalCap": 5000000, - "notionalFloor": 2500000, + "notionalCap": 800000, + "notionalFloor": 500000, "maintMarginRatio": 0.5, - "cum": 652520.0 + "cum": 151770.0 } } ], @@ -92464,13 +93620,13 @@ "symbol": "W/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 7500.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, "maxLeverage": 75.0, "info": { "bracket": 1, "initialLeverage": 75, - "notionalCap": 7500, + "notionalCap": 5000, "notionalFloor": 0, "maintMarginRatio": 0.01, "cum": 0.0 @@ -92480,136 +93636,136 @@ "tier": 2.0, "symbol": "W/USDT:USDT", "currency": "USDT", - "minNotional": 7500.0, - "maxNotional": 15000.0, + "minNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": 2, "initialLeverage": 50, - "notionalCap": 15000, - "notionalFloor": 7500, + "notionalCap": 10000, + "notionalFloor": 5000, "maintMarginRatio": 0.015, - "cum": 37.5 + "cum": 25.0 } }, { "tier": 3.0, "symbol": "W/USDT:USDT", "currency": "USDT", - "minNotional": 15000.0, - "maxNotional": 75000.0, + "minNotional": 10000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": 3, "initialLeverage": 25, - "notionalCap": 75000, - "notionalFloor": 15000, + "notionalCap": 25000, + "notionalFloor": 10000, "maintMarginRatio": 0.02, - "cum": 112.5 + "cum": 75.0 } }, { "tier": 4.0, "symbol": "W/USDT:USDT", "currency": "USDT", - "minNotional": 75000.0, - "maxNotional": 200000.0, + "minNotional": 25000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": 4, "initialLeverage": 20, - "notionalCap": 200000, - "notionalFloor": 75000, + "notionalCap": 50000, + "notionalFloor": 25000, "maintMarginRatio": 0.025, - "cum": 487.5 + "cum": 200.0 } }, { "tier": 5.0, "symbol": "W/USDT:USDT", "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 350000.0, - "maintenanceMarginRate": 0.0333, - "maxLeverage": 15.0, + "minNotional": 50000.0, + "maxNotional": 125000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 5, - "initialLeverage": 15, - "notionalCap": 350000, - "notionalFloor": 200000, - "maintMarginRatio": 0.0333, - "cum": 2147.5 + "initialLeverage": 10, + "notionalCap": 125000, + "notionalFloor": 50000, + "maintMarginRatio": 0.05, + "cum": 1450.0 } }, { "tier": 6.0, "symbol": "W/USDT:USDT", "currency": "USDT", - "minNotional": 350000.0, - "maxNotional": 750000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 125000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 6, - "initialLeverage": 10, - "notionalCap": 750000, - "notionalFloor": 350000, - "maintMarginRatio": 0.05, - "cum": 7992.5 + "initialLeverage": 5, + "notionalCap": 250000, + "notionalFloor": 125000, + "maintMarginRatio": 0.1, + "cum": 7700.0 } }, { "tier": 7.0, "symbol": "W/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 3000000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 7, - "initialLeverage": 5, - "notionalCap": 3000000, - "notionalFloor": 750000, - "maintMarginRatio": 0.1, - "cum": 45492.5 + "initialLeverage": 4, + "notionalCap": 500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.125, + "cum": 13950.0 } }, { "tier": 8.0, "symbol": "W/USDT:USDT", "currency": "USDT", - "minNotional": 3000000.0, - "maxNotional": 4500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 500000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 8, - "initialLeverage": 4, - "notionalCap": 4500000, - "notionalFloor": 3000000, - "maintMarginRatio": 0.125, - "cum": 120492.5 + "initialLeverage": 3, + "notionalCap": 1000000, + "notionalFloor": 500000, + "maintMarginRatio": 0.1667, + "cum": 34800.0 } }, { "tier": 9.0, "symbol": "W/USDT:USDT", "currency": "USDT", - "minNotional": 4500000.0, + "minNotional": 1000000.0, "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": 9, - "initialLeverage": 3, + "initialLeverage": 2, "notionalCap": 7500000, - "notionalFloor": 4500000, - "maintMarginRatio": 0.1667, - "cum": 308142.5 + "notionalFloor": 1000000, + "maintMarginRatio": 0.25, + "cum": 118100.0 } }, { @@ -92617,33 +93773,16 @@ "symbol": "W/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, - "maxNotional": 12000000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 10, - "initialLeverage": 2, - "notionalCap": 12000000, - "notionalFloor": 7500000, - "maintMarginRatio": 0.25, - "cum": 932892.5 - } - }, - { - "tier": 11.0, - "symbol": "W/USDT:USDT", - "currency": "USDT", - "minNotional": 12000000.0, - "maxNotional": 18000000.0, + "maxNotional": 12500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 11, + "bracket": 10, "initialLeverage": 1, - "notionalCap": 18000000, - "notionalFloor": 12000000, + "notionalCap": 12500000, + "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 3932892.5 + "cum": 1993100.0 } } ], @@ -93515,13 +94654,13 @@ "symbol": "WIF/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 20000.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, "maxLeverage": 75.0, "info": { "bracket": 1, "initialLeverage": 75, - "notionalCap": 20000, + "notionalCap": 5000, "notionalFloor": 0, "maintMarginRatio": 0.01, "cum": 0.0 @@ -93531,136 +94670,170 @@ "tier": 2.0, "symbol": "WIF/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 200000.0, + "minNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": 2, "initialLeverage": 50, - "notionalCap": 200000, - "notionalFloor": 20000, + "notionalCap": 10000, + "notionalFloor": 5000, "maintMarginRatio": 0.015, - "cum": 100.0 + "cum": 25.0 } }, { "tier": 3.0, "symbol": "WIF/USDT:USDT", "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 1000000.0, + "minNotional": 10000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": 3, "initialLeverage": 25, - "notionalCap": 1000000, - "notionalFloor": 200000, + "notionalCap": 50000, + "notionalFloor": 10000, "maintMarginRatio": 0.02, - "cum": 1100.0 + "cum": 75.0 } }, { "tier": 4.0, "symbol": "WIF/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": 4, "initialLeverage": 20, - "notionalCap": 2000000, - "notionalFloor": 1000000, + "notionalCap": 100000, + "notionalFloor": 50000, "maintMarginRatio": 0.025, - "cum": 6100.0 + "cum": 325.0 } }, { "tier": 5.0, "symbol": "WIF/USDT:USDT", "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 5000000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 100000.0, + "maxNotional": 175000.0, + "maintenanceMarginRate": 0.0333, + "maxLeverage": 15.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 5000000, - "notionalFloor": 2000000, - "maintMarginRatio": 0.05, - "cum": 56100.0 + "initialLeverage": 15, + "notionalCap": 175000, + "notionalFloor": 100000, + "maintMarginRatio": 0.0333, + "cum": 1155.0 } }, { "tier": 6.0, "symbol": "WIF/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 175000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 7500000, - "notionalFloor": 5000000, - "maintMarginRatio": 0.1, - "cum": 306100.0 + "initialLeverage": 10, + "notionalCap": 250000, + "notionalFloor": 175000, + "maintMarginRatio": 0.05, + "cum": 4077.5 } }, { "tier": 7.0, "symbol": "WIF/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 10000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 250000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 7, - "initialLeverage": 4, - "notionalCap": 10000000, - "notionalFloor": 7500000, - "maintMarginRatio": 0.125, - "cum": 493600.0 + "initialLeverage": 5, + "notionalCap": 750000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1, + "cum": 16577.5 } }, { "tier": 8.0, "symbol": "WIF/USDT:USDT", "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 12500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 8, - "initialLeverage": 2, - "notionalCap": 12500000, - "notionalFloor": 10000000, - "maintMarginRatio": 0.25, - "cum": 1743600.0 + "initialLeverage": 4, + "notionalCap": 1500000, + "notionalFloor": 750000, + "maintMarginRatio": 0.125, + "cum": 35327.5 } }, { "tier": 9.0, "symbol": "WIF/USDT:USDT", "currency": "USDT", - "minNotional": 12500000.0, - "maxNotional": 15000000.0, + "minNotional": 1500000.0, + "maxNotional": 4500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 9, + "initialLeverage": 3, + "notionalCap": 4500000, + "notionalFloor": 1500000, + "maintMarginRatio": 0.1667, + "cum": 97877.5 + } + }, + { + "tier": 10.0, + "symbol": "WIF/USDT:USDT", + "currency": "USDT", + "minNotional": 4500000.0, + "maxNotional": 7500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 10, + "initialLeverage": 2, + "notionalCap": 7500000, + "notionalFloor": 4500000, + "maintMarginRatio": 0.25, + "cum": 472727.5 + } + }, + { + "tier": 11.0, + "symbol": "WIF/USDT:USDT", + "currency": "USDT", + "minNotional": 7500000.0, + "maxNotional": 12500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 9, + "bracket": 11, "initialLeverage": 1, - "notionalCap": 15000000, - "notionalFloor": 12500000, + "notionalCap": 12500000, + "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 4868600.0 + "cum": 2347727.5 } } ], @@ -95110,6 +96283,195 @@ } } ], + "XAUT/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "XAUT/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": "XAUT/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": "XAUT/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": 3, + "initialLeverage": 25, + "notionalCap": 50000, + "notionalFloor": 10000, + "maintMarginRatio": 0.02, + "cum": 75.0 + } + }, + { + "tier": 4.0, + "symbol": "XAUT/USDT:USDT", + "currency": "USDT", + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, + "info": { + "bracket": 4, + "initialLeverage": 20, + "notionalCap": 100000, + "notionalFloor": 50000, + "maintMarginRatio": 0.025, + "cum": 325.0 + } + }, + { + "tier": 5.0, + "symbol": "XAUT/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 175000.0, + "maintenanceMarginRate": 0.03333, + "maxLeverage": 15.0, + "info": { + "bracket": 5, + "initialLeverage": 15, + "notionalCap": 175000, + "notionalFloor": 100000, + "maintMarginRatio": 0.03333, + "cum": 1158.0 + } + }, + { + "tier": 6.0, + "symbol": "XAUT/USDT:USDT", + "currency": "USDT", + "minNotional": 175000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": 6, + "initialLeverage": 10, + "notionalCap": 500000, + "notionalFloor": 175000, + "maintMarginRatio": 0.05, + "cum": 4075.25 + } + }, + { + "tier": 7.0, + "symbol": "XAUT/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": 7, + "initialLeverage": 5, + "notionalCap": 750000, + "notionalFloor": 500000, + "maintMarginRatio": 0.1, + "cum": 29075.25 + } + }, + { + "tier": 8.0, + "symbol": "XAUT/USDT:USDT", + "currency": "USDT", + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": 8, + "initialLeverage": 4, + "notionalCap": 1500000, + "notionalFloor": 750000, + "maintMarginRatio": 0.125, + "cum": 47825.25 + } + }, + { + "tier": 9.0, + "symbol": "XAUT/USDT:USDT", + "currency": "USDT", + "minNotional": 1500000.0, + "maxNotional": 4500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 9, + "initialLeverage": 3, + "notionalCap": 4500000, + "notionalFloor": 1500000, + "maintMarginRatio": 0.1667, + "cum": 110375.25 + } + }, + { + "tier": 10.0, + "symbol": "XAUT/USDT:USDT", + "currency": "USDT", + "minNotional": 4500000.0, + "maxNotional": 7500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 10, + "initialLeverage": 2, + "notionalCap": 7500000, + "notionalFloor": 4500000, + "maintMarginRatio": 0.25, + "cum": 485225.25 + } + }, + { + "tier": 11.0, + "symbol": "XAUT/USDT:USDT", + "currency": "USDT", + "minNotional": 7500000.0, + "maxNotional": 12500000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": 11, + "initialLeverage": 1, + "notionalCap": 12500000, + "notionalFloor": 7500000, + "maintMarginRatio": 0.5, + "cum": 2360225.25 + } + } + ], "XCN/USDT:USDT": [ { "tier": 1.0, @@ -96099,13 +97461,13 @@ "symbol": "XPL/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 20000.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, "maxLeverage": 75.0, "info": { "bracket": 1, "initialLeverage": 75, - "notionalCap": 20000, + "notionalCap": 5000, "notionalFloor": 0, "maintMarginRatio": 0.01, "cum": 0.0 @@ -96115,136 +97477,170 @@ "tier": 2.0, "symbol": "XPL/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 200000.0, + "minNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": 2, "initialLeverage": 50, - "notionalCap": 200000, - "notionalFloor": 20000, + "notionalCap": 10000, + "notionalFloor": 5000, "maintMarginRatio": 0.015, - "cum": 100.0 + "cum": 25.0 } }, { "tier": 3.0, "symbol": "XPL/USDT:USDT", "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 1000000.0, + "minNotional": 10000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": 3, "initialLeverage": 25, - "notionalCap": 1000000, - "notionalFloor": 200000, + "notionalCap": 50000, + "notionalFloor": 10000, "maintMarginRatio": 0.02, - "cum": 1100.0 + "cum": 75.0 } }, { "tier": 4.0, "symbol": "XPL/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2000000.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": 4, "initialLeverage": 20, - "notionalCap": 2000000, - "notionalFloor": 1000000, + "notionalCap": 100000, + "notionalFloor": 50000, "maintMarginRatio": 0.025, - "cum": 6100.0 + "cum": 325.0 } }, { "tier": 5.0, "symbol": "XPL/USDT:USDT", "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 5000000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 100000.0, + "maxNotional": 175000.0, + "maintenanceMarginRate": 0.0333, + "maxLeverage": 15.0, "info": { "bracket": 5, - "initialLeverage": 10, - "notionalCap": 5000000, - "notionalFloor": 2000000, - "maintMarginRatio": 0.05, - "cum": 56100.0 + "initialLeverage": 15, + "notionalCap": 175000, + "notionalFloor": 100000, + "maintMarginRatio": 0.0333, + "cum": 1155.0 } }, { "tier": 6.0, "symbol": "XPL/USDT:USDT", "currency": "USDT", - "minNotional": 5000000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 175000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 6, - "initialLeverage": 5, - "notionalCap": 7500000, - "notionalFloor": 5000000, - "maintMarginRatio": 0.1, - "cum": 306100.0 + "initialLeverage": 10, + "notionalCap": 250000, + "notionalFloor": 175000, + "maintMarginRatio": 0.05, + "cum": 4077.5 } }, { "tier": 7.0, "symbol": "XPL/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 10000000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 250000.0, + "maxNotional": 750000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 7, - "initialLeverage": 4, - "notionalCap": 10000000, - "notionalFloor": 7500000, - "maintMarginRatio": 0.125, - "cum": 493600.0 + "initialLeverage": 5, + "notionalCap": 750000, + "notionalFloor": 250000, + "maintMarginRatio": 0.1, + "cum": 16577.5 } }, { "tier": 8.0, "symbol": "XPL/USDT:USDT", "currency": "USDT", - "minNotional": 10000000.0, - "maxNotional": 12500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, + "minNotional": 750000.0, + "maxNotional": 1500000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 8, - "initialLeverage": 2, - "notionalCap": 12500000, - "notionalFloor": 10000000, - "maintMarginRatio": 0.25, - "cum": 1743600.0 + "initialLeverage": 4, + "notionalCap": 1500000, + "notionalFloor": 750000, + "maintMarginRatio": 0.125, + "cum": 35327.5 } }, { "tier": 9.0, "symbol": "XPL/USDT:USDT", "currency": "USDT", - "minNotional": 12500000.0, - "maxNotional": 15000000.0, + "minNotional": 1500000.0, + "maxNotional": 4500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": 9, + "initialLeverage": 3, + "notionalCap": 4500000, + "notionalFloor": 1500000, + "maintMarginRatio": 0.1667, + "cum": 97877.5 + } + }, + { + "tier": 10.0, + "symbol": "XPL/USDT:USDT", + "currency": "USDT", + "minNotional": 4500000.0, + "maxNotional": 7500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 10, + "initialLeverage": 2, + "notionalCap": 7500000, + "notionalFloor": 4500000, + "maintMarginRatio": 0.25, + "cum": 472727.5 + } + }, + { + "tier": 11.0, + "symbol": "XPL/USDT:USDT", + "currency": "USDT", + "minNotional": 7500000.0, + "maxNotional": 12500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 9, + "bracket": 11, "initialLeverage": 1, - "notionalCap": 15000000, - "notionalFloor": 12500000, + "notionalCap": 12500000, + "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 4868600.0 + "cum": 2347727.5 } } ], @@ -97374,14 +98770,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": 1, - "initialLeverage": 50, + "initialLeverage": 20, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.025, "cum": 0.0 } }, @@ -97391,15 +98787,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 2, - "initialLeverage": 20, + "initialLeverage": 10, "notionalCap": 10000, "notionalFloor": 5000, - "maintMarginRatio": 0.025, - "cum": 50.0 + "maintMarginRatio": 0.05, + "cum": 125.0 } }, { @@ -97407,50 +98803,50 @@ "symbol": "YB/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 3, - "initialLeverage": 10, - "notionalCap": 20000, + "initialLeverage": 5, + "notionalCap": 60000, "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 300.0 + "maintMarginRatio": 0.1, + "cum": 625.0 } }, { "tier": 4.0, "symbol": "YB/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 60000.0, + "maxNotional": 70000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 4, - "initialLeverage": 5, - "notionalCap": 50000, - "notionalFloor": 20000, - "maintMarginRatio": 0.1, - "cum": 1300.0 + "initialLeverage": 4, + "notionalCap": 70000, + "notionalFloor": 60000, + "maintMarginRatio": 0.125, + "cum": 2125.0 } }, { "tier": 5.0, "symbol": "YB/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, + "minNotional": 70000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 5, - "initialLeverage": 4, + "initialLeverage": 3, "notionalCap": 250000, - "notionalFloor": 50000, - "maintMarginRatio": 0.125, - "cum": 2550.0 + "notionalFloor": 70000, + "maintMarginRatio": 0.1667, + "cum": 5044.0 } }, { @@ -97459,15 +98855,15 @@ "currency": "USDT", "minNotional": 250000.0, "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": 6, - "initialLeverage": 3, + "initialLeverage": 2, "notionalCap": 500000, "notionalFloor": 250000, - "maintMarginRatio": 0.1667, - "cum": 12975.0 + "maintMarginRatio": 0.25, + "cum": 25869.0 } }, { @@ -97475,33 +98871,16 @@ "symbol": "YB/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 7, - "initialLeverage": 2, - "notionalCap": 7500000, - "notionalFloor": 500000, - "maintMarginRatio": 0.25, - "cum": 54625.0 - } - }, - { - "tier": 8.0, - "symbol": "YB/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 8, + "bracket": 7, "initialLeverage": 1, - "notionalCap": 12500000, - "notionalFloor": 7500000, + "notionalCap": 800000, + "notionalFloor": 500000, "maintMarginRatio": 0.5, - "cum": 1929625.0 + "cum": 150869.0 } } ], @@ -98458,13 +99837,13 @@ "symbol": "ZEN/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 7500.0, + "maxNotional": 5000.0, "maintenanceMarginRate": 0.01, "maxLeverage": 75.0, "info": { "bracket": 1, "initialLeverage": 75, - "notionalCap": 7500, + "notionalCap": 5000, "notionalFloor": 0, "maintMarginRatio": 0.01, "cum": 0.0 @@ -98474,170 +99853,170 @@ "tier": 2.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 7500.0, - "maxNotional": 15000.0, + "minNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.015, "maxLeverage": 50.0, "info": { "bracket": 2, "initialLeverage": 50, - "notionalCap": 15000, - "notionalFloor": 7500, + "notionalCap": 10000, + "notionalFloor": 5000, "maintMarginRatio": 0.015, - "cum": 37.5 + "cum": 25.0 } }, { "tier": 3.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 15000.0, - "maxNotional": 75000.0, + "minNotional": 10000.0, + "maxNotional": 50000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": 3, "initialLeverage": 25, - "notionalCap": 75000, - "notionalFloor": 15000, + "notionalCap": 50000, + "notionalFloor": 10000, "maintMarginRatio": 0.02, - "cum": 112.5 + "cum": 75.0 } }, { "tier": 4.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 75000.0, - "maxNotional": 200000.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": 4, "initialLeverage": 20, - "notionalCap": 200000, - "notionalFloor": 75000, + "notionalCap": 100000, + "notionalFloor": 50000, "maintMarginRatio": 0.025, - "cum": 487.5 + "cum": 325.0 } }, { "tier": 5.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 200000.0, - "maxNotional": 350000.0, + "minNotional": 100000.0, + "maxNotional": 175000.0, "maintenanceMarginRate": 0.0333, "maxLeverage": 15.0, "info": { "bracket": 5, "initialLeverage": 15, - "notionalCap": 350000, - "notionalFloor": 200000, + "notionalCap": 175000, + "notionalFloor": 100000, "maintMarginRatio": 0.0333, - "cum": 2147.5 + "cum": 1155.0 } }, { "tier": 6.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 350000.0, - "maxNotional": 750000.0, + "minNotional": 175000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": 6, "initialLeverage": 10, - "notionalCap": 750000, - "notionalFloor": 350000, + "notionalCap": 250000, + "notionalFloor": 175000, "maintMarginRatio": 0.05, - "cum": 7992.5 + "cum": 4077.5 } }, { "tier": 7.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 750000.0, - "maxNotional": 3000000.0, + "minNotional": 250000.0, + "maxNotional": 750000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": 7, "initialLeverage": 5, - "notionalCap": 3000000, - "notionalFloor": 750000, + "notionalCap": 750000, + "notionalFloor": 250000, "maintMarginRatio": 0.1, - "cum": 45492.5 + "cum": 16577.5 } }, { "tier": 8.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 3000000.0, - "maxNotional": 4500000.0, + "minNotional": 750000.0, + "maxNotional": 1500000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": 8, "initialLeverage": 4, - "notionalCap": 4500000, - "notionalFloor": 3000000, + "notionalCap": 1500000, + "notionalFloor": 750000, "maintMarginRatio": 0.125, - "cum": 120492.5 + "cum": 35327.5 } }, { "tier": 9.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 4500000.0, - "maxNotional": 7500000.0, + "minNotional": 1500000.0, + "maxNotional": 4500000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { "bracket": 9, "initialLeverage": 3, - "notionalCap": 7500000, - "notionalFloor": 4500000, + "notionalCap": 4500000, + "notionalFloor": 1500000, "maintMarginRatio": 0.1667, - "cum": 308142.5 + "cum": 97877.5 } }, { "tier": 10.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12000000.0, + "minNotional": 4500000.0, + "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": 10, "initialLeverage": 2, - "notionalCap": 12000000, - "notionalFloor": 7500000, + "notionalCap": 7500000, + "notionalFloor": 4500000, "maintMarginRatio": 0.25, - "cum": 932892.5 + "cum": 472727.5 } }, { "tier": 11.0, "symbol": "ZEN/USDT:USDT", "currency": "USDT", - "minNotional": 12000000.0, - "maxNotional": 18000000.0, + "minNotional": 7500000.0, + "maxNotional": 12500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": 11, "initialLeverage": 1, - "notionalCap": 18000000, - "notionalFloor": 12000000, + "notionalCap": 12500000, + "notionalFloor": 7500000, "maintMarginRatio": 0.5, - "cum": 3932892.5 + "cum": 2347727.5 } } ], @@ -99389,14 +100768,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 40.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 1, - "initialLeverage": 40, + "initialLeverage": 10, "notionalCap": 5000, "notionalFloor": 0, - "maintMarginRatio": 0.02, + "maintMarginRatio": 0.05, "cum": 0.0 } }, @@ -99405,71 +100784,37 @@ "symbol": "ZKJ/USDT:USDT", "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 40000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 2, - "initialLeverage": 20, - "notionalCap": 10000, + "initialLeverage": 5, + "notionalCap": 40000, "notionalFloor": 5000, - "maintMarginRatio": 0.025, - "cum": 25.0 + "maintMarginRatio": 0.1, + "cum": 250.0 } }, { "tier": 3.0, "symbol": "ZKJ/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, - "info": { - "bracket": 3, - "initialLeverage": 10, - "notionalCap": 20000, - "notionalFloor": 10000, - "maintMarginRatio": 0.05, - "cum": 275.0 - } - }, - { - "tier": 4.0, - "symbol": "ZKJ/USDT:USDT", - "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, - "info": { - "bracket": 4, - "initialLeverage": 5, - "notionalCap": 50000, - "notionalFloor": 20000, - "maintMarginRatio": 0.1, - "cum": 1275.0 - } - }, - { - "tier": 5.0, - "symbol": "ZKJ/USDT:USDT", - "currency": "USDT", - "minNotional": 50000.0, + "minNotional": 40000.0, "maxNotional": 100000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": 5, + "bracket": 3, "initialLeverage": 4, "notionalCap": 100000, - "notionalFloor": 50000, + "notionalFloor": 40000, "maintMarginRatio": 0.125, - "cum": 2525.0 + "cum": 1250.0 } }, { - "tier": 6.0, + "tier": 4.0, "symbol": "ZKJ/USDT:USDT", "currency": "USDT", "minNotional": 100000.0, @@ -99477,46 +100822,46 @@ "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { - "bracket": 6, + "bracket": 4, "initialLeverage": 3, "notionalCap": 250000, "notionalFloor": 100000, "maintMarginRatio": 0.1667, - "cum": 6695.0 + "cum": 5420.0 } }, { - "tier": 7.0, + "tier": 5.0, "symbol": "ZKJ/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 2500000.0, + "maxNotional": 400000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": 7, + "bracket": 5, "initialLeverage": 2, - "notionalCap": 2500000, + "notionalCap": 400000, "notionalFloor": 250000, "maintMarginRatio": 0.25, - "cum": 27520.0 + "cum": 26245.0 } }, { - "tier": 8.0, + "tier": 6.0, "symbol": "ZKJ/USDT:USDT", "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 400000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 8, + "bracket": 6, "initialLeverage": 1, - "notionalCap": 5000000, - "notionalFloor": 2500000, + "notionalCap": 500000, + "notionalFloor": 400000, "maintMarginRatio": 0.5, - "cum": 652520.0 + "cum": 126245.0 } } ], @@ -100284,15 +101629,15 @@ "symbol": "\u5e01\u5b89\u4eba\u751f/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 1, - "initialLeverage": 50, - "notionalCap": 5000, + "initialLeverage": 10, + "notionalCap": 10000, "notionalFloor": 0, - "maintMarginRatio": 0.015, + "maintMarginRatio": 0.05, "cum": 0.0 } }, @@ -100300,136 +101645,85 @@ "tier": 2.0, "symbol": "\u5e01\u5b89\u4eba\u751f/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "minNotional": 10000.0, + "maxNotional": 60000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 2, - "initialLeverage": 25, - "notionalCap": 10000, - "notionalFloor": 5000, - "maintMarginRatio": 0.02, - "cum": 25.0 + "initialLeverage": 5, + "notionalCap": 60000, + "notionalFloor": 10000, + "maintMarginRatio": 0.1, + "cum": 500.0 } }, { "tier": 3.0, "symbol": "\u5e01\u5b89\u4eba\u751f/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 60000.0, + "maxNotional": 120000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 3, - "initialLeverage": 20, - "notionalCap": 25000, - "notionalFloor": 10000, - "maintMarginRatio": 0.025, - "cum": 75.0 + "initialLeverage": 4, + "notionalCap": 120000, + "notionalFloor": 60000, + "maintMarginRatio": 0.125, + "cum": 2000.0 } }, { "tier": 4.0, "symbol": "\u5e01\u5b89\u4eba\u751f/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 62500.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 120000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 4, - "initialLeverage": 10, - "notionalCap": 62500, - "notionalFloor": 25000, - "maintMarginRatio": 0.05, - "cum": 700.0 + "initialLeverage": 3, + "notionalCap": 300000, + "notionalFloor": 120000, + "maintMarginRatio": 0.1667, + "cum": 7004.0 } }, { "tier": 5.0, "symbol": "\u5e01\u5b89\u4eba\u751f/USDT:USDT", "currency": "USDT", - "minNotional": 62500.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 300000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": 5, - "initialLeverage": 5, - "notionalCap": 125000, - "notionalFloor": 62500, - "maintMarginRatio": 0.1, - "cum": 3825.0 + "initialLeverage": 2, + "notionalCap": 1000000, + "notionalFloor": 300000, + "maintMarginRatio": 0.25, + "cum": 31994.0 } }, { "tier": 6.0, "symbol": "\u5e01\u5b89\u4eba\u751f/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, - "info": { - "bracket": 6, - "initialLeverage": 4, - "notionalCap": 250000, - "notionalFloor": 125000, - "maintMarginRatio": 0.125, - "cum": 6950.0 - } - }, - { - "tier": 7.0, - "symbol": "\u5e01\u5b89\u4eba\u751f/USDT:USDT", - "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": 7, - "initialLeverage": 3, - "notionalCap": 500000, - "notionalFloor": 250000, - "maintMarginRatio": 0.1667, - "cum": 17375.0 - } - }, - { - "tier": 8.0, - "symbol": "\u5e01\u5b89\u4eba\u751f/USDT:USDT", - "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": 8, - "initialLeverage": 2, - "notionalCap": 7500000, - "notionalFloor": 500000, - "maintMarginRatio": 0.25, - "cum": 59025.0 - } - }, - { - "tier": 9.0, - "symbol": "\u5e01\u5b89\u4eba\u751f/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 1000000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 9, + "bracket": 6, "initialLeverage": 1, - "notionalCap": 12500000, - "notionalFloor": 7500000, + "notionalCap": 3000000, + "notionalFloor": 1000000, "maintMarginRatio": 0.5, - "cum": 1934025.0 + "cum": 281994.0 } } ], @@ -100560,15 +101854,15 @@ "symbol": "\u9f99\u867e/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": 1, - "initialLeverage": 5, - "notionalCap": 5000, + "initialLeverage": 10, + "notionalCap": 10000, "notionalFloor": 0, - "maintMarginRatio": 0.1, + "maintMarginRatio": 0.05, "cum": 0.0 } }, @@ -100576,68 +101870,85 @@ "tier": 2.0, "symbol": "\u9f99\u867e/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 10000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": 2, - "initialLeverage": 4, - "notionalCap": 10000, - "notionalFloor": 5000, - "maintMarginRatio": 0.125, - "cum": 125.0 + "initialLeverage": 5, + "notionalCap": 50000, + "notionalFloor": 10000, + "maintMarginRatio": 0.1, + "cum": 500.0 } }, { "tier": 3.0, "symbol": "\u9f99\u867e/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 30000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": 3, - "initialLeverage": 3, - "notionalCap": 30000, - "notionalFloor": 10000, - "maintMarginRatio": 0.1667, - "cum": 542.0 + "initialLeverage": 4, + "notionalCap": 100000, + "notionalFloor": 50000, + "maintMarginRatio": 0.125, + "cum": 1750.0 } }, { "tier": 4.0, "symbol": "\u9f99\u867e/USDT:USDT", "currency": "USDT", - "minNotional": 30000.0, - "maxNotional": 80000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": 4, - "initialLeverage": 2, - "notionalCap": 80000, - "notionalFloor": 30000, - "maintMarginRatio": 0.25, - "cum": 3041.0 + "initialLeverage": 3, + "notionalCap": 250000, + "notionalFloor": 100000, + "maintMarginRatio": 0.1667, + "cum": 5920.0 } }, { "tier": 5.0, "symbol": "\u9f99\u867e/USDT:USDT", "currency": "USDT", - "minNotional": 80000.0, - "maxNotional": 200000.0, + "minNotional": 250000.0, + "maxNotional": 2500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": 5, + "initialLeverage": 2, + "notionalCap": 2500000, + "notionalFloor": 250000, + "maintMarginRatio": 0.25, + "cum": 26745.0 + } + }, + { + "tier": 6.0, + "symbol": "\u9f99\u867e/USDT:USDT", + "currency": "USDT", + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": 5, + "bracket": 6, "initialLeverage": 1, - "notionalCap": 200000, - "notionalFloor": 80000, + "notionalCap": 5000000, + "notionalFloor": 2500000, "maintMarginRatio": 0.5, - "cum": 23041.0 + "cum": 651745.0 } } ] diff --git a/freqtrade/exchange/bitget.py b/freqtrade/exchange/bitget.py index 277ae5532..fa9cae3f4 100644 --- a/freqtrade/exchange/bitget.py +++ b/freqtrade/exchange/bitget.py @@ -7,6 +7,7 @@ from freqtrade.constants import BuySell from freqtrade.enums import OPTIMIZE_MODES, CandleType, MarginMode, PriceType, TradingMode from freqtrade.exceptions import ( DDosProtection, + InvalidOrderException, OperationalException, RetryableOrderError, TemporaryError, @@ -34,16 +35,18 @@ class Bitget(Exchange): "stoploss_query_requires_stop_flag": True, "ohlcv_candle_limit": 200, # 200 for historical candles, 1000 for recent ones. "order_time_in_force": ["GTC", "FOK", "IOC", "PO"], + } + _ft_has_futures: FtHas = { + "funding_fee_candle_limit": 100, + "has_delisting": True, + "stop_price_param": "stopLossPrice", + "stop_price_prop": "stopLossPrice", "stop_price_type_field": "triggerType", "stop_price_type_value_mapping": { PriceType.LAST: "fill_price", PriceType.MARK: "mark_price", }, } - _ft_has_futures: FtHas = { - "funding_fee_candle_limit": 100, - "has_delisting": True, - } _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [ (TradingMode.SPOT, MarginMode.NONE), @@ -99,30 +102,36 @@ class Bitget(Exchange): return order def _fetch_stop_order_fallback(self, order_id: str, pair: str) -> CcxtOrder: - params2 = { - "stop": True, - } - for method in ( - self._api.fetch_open_orders, - self._api.fetch_canceled_and_closed_orders, - ): - try: - orders = method(pair, params=params2) - orders_f = [order for order in orders if order["id"] == order_id] - if orders_f: - order = orders_f[0] - self._log_exchange_response("get_stop_order_fallback", order) - return self._convert_stop_order(pair, order_id, order) - except (ccxt.OrderNotFound, ccxt.InvalidOrder): - pass - except ccxt.DDoSProtection as e: - raise DDosProtection(e) from e - except (ccxt.OperationFailed, ccxt.ExchangeError) as e: - raise TemporaryError( - f"Could not get order due to {e.__class__.__name__}. Message: {e}" - ) from e - except ccxt.BaseError as e: - raise OperationalException(e) from e + # old stoploss orders + paramsold = {"stop": True} + # new stoploss orders with stopLossPrice (used in futures starting 2026.4) + paramsnew = {"planType": "profit_loss"} + params_to_try = ( + (paramsnew, paramsold) if self.trading_mode == TradingMode.FUTURES else (paramsold,) + ) + + for params2 in params_to_try: + for method in ( + self._api.fetch_open_orders, + self._api.fetch_canceled_and_closed_orders, + ): + try: + orders = method(pair, params=params2) + orders_f = [order for order in orders if order["id"] == order_id] + if orders_f: + order = orders_f[0] + self._log_exchange_response("get_stop_order_fallback", order) + return self._convert_stop_order(pair, order_id, order) + except (ccxt.OrderNotFound, ccxt.InvalidOrder): + pass + except ccxt.DDoSProtection as e: + raise DDosProtection(e) from e + except (ccxt.OperationFailed, ccxt.ExchangeError) as e: + raise TemporaryError( + f"Could not get order due to {e.__class__.__name__}. Message: {e}" + ) from e + except ccxt.BaseError as e: + raise OperationalException(e) from e raise RetryableOrderError(f"StoplossOrder not found (pair: {pair} id: {order_id}).") @retrier(retries=API_RETRY_COUNT) @@ -134,6 +143,19 @@ class Bitget(Exchange): return self._fetch_stop_order_fallback(order_id, pair) + def cancel_stoploss_order(self, order_id: str, pair: str, params: dict | None = None) -> dict: + cancel_params = params.copy() if params else {} + cancel_params["stop"] = True + + if self.trading_mode != TradingMode.FUTURES: + return self.cancel_order(order_id, pair, cancel_params) + + try: + return self.cancel_order(order_id, pair, {**cancel_params, "planType": "pos_loss"}) + except (InvalidOrderException, IndexError): + # Keep compatibility with stoploss orders created by older versions. + return self.cancel_order(order_id, pair, cancel_params) + @retrier def additional_exchange_init(self) -> None: """ @@ -155,12 +177,6 @@ class Bitget(Exchange): except ccxt.BaseError as e: raise OperationalException(e) from e - def _lev_prep(self, pair: str, leverage: float, side: BuySell, accept_fail: bool = False): - if self.trading_mode != TradingMode.SPOT: - # Explicitly setting margin_mode is not necessary as marginMode can be set per order. - # self.set_margin_mode(pair, self.margin_mode, accept_fail) - self._set_leverage(leverage, pair, accept_fail) - def _get_params( self, side: BuySell, diff --git a/freqtrade/exchange/bybit.py b/freqtrade/exchange/bybit.py index 0184c6c42..bd50b232b 100644 --- a/freqtrade/exchange/bybit.py +++ b/freqtrade/exchange/bybit.py @@ -35,6 +35,9 @@ class Bybit(Exchange): # TODO: Can be removed once bybit fully forces all accounts to unified mode. "fetchOrder": False, }, + # Demo trading + # https://learn.bybit.com/en/bybit-guide/how-to-use-bybit-demo-trading + "supports_demo_trading": True, } _ft_has_futures: FtHas = { "ohlcv_has_history": True, diff --git a/freqtrade/exchange/check_exchange.py b/freqtrade/exchange/check_exchange.py index 583868744..30a416cdd 100644 --- a/freqtrade/exchange/check_exchange.py +++ b/freqtrade/exchange/check_exchange.py @@ -51,12 +51,10 @@ def check_exchange(config: Config, check_for_bad: bool = True) -> bool: if not valid: if check_for_bad: raise OperationalException( - f'Exchange "{exchange}" will not work with Freqtrade. 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}.' - ) + logger.warning(f'Exchange "{exchange}" will not work with Freqtrade. Reason: {reason}.') if MAP_EXCHANGE_CHILDCLASS.get(exchange, exchange) in SUPPORTED_EXCHANGES: logger.info( diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 54d2bde51..9531014c8 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -13,6 +13,7 @@ from datetime import UTC, datetime, timedelta from math import floor, isnan from threading import Lock from typing import Any, Literal, TypeGuard, TypeVar +from uuid import uuid4 import ccxt import ccxt.pro as ccxt_pro @@ -249,7 +250,7 @@ class Exchange: # Holds all open sell orders for dry_run self._dry_run_open_orders: dict[str, Any] = {} - + self._is_demo_trading = exchange_conf.get("demo_trading", False) if self._config["dry_run"]: logger.info("Instance is running with dry_run enabled") logger.info(f"Using CCXT {ccxt.__version__}") @@ -365,6 +366,7 @@ class Exchange: self.validate_pricing(config["exit_pricing"]) self.validate_pricing(config["entry_pricing"]) self.validate_orderflow(config["exchange"]) + self.validate_demo_trading(config["exchange"]) self.validate_freqai(config) self._set_startup_candle_count(config) @@ -418,6 +420,9 @@ class Exchange: except ccxt.BaseError as e: raise OperationalException(f"Initialization of ccxt failed. Reason: {e}") from e + if self.get_option("supports_demo_trading") and exchange_config.get("demo_trading", False): + api.enable_demo_trading(True) + return api @property @@ -433,12 +438,12 @@ class Exchange: @property def name(self) -> str: """exchange Name (from ccxt)""" - return self._api.name + return self._api.name if not self._is_demo_trading else f"{self._api.name} (Demo)" @property def id(self) -> str: """exchange ccxt id""" - return self._api.id + return self._api.id if not self._is_demo_trading else f"{self._api.id}_demo" @property def timeframes(self) -> list[str]: @@ -870,6 +875,16 @@ class Exchange: "fetching historic OHLCV data, otherwise freqAI will not work." ) + def validate_demo_trading(self, exchange_conf: dict) -> None: + """Validate demo trading configuration + Prevents accidental configuration with wrong expectations. + """ + if exchange_conf.get("demo_trading", False): + if not self.get_option("supports_demo_trading"): + raise ConfigurationError(f"Demo trading is not supported for {self.name}.") + else: + logger.info(f"Demo trading enabled for {self.name}") + def validate_required_startup_candles(self, startup_candles: int, timeframe: str) -> int: """ Checks if required startup_candles is more than ohlcv_candle_limit(). @@ -1138,7 +1153,7 @@ class Exchange: stop_price: float | None = None, ) -> CcxtOrder: now = dt_now() - order_id = f"dry_run_{side}_{pair}_{now.timestamp()}" + order_id = f"dry_run_{side}_{pair}_{uuid4()}" # Rounding here must respect to contract sizes _amount = self._contracts_to_amount( pair, self.amount_to_precision(pair, self._amount_to_contracts(pair, amount)) @@ -2655,11 +2670,11 @@ class Exchange: if self._can_use_websocket(self._exchange_ws, pair, timeframe, candle_type): 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) + candles, last_refresh_time = self._exchange_ws.get_ohlcv_with_refresh( + pair, timeframe, candle_type ) + last_refresh_time = int(last_refresh_time) + half_candle = int(candle_ts - (candle_ts - prev_candle_ts) * 0.5) if ( candles diff --git a/freqtrade/exchange/exchange_types.py b/freqtrade/exchange/exchange_types.py index 842bc7c14..8cc9ca5b7 100644 --- a/freqtrade/exchange/exchange_types.py +++ b/freqtrade/exchange/exchange_types.py @@ -67,6 +67,8 @@ class FtHas(TypedDict, total=False): # Delisting check has_delisting: bool + # Demo mode - this is not sandbox but an exchange-provided demo mode. + supports_demo_trading: bool class Ticker(TypedDict): diff --git a/freqtrade/exchange/exchange_ws.py b/freqtrade/exchange/exchange_ws.py index cbc9772a3..7cffa3f57 100644 --- a/freqtrade/exchange/exchange_ws.py +++ b/freqtrade/exchange/exchange_ws.py @@ -1,9 +1,8 @@ import asyncio import logging -import time from copy import deepcopy from functools import partial -from threading import Thread +from threading import Event, RLock, Thread import ccxt @@ -24,49 +23,71 @@ class ExchangeWS: self.config = config self._ccxt_object = ccxt_object self._background_tasks: set[asyncio.Task] = set() + self._state_lock = RLock() + self._loop_ready = Event() self._klines_watching: set[PairWithTimeframe] = set() self._klines_scheduled: set[PairWithTimeframe] = set() - self.klines_last_refresh: dict[PairWithTimeframe, float] = {} - self.klines_last_request: dict[PairWithTimeframe, float] = {} + self._klines_last_refresh: dict[PairWithTimeframe, float] = {} + self._klines_last_request: dict[PairWithTimeframe, float] = {} self._thread = Thread(name="ccxt_ws", target=self._start_forever) self._thread.start() - self.__cleanup_called = False def _start_forever(self) -> None: self._loop = asyncio.new_event_loop() + self._loop_ready.set() try: self._loop.run_forever() finally: - if self._loop.is_running(): - self._loop.stop() + if not self._loop.is_closed(): + # Cancel remaining tasks and close the loop in the owning thread. + pending = asyncio.all_tasks(self._loop) + for task in pending: + task.cancel() + if pending: + self._loop.run_until_complete(asyncio.gather(*pending, return_exceptions=True)) + self._loop.run_until_complete(self._loop.shutdown_asyncgens()) + self._loop.close() + self._loop_ready.clear() + + def _wait_for_loop(self, timeout: float = 1.0) -> bool: + """ + Wait for the event loop to be ready + Returns True once the loop is ready. + Will probably only return false during startup/shutdown. + """ + if hasattr(self, "_loop"): + return True + return self._loop_ready.wait(timeout=timeout) and hasattr(self, "_loop") def cleanup(self) -> None: logger.debug("Cleanup called - stopping") - self._klines_watching.clear() - for task in self._background_tasks: + with self._state_lock: + self._klines_watching.clear() + tasks = list(self._background_tasks) + for task in tasks: task.cancel() - if hasattr(self, "_loop") and not self._loop.is_closed(): - self.reset_connections() - + if self._wait_for_loop(timeout=0.2) and not self._loop.is_closed(): + self.reset_connections(cleanup=True) self._loop.call_soon_threadsafe(self._loop.stop) - time.sleep(0.1) - if not self._loop.is_closed(): - self._loop.close() - - self._thread.join() + self._thread.join(timeout=5) + if self._thread.is_alive(): + logger.warning("Websocket loop thread did not stop within timeout.") logger.debug("Stopped") - def reset_connections(self) -> None: + def reset_connections(self, cleanup: bool = False) -> None: """ Reset all connections - avoids "connection-reset" errors that happen after ~9 days """ - if hasattr(self, "_loop") and not self._loop.is_closed(): - logger.info("Resetting WS connections.") - asyncio.run_coroutine_threadsafe(self._cleanup_async(), loop=self._loop) - while not self.__cleanup_called: - time.sleep(0.1) - self.__cleanup_called = False + if self._wait_for_loop() and not self._loop.is_closed(): + logger.info(f"{'Cleaning up' if cleanup else 'Resetting'} exchange WS connections.") + try: + fut = asyncio.run_coroutine_threadsafe(self._cleanup_async(), loop=self._loop) + fut.result(timeout=10) + except TimeoutError: + logger.warning("Timed out while resetting websocket connections.") + except Exception: + logger.exception("Exception while resetting websocket connections") async def _cleanup_async(self) -> None: try: @@ -76,15 +97,14 @@ class ExchangeWS: self._ccxt_object.ohlcvs.clear() except Exception: logger.exception("Exception in _cleanup_async") - finally: - self.__cleanup_called = True def _pop_history(self, paircomb: PairWithTimeframe) -> None: """ Remove history for a pair/timeframe combination from ccxt cache """ - self._ccxt_object.ohlcvs.get(paircomb[0], {}).pop(paircomb[1], None) - self.klines_last_refresh.pop(paircomb, None) + with self._state_lock: + 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]: @@ -100,81 +120,129 @@ class ExchangeWS: # TemporaryError does not cause backoff - so we're essentially retrying immediately raise TemporaryError(f"Error deepcopying: {e}") from e + def get_ohlcv_with_refresh( + self, pair: str, timeframe: str, candle_type: CandleType + ) -> tuple[list[list], float]: + """ + Get deepcopied klines and update the last refresh time + """ + ohlcvs = self.ohlcvs(pair, timeframe) + with self._state_lock: + last_refresh = self._klines_last_refresh.get((pair, timeframe, candle_type), 0) + return ohlcvs, last_refresh + def cleanup_expired(self) -> None: """ Remove pairs from watchlist if they've not been requested within the last timeframe (+ offset) """ changed = False - for p in list(self._klines_watching): - _, timeframe, _ = p - timeframe_s = timeframe_to_seconds(timeframe) - last_refresh = self.klines_last_request.get(p, 0) - if last_refresh > 0 and (dt_ts() - last_refresh) > ((timeframe_s + 20) * 1000): - logger.info(f"Removing {p} from websocket watchlist.") - self._klines_watching.discard(p) - # Pop history to avoid getting stale data - self._pop_history(p) - changed = True + with self._state_lock: + for p in list(self._klines_watching): + _, timeframe, _ = p + timeframe_s = timeframe_to_seconds(timeframe) + last_refresh = self._klines_last_request.get(p, 0) + if last_refresh > 0 and (dt_ts() - last_refresh) > ((timeframe_s + 20) * 1000): + logger.info(f"Removing {p} from websocket watchlist.") + self._klines_watching.discard(p) + # Pop history to avoid getting stale data + self._pop_history(p) + changed = True if changed: logger.info(f"Removal done: new watch list ({len(self._klines_watching)})") async def _schedule_while_true(self) -> None: # For the ones we should be watching - for p in self._klines_watching: + with self._state_lock: + pairs_to_check = list(self._klines_watching) + + for p in pairs_to_check: # Check if they're already scheduled - if p not in self._klines_scheduled: + with self._state_lock: + if p in self._klines_scheduled: + continue self._klines_scheduled.add(p) - pair, timeframe, candle_type = p - task = asyncio.create_task( - self._continuously_async_watch_ohlcv(pair, timeframe, candle_type) - ) + pair, timeframe, candle_type = p + task = asyncio.create_task( + self._continuously_async_watch_ohlcv(pair, timeframe, candle_type) + ) + with self._state_lock: self._background_tasks.add(task) - task.add_done_callback( - partial( - self._continuous_stopped, - pair=pair, - timeframe=timeframe, - candle_type=candle_type, - ) + task.add_done_callback( + partial( + self._continuous_stopped, + pair=pair, + timeframe=timeframe, + candle_type=candle_type, ) + ) + + def exchange_has(self, endpoint: str) -> bool: + """ + Checks if exchange implements a specific API endpoint. + Wrapper around ccxt 'has' attribute + :param endpoint: Name of endpoint (e.g. 'fetchOHLCV', 'fetchTickers') + :return: bool + """ + return endpoint in self._ccxt_object.has and self._ccxt_object.has[endpoint] 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]]) + if self.exchange_has("unWatchOHLCVForSymbols"): + await self._ccxt_object.un_watch_ohlcv_for_symbols([[pair, timeframe]]) + elif self.exchange_has("unWatchOHLCV"): + await self._ccxt_object.un_watch_ohlcv(pair, timeframe) + else: + logger.debug("un_watch_ohlcv not supported for %s, %s", pair, timeframe) + except ccxt.NotSupported as e: logger.debug("un_watch_ohlcv_for_symbols not supported: %s", e) pass + except ccxt.NetworkError as e: + # Network errors are common on shutdown so we can ignore them. + # It's a network error - which most likely means that the connection is already closed. + logger.debug("Network error during unwatch for %s, %s: %s", pair, timeframe, e) except Exception: - logger.exception("Exception in _unwatch_ohlcv") + logger.exception(f"Exception in _unwatch_ohlcv for {pair}, {timeframe},") def _continuous_stopped( self, task: asyncio.Task, pair: str, timeframe: str, candle_type: CandleType - ): - self._background_tasks.discard(task) + ) -> None: + with self._state_lock: + self._background_tasks.discard(task) result = "done" - if task.cancelled(): - result = "cancelled" - else: - if (result1 := task.result()) is not None: - result = str(result1) + try: + if task.cancelled(): + result = "cancelled" + else: + if (result1 := task.result()) is not None: + result = str(result1) + except Exception: + result = "error" + logger.exception(f"Unhandled exception in watch task callback for {pair}, {timeframe}") + finally: + logger.info(f"{pair}, {timeframe}, {candle_type} - Task finished - {result}") + if hasattr(self, "_loop") and not self._loop.is_closed(): + asyncio.run_coroutine_threadsafe( + self._unwatch_ohlcv(pair, timeframe, candle_type), loop=self._loop + ) - 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)) + with self._state_lock: + self._klines_scheduled.discard((pair, timeframe, candle_type)) + self._pop_history((pair, timeframe, candle_type)) async def _continuously_async_watch_ohlcv( self, pair: str, timeframe: str, candle_type: CandleType ) -> None: try: - while (pair, timeframe, candle_type) in self._klines_watching: + while True: + with self._state_lock: + if (pair, timeframe, candle_type) not in self._klines_watching: + break start = dt_ts() data = await self._ccxt_object.watch_ohlcv(pair, timeframe) - self.klines_last_refresh[(pair, timeframe, candle_type)] = dt_ts() + with self._state_lock: + 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) / 1000:.3f}s" @@ -184,14 +252,19 @@ class ExchangeWS: except ccxt.BaseError: logger.exception(f"Exception in continuously_async_watch_ohlcv for {pair}, {timeframe}") finally: - self._klines_watching.discard((pair, timeframe, candle_type)) + with self._state_lock: + self._klines_watching.discard((pair, timeframe, candle_type)) def schedule_ohlcv(self, pair: str, timeframe: str, candle_type: CandleType) -> None: """ Schedule a pair/timeframe combination to be watched """ - self._klines_watching.add((pair, timeframe, candle_type)) - self.klines_last_request[(pair, timeframe, candle_type)] = dt_ts() + if not self._wait_for_loop(): + logger.warning(f"Websocket loop not ready. Could not schedule {pair}, {timeframe}.") + return + with self._state_lock: + self._klines_watching.add((pair, timeframe, candle_type)) + self._klines_last_request[(pair, timeframe, candle_type)] = dt_ts() # asyncio.run_coroutine_threadsafe(self.schedule_schedule(), loop=self._loop) asyncio.run_coroutine_threadsafe(self._schedule_while_true(), loop=self._loop) self.cleanup_expired() @@ -207,12 +280,10 @@ class ExchangeWS: Returns cached klines from ccxt's "watch" cache. :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 = self.ohlcvs(pair, timeframe) - refresh_date = self.klines_last_refresh[(pair, timeframe, candle_type)] + candles, refresh_date = self.get_ohlcv_with_refresh(pair, timeframe, candle_type) received_ts = candles[-1][0] if candles else 0 drop_hint = received_ts >= candle_ts - if received_ts > refresh_date: + if refresh_date and 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)}). " diff --git a/freqtrade/freqai/data_drawer.py b/freqtrade/freqai/data_drawer.py index 960c822b5..9e9381937 100644 --- a/freqtrade/freqai/data_drawer.py +++ b/freqtrade/freqai/data_drawer.py @@ -361,7 +361,7 @@ class FreqaiDataDrawer: label_loc = df.columns.get_loc(label) pred_label_loc = predictions.columns.get_loc(label) df.iloc[-1, label_loc] = predictions.iloc[-1, pred_label_loc] - if df[label].dtype == object: + if pd.api.types.is_string_dtype(df[label].dtype): continue label_mean_loc = df.columns.get_loc(f"{label}_mean") label_std_loc = df.columns.get_loc(f"{label}_std") diff --git a/freqtrade/freqai/data_kitchen.py b/freqtrade/freqai/data_kitchen.py index 30826b174..16a3bc3c7 100644 --- a/freqtrade/freqai/data_kitchen.py +++ b/freqtrade/freqai/data_kitchen.py @@ -24,8 +24,6 @@ from freqtrade.strategy import merge_informative_pair from freqtrade.strategy.interface import IStrategy -pd.set_option("future.no_silent_downcasting", True) - SECONDS_IN_DAY = 86400 SECONDS_IN_HOUR = 3600 @@ -239,16 +237,14 @@ class FreqaiDataKitchen: filtered_df = filtered_df.replace([np.inf, -np.inf], np.nan) drop_index = pd.isnull(filtered_df).any(axis=1) # get the rows that have NaNs, - drop_index = drop_index.replace(True, 1).replace(False, 0).infer_objects(copy=False) + drop_index = drop_index.replace(True, 1).replace(False, 0).infer_objects() if training_filter: # we don't care about total row number (total no. datapoints) in training, we only care # about removing any row with NaNs # if labels has multiple columns (user wants to train multiple modelEs), we detect here labels = unfiltered_df.filter(label_list or [], axis=1) drop_index_labels = pd.isnull(labels).any(axis=1) - drop_index_labels = ( - drop_index_labels.replace(True, 1).replace(False, 0).infer_objects(copy=False) - ) + drop_index_labels = drop_index_labels.replace(True, 1).replace(False, 0).infer_objects() dates = unfiltered_df["date"] filtered_df = filtered_df[ (drop_index == 0) & (drop_index_labels == 0) @@ -435,7 +431,7 @@ class FreqaiDataKitchen: for label in predictions.columns: append_dict[label] = predictions[label] - if predictions[label].dtype == object: + if pd.api.types.is_string_dtype(predictions[label].dtype): continue if "labels_mean" in self.data and label in self.data["labels_mean"]: append_dict[f"{label}_mean"] = self.data["labels_mean"][label] @@ -879,7 +875,7 @@ class FreqaiDataKitchen: self.data["labels_mean"], self.data["labels_std"] = {}, {} for label in self.data_dictionary["train_labels"].columns: - if self.data_dictionary["train_labels"][label].dtype == object: + if pd.api.types.is_string_dtype(self.data_dictionary["train_labels"][label].dtype): continue f = spy.stats.norm.fit(self.data_dictionary["train_labels"][label]) self.data["labels_mean"][label], self.data["labels_std"][label] = f[0], f[1] @@ -905,7 +901,7 @@ class FreqaiDataKitchen: self.find_labels(dataframe) for key in self.label_list: - if dataframe[key].dtype == object: + if pd.api.types.is_string_dtype(dataframe[key].dtype): self.unique_classes[key] = dataframe[key].dropna().unique() if self.unique_classes: @@ -990,7 +986,7 @@ class FreqaiDataKitchen: are populated. The main example use is when predicting maxima and minima, the argrelextrema - function cannot know the maxima/minima at the edges of the timerange. To improve + function cannot know the maxima/minima at the edges of the timerange. To improve model accuracy, it is best to compute argrelextrema on the full timerange and then use this function to cut off the edges (buffer) by the kernel. diff --git a/freqtrade/freqai/freqai_interface.py b/freqtrade/freqai/freqai_interface.py index 1ba58d3e8..2e3e74400 100644 --- a/freqtrade/freqai/freqai_interface.py +++ b/freqtrade/freqai/freqai_interface.py @@ -676,7 +676,7 @@ class IFreqaiModel(ABC): self.set_start_dry_live_date(strat_df) for label in hist_preds_df.columns: - if hist_preds_df[label].dtype == object: + if pd.api.types.is_string_dtype(hist_preds_df[label].dtype): continue hist_preds_df[f"{label}_mean"] = 0 hist_preds_df[f"{label}_std"] = 0 @@ -706,7 +706,7 @@ class IFreqaiModel(ABC): num_candles = self.freqai_info.get("fit_live_predictions_candles", 100) dk.data["labels_mean"], dk.data["labels_std"] = {}, {} for label in full_labels: - if self.dd.historic_predictions[dk.pair][label].dtype == object: + if pd.api.types.is_string_dtype(self.dd.historic_predictions[dk.pair][label].dtype): continue f = spy.stats.norm.fit(self.dd.historic_predictions[dk.pair][label].tail(num_candles)) dk.data["labels_mean"][label], dk.data["labels_std"][label] = f[0], f[1] @@ -896,7 +896,7 @@ class IFreqaiModel(ABC): ] self.fit_live_predictions(self.dk, self.dk.pair) for label in label_columns: - if dk.full_df[label].dtype == object: + if pd.api.types.is_string_dtype(dk.full_df[label].dtype): continue if "labels_mean" in self.dk.data: dk.full_df.at[index, f"{label}_mean"] = self.dk.data["labels_mean"][ diff --git a/freqtrade/freqai/prediction_models/LightGBMClassifier.py b/freqtrade/freqai/prediction_models/LightGBMClassifier.py index e17f7417c..0b78c4129 100644 --- a/freqtrade/freqai/prediction_models/LightGBMClassifier.py +++ b/freqtrade/freqai/prediction_models/LightGBMClassifier.py @@ -1,10 +1,12 @@ import logging +from collections.abc import Callable from typing import Any from lightgbm import LGBMClassifier from freqtrade.freqai.base_models.BaseClassifierModel import BaseClassifierModel from freqtrade.freqai.data_kitchen import FreqaiDataKitchen +from freqtrade.freqai.tensorboard import LightGBMCallback logger = logging.getLogger(__name__) @@ -46,6 +48,10 @@ class LightGBMClassifier(BaseClassifierModel): init_model = self.get_init_model(dk.pair) model = LGBMClassifier(**self.model_training_parameters) + activate_tensorboard = self.freqai_info.get("activate_tensorboard", True) + callbacks: list[Callable[..., Any]] = [] + if LightGBMCallback is not None: + callbacks = [LightGBMCallback(dk.data_path, activate_tensorboard)] model.fit( X=X, y=y, @@ -53,6 +59,7 @@ class LightGBMClassifier(BaseClassifierModel): sample_weight=train_weights, eval_sample_weight=[test_weights], init_model=init_model, + callbacks=callbacks, ) return model diff --git a/freqtrade/freqai/prediction_models/LightGBMClassifierMultiTarget.py b/freqtrade/freqai/prediction_models/LightGBMClassifierMultiTarget.py index 9fb775614..4e4d981b8 100644 --- a/freqtrade/freqai/prediction_models/LightGBMClassifierMultiTarget.py +++ b/freqtrade/freqai/prediction_models/LightGBMClassifierMultiTarget.py @@ -6,6 +6,7 @@ from lightgbm import LGBMClassifier from freqtrade.freqai.base_models.BaseClassifierModel import BaseClassifierModel from freqtrade.freqai.base_models.FreqaiMultiOutputClassifier import FreqaiMultiOutputClassifier from freqtrade.freqai.data_kitchen import FreqaiDataKitchen +from freqtrade.freqai.tensorboard import LightGBMCallback logger = logging.getLogger(__name__) @@ -53,6 +54,11 @@ class LightGBMClassifierMultiTarget(BaseClassifierModel): else: init_models = [None] * y.shape[1] + activate_tensorboard = self.freqai_info.get("activate_tensorboard", True) + callbacks = [] + if LightGBMCallback is not None: + callbacks = [LightGBMCallback(dk.data_path, activate_tensorboard)] + fit_params = [] for i in range(len(eval_sets)): fit_params.append( @@ -60,6 +66,7 @@ class LightGBMClassifierMultiTarget(BaseClassifierModel): "eval_set": eval_sets[i], "eval_sample_weight": eval_weights, "init_model": init_models[i], + "callbacks": callbacks, } ) diff --git a/freqtrade/freqai/prediction_models/LightGBMRegressor.py b/freqtrade/freqai/prediction_models/LightGBMRegressor.py index d55cd0ca2..abd838eee 100644 --- a/freqtrade/freqai/prediction_models/LightGBMRegressor.py +++ b/freqtrade/freqai/prediction_models/LightGBMRegressor.py @@ -1,10 +1,12 @@ import logging +from collections.abc import Callable from typing import Any from lightgbm import LGBMRegressor from freqtrade.freqai.base_models.BaseRegressionModel import BaseRegressionModel from freqtrade.freqai.data_kitchen import FreqaiDataKitchen +from freqtrade.freqai.tensorboard import LightGBMCallback logger = logging.getLogger(__name__) @@ -42,6 +44,11 @@ class LightGBMRegressor(BaseRegressionModel): model = LGBMRegressor(**self.model_training_parameters) + activate_tensorboard = self.freqai_info.get("activate_tensorboard", True) + callbacks: list[Callable[..., Any]] = [] + if LightGBMCallback is not None: + callbacks = [LightGBMCallback(dk.data_path, activate_tensorboard)] + model.fit( X=X, y=y, @@ -49,6 +56,7 @@ class LightGBMRegressor(BaseRegressionModel): sample_weight=train_weights, eval_sample_weight=[eval_weights], init_model=init_model, + callbacks=callbacks, ) return model diff --git a/freqtrade/freqai/prediction_models/LightGBMRegressorMultiTarget.py b/freqtrade/freqai/prediction_models/LightGBMRegressorMultiTarget.py index c4669a79d..8f374190b 100644 --- a/freqtrade/freqai/prediction_models/LightGBMRegressorMultiTarget.py +++ b/freqtrade/freqai/prediction_models/LightGBMRegressorMultiTarget.py @@ -6,6 +6,7 @@ from lightgbm import LGBMRegressor from freqtrade.freqai.base_models.BaseRegressionModel import BaseRegressionModel from freqtrade.freqai.base_models.FreqaiMultiOutputRegressor import FreqaiMultiOutputRegressor from freqtrade.freqai.data_kitchen import FreqaiDataKitchen +from freqtrade.freqai.tensorboard import LightGBMCallback logger = logging.getLogger(__name__) @@ -55,6 +56,11 @@ class LightGBMRegressorMultiTarget(BaseRegressionModel): else: init_models = [None] * y.shape[1] + activate_tensorboard = self.freqai_info.get("activate_tensorboard", True) + callbacks = [] + if LightGBMCallback is not None: + callbacks = [LightGBMCallback(dk.data_path, activate_tensorboard)] + fit_params = [] for i in range(len(eval_sets)): fit_params.append( @@ -62,6 +68,7 @@ class LightGBMRegressorMultiTarget(BaseRegressionModel): "eval_set": eval_sets[i], "eval_sample_weight": eval_weights, "init_model": init_models[i], + "callbacks": callbacks, } ) diff --git a/freqtrade/freqai/prediction_models/SKLearnRandomForestClassifier.py b/freqtrade/freqai/prediction_models/SKLearnRandomForestClassifier.py index b008a8ecd..9d32b148b 100644 --- a/freqtrade/freqai/prediction_models/SKLearnRandomForestClassifier.py +++ b/freqtrade/freqai/prediction_models/SKLearnRandomForestClassifier.py @@ -63,7 +63,7 @@ class SKLearnRandomForestClassifier(BaseClassifierModel): ) -> tuple[DataFrame, npt.NDArray[np.int_]]: """ Filter the prediction features data and predict with it. - :param unfiltered_df: Full dataframe for the current backtest period. + :param unfiltered_df: Full dataframe for the current backtest period. :return: :pred_df: dataframe containing the predictions :do_predict: np.array of 1s and 0s to indicate places where freqai needed to remove diff --git a/freqtrade/freqai/prediction_models/XGBoostRFClassifier.py b/freqtrade/freqai/prediction_models/XGBoostRFClassifier.py index 6760ad285..1028eebbb 100644 --- a/freqtrade/freqai/prediction_models/XGBoostRFClassifier.py +++ b/freqtrade/freqai/prediction_models/XGBoostRFClassifier.py @@ -67,7 +67,7 @@ class XGBoostRFClassifier(BaseClassifierModel): ) -> tuple[DataFrame, npt.NDArray[np.int_]]: """ Filter the prediction features data and predict with it. - :param unfiltered_df: Full dataframe for the current backtest period. + :param unfiltered_df: Full dataframe for the current backtest period. :return: :pred_df: dataframe containing the predictions :do_predict: np.array of 1s and 0s to indicate places where freqai needed to remove diff --git a/freqtrade/freqai/tensorboard/__init__.py b/freqtrade/freqai/tensorboard/__init__.py index 183c25b22..68d045bf8 100644 --- a/freqtrade/freqai/tensorboard/__init__.py +++ b/freqtrade/freqai/tensorboard/__init__.py @@ -1,9 +1,11 @@ # ensure users can still use a non-torch freqai version try: + from freqtrade.freqai.tensorboard.lightgbm_callback import LightGBMTensorboardCallback from freqtrade.freqai.tensorboard.tensorboard import TensorBoardCallback, TensorboardLogger TBLogger = TensorboardLogger TBCallback = TensorBoardCallback + LightGBMCallback = LightGBMTensorboardCallback except ModuleNotFoundError: from freqtrade.freqai.tensorboard.base_tensorboard import ( BaseTensorBoardCallback, @@ -12,5 +14,6 @@ except ModuleNotFoundError: TBLogger = BaseTensorboardLogger # type: ignore TBCallback = BaseTensorBoardCallback # type: ignore + LightGBMCallback = None # type: ignore -__all__ = ("TBLogger", "TBCallback") +__all__ = ("TBLogger", "TBCallback", "LightGBMCallback") diff --git a/freqtrade/freqai/tensorboard/lightgbm_callback.py b/freqtrade/freqai/tensorboard/lightgbm_callback.py new file mode 100644 index 000000000..c71a43ec5 --- /dev/null +++ b/freqtrade/freqai/tensorboard/lightgbm_callback.py @@ -0,0 +1,24 @@ +from __future__ import annotations + +from freqtrade.freqai.tensorboard.tensorboard import TensorboardLogger + + +class LightGBMTensorboardCallback: + def __init__(self, logdir, activate: bool) -> None: + self.activate = activate + self.logger = TensorboardLogger(logdir, activate) + + def __call__(self, env) -> None: + if not self.activate: + return + + evals = getattr(env, "evaluation_result_list", None) + if not evals: + return + + for data_name, metric_name, value, _ in evals: + self.logger.log_scalar(f"{data_name}-{metric_name}", value, env.iteration) + + end_iteration = getattr(env, "end_iteration", None) + if end_iteration is not None and env.iteration + 1 >= end_iteration: + self.logger.close() diff --git a/freqtrade/freqtradebot.py b/freqtrade/freqtradebot.py index 28560a460..360008dc6 100644 --- a/freqtrade/freqtradebot.py +++ b/freqtrade/freqtradebot.py @@ -92,100 +92,106 @@ class FreqtradeBot(LoggingMixin): exchange_config: ExchangeConfig = deepcopy(config["exchange"]) # Remove credentials from original exchange config to avoid accidental credential exposure remove_exchange_credentials(config["exchange"], True) - - self.exchange = ExchangeResolver.load_exchange( - self.config, exchange_config=exchange_config, load_leverage_tiers=True - ) - - self.strategy: IStrategy = StrategyResolver.load_strategy(self.config) - - # Check config consistency here since strategies can set certain options - validate_config_consistency(config) - # Re-validate exchange compatibility - self.exchange.validate_config(self.config) - - init_db(self.config["db_url"]) - - self.wallets = Wallets(self.config, self.exchange) - - PairLocks.timeframe = self.config["timeframe"] - - self.trading_mode: TradingMode = self.config.get("trading_mode", TradingMode.SPOT) - self.margin_mode: MarginMode = self.config.get("margin_mode", MarginMode.NONE) - self.last_process: datetime | None = None - - # RPC runs in separate threads, can start handling external commands just after - # initialization, even before Freqtradebot has a chance to start its throttling, - # so anything in the Freqtradebot instance should be ready (initialized), including - # the initial state of the bot. - # Keep this at the end of this initialization method. - self.rpc: RPCManager = RPCManager(self) - - self.dataprovider = DataProvider(self.config, self.exchange, rpc=self.rpc) - self.pairlists = PairListManager(self.exchange, self.config, self.dataprovider) - - self.dataprovider.add_pairlisthandler(self.pairlists) - - # Attach Dataprovider to strategy instance - self.strategy.dp = self.dataprovider - # Attach Wallets to strategy instance - self.strategy.wallets = self.wallets - - # Init ExternalMessageConsumer if enabled - self.emc = ( - ExternalMessageConsumer(self.config, self.dataprovider) - if self.config.get("external_message_consumer", {}).get("enabled", False) - else None - ) - - logger.info("Starting initial pairlist refresh") - with MeasureTime( - lambda duration, _: logger.info(f"Initial Pairlist refresh took {duration:.2f}s"), 0 - ): - self.active_pair_whitelist = self._refresh_active_whitelist() - - # Set initial bot state from config - initial_state = self.config.get("initial_state") - self.state = State[initial_state.upper()] if initial_state else State.STOPPED - - # Protect exit-logic from forcesell and vice versa - self._exit_lock = Lock() - timeframe_secs = timeframe_to_seconds(self.strategy.timeframe) - self._exit_reason_cache = PeriodicCache(100, ttl=timeframe_secs) - LoggingMixin.__init__(self, logger, timeframe_secs) - - self._schedule = Scheduler() - - if self.trading_mode == TradingMode.FUTURES: - - 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 - # funding interval, specified by funding_fee_times on the exchange classes - # However, this reduces the precision - and might therefore lead to problems. - for time_slot in range(0, 24): - for minutes in [1, 31]: - t = str(time(time_slot, minutes, 2)) - self._schedule.every().day.at(t).do(update) - - self._schedule.every().day.at("00:02").do(self.exchange.ws_connection_reset) - - self.strategy.ft_bot_start() - # Initialize protections AFTER bot start - otherwise parameters are not loaded. - self.protections = ProtectionManager(self.config, self.strategy.protections) - - def log_took_too_long(duration: float, time_limit: float): - logger.warning( - f"Strategy analysis took {duration:.2f}s, more than 25% of the timeframe " - f"({time_limit:.2f}s). This can lead to delayed orders and missed signals." - "Consider either reducing the amount of work your strategy performs " - "or reduce the amount of pairs in the Pairlist." + try: + self.exchange = ExchangeResolver.load_exchange( + self.config, exchange_config=exchange_config, load_leverage_tiers=True ) - self._measure_execution = MeasureTime(log_took_too_long, timeframe_secs * 0.25) + self.strategy: IStrategy = StrategyResolver.load_strategy(self.config) + + # Check config consistency here since strategies can set certain options + validate_config_consistency(config) + # Re-validate exchange compatibility + self.exchange.validate_config(self.config) + + init_db(self.config["db_url"]) + + self.wallets = Wallets(self.config, self.exchange) + + PairLocks.timeframe = self.config["timeframe"] + + self.trading_mode: TradingMode = self.config.get("trading_mode", TradingMode.SPOT) + self.margin_mode: MarginMode = self.config.get("margin_mode", MarginMode.NONE) + self.last_process: datetime | None = None + + # RPC runs in separate threads, can start handling external commands just after + # initialization, even before Freqtradebot has a chance to start its throttling, + # so anything in the Freqtradebot instance should be ready (initialized), including + # the initial state of the bot. + # Keep this at the end of this initialization method. + self.rpc: RPCManager = RPCManager(self) + + self.dataprovider = DataProvider(self.config, self.exchange, rpc=self.rpc) + self.pairlists = PairListManager(self.exchange, self.config, self.dataprovider) + + self.dataprovider.add_pairlisthandler(self.pairlists) + + # Attach Dataprovider to strategy instance + self.strategy.dp = self.dataprovider + # Attach Wallets to strategy instance + self.strategy.wallets = self.wallets + + # Init ExternalMessageConsumer if enabled + self.emc: ExternalMessageConsumer | None = ( + ExternalMessageConsumer(self.config, self.dataprovider) + if self.config.get("external_message_consumer", {}).get("enabled", False) + else None + ) + + logger.info("Starting initial pairlist refresh") + with MeasureTime( + lambda duration, _: logger.info(f"Initial Pairlist refresh took {duration:.2f}s"), 0 + ): + self.active_pair_whitelist = self._refresh_active_whitelist() + + # Set initial bot state from config + initial_state = self.config.get("initial_state") + self.state = State[initial_state.upper()] if initial_state else State.STOPPED + + # Protect exit-logic from forcesell and vice versa + self._exit_lock = Lock() + timeframe_secs = timeframe_to_seconds(self.strategy.timeframe) + self._exit_reason_cache = PeriodicCache(100, ttl=timeframe_secs) + LoggingMixin.__init__(self, logger, timeframe_secs) + + self._schedule = Scheduler() + + if self.trading_mode == TradingMode.FUTURES: + + 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 + # funding interval, specified by funding_fee_times on the exchange classes + # However, this reduces the precision - and might therefore lead to problems. + for time_slot in range(0, 24): + for minutes in [1, 31]: + t = str(time(time_slot, minutes, 2)) + self._schedule.every().day.at(t).do(update) + + self._schedule.every().day.at("00:02").do(self.exchange.ws_connection_reset) + self._schedule.every().day.at("00:07").do(self.wallets.record_wallet_state) + + self.strategy.ft_bot_start() + # Initialize protections AFTER bot start - otherwise parameters are not loaded. + self.protections = ProtectionManager(self.config, self.strategy.protections) + + def log_took_too_long(duration: float, time_limit: float): + logger.warning( + f"Strategy analysis took {duration:.2f}s, more than 25% of the timeframe " + f"({time_limit:.2f}s). This can lead to delayed orders and missed signals." + "Consider either reducing the amount of work your strategy performs " + "or reduce the amount of pairs in the Pairlist." + ) + + self._measure_execution = MeasureTime(log_took_too_long, timeframe_secs * 0.25) + + except Exception as e: + # Graceful shutdown in case of failed initialization. + self.cleanup() + raise e from e def notify_status(self, msg: str, msg_type=RPCMessageType.STATUS) -> None: """ @@ -211,14 +217,18 @@ class FreqtradeBot(LoggingMixin): logger.warning(f"Exception during cleanup: {e.__class__.__name__} {e}") finally: - self.strategy.ft_bot_cleanup() + if getattr(self, "strategy", None): + self.strategy.ft_bot_cleanup() - self.rpc.cleanup() - if self.emc: + if getattr(self, "rpc", None): + self.rpc.cleanup() + if hasattr(self, "emc") and self.emc: self.emc.shutdown() - self.exchange.close() + if getattr(self, "exchange", None): + self.exchange.close() try: - Trade.commit() + if hasattr(Trade, "session"): + Trade.commit() except Exception: # Exceptions here will be happening if the db disappeared. # At which point we can no longer commit anyway. @@ -229,7 +239,7 @@ class FreqtradeBot(LoggingMixin): Called on startup and after reloading the bot - triggers notifications and performs startup tasks """ - migrate_live_content(self.config, self.exchange) + migrate_live_content(self.config, self.exchange, self.wallets.get_starting_balance()) set_startup_time() self.rpc.startup_messages(self.config, self.pairlists, self.protections) diff --git a/freqtrade/ft_types/backtest_result_type.py b/freqtrade/ft_types/backtest_result_type.py index b253231a1..768d13517 100644 --- a/freqtrade/ft_types/backtest_result_type.py +++ b/freqtrade/ft_types/backtest_result_type.py @@ -55,6 +55,7 @@ class BacktestContentTypeIcomplete(TypedDict, total=False): backtest_start_time: int backtest_end_time: int run_id: str + wallet_summary: DataFrame class BacktestContentType(BacktestContentTypeIcomplete, total=True): diff --git a/freqtrade/optimize/analysis/lookahead_helpers.py b/freqtrade/optimize/analysis/lookahead_helpers.py index c9434c3d8..affa0c652 100644 --- a/freqtrade/optimize/analysis/lookahead_helpers.py +++ b/freqtrade/optimize/analysis/lookahead_helpers.py @@ -126,14 +126,14 @@ class LookaheadAnalysisSubFunctions: csv_df = add_or_update_row(csv_df, new_row_data) # Fill NaN values with a default value (e.g., 0) - csv_df["total_signals"] = csv_df["total_signals"].astype(int).fillna(0) - csv_df["biased_entry_signals"] = csv_df["biased_entry_signals"].astype(int).fillna(0) - csv_df["biased_exit_signals"] = csv_df["biased_exit_signals"].astype(int).fillna(0) + csv_df["total_signals"] = csv_df["total_signals"].astype("int64").fillna(0) + csv_df["biased_entry_signals"] = csv_df["biased_entry_signals"].astype("int64").fillna(0) + csv_df["biased_exit_signals"] = csv_df["biased_exit_signals"].astype("int64").fillna(0) # Convert columns to integers - csv_df["total_signals"] = csv_df["total_signals"].astype(int) - csv_df["biased_entry_signals"] = csv_df["biased_entry_signals"].astype(int) - csv_df["biased_exit_signals"] = csv_df["biased_exit_signals"].astype(int) + csv_df["total_signals"] = csv_df["total_signals"].astype("int64") + csv_df["biased_entry_signals"] = csv_df["biased_entry_signals"].astype("int64") + csv_df["biased_exit_signals"] = csv_df["biased_exit_signals"].astype("int64") logger.info(f"saving {config['lookahead_analysis_exportfilename']}") csv_df.to_csv(config["lookahead_analysis_exportfilename"], index=False) diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index a95b29005..f4f5e9d62 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -51,6 +51,7 @@ from freqtrade.mixins import LoggingMixin from freqtrade.optimize.backtest_caching import get_strategy_run_id from freqtrade.optimize.bt_progress import BTProgress from freqtrade.optimize.optimize_reports import ( + convert_bt_wallet_collection, generate_backtest_stats, generate_rejected_signals, generate_trade_signal_candles, @@ -137,6 +138,7 @@ class Backtesting: } self.rejected_dict: dict[str, list] = {} self.starting_balance: float = 0.0 + self.wallet_captures: list = [] self._exchange_name = self.config["exchange"]["name"] self.__initial_backtest = exchange is None @@ -451,6 +453,7 @@ class Backtesting: self.replaced_entry_orders = 0 self.canceled_exit_orders = 0 self.replaced_exit_orders = 0 + self.wallet_captures = [] self.dataprovider.clear_cache() if enable_protections: self._load_protections(self.strategy) @@ -754,7 +757,7 @@ class Backtesting: ) -> bool: """ Check if an order is open and if it should've filled. - :return: True if the order filled. + :return: True if the order filled. """ if order and self._get_order_filled(order.ft_price, row): order.close_bt_order(current_date, trade) @@ -1603,6 +1606,7 @@ class Backtesting: 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] + self._capture_wallet(current_time, self.strategy.config["stake_currency"], 1) for current_time_det, is_first, has_detail, idx, pair in self._time_pair_generator_det( current_time, pairs @@ -1627,6 +1631,7 @@ class Backtesting: ) trade_dir = self.check_for_trade_entry(row) pair_tradedir_cache[pair] = trade_dir + self._capture_wallet(current_time, pair.split("/")[0], row[OPEN_IDX]) else: # Detail candle - from cache. @@ -1680,6 +1685,15 @@ class Backtesting: yield current_time_det, pair, row, is_last_row, trade_dir self.progress.increment() + def _capture_wallet(self, current_time: datetime, currency: str, price: float) -> None: + """ + Capture the current wallet state. + """ + if self.dataprovider.runmode != RunMode.BACKTEST: + return + if total := self.wallets.get_total(currency): + self.wallet_captures.append((current_time, currency, price, total)) + def backtest( self, processed: dict, start_date: datetime, end_date: datetime ) -> BacktestContentTypeIcomplete: @@ -1739,6 +1753,7 @@ class Backtesting: "canceled_entry_orders": self.canceled_entry_orders, "replaced_entry_orders": self.replaced_entry_orders, "final_balance": self.wallets.get_total(self.strategy.config["stake_currency"]), + "wallet_summary": convert_bt_wallet_collection(self.wallet_captures), } def backtest_one_strategy( @@ -1867,6 +1882,11 @@ class Backtesting: dt_appendix, market_change_data=combined_res, analysis_results=self.analysis_results, + wallet_summary={ + s: x["wallet_summary"] + for s, x in self.all_bt_content.items() + if "wallet_summary" in x + }, strategy_files={s.get_strategy_name(): s.__file__ for s in self.strategylist}, ) diff --git a/freqtrade/optimize/optimize_reports/__init__.py b/freqtrade/optimize/optimize_reports/__init__.py index 5cf8e51ad..a41a8ebbf 100644 --- a/freqtrade/optimize/optimize_reports/__init__.py +++ b/freqtrade/optimize/optimize_reports/__init__.py @@ -12,6 +12,7 @@ from freqtrade.optimize.optimize_reports.bt_output import ( ) from freqtrade.optimize.optimize_reports.bt_storage import store_backtest_results from freqtrade.optimize.optimize_reports.optimize_reports import ( + convert_bt_wallet_collection, generate_all_periodic_breakdown_stats, generate_backtest_stats, generate_daily_stats, diff --git a/freqtrade/optimize/optimize_reports/bt_output.py b/freqtrade/optimize/optimize_reports/bt_output.py index 338fe5ca5..f42054c65 100644 --- a/freqtrade/optimize/optimize_reports/bt_output.py +++ b/freqtrade/optimize/optimize_reports/bt_output.py @@ -1,6 +1,8 @@ import logging from typing import Any, Literal +from rich.text import Text + from freqtrade.constants import UNLIMITED_STAKE_AMOUNT, Config from freqtrade.ft_types import BacktestResultType from freqtrade.optimize.optimize_reports.optimize_reports import generate_periodic_breakdown_stats @@ -9,6 +11,8 @@ from freqtrade.util import decimals_per_coin, fmt_coin, print_rich_table logger = logging.getLogger(__name__) +__EMPTY_LINE = ("", "") + def _get_line_floatfmt(stake_currency: str) -> list[str]: """ @@ -201,7 +205,7 @@ def text_table_add_metrics(strat_results: dict) -> None: short_metrics = ( [ - ("", ""), # Empty line to improve readability + __EMPTY_LINE, # Empty line to improve readability ( "Long / Short trades", f"{strat_results.get('trade_count_long', 'total_trades')} / " @@ -222,7 +226,7 @@ def text_table_add_metrics(strat_results: dict) -> None: else [] ) - drawdown_metrics = [] + drawdown_metrics: list[tuple[str | Text, str | Text]] = [] if "max_relative_drawdown" in strat_results: # Compatibility to show old hyperopt results drawdown_metrics.append( @@ -287,6 +291,79 @@ def text_table_add_metrics(strat_results: dict) -> None: if "trading_mode" in strat_results else [] ) + wallet_metrics: list[tuple[str, str]] = [ + ( + "Min/Max balance (closed trades)", + f"{fmt_coin(strat_results['csum_min'], stake)} / " + f"{fmt_coin(strat_results['csum_max'], stake)}", + ), + ] + wallet_stats = strat_results.get("wallet_stats", {}) + if wallet_stats: + drawdown_metrics.extend( + [ + __EMPTY_LINE, # Empty line to improve readability + (Text("Wallet based Metrics", style="bold"), ""), + ( + "Min/Max balance (wallet balance)", + f"{fmt_coin(wallet_stats['low_balance'], stake)} / " + f"{fmt_coin(wallet_stats['high_balance'], stake)}", + ), + ( + "Min/Max balance dates (wallet balance)", + f"{wallet_stats['low_date']} / {wallet_stats['high_date']}", + ), + ] + ) + if "max_drawdown_abs" in wallet_stats: + # Assume that if sharpe is there, all others are there as well. + drawdown_metrics.extend( + [ + ( + "Max % of account underwater (balance)", + f"{wallet_stats['max_relative_drawdown']:.2%}", + ), + ( + "Absolute drawdown (wallet balance)", + f"{fmt_coin(wallet_stats['max_drawdown_abs'], stake)} " + f"({wallet_stats['max_drawdown_account']:.2%})", + ), + ( + "Drawdown duration", + wallet_stats["drawdown_duration"] + if "drawdown_duration" in wallet_stats + else "N/A", + ), + ( + "Profit at drawdown start", + fmt_coin(wallet_stats["max_drawdown_high"], stake), + ), + ( + "Profit at drawdown end", + fmt_coin(wallet_stats["max_drawdown_low"], stake), + ), + ("Drawdown start", wallet_stats["drawdown_start"]), + ("Drawdown end", wallet_stats["drawdown_end"]), + ( + "Sharpe (daily wallet balance)", + f"{wallet_stats['sharpe']:.2f}" + if wallet_stats and "sharpe" in wallet_stats + else "N/A", + ), + ( + "Sortino (daily wallet balance)", + f"{wallet_stats['sortino']:.2f}" + if wallet_stats and "sortino" in wallet_stats + else "N/A", + ), + ( + "Calmar (daily wallet balance)", + f"{wallet_stats['calmar']:.2f}" + if wallet_stats and "calmar" in wallet_stats + else "N/A", + ), + ] + ) # Newly added fields should be ignored if they are missing in strat_results. hyperopt-show # command stores these results and newer version of freqtrade must be able to handle old @@ -296,7 +373,7 @@ def text_table_add_metrics(strat_results: dict) -> None: ("Backtesting to", strat_results["backtest_end"]), *trading_mode, ("Max open trades", strat_results["max_open_trades"]), - ("", ""), # Empty line to improve readability + __EMPTY_LINE, # Empty line to improve readability ( "Total/Daily Avg Trades", f"{strat_results['total_trades']} / {strat_results['trades_per_day']}", @@ -315,9 +392,18 @@ def text_table_add_metrics(strat_results: dict) -> None: ), ("Total profit %", f"{strat_results['profit_total']:.2%}"), ("CAGR %", f"{strat_results['cagr']:.2%}" if "cagr" in strat_results else "N/A"), - ("Sortino", f"{strat_results['sortino']:.2f}" if "sortino" in strat_results else "N/A"), - ("Sharpe", f"{strat_results['sharpe']:.2f}" if "sharpe" in strat_results else "N/A"), - ("Calmar", f"{strat_results['calmar']:.2f}" if "calmar" in strat_results else "N/A"), + ( + "Sharpe (closed trades)", + f"{strat_results['sharpe']:.2f}" if "sharpe" in strat_results else "N/A", + ), + ( + "Sortino (closed trades)", + f"{strat_results['sortino']:.2f}" if "sortino" in strat_results else "N/A", + ), + ( + "Calmar (closed trades)", + f"{strat_results['calmar']:.2f}" if "calmar" in strat_results else "N/A", + ), ("SQN", f"{strat_results['sqn']:.2f}" if "sqn" in strat_results else "N/A"), ( "Profit factor", @@ -346,12 +432,13 @@ def text_table_add_metrics(strat_results: dict) -> None: "Avg. stake amount", fmt_coin(strat_results["avg_stake_amount"], stake), ), + ("Market change", f"{strat_results['market_change']:.2%}"), ( "Total trade volume", fmt_coin(strat_results["total_volume"], stake), ), *short_metrics, - ("", ""), # Empty line to improve readability + __EMPTY_LINE, # Empty line to improve readability ( "Best Pair", f"{strat_results['best_pair']['key']} " @@ -407,11 +494,9 @@ def text_table_add_metrics(strat_results: dict) -> None: f"{strat_results.get('timedout_exit_orders', 'N/A')}", ), *entry_adjustment_metrics, - ("", ""), # Empty line to improve readability - ("Min balance", fmt_coin(strat_results["csum_min"], stake)), - ("Max balance", fmt_coin(strat_results["csum_max"], stake)), + __EMPTY_LINE, # Empty line to improve readability + *wallet_metrics, *drawdown_metrics, - ("Market change", f"{strat_results['market_change']:.2%}"), ] print_rich_table(metrics, ["Metric", "Value"], summary="SUMMARY METRICS", justify="left") diff --git a/freqtrade/optimize/optimize_reports/bt_storage.py b/freqtrade/optimize/optimize_reports/bt_storage.py index ef73d4721..c4f20e3e4 100644 --- a/freqtrade/optimize/optimize_reports/bt_storage.py +++ b/freqtrade/optimize/optimize_reports/bt_storage.py @@ -52,6 +52,7 @@ def store_backtest_results( dtappendix: str, *, market_change_data: DataFrame | None = None, + wallet_summary: dict[str, DataFrame] | None = None, analysis_results: dict[str, dict[str, DataFrame]] | None = None, strategy_files: dict[str, str] | None = None, ) -> Path: @@ -123,6 +124,15 @@ def store_backtest_results( market_change_buf.seek(0) zipf.writestr(market_change_name, market_change_buf.getvalue()) + # Add wallet summary if present + if wallet_summary is not None: + for strategy, df in wallet_summary.items(): + wallet_name = f"{base_filename.stem}_{strategy}_wallet.feather" + wallet_buf = BytesIO() + df.reset_index().to_feather(wallet_buf, compression_level=9, compression="lz4") + wallet_buf.seek(0) + zipf.writestr(wallet_name, wallet_buf.getvalue()) + # Add analysis results if present and running in backtest mode if ( config.get("export", "none") == "signals" diff --git a/freqtrade/optimize/optimize_reports/optimize_reports.py b/freqtrade/optimize/optimize_reports/optimize_reports.py index 57b7740d8..15d25a19c 100644 --- a/freqtrade/optimize/optimize_reports/optimize_reports.py +++ b/freqtrade/optimize/optimize_reports/optimize_reports.py @@ -10,12 +10,16 @@ from freqtrade.constants import BACKTEST_BREAKDOWNS, DATETIME_PRINT_FORMAT from freqtrade.data.metrics import ( calculate_cagr, calculate_calmar, + calculate_calmar_from_balance, calculate_csum, calculate_expectancy, calculate_market_change, calculate_max_drawdown, + calculate_max_drawdown_from_balance, calculate_sharpe, + calculate_sharpe_from_balance, calculate_sortino, + calculate_sortino_from_balance, calculate_sqn, ) from freqtrade.ft_types import ( @@ -29,6 +33,94 @@ from freqtrade.util import decimals_per_coin, fmt_coin, format_duration, get_dry logger = logging.getLogger(__name__) +def convert_bt_wallet_collection(wallet_captures: list[tuple]) -> DataFrame: + """ + Convert the wallet capture list to a DataFrame. + Assumes the wallet_captures list contains tuples with the following structure: + (date, currency, price, balance). + """ + if len(wallet_captures) == 0: + return DataFrame() + return DataFrame( + wallet_captures, + columns=["date", "currency", "rate", "balance"], + ) + + +def generate_wallet_stats(wallet_df: DataFrame, stake_currency: str) -> dict[str, Any]: + """Generate wallet statistics from the wallet DataFrame.""" + if wallet_df is None or wallet_df.empty: + return {} + wallet_df.loc[:, "total_quote"] = wallet_df["rate"] * wallet_df["balance"] + # Group by date to get total wallet value at each timestamp + wallet = wallet_df.groupby("date")["total_quote"].sum().reset_index() + total_quote = wallet["total_quote"] + low_idx = total_quote.idxmin() + high_idx = total_quote.idxmax() + start_balance = wallet.iloc[0]["total_quote"] + end_balance = wallet.iloc[-1]["total_quote"] + high_balance = total_quote.loc[high_idx] + low_balance = total_quote.loc[low_idx] + low_date = wallet.loc[low_idx, "date"] + high_date = wallet.loc[high_idx, "date"] + sharpe = calculate_sharpe_from_balance(wallet) + sortino = calculate_sortino_from_balance(wallet) + calmar = calculate_calmar_from_balance(wallet) + try: + drawdown = calculate_max_drawdown_from_balance(wallet) + # max_relative_drawdown = Underwater + drawdown_duration = drawdown.low_date - drawdown.high_date + + except ValueError: + drawdown = None + drawdown_duration = timedelta() + try: + underwater = calculate_max_drawdown_from_balance(wallet, relative=True) + except ValueError: + underwater = None + return { + "start_balance": start_balance, + "end_balance": end_balance, + "high_balance": high_balance, + "low_balance": low_balance, + "sharpe": sharpe, + "sortino": sortino, + "calmar": calmar, + "low_date": low_date.strftime(DATETIME_PRINT_FORMAT), + "low_ts": int(low_date.timestamp() * 1000), + "high_date": high_date.strftime(DATETIME_PRINT_FORMAT), + "high_ts": int(high_date.timestamp() * 1000), + # Drawdown metrics + "max_drawdown_account": drawdown.relative_account_drawdown if drawdown else 0.0, + "max_relative_drawdown": underwater.relative_account_drawdown if underwater else 0.0, + "max_drawdown_abs": drawdown.drawdown_abs if drawdown else 0.0, + "drawdown_start": ( + drawdown.high_date.strftime(DATETIME_PRINT_FORMAT) + if drawdown and drawdown.high_date is not None + else None + ), + "drawdown_start_ts": ( + int(drawdown.high_date.timestamp() * 1000) + if drawdown and drawdown.high_date is not None + else None + ), + "drawdown_end": ( + drawdown.low_date.strftime(DATETIME_PRINT_FORMAT) + if drawdown and drawdown.low_date is not None + else None + ), + "drawdown_end_ts": ( + int(drawdown.low_date.timestamp() * 1000) + if drawdown and drawdown.low_date is not None + else None + ), + "drawdown_duration": drawdown_duration, + "drawdown_duration_s": drawdown_duration.total_seconds(), + "max_drawdown_low": drawdown.low_value if drawdown else 0.0, + "max_drawdown_high": drawdown.high_value if drawdown else 0.0, + } + + def generate_trade_signal_candles( preprocessed_df: dict[str, DataFrame], bt_results: BacktestContentType, date_col: str ) -> dict[str, DataFrame]: @@ -155,7 +247,7 @@ def generate_pair_metrics( # skip_nan: bool = False, ) -> list[dict]: """ - Generates and returns a list for the given backtest data and the results dataframe + Generates and returns a list for the given backtest data and the results dataframe :param pairlist: Pairlist used :param stake_currency: stake-currency - used to correctly name headers :param starting_balance: Starting balance @@ -248,7 +340,7 @@ def generate_strategy_comparison(bt_stats: dict) -> list[dict]: def _get_resample_from_period(period: str) -> str: if period == "day": - return "1d" + return "1D" if period == "week": # Weekly defaulting to Monday. return "1W-MON" @@ -438,8 +530,8 @@ def generate_daily_stats(results: DataFrame) -> dict[str, Any]: "losing_days": 0, "daily_profit_list": [], } - daily_profit_rel = results.resample("1d", on="close_date")["profit_ratio"].sum() - daily_profit = results.resample("1d", on="close_date")["profit_abs"].sum().round(10) + daily_profit_rel = results.resample("1D", on="close_date")["profit_ratio"].sum() + daily_profit = results.resample("1D", on="close_date")["profit_abs"].sum().round(10) worst_rel = min(daily_profit_rel) best_rel = max(daily_profit_rel) worst = min(daily_profit) @@ -592,6 +684,7 @@ def generate_strategy_stats( "sharpe": calculate_sharpe(results, min_date, max_date, start_balance), "calmar": calculate_calmar(results, min_date, max_date, start_balance), "sqn": calculate_sqn(results, start_balance), + "wallet_stats": generate_wallet_stats(content.get("wallet_summary"), stake_currency), "profit_factor": profit_factor, "backtest_start": min_date.strftime(DATETIME_PRINT_FORMAT), "backtest_start_ts": int(min_date.timestamp() * 1000), diff --git a/freqtrade/optimize/space/decimalspace.py b/freqtrade/optimize/space/decimalspace.py index dc6dba04d..d1036bb93 100644 --- a/freqtrade/optimize/space/decimalspace.py +++ b/freqtrade/optimize/space/decimalspace.py @@ -9,7 +9,7 @@ class SKDecimal(FloatDistribution): *, step: float | None = None, decimals: int | None = None, - name=None, + name: str | None = None, ): """ FloatDistribution with a fixed step size. @@ -26,7 +26,7 @@ class SKDecimal(FloatDistribution): raise ValueError("You must set one of decimals or step") # Convert decimals to step self.step = step or (1 / 10**decimals if decimals else 1) - self.name = name + self.name = name or "" super().__init__( low=round(low, decimals) if decimals else low, diff --git a/freqtrade/persistence/__init__.py b/freqtrade/persistence/__init__.py index 3612544ee..4966c0b83 100644 --- a/freqtrade/persistence/__init__.py +++ b/freqtrade/persistence/__init__.py @@ -10,3 +10,4 @@ from freqtrade.persistence.usedb_context import ( disable_database_use, enable_database_use, ) +from freqtrade.persistence.wallet_history import WalletHistory diff --git a/freqtrade/persistence/db_migration.py b/freqtrade/persistence/db_migration.py new file mode 100644 index 000000000..9c535894c --- /dev/null +++ b/freqtrade/persistence/db_migration.py @@ -0,0 +1,81 @@ +import logging + +from sqlalchemy import func, select +from sqlalchemy.orm import make_transient + +from freqtrade.persistence.base import SessionType +from freqtrade.persistence.custom_data import _CustomData +from freqtrade.persistence.key_value_store import _KeyValueStoreModel +from freqtrade.persistence.migrations import set_sequence_ids +from freqtrade.persistence.pairlock import PairLock +from freqtrade.persistence.trade_model import Order, Trade +from freqtrade.persistence.wallet_history import WalletHistory + + +logger = logging.getLogger(__name__) + + +def migrate_db(session_target: SessionType): + + trade_count = 0 + pairlock_count = 0 + kv_count = 0 + custom_data_count = 0 + wallet_history_count = 0 + for trade in Trade.get_trades(): + trade_count += 1 + make_transient(trade) + for o in trade.orders: + make_transient(o) + + session_target.add(trade) + + session_target.commit() + + for pairlock in PairLock.get_all_locks(): + pairlock_count += 1 + make_transient(pairlock) + session_target.add(pairlock) + session_target.commit() + + for kv in _KeyValueStoreModel.session.scalars(select(_KeyValueStoreModel)): + kv_count += 1 + make_transient(kv) + session_target.add(kv) + session_target.commit() + + for cd in _CustomData.session.scalars(select(_CustomData)): + custom_data_count += 1 + make_transient(cd) + session_target.add(cd) + session_target.commit() + + for wh in WalletHistory.session.scalars(select(WalletHistory)): + wallet_history_count += 1 + make_transient(wh) + session_target.add(wh) + session_target.commit() + + # Update sequences + max_trade_id = session_target.scalar(select(func.max(Trade.id))) + max_order_id = session_target.scalar(select(func.max(Order.id))) + max_pairlock_id = session_target.scalar(select(func.max(PairLock.id))) + max_kv_id = session_target.scalar(select(func.max(_KeyValueStoreModel.id))) + max_custom_data_id = session_target.scalar(select(func.max(_CustomData.id))) + max_wallet_history_id = session_target.scalar(select(func.max(WalletHistory.id))) + + set_sequence_ids( + session_target.get_bind(), + trade_id=(max_trade_id or 0) + 1, + order_id=(max_order_id or 0) + 1, + pairlock_id=(max_pairlock_id or 0) + 1, + kv_id=(max_kv_id or 0) + 1, + custom_data_id=(max_custom_data_id or 0) + 1, + wallet_history_id=(max_wallet_history_id or 0) + 1, + ) + + logger.info( + f"Migrated {trade_count} Trades, {pairlock_count} Pairlocks, " + f"{kv_count} Key-Value pairs, {custom_data_count} Custom Data entries, " + f"and {wallet_history_count} Wallet History entries." + ) diff --git a/freqtrade/persistence/key_value_store.py b/freqtrade/persistence/key_value_store.py index 310e82b4b..9010ad0e2 100644 --- a/freqtrade/persistence/key_value_store.py +++ b/freqtrade/persistence/key_value_store.py @@ -18,10 +18,13 @@ class ValueTypesEnum(StrEnum): INT = "int" +# must be < 50 characters to fit the database column KeyStoreKeys = Literal[ "bot_start_time", "startup_time", "binance_migration", + "wallet_history_migration", + "wallet_history_migration_date", ] @@ -35,7 +38,7 @@ class _KeyValueStoreModel(ModelBase): id: Mapped[int] = mapped_column(primary_key=True) - key: Mapped[KeyStoreKeys] = mapped_column(String(25), nullable=False, index=True) + key: Mapped[KeyStoreKeys] = mapped_column(String(50), nullable=False, index=True) value_type: Mapped[ValueTypesEnum] = mapped_column(String(20), nullable=False) diff --git a/freqtrade/persistence/migrations.py b/freqtrade/persistence/migrations.py index 27970930f..b7c199021 100644 --- a/freqtrade/persistence/migrations.py +++ b/freqtrade/persistence/migrations.py @@ -35,10 +35,12 @@ def get_last_sequence_ids(engine, sequence_name: str, table_back_name: str) -> i if engine.name == "postgresql": with engine.begin() as connection: - last_id = connection.execute(text(f"select nextval('{sequence_name}')")).fetchone()[0] + last_id = connection.execute( + text(f"""select nextval('"{sequence_name}"')""") + ).fetchone()[0] with engine.begin() as connection: connection.execute( - text(f"ALTER SEQUENCE {sequence_name} rename to {table_back_name}_id_seq_bak") + text(f'ALTER SEQUENCE "{sequence_name}" rename to "{table_back_name}_id_seq_bak"') ) return last_id @@ -51,6 +53,7 @@ def set_sequence_ids( pairlock_id: int | None = None, kv_id: int | None = None, custom_data_id: int | None = None, + wallet_history_id: int | None = None, ): """ Set sequence ids to the given values. @@ -62,6 +65,7 @@ def set_sequence_ids( :param pairlock_id: value to set for pairlocks_id_seq (optional) :param kv_id: value to set for KeyValueStore_id_seq (optional) :param custom_data_id: value to set for trade_custom_data_id_seq (optional) + :param wallet_history_id: value to set for wallet_history_id_seq (optional) """ if engine.name == "postgresql": with engine.begin() as connection: @@ -81,6 +85,10 @@ def set_sequence_ids( connection.execute( text(f"ALTER SEQUENCE trade_custom_data_id_seq RESTART WITH {custom_data_id}") ) + if wallet_history_id: + connection.execute( + text(f"ALTER SEQUENCE wallet_history_id_seq RESTART WITH {wallet_history_id}") + ) def drop_index_on_table(engine, inspector, table_bak_name): @@ -88,9 +96,9 @@ def drop_index_on_table(engine, inspector, table_bak_name): # drop indexes on backup table in new session for index in inspector.get_indexes(table_bak_name): if engine.name == "mysql": - connection.execute(text(f"drop index {index['name']} on {table_bak_name}")) + connection.execute(text(f'drop index "{index["name"]}" on {table_bak_name}')) else: - connection.execute(text(f"drop index {index['name']}")) + connection.execute(text(f'drop index "{index["name"]}"')) def migrate_trades_and_orders_table( @@ -315,6 +323,31 @@ def migrate_pairlocks_table(decl_base, inspector, engine, pairlock_back_name: st set_sequence_ids(engine, pairlock_id=pairlock_id) +def migrate_kv_store_table(decl_base, inspector, engine, kv_store_back_name: str, cols: list): + # Schema migration necessary + with engine.begin() as connection: + connection.execute(text(f'alter table "KeyValueStore" rename to "{kv_store_back_name}"')) + + drop_index_on_table(engine, inspector, kv_store_back_name) + kv_store_id = get_last_sequence_ids(engine, "KeyValueStore_id_seq", kv_store_back_name) + + # let SQLAlchemy create the schema as required + decl_base.metadata.create_all(engine) + # Copy data back - following the correct schema + with engine.begin() as connection: + connection.execute( + text( + f"""insert into "KeyValueStore" + (id, key, value_type, string_value, datetime_value, float_value, int_value) + select id, key, value_type, string_value, datetime_value, float_value, int_value + from "{kv_store_back_name}" + """ + ) + ) + + set_sequence_ids(engine, kv_id=kv_store_id) + + def set_sqlite_to_wal(engine): if engine.name == "sqlite" and str(engine.url) != "sqlite://": # Set Mode to @@ -385,12 +418,15 @@ def check_migrate(engine: Engine, decl_base, previous_tables: list[str]) -> None cols_trades = inspector.get_columns("trades") cols_orders = inspector.get_columns("orders") cols_pairlocks = inspector.get_columns("pairlocks") + cols_kv_store = inspector.get_columns("KeyValueStore") tabs = get_table_names_for_table(inspector, "trades") table_back_name = get_backup_name(tabs, "trades_bak") order_tabs = get_table_names_for_table(inspector, "orders") order_table_bak_name = get_backup_name(order_tabs, "orders_bak") pairlock_tabs = get_table_names_for_table(inspector, "pairlocks") pairlock_table_bak_name = get_backup_name(pairlock_tabs, "pairlocks_bak") + kv_store_tabs = get_table_names_for_table(inspector, "KeyValueStore") + kv_store_back_name = get_backup_name(kv_store_tabs, "KeyValueStore_bak") # Check if migration necessary # Migrates both trades and orders table! @@ -421,6 +457,16 @@ def check_migrate(engine: Engine, decl_base, previous_tables: list[str]) -> None migrate_pairlocks_table( decl_base, inspector, engine, pairlock_table_bak_name, cols_pairlocks ) + if "KeyValueStore" in previous_tables: + key_column = next(filter(lambda x: x["name"] == "key", cols_kv_store), None) + # length of key column < 50, recreate table with correct length and migrate data + if key_column and getattr(key_column["type"], "length", -1) < 50: + migrating = True + logger.info( + f"Running database migration for KeyValueStore - backup: {kv_store_back_name}" + ) + migrate_kv_store_table(decl_base, inspector, engine, kv_store_back_name, cols_kv_store) + if "orders" not in previous_tables and "trades" in previous_tables: raise OperationalException( "Your database seems to be very old. " diff --git a/freqtrade/persistence/models.py b/freqtrade/persistence/models.py index 4d4808eeb..05905abfe 100644 --- a/freqtrade/persistence/models.py +++ b/freqtrade/persistence/models.py @@ -20,6 +20,7 @@ from freqtrade.persistence.key_value_store import _KeyValueStoreModel from freqtrade.persistence.migrations import check_migrate from freqtrade.persistence.pairlock import PairLock from freqtrade.persistence.trade_model import Order, Trade +from freqtrade.persistence.wallet_history import WalletHistory logger = logging.getLogger(__name__) @@ -91,6 +92,7 @@ def init_db(db_url: str) -> None: _CustomData.session = scoped_session( sessionmaker(bind=engine, autoflush=True), scopefunc=get_request_or_thread_id ) + WalletHistory.session = Trade.session previous_tables = inspect(engine).get_table_names() ModelBase.metadata.create_all(engine) diff --git a/freqtrade/persistence/pairlock.py b/freqtrade/persistence/pairlock.py index eedc7286f..21439e5f7 100644 --- a/freqtrade/persistence/pairlock.py +++ b/freqtrade/persistence/pairlock.py @@ -29,6 +29,11 @@ class PairLock(ModelBase): active: Mapped[bool] = mapped_column(nullable=False, default=True, index=True) + @property + def lock_end_time_utc(self) -> datetime: + """Lock end time with UTC timezoneinfo""" + return self.lock_end_time.replace(tzinfo=UTC) + def __repr__(self) -> str: lock_time = self.lock_time.strftime(DATETIME_PRINT_FORMAT) lock_end_time = self.lock_end_time.strftime(DATETIME_PRINT_FORMAT) diff --git a/freqtrade/persistence/pairlock_middleware.py b/freqtrade/persistence/pairlock_middleware.py index 94544928d..ccf302a22 100644 --- a/freqtrade/persistence/pairlock_middleware.py +++ b/freqtrade/persistence/pairlock_middleware.py @@ -42,6 +42,7 @@ class PairLocks: ) -> PairLock: """ Create PairLock from now to "until". + Doesn't create a new lock if there is already a lock with the same Reason, side and endtime. Uses database by default, unless PairLocks.use_db is set to False, in which case a list is maintained. :param pair: pair to lock. use '*' to lock all pairs @@ -50,10 +51,19 @@ class PairLocks: :param now: Current timestamp. Used to determine lock start time. :param side: Side to lock pair, can be 'long', 'short' or '*' """ + lock_end_time = timeframe_to_next_date(PairLocks.timeframe, until) + existing_locks = PairLocks.get_pair_locks(pair, now, side=side) + for lock in existing_locks: + if ( + lock.reason == reason + and lock.lock_end_time_utc == lock_end_time + and lock.side == side + ): + return lock lock = PairLock( pair=pair, lock_time=now or datetime.now(UTC), - lock_end_time=timeframe_to_next_date(PairLocks.timeframe, until), + lock_end_time=lock_end_time, reason=reason, side=side, active=True, diff --git a/freqtrade/persistence/trade_model.py b/freqtrade/persistence/trade_model.py index e6107eac6..7e8dd2e3c 100644 --- a/freqtrade/persistence/trade_model.py +++ b/freqtrade/persistence/trade_model.py @@ -858,9 +858,9 @@ class LocalTrade: higher_stop = stop_loss_norm > self.stop_loss lower_stop = stop_loss_norm < self.stop_loss - # stop losses only walk up, never down!, - # ? But adding more to a leveraged trade would create a lower liquidation price, - # ? decreasing the minimum stoploss + # stop losses only walk up, never down! + # but adding more to a leveraged trade would create a lower liquidation price, + # decreasing the minimum stoploss if ( allow_refresh or (higher_stop and not self.is_short) diff --git a/freqtrade/persistence/wallet_history.py b/freqtrade/persistence/wallet_history.py new file mode 100644 index 000000000..08e180937 --- /dev/null +++ b/freqtrade/persistence/wallet_history.py @@ -0,0 +1,50 @@ +from datetime import datetime +from typing import ClassVar + +from sqlalchemy import DateTime, Float, Integer, String, UniqueConstraint +from sqlalchemy.orm import Mapped, mapped_column + +from freqtrade.persistence.base import ModelBase, SessionType + + +class WalletHistory(ModelBase): + """ + Daily wallet state tracking with minimal fields + """ + + __tablename__ = "wallet_history" + session: ClassVar[SessionType] + + id: Mapped[int] = mapped_column(Integer, primary_key=True) + timestamp: Mapped[datetime] = mapped_column(DateTime, nullable=False, index=True) + currency: Mapped[str] = mapped_column(String(25), nullable=False) + # Rate: price of 1 unit of `currency` quoted in `quote_currency`. + # e.g., USDT/ETH -> USDT per ETH + rate: Mapped[float] = mapped_column(Float, nullable=True) + # Quote currency for rate/total fields (e.g., 'USDT') + quote_currency: Mapped[str] = mapped_column(String(25), nullable=False) + + # Balance in `currency` units + balance: Mapped[float] = mapped_column(Float, nullable=False) + + # Canonical total wallet equity/value denominated in `quote_currency` (if available) + # For futures positions, collateral + PnL is used to compute this value. + total_quote: Mapped[float] = mapped_column(Float, nullable=True) + # Total position value in `quote_currency` - including leverage + total_position_value: Mapped[float] = mapped_column(Float, nullable=True) + collateral: Mapped[float] = mapped_column(Float, nullable=True) + leverage: Mapped[float] = mapped_column(Float, nullable=False, default=1.0) + + bot_managed: Mapped[bool] = mapped_column(nullable=False, default=True) + + __table_args__ = ( + # Ensure one record per currency per day + UniqueConstraint("timestamp", "currency", name="unique_wallet_daily"), + ) + + def __repr__(self) -> str: + return ( + f"WalletHistory(timestamp={self.timestamp}, currency={self.currency}, " + f"rate={self.rate}, total_quote={self.total_quote}, " + f"balance={self.balance}, leverage={self.leverage})" + ) diff --git a/freqtrade/plot/plotting.py b/freqtrade/plot/plotting.py index 6c6f32ea1..ed15c8e79 100644 --- a/freqtrade/plot/plotting.py +++ b/freqtrade/plot/plotting.py @@ -263,7 +263,7 @@ def plot_trades(fig, trades: pd.DataFrame) -> make_subplots: trades["desc"] = trades.apply( lambda row: ( f"{row['profit_ratio']:.2%}, " - + (f"{row['enter_tag']}, " if row["enter_tag"] is not None else "") + + (f"{row['enter_tag']}, " if pd.notna(row["enter_tag"]) else "") + f"{row['exit_reason']}, " + f"{row['trade_duration']} min" ), @@ -356,7 +356,7 @@ def plot_area( :param indicator_b: indicator name as populated in strategy :param label: label for the filled area :param fill_color: color to be used for the filled area - :return: fig with added filled_traces plot + :return: fig with added filled_traces plot """ if indicator_a in data and indicator_b in data: # make lines invisible to get the area plotted, only. @@ -383,7 +383,7 @@ def add_areas(fig, row: int, data: pd.DataFrame, indicators) -> make_subplots: :param data: candlestick DataFrame :param indicators: dict with indicators. ie.: plot_config['main_plot'] or plot_config['subplots'][subplot_label] - :return: fig with added filled_traces plot + :return: fig with added filled_traces plot """ for indicator, ind_conf in indicators.items(): if "fill_to" in ind_conf: diff --git a/freqtrade/plugins/pairlist/DelistFilter.py b/freqtrade/plugins/pairlist/DelistFilter.py index 076751aa6..8a2491c7d 100644 --- a/freqtrade/plugins/pairlist/DelistFilter.py +++ b/freqtrade/plugins/pairlist/DelistFilter.py @@ -23,9 +23,10 @@ class DelistFilter(IPairList): self._max_days_from_now = self._pairlistconfig.get("max_days_from_now", 0) if self._max_days_from_now < 0: raise ConfigurationError("DelistFilter requires max_days_from_now to be >= 0") - if not self._exchange._ft_has["has_delisting"]: + if not self._exchange.get_option("has_delisting"): raise ConfigurationError( - "DelistFilter doesn't support this exchange and trading mode combination.", + f"DelistFilter doesn't support {self._exchange.name} in " + f"{self._exchange.trading_mode} mode." ) def short_desc(self) -> str: diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index 428fea1c9..afbd44101 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -16,10 +16,11 @@ from freqtrade.data.btanalysis import ( get_backtest_market_change, get_backtest_result, get_backtest_resultlist, + get_backtest_wallet_change, load_and_merge_backtest_result, update_backtest_metadata, ) -from freqtrade.enums import BacktestState +from freqtrade.enums import BacktestState, RunMode from freqtrade.exceptions import ConfigurationError, DependencyException, OperationalException from freqtrade.ft_types import get_BacktestResultType_default from freqtrade.misc import deep_merge_dicts, is_file_in_dir @@ -29,6 +30,7 @@ from freqtrade.rpc.api_server.api_schemas import ( BacktestMetadataUpdate, BacktestRequest, BacktestResponse, + WalletHistoryResponse, ) from freqtrade.rpc.api_server.deps import get_config, verify_strategy from freqtrade.rpc.api_server.webserver_bgwork import ApiBG @@ -106,6 +108,11 @@ def __run_backtest_bg(btconfig: Config): ApiBG.bt["bt"].results, datetime.now().strftime("%Y-%m-%d_%H-%M-%S"), market_change_data=combined_res, + wallet_summary={ + s: x["wallet_summary"] + for s, x in ApiBG.bt["bt"].all_bt_content.items() + if "wallet_summary" in x + }, strategy_files={ s.get_strategy_name(): s.__file__ for s in ApiBG.bt["bt"].strategylist }, @@ -137,6 +144,7 @@ async def api_start_backtest( verify_strategy(bt_settings.strategy) btconfig = deepcopy(config) + btconfig["runmode"] = RunMode.BACKTEST remove_exchange_credentials(btconfig["exchange"], True) settings = dict(bt_settings) if settings.get("freqai", None) is not None: @@ -354,3 +362,29 @@ def api_get_backtest_market_change(file: str, config=Depends(get_config)): "data": df.values.tolist(), "length": len(df), } + + +@router.get( + "/backtest/history/{file}/{strategy}/wallet", + response_model=WalletHistoryResponse, + tags=["webserver", "backtest"], +) +def api_get_backtest_wallet(file: str, strategy: str, config=Depends(get_config)): + bt_results_base: Path = config["user_data_dir"] / "backtest_results" + file_abs = (bt_results_base / file).with_suffix(".zip") + # Ensure file is in backtest_results directory + if not is_file_in_dir(file_abs, bt_results_base): + raise HTTPException(status_code=400, detail="Unable to retrieve wallet history.") + + results = get_backtest_wallet_change(file_abs, strategy) + if results is None: + raise HTTPException(status_code=404, detail="Unable to retrieve wallet history.") + # Consolidate the wallet to the base currency + results.loc[:, "total_quote"] = results["rate"] * results["balance"] + results = results.groupby(["date", "__date_ts"]).agg({"total_quote": "sum"}).reset_index() + + return { + "columns": results.columns.tolist(), + "data": results.values.tolist(), + "length": len(results), + } diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 7952d5724..5f4af98d1 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -255,6 +255,7 @@ class ShowConfig(BaseModel): timeframe_ms: int timeframe_min: int exchange: str + demo_trading: bool strategy: str | None = None force_entry_enable: bool exit_pricing: dict[str, Any] @@ -679,6 +680,15 @@ class BacktestMarketChange(BaseModel): data: list[list[Any]] +class WalletHistoryResponse(BaseModel): + columns: list[str] + length: int + data: list[list[Any]] + # start date of the effectively captured data + # Before this date, it's based on a reconstructed wallet history + capture_start_ts: int | None = None + + class MarketRequest(ExchangeModePayloadMixin, BaseModel): base: str | None = None quote: str | None = None diff --git a/freqtrade/rpc/api_server/api_trading.py b/freqtrade/rpc/api_server/api_trading.py index 3ec7a08b3..0bae0eefb 100644 --- a/freqtrade/rpc/api_server/api_trading.py +++ b/freqtrade/rpc/api_server/api_trading.py @@ -31,6 +31,7 @@ from freqtrade.rpc.api_server.api_schemas import ( ResultMsg, Stats, StatusMsg, + WalletHistoryResponse, WhitelistResponse, ) from freqtrade.rpc.api_server.deps import get_config, get_rpc @@ -104,6 +105,22 @@ def stats(rpc: RPC = Depends(get_rpc)): return rpc._rpc_stats() +@router.get( + "/historic_balance", + response_model=WalletHistoryResponse, + tags=["info"], +) +def api_get_wallet_history(rpc: RPC = Depends(get_rpc)): + results, capture_date_ts = rpc._rpc_get_historic_balance() + + return { + "columns": results.columns.tolist(), + "data": results.values.tolist(), + "length": len(results), + "capture_start_ts": capture_date_ts, + } + + @router.get("/daily", response_model=DailyWeeklyMonthly, tags=["Trading-info"]) def daily( timescale: int = Query(7, ge=1, description="Number of days to fetch data for"), diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index d25bda78e..64283898f 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -69,7 +69,8 @@ logger = logging.getLogger(__name__) # 2.45: Add price to forceexit endpoint # 2.46: Add prepend_data to download-data endpoint # 2.47: Add Strategy parameters -API_VERSION = 2.47 +# 2.48: add /backtest/history/wallets endpoint +API_VERSION = 2.48 # Public API, requires no auth. router_public = APIRouter() diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 23a78fad8..0b47ecf7c 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -11,8 +11,8 @@ from typing import TYPE_CHECKING, Any import psutil from dateutil.relativedelta import relativedelta from dateutil.tz import tzlocal -from numpy import inf, int64, isnan, mean, nan -from pandas import DataFrame, NaT +from numpy import inf, isnan, mean, nan +from pandas import DataFrame, NaT, read_sql from sqlalchemy import func, select from freqtrade import __version__ @@ -176,6 +176,7 @@ class RPC: timeframe_to_minutes(config["timeframe"]) if "timeframe" in config else 0 ), "exchange": config["exchange"]["name"], + "demo_trading": config["exchange"].get("demo_trading", False), "strategy": config["strategy"], "force_entry_enable": config.get("force_entry_enable", False), "exit_pricing": config.get("exit_pricing", {}), @@ -785,6 +786,26 @@ class RPC: "bot_start_date": format_date(bot_start), } + def _rpc_get_historic_balance(self) -> tuple[DataFrame, int]: + """ + Returns the historic balance of the bot + :return: DataFrame with the balance history and the timestamp of the migration + """ + results = read_sql("wallet_history", con=Trade.session.bind, parse_dates=["timestamp"]) + + results = results.rename({"timestamp": "date"}, axis=1) + results.loc[:, "__date_ts"] = results.loc[:, "date"].dt.as_unit("ms").astype("int64") + # Exclude non-bot managed for now + results_filtered = results.loc[results["bot_managed"]] + + results_final = ( + results_filtered.groupby(["date", "__date_ts"]) + .agg({"total_quote": "sum"}) + .reset_index() + ) + hist = KeyValueStore.get_datetime_value("wallet_history_migration_date") + return results_final, dt_ts_def(hist, 0) + def __balance_get_est_stake( self, coin: str, stake_currency: str, amount: float, balance: Wallet ) -> tuple[float, float]: @@ -875,7 +896,7 @@ class RPC: for symbol, pos in self._freqtrade.wallets.get_all_positions().items(): est_stake = pos.collateral pos_base = self._freqtrade.exchange.get_pair_base_currency(symbol) - if pos.leverage: + if pos.leverage and pos.position: try: rate = self._freqtrade.exchange.get_conversion_rate(pos_base, stake_currency) if rate: @@ -1386,7 +1407,7 @@ class RPC: } def _rpc_locks(self) -> dict[str, Any]: - """Returns the current locks""" + """Returns the current locks""" locks = PairLocks.get_pair_locks(None) return {"lock_count": len(locks), "locks": [lock.to_json() for lock in locks]} @@ -1515,7 +1536,9 @@ class RPC: df_cols = [col for col in dataframe_columns if col in cols_set] dataframe = dataframe.loc[:, df_cols] - dataframe.loc[:, "__date_ts"] = dataframe.loc[:, "date"].astype(int64) // 1000 // 1000 + dataframe.loc[:, "__date_ts"] = ( + dataframe.loc[:, "date"].dt.as_unit("ms").astype("int64") + ) # Move signal close to separate column when signal for easy plotting for sig_type in signals.keys(): if sig_type in dataframe.columns: @@ -1525,7 +1548,7 @@ class RPC: # band-aid until this is fixed: # https://github.com/pandas-dev/pandas/issues/45836 - datetime_types = ["datetime", "datetime64", "datetime64[ns, UTC]"] + datetime_types = ["datetime", "datetime64", "datetimetz"] date_columns = dataframe.select_dtypes(include=datetime_types) for date_column in date_columns: # replace NaT with `None` diff --git a/freqtrade/rpc/rpc_manager.py b/freqtrade/rpc/rpc_manager.py index 0a4f48a35..3c9ee89ec 100644 --- a/freqtrade/rpc/rpc_manager.py +++ b/freqtrade/rpc/rpc_manager.py @@ -59,7 +59,7 @@ class RPCManager: logger.info("Cleaning up rpc modules ...") while self.registered_modules: mod = self.registered_modules.pop() - logger.info("Cleaning up rpc.%s ...", mod.name) + logger.info(f"Cleaning up rpc.{mod.name} ...") mod.cleanup() del mod @@ -73,7 +73,7 @@ class RPCManager: } """ if msg.get("type") not in NO_ECHO_MESSAGES: - logger.info("Sending rpc message: %s", msg) + logger.info(f"Sending rpc message: {msg}") for mod in self.registered_modules: logger.debug("Forwarding message to rpc.%s", mod.name) try: @@ -81,7 +81,7 @@ class RPCManager: except NotImplementedError: logger.error(f"Message type '{msg['type']}' not implemented by handler {mod.name}.") except Exception: - logger.exception("Exception occurred within RPC module %s", mod.name) + logger.exception(f"Exception occurred within RPC module {mod.name}") def process_msg_queue(self, queue: deque) -> None: """ @@ -89,7 +89,7 @@ class RPCManager: """ while queue: msg = queue.popleft() - logger.info("Sending rpc strategy_msg: %s", msg) + logger.info(f"Sending rpc strategy_msg: {msg}") for mod in self.registered_modules: if mod._config.get(mod.name, {}).get("allow_custom_messages", False): mod.send_msg( @@ -114,6 +114,8 @@ class RPCManager: trailing_stop = config["trailing_stop"] timeframe = config["timeframe"] exchange_name = config["exchange"]["name"] + if config["exchange"].get("demo_trading"): + exchange_name += " (demo trading)" strategy_name = config.get("strategy", "") pos_adjust_enabled = "On" if config["position_adjustment_enable"] else "Off" self.send_msg( diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 03486e3d3..4d3b5372c 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -483,7 +483,7 @@ class Telegram(RPCHandler): profit_prefix = "Sub " cp_extra = ( f"*Final Profit:* `{format_pct(msg['final_profit_ratio'])} " - f"({msg['cumulative_profit']:.8f} {msg['quote_currency']}{cp_fiat})`\n" + f"({fmt_coin(msg['cumulative_profit'], msg['stake_currency'])}{cp_fiat})`\n" ) else: exit_wording = f"Partially {exit_wording.lower()}" @@ -832,7 +832,7 @@ class Telegram(RPCHandler): ): # Adding initial stoploss only if it is different from stoploss lines.append( - f"*Initial Stoploss:* `{r['initial_stop_loss_abs']:.8f}` " + f"*Initial Stoploss:* `{round_value(r['initial_stop_loss_abs'], 8)}` " f"`({format_pct(r['initial_stop_loss_ratio'])})`" ) @@ -2049,7 +2049,7 @@ class Telegram(RPCHandler): await self._send_msg( f"*Mode:* `{'Dry-run' if val['dry_run'] else 'Live'}`\n" - f"*Exchange:* `{val['exchange']}`\n" + f"*Exchange:* `{val['exchange']}{' (Demo)' if val['demo_trading'] else ''}`\n" f"*Market: * `{val['trading_mode']}`\n" f"*Stake per trade:* `{val['stake_amount']} {val['stake_currency']}`\n" f"*Max open Trades:* `{val['max_open_trades']}`\n" @@ -2243,7 +2243,7 @@ class Telegram(RPCHandler): else: raise RPCException( "Invalid usage of command /marketdir. \n" - "Usage: */marketdir [short | long | even | none]*" + "Usage: */marketdir [short | long | even | none]*" ) async def _tg_info(self, update: Update, context: CallbackContext) -> None: diff --git a/freqtrade/strategy/interface.py b/freqtrade/strategy/interface.py index 6e32039c9..13f0ed4b4 100644 --- a/freqtrade/strategy/interface.py +++ b/freqtrade/strategy/interface.py @@ -222,7 +222,8 @@ class IStrategy(ABC, HyperStrategyMixin): """ Clean up FreqAI and child threads """ - self.freqai.shutdown() + if getattr(self, "freqai", None): + self.freqai.shutdown() @abstractmethod def populate_indicators(self, dataframe: DataFrame, metadata: dict) -> DataFrame: diff --git a/freqtrade/util/__init__.py b/freqtrade/util/__init__.py index 06deb3ce0..0e9c1ac12 100644 --- a/freqtrade/util/__init__.py +++ b/freqtrade/util/__init__.py @@ -3,6 +3,7 @@ from freqtrade.util.datetime_helpers import ( dt_from_ts, dt_humanize_delta, dt_now, + dt_now_no_micro, dt_ts, dt_ts_def, dt_ts_none, @@ -39,6 +40,7 @@ __all__ = [ "dt_from_ts", "dt_humanize_delta", "dt_now", + "dt_now_no_micro", "dt_ts", "dt_ts_def", "dt_ts_none", diff --git a/freqtrade/util/datetime_helpers.py b/freqtrade/util/datetime_helpers.py index b6535db5d..55bf29419 100644 --- a/freqtrade/util/datetime_helpers.py +++ b/freqtrade/util/datetime_helpers.py @@ -12,6 +12,13 @@ def dt_now() -> datetime: return datetime.now(UTC) +def dt_now_no_micro() -> datetime: + """Return the current datetime in UTC without microseconds. + Should not be used outside of tests. + """ + return dt_now().replace(microsecond=0) + + def dt_utc( year: int, month: int, diff --git a/freqtrade/util/migrations/__init__.py b/freqtrade/util/migrations/__init__.py index 20aafb04b..90f866075 100644 --- a/freqtrade/util/migrations/__init__.py +++ b/freqtrade/util/migrations/__init__.py @@ -1,8 +1,10 @@ +from freqtrade.constants import Config from freqtrade.exchange import Exchange from freqtrade.util.migrations.funding_rate_mig import migrate_funding_fee_timeframe +from freqtrade.util.migrations.migrate_wallet_history import migrate_wallet_history -def migrate_data(config, exchange: Exchange | None = None) -> None: +def migrate_data(config: Config, exchange: Exchange | None = None) -> None: """ Migrate persisted data from old formats to new formats """ @@ -10,10 +12,9 @@ def migrate_data(config, exchange: Exchange | None = None) -> None: migrate_funding_fee_timeframe(config, exchange) -def migrate_live_content(config, exchange: Exchange | None = None) -> None: +def migrate_live_content(config: Config, exchange: Exchange, starting_balance: float) -> None: """ Migrate database content from old formats to new formats Used for dry/live mode. """ - # Currently not used - pass + migrate_wallet_history(config, exchange, starting_balance) diff --git a/freqtrade/util/migrations/migrate_wallet_history.py b/freqtrade/util/migrations/migrate_wallet_history.py new file mode 100644 index 000000000..d7070ed5f --- /dev/null +++ b/freqtrade/util/migrations/migrate_wallet_history.py @@ -0,0 +1,214 @@ +import logging + +import numpy as np +import pandas as pd + +from freqtrade.constants import Config +from freqtrade.data.btanalysis.bt_fileutils import trade_list_to_dataframe +from freqtrade.data.btanalysis.trade_parallelism import balance_distribution_over_time +from freqtrade.exchange import Exchange +from freqtrade.exchange.exchange_utils_timeframe import timeframe_to_prev_date +from freqtrade.persistence import KeyValueStore, Trade, WalletHistory +from freqtrade.util import dt_now, dt_ts + + +logger = logging.getLogger(__name__) + + +def migrate_wallet_history(config: Config, exchange: Exchange, starting_balance: float): + if config.get("skip_wallet_history_migration") or not exchange.get_option( + "ohlcv_has_history", True + ): + # we can't fill up wallet history without ohlcv history + return + if KeyValueStore.get_int_value("wallet_history_migration"): + logger.debug("Wallet history migration already completed.") + return + logger.info("Starting wallet history migration...") + _migrate_wallet_history(config, exchange, starting_balance) + logger.info("Wallet history migration completed.") + KeyValueStore.store_value("wallet_history_migration", 1) + KeyValueStore.store_value("wallet_history_migration_date", dt_now()) + + +def _migrate_wallet_history(config: Config, exchange: Exchange, starting_balance: float): + # Prepare balance distribution data with OHLCV rates + balance_dist, pairlist_valid = _prepare_balance_distribution(config, exchange, starting_balance) + if not balance_dist.empty and pairlist_valid: + _create_wallet_history_entries( + config, exchange, balance_dist, pairlist_valid, config["stake_currency"] + ) + + +def _prepare_balance_distribution( + config: Config, exchange: Exchange, starting_balance: float +) -> tuple[pd.DataFrame, list[str]]: + trade_df = trade_list_to_dataframe(Trade.get_trades_proxy(), minified=False) + if trade_df.empty: + # no trades, nothing to do + return pd.DataFrame(), [] + pairlist = list(trade_df["pair"].unique()) + timeframe = "1d" + stake_currency = config["stake_currency"] + min_date = timeframe_to_prev_date(timeframe, KeyValueStore.get_datetime_value("bot_start_time")) + balance_dist = balance_distribution_over_time( + trade_df, + min_date=min_date, + max_date=dt_now(), + start_balance=starting_balance, + stake_currency=stake_currency, + timeframe=timeframe, + pairlist=pairlist, + ) + pairlist_valid = [p for p in pairlist if p in exchange.markets] + pairlist_invalid = set(pairlist) - set(pairlist_valid) + if pairlist_invalid: + logger.warning( + f"The following trading pairs from the trade history are not available on the exchange " + f"and will be skipped during wallet history migration: {', '.join(pairlist_invalid)}" + ) + + logger.info("Wallet History migration: Fetching OHLCV data ...") + data = exchange.refresh_latest_ohlcv( + [(p, timeframe, config["candle_type_def"]) for p in pairlist_valid], + since_ms=dt_ts(min_date), + cache=False, + drop_incomplete=False, + ) + logger.info( + "Wallet History migration: Done fetching OHLCV data for wallet history migration..." + ) + + dfs = [] + # Combine all dataframes into one using the open rate + for p, x in data.items(): + x = x.set_index("date", drop=True) + col = f"{p[0]}_open" + x[col] = x["open"] + dfs.append(x[[col]]) + + if not dfs: + logger.warning( + "No OHLCV data available for the trading pairs; skipping wallet history migration." + ) + return pd.DataFrame(), [] + merged = pd.concat(dfs, axis=1) + + balance_dist = balance_dist.join(merged, how="left") + df_value = pd.DataFrame( + index=balance_dist.index, columns=[f"{p}_value" for p in pairlist_valid], dtype=float + ) + for p in pairlist_valid: + # df_value[f"{p}_value"] = balance_dist[f"{p}_open"] * balance_dist[p] + # Identical calculation to rpc and wallets.py + df_value[f"{p}_value"] = np.where( + balance_dist[f"{p}_is_short"] == 0, + (balance_dist[f"{p}_open"] * balance_dist[p]) + - balance_dist[f"{p}_collateral"] * (balance_dist[f"{p}_leverage"] - 1), + ( + balance_dist[f"{p}_collateral"] * (1 + balance_dist[f"{p}_leverage"]) + - balance_dist[f"{p}_open"] * balance_dist[p] + ), + ) + balance_dist = pd.concat([balance_dist, df_value], axis=1) + + # Aggregate total value at each point in time + balance_dist["total_value"] = balance_dist[ + [f"{p}_value" for p in pairlist_valid] + [stake_currency] + ].sum(axis=1) + + return balance_dist, pairlist_valid + + +def _create_wallet_history_entries( + config: Config, + exchange: Exchange, + balance_dist: pd.DataFrame, + pairlist_valid: list[str], + stake_currency: str, +): + is_futures = config["trading_mode"] == "futures" + # Precompute column indices for faster tuple-based iteration + # Assume the first column is the index (date) + stake_idx = balance_dist.columns.get_loc(stake_currency) + pair_balance_idx = {pair: balance_dist.columns.get_loc(pair) + 1 for pair in pairlist_valid} + pair_leverage_idx = { + pair: balance_dist.columns.get_loc(f"{pair}_leverage") + 1 for pair in pairlist_valid + } + pair_collateral_idx = { + pair: balance_dist.columns.get_loc(f"{pair}_collateral") + 1 for pair in pairlist_valid + } + pair_is_short_idx = { + pair: balance_dist.columns.get_loc(f"{pair}_is_short") + 1 for pair in pairlist_valid + } + pair_rate_idx = { + pair: balance_dist.columns.get_loc(f"{pair}_open") + 1 for pair in pairlist_valid + } + # Convert balance_dist to WalletHistory entries + wallet_entries = [] + for row in balance_dist.itertuples(index=True, name=None): + date = row[0] + + # Add stake currency entry + stake_balance = row[stake_idx + 1] + if not pd.isna(stake_balance): + wallet_entries.append( + WalletHistory( + timestamp=date, + currency=stake_currency, + rate=1.0, # Stake currency price is always 1.0 + balance=stake_balance, + total_quote=stake_balance, + quote_currency=stake_currency, + leverage=1.0, + bot_managed=True, + ) + ) + + # Add entries for each trading pair + for pair in pairlist_valid: + base_currency = exchange.get_pair_base_currency(pair) + balance = row[pair_balance_idx[pair]] + leverage = row[pair_leverage_idx[pair]] + # Only add entry if balance is not empty/NaN + if not pd.isna(balance) and balance > 0: + rate_value = row[pair_rate_idx[pair]] + rate = rate_value if not pd.isna(rate_value) else None + + total_quote = balance * rate if rate else None + collateral: float | None = None + if is_futures: + collateral = row[pair_collateral_idx[pair]] + is_short = row[pair_is_short_idx[pair]] + if collateral is not None and not pd.isna(collateral) and rate is not None: + # Same formula than in rpc's _rpc_balance + total_quote = ( + (rate * balance - collateral * (leverage - 1)) + if is_short == 0 + else (collateral * (1 + leverage) - rate * balance) + ) + wallet_entries.append( + WalletHistory( + timestamp=date, + currency=base_currency, + quote_currency=stake_currency, + rate=rate, + balance=balance, + total_quote=total_quote, + leverage=leverage if not pd.isna(leverage) else 1.0, + bot_managed=True, + total_position_value=balance * rate if is_futures and rate else None, + # collateral=collateral, + ) + ) + + # Save entries to database + if wallet_entries: + try: + # Use bulk_save_objects for better performance + WalletHistory.session.bulk_save_objects(wallet_entries) + WalletHistory.session.commit() + logger.info(f"Successfully created {len(wallet_entries)} wallet balance records") + except Exception as e: + WalletHistory.session.rollback() + logger.error(f"Error saving wallet balance records: {e}") diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index 56c32adb6..e3ac1288b 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -10,8 +10,8 @@ from freqtrade.enums import RunMode, TradingMode from freqtrade.exceptions import DependencyException from freqtrade.exchange import Exchange from freqtrade.misc import safe_value_fallback -from freqtrade.persistence import LocalTrade, Trade -from freqtrade.util.datetime_helpers import dt_now +from freqtrade.persistence import LocalTrade, Trade, WalletHistory +from freqtrade.util import dt_floor_day, dt_now logger = logging.getLogger(__name__) @@ -445,3 +445,71 @@ class Wallets: logger.debug(msg) else: logger.info(msg) + + def record_wallet_state(self) -> None: + """Record daily wallet totals to database""" + if self._is_backtest: + # only record in live mode. + return + timestamp = dt_floor_day(dt_now()) + + # Record total balances for all currencies + wallet_records = [] + position_collaterals = 0.0 + open_assets: dict[str, Trade] = {t.safe_base_currency: t for t in Trade.get_open_trades()} + for pos in self.get_all_positions().values(): + base = self._exchange.get_pair_base_currency(pos.symbol) + rate = self._exchange.get_conversion_rate(base, self._stake_currency) + total_quote = None + leverage = pos.leverage or 1.0 + if rate: + # Same formula than in rpc's _rpc_balance + total_quote = ( + rate * pos.position - pos.collateral * (leverage - 1) + if pos.side == "long" + else pos.collateral * (1 + leverage) - rate * pos.position + ) + + position_record = WalletHistory( + timestamp=timestamp, + currency=pos.symbol, + quote_currency=self._stake_currency, + rate=rate, + balance=pos.position, + total_quote=total_quote, + total_position_value=rate * pos.position if rate else None, + collateral=pos.collateral, + leverage=leverage, + bot_managed=base in open_assets, + ) + position_collaterals += pos.collateral + wallet_records.append(position_record) + + for wallet in self.get_all_balances().values(): + # TODO: (needs decision) exclude minimal balances? + rate = self._exchange.get_conversion_rate(wallet.currency, self._stake_currency) + is_bot_managed = ( + self._stake_currency == wallet.currency or wallet.currency in open_assets + ) + balance = wallet.total - ( + position_collaterals if wallet.currency == self._stake_currency else 0 + ) + total_quote = rate * balance if rate else None + + wallet_record = WalletHistory( + timestamp=timestamp, + currency=wallet.currency, + quote_currency=self._stake_currency, + rate=rate, + balance=balance, + leverage=1.0, + total_quote=total_quote, + bot_managed=is_bot_managed, + ) + wallet_records.append(wallet_record) + try: + WalletHistory.session.bulk_save_objects(wallet_records) + WalletHistory.session.commit() + except Exception as e: + WalletHistory.session.rollback() + logger.error(f"Error saving wallet balance records: {e}") diff --git a/ft_client/freqtrade_client/__init__.py b/ft_client/freqtrade_client/__init__.py index 9f3a63a10..20f75327b 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__ = "2026.3" +__version__ = "2026.4" if "dev" in __version__: from pathlib import Path diff --git a/ft_client/requirements.txt b/ft_client/requirements.txt index 5cabe57a3..3a236a252 100644 --- a/ft_client/requirements.txt +++ b/ft_client/requirements.txt @@ -1,3 +1,3 @@ # Requirements for freqtrade client library -requests==2.33.0 +requests==2.33.1 python-rapidjson==1.23 diff --git a/pyproject.toml b/pyproject.toml index 3a2d88b17..c6024a3b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,7 +40,7 @@ dependencies = [ "urllib3", "jsonschema", "numpy>2.0,<3.0", - "pandas>=2.2.0,<3.0", + "pandas>=2.2.0,<4.0", "TA-Lib<0.7", "ft-pandas-ta", "technical", @@ -217,6 +217,11 @@ reportRedeclaration = false # 1 reportReturnType = false # 28 reportTypedDictNotRequiredAccess = false # 27 +[tool.uv] +exclude-newer = "1 week" + +[tool.uv.exclude-newer-package] +ccxt = false [tool.ruff] line-length = 100 diff --git a/requirements-dev.txt b/requirements-dev.txt index 850cf4578..b3c6962e7 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,12 +6,12 @@ -r requirements-freqai-rl.txt -r docs/requirements-docs.txt -ruff==0.15.6 -mypy==1.19.1 +ruff==0.15.11 +mypy==1.20.1 pre-commit==4.5.1 -pytest==9.0.2 +pytest==9.0.3 pytest-asyncio==1.3.0 -pytest-cov==7.0.0 +pytest-cov==7.1.0 pytest-mock==3.15.1 pytest-random-order==1.2.0 pytest-timeout==2.4.0 @@ -20,17 +20,17 @@ pytest-xdist==3.8.0 time-machine==3.2.0 # Convert jupyter notebooks to markdown documents -nbconvert==7.17.0 +nbconvert==7.17.1 # mypy types -scipy-stubs==1.17.1.2 # keep in sync with `scipy` in `requirements-hyperopt.txt` -types-cachetools==6.2.0.20251022 +scipy-stubs==1.17.1.4 # keep in sync with `scipy` in `requirements-hyperopt.txt` +types-cachetools==6.2.0.20260408 types-filelock==3.2.7 -types-requests==2.32.4.20260107 -types-tabulate==0.10.0.20260308 -types-python-dateutil==2.9.0.20260305 +types-requests==2.33.0.20260408 +types-tabulate==0.10.0.20260408 +types-python-dateutil==2.9.0.20260408 pip-audit==2.10.0 # For build step in CI -build==1.4.2 +build==1.4.3 # For pre-commit-update check pyyaml==6.0.3 diff --git a/requirements-freqai-rl.txt b/requirements-freqai-rl.txt index e2aef02ae..2056dac30 100644 --- a/requirements-freqai-rl.txt +++ b/requirements-freqai-rl.txt @@ -2,10 +2,10 @@ -r requirements-freqai.txt # Required for freqai-rl -torch==2.10.0; sys_platform != 'darwin' or platform_machine != 'x86_64' +torch==2.11.0; sys_platform != 'darwin' or platform_machine != 'x86_64' gymnasium==1.2.3 # SB3 >=2.5.0 depends on torch 2.3.0 - which implies it dropped support x86 macos -stable_baselines3==2.7.1; sys_platform != 'darwin' or platform_machine != 'x86_64' -sb3_contrib>=2.2.1; sys_platform != 'darwin' or platform_machine != 'x86_64' +stable_baselines3==2.8.0; sys_platform != 'darwin' or platform_machine != 'x86_64' +sb3_contrib==2.8.0; sys_platform != 'darwin' or platform_machine != 'x86_64' # Progress bar for stable-baselines3 and sb3-contrib tqdm==4.67.3 diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index a632bd3b2..013c18ee4 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -4,6 +4,6 @@ # Required for hyperopt scipy==1.17.1 scikit-learn==1.8.0 -filelock==3.25.2 -optuna==4.7.0 -cmaes==0.12.0 +filelock==3.29.0 +optuna==4.8.0 +cmaes==0.13.0 diff --git a/requirements-plot.txt b/requirements-plot.txt index 8a66f60f7..4ab988501 100644 --- a/requirements-plot.txt +++ b/requirements-plot.txt @@ -1,4 +1,4 @@ # Include all requirements to run the bot. -r requirements.txt -plotly==6.6.0 +plotly==6.7.0 diff --git a/requirements.txt b/requirements.txt index 9998fd3ff..bd84d6c76 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,22 +1,22 @@ -numpy==2.4.3 -pandas==2.3.3 +numpy==2.4.4 +pandas==3.0.2 bottleneck==1.6.0 numexpr==2.14.1 # Indicator libraries ft-pandas-ta==0.3.16 ta-lib==0.6.8 -technical==1.5.4 +technical==1.6.0 -ccxt==4.5.44 -cryptography==46.0.6 -aiohttp==3.13.3 -SQLAlchemy==2.0.48 -python-telegram-bot==22.6 +ccxt==4.5.50 +cryptography==46.0.7 +aiohttp==3.13.5 +SQLAlchemy==2.0.49 +python-telegram-bot==22.7 # can't be hard-pinned due to telegram-bot pinning httpx with ~ httpx>=0.24.1 humanize==4.15.0 cachetools==7.0.5 -requests==2.33.0 +requests==2.33.1 urllib3==2.6.3 certifi==2026.2.25 jsonschema==4.26.0 @@ -24,22 +24,22 @@ tabulate==0.10.0 pycoingecko==3.2.0 jinja2==3.1.6 joblib==1.5.3 -rich==14.3.3 +rich==15.0.0 pyarrow==23.0.1; platform_machine != 'armv7l' # Load ticker files 30% faster python-rapidjson==1.23 # Properly format api responses -orjson==3.11.7 +orjson==3.11.8 # Notify systemd sdnotify==0.3.2 # API Server -fastapi==0.135.1 -pydantic==2.12.5 -uvicorn==0.41.0 +fastapi==0.136.0 +pydantic==2.13.2 +uvicorn==0.44.0 pyjwt==2.12.1 aiofiles==25.1.0 psutil==7.2.2 @@ -59,4 +59,4 @@ websockets==16.0 janus==2.0.0 ast-comments==1.3.0 -packaging==26.0 +packaging==26.1 diff --git a/setup.ps1 b/setup.ps1 index cf57915fa..4fea27257 100644 --- a/setup.ps1 +++ b/setup.ps1 @@ -9,8 +9,8 @@ $VenvName = ".venv" $VenvDir = Join-Path $PSScriptRoot $VenvName # Supported Python minor versions (detection order: prefer newest first) -$SupportedMinorVersions = @(13,12,11) -# Build a human-readable supported versions string like "3.11, 3.12 and 3.13" +$SupportedMinorVersions = @(14,13,12,11) +# Build a human-readable supported versions string like "3.11, 3.12 3.13 and 3.14" $asc = $SupportedMinorVersions | Sort-Object if ($asc.Count -eq 1) { $SupportedPythonVersions = "3.$($asc[0])" diff --git a/setup.sh b/setup.sh index f4b9e0946..ad4d7455a 100755 --- a/setup.sh +++ b/setup.sh @@ -8,8 +8,8 @@ function echo_block() { } UV=false # Supported Python minor versions (order matters for detection) -SUPPORTED_MINOR_VERS=(13 12 11) -SUPPORTED_PY_VERSIONS="3.11, 3.12 and 3.13" +SUPPORTED_MINOR_VERS=(14 13 12 11) +SUPPORTED_PY_VERSIONS="3.11, 3.12, 3.13 and 3.14" function check_installed_pip() { ${PYTHON} -m pip > /dev/null @@ -254,7 +254,7 @@ function install() { install_redhat else echo "This script does not support your OS." - echo "If you have Python version 3.11 - 3.13, pip, virtualenv installed you can continue." + echo "If you have Python version 3.11 - 3.14, pip, virtualenv installed you can continue." echo "Wait 10 seconds to continue the next install steps or use ctrl+c to interrupt this shell." sleep 10 fi diff --git a/tests/conftest.py b/tests/conftest.py index abd15a6a1..dc0860466 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -169,25 +169,27 @@ def generate_trades_history(n_rows, start_date: datetime | None = None, days=5): return df -def generate_test_data(timeframe: str, size: int, start: str = "2020-07-05", random_seed=42): +def generate_test_data( + timeframe: str, size: int, start: str = "2020-07-05", random_seed=42, base=20 +): np.random.seed(random_seed) - base = np.random.normal(20, 2, size=size) + base = np.random.normal(base, 2, size=size) if timeframe == "1y": - date = pd.date_range(start, periods=size, freq="1YS", tz="UTC") + date = pd.date_range(start, periods=size, freq="1YS", tz="UTC", unit="ms") elif timeframe == "1M": - date = pd.date_range(start, periods=size, freq="1MS", tz="UTC") + date = pd.date_range(start, periods=size, freq="1MS", tz="UTC", unit="ms") elif timeframe == "3M": - date = pd.date_range(start, periods=size, freq="3MS", tz="UTC") + date = pd.date_range(start, periods=size, freq="3MS", tz="UTC", unit="ms") elif timeframe == "1w" or timeframe == "7d": - date = pd.date_range(start, periods=size, freq="1W-MON", tz="UTC") + date = pd.date_range(start, periods=size, freq="1W-MON", tz="UTC", unit="ms") else: tf_mins = timeframe_to_minutes(timeframe) if tf_mins >= 1: - date = pd.date_range(start, periods=size, freq=f"{tf_mins}min", tz="UTC") + date = pd.date_range(start, periods=size, freq=f"{tf_mins}min", tz="UTC", unit="ms") else: tf_secs = timeframe_to_seconds(timeframe) - date = pd.date_range(start, periods=size, freq=f"{tf_secs}s", tz="UTC") + date = pd.date_range(start, periods=size, freq=f"{tf_secs}s", tz="UTC", unit="ms") df = pd.DataFrame( { "date": date, @@ -205,7 +207,7 @@ def generate_test_data(timeframe: str, size: int, start: str = "2020-07-05", ran def generate_test_data_raw(timeframe: str, size: int, start: str = "2020-07-05", random_seed=42): """Generates data in the ohlcv format used by ccxt""" df = generate_test_data(timeframe, size, start, random_seed) - df["date"] = df.loc[:, "date"].astype(np.int64) // 1000 // 1000 + df["date"] = df.loc[:, "date"].dt.as_unit("ms").astype("int64") return list(list(x) for x in zip(*(df[x].values.tolist() for x in df.columns), strict=False)) diff --git a/tests/data/test_btanalysis.py b/tests/data/test_btanalysis.py index c869e6a92..e6ca0bd8a 100644 --- a/tests/data/test_btanalysis.py +++ b/tests/data/test_btanalysis.py @@ -1,17 +1,18 @@ -from datetime import UTC, datetime, timedelta +from datetime import UTC, datetime from pathlib import Path from unittest.mock import MagicMock from zipfile import ZipFile import pytest -from pandas import DataFrame, DateOffset, Timestamp, to_datetime +from pandas import DataFrame, to_datetime from freqtrade.configuration import TimeRange from freqtrade.constants import LAST_BT_RESULT_FN from freqtrade.data.btanalysis import ( BT_DATA_COLUMNS, - analyze_trade_parallelism, extract_trades_of_period, + get_backtest_market_change, + get_backtest_wallet_change, get_latest_backtest_filename, get_latest_hyperopt_file, load_backtest_data, @@ -20,22 +21,7 @@ from freqtrade.data.btanalysis import ( load_trades, load_trades_from_db, ) -from freqtrade.data.history import load_data, load_pair_history -from freqtrade.data.metrics import ( - calculate_cagr, - calculate_calmar, - calculate_csum, - calculate_expectancy, - calculate_market_change, - calculate_max_drawdown, - calculate_sharpe, - calculate_sortino, - calculate_sqn, - calculate_underwater, - combine_dataframes_with_mean, - combined_dataframes_with_rel_mean, - create_cum_profit, -) +from freqtrade.data.history import load_pair_history from freqtrade.exceptions import OperationalException from freqtrade.util import dt_utc from tests.conftest import CURRENT_TEST_STRATEGY, create_mock_trades @@ -209,17 +195,6 @@ def test_extract_trades_of_period(testdatadir): assert trades1.iloc[-1].close_date == datetime(2017, 11, 14, 15, 25, 0, tzinfo=UTC) -def test_analyze_trade_parallelism(testdatadir): - filename = testdatadir / "backtest_results/backtest-result.json" - bt_data = load_backtest_data(filename) - - res = analyze_trade_parallelism(bt_data, "5m") - assert isinstance(res, DataFrame) - assert "open_trades" in res.columns - assert res["open_trades"].max() == 3 - assert res["open_trades"].min() == 0 - - def test_load_trades(default_conf, mocker): db_mock = mocker.patch( "freqtrade.data.btanalysis.bt_fileutils.load_trades_from_db", MagicMock() @@ -263,375 +238,6 @@ def test_load_trades(default_conf, mocker): assert bt_mock.call_count == 0 -def test_calculate_market_change(testdatadir): - pairs = ["ETH/BTC", "ADA/BTC"] - data = load_data(datadir=testdatadir, pairs=pairs, timeframe="5m") - result = calculate_market_change(data) - assert isinstance(result, float) - assert pytest.approx(result) == 0.01100002 - - result = calculate_market_change(data, min_date=dt_utc(2018, 1, 20)) - assert isinstance(result, float) - assert pytest.approx(result) == 0.0375149 - - # Move min-date after the last date - result = calculate_market_change(data, min_date=dt_utc(2018, 2, 20)) - assert pytest.approx(result) == 0.0 - - -def test_combine_dataframes_with_mean(testdatadir): - pairs = ["ETH/BTC", "ADA/BTC"] - data = load_data(datadir=testdatadir, pairs=pairs, timeframe="5m") - df = combine_dataframes_with_mean(data) - assert isinstance(df, DataFrame) - assert "ETH/BTC" in df.columns - assert "ADA/BTC" in df.columns - assert "mean" in df.columns - - -def test_combined_dataframes_with_rel_mean(testdatadir): - pairs = ["BTC/USDT", "XRP/USDT"] - data = load_data(datadir=testdatadir, pairs=pairs, timeframe="5m") - df = combined_dataframes_with_rel_mean( - data, - fromdt=data["BTC/USDT"].at[0, "date"], - todt=data["BTC/USDT"].at[data["BTC/USDT"].index[-1], "date"], - ) - assert isinstance(df, DataFrame) - assert "BTC/USDT" not in df.columns - assert "XRP/USDT" not in df.columns - assert "mean" in df.columns - assert "rel_mean" in df.columns - assert "count" in df.columns - assert df.iloc[0]["count"] == 2 - assert df.iloc[-1]["count"] == 2 - assert len(df) < len(data["BTC/USDT"]) - assert df["rel_mean"].between(-0.5, 0.5).all() - - -def test_combine_dataframes_with_mean_no_data(testdatadir): - pairs = ["ETH/BTC", "ADA/BTC"] - data = load_data(datadir=testdatadir, pairs=pairs, timeframe="6m") - with pytest.raises(ValueError, match=r"No data provided\."): - combine_dataframes_with_mean(data) - - -def test_create_cum_profit(testdatadir): - filename = testdatadir / "backtest_results/backtest-result.json" - bt_data = load_backtest_data(filename) - timerange = TimeRange.parse_timerange("20180110-20180112") - - df = load_pair_history(pair="TRX/BTC", timeframe="5m", datadir=testdatadir, timerange=timerange) - - cum_profits = create_cum_profit( - df.set_index("date"), bt_data[bt_data["pair"] == "TRX/BTC"], "cum_profits", timeframe="5m" - ) - assert "cum_profits" in cum_profits.columns - assert cum_profits.iloc[0]["cum_profits"] == 0 - assert pytest.approx(cum_profits.iloc[-1]["cum_profits"]) == 9.0225563e-05 - - -def test_create_cum_profit1(testdatadir): - filename = testdatadir / "backtest_results/backtest-result.json" - bt_data = load_backtest_data(filename) - # Move close-time to "off" the candle, to make sure the logic still works - bt_data["close_date"] = bt_data.loc[:, "close_date"] + DateOffset(seconds=20) - timerange = TimeRange.parse_timerange("20180110-20180112") - - df = load_pair_history(pair="TRX/BTC", timeframe="5m", datadir=testdatadir, timerange=timerange) - - cum_profits = create_cum_profit( - df.set_index("date"), bt_data[bt_data["pair"] == "TRX/BTC"], "cum_profits", timeframe="5m" - ) - assert "cum_profits" in cum_profits.columns - assert cum_profits.iloc[0]["cum_profits"] == 0 - assert pytest.approx(cum_profits.iloc[-1]["cum_profits"]) == 9.0225563e-05 - - with pytest.raises(ValueError, match=r"Trade dataframe empty\."): - create_cum_profit( - df.set_index("date"), - bt_data[bt_data["pair"] == "NOTAPAIR"], - "cum_profits", - timeframe="5m", - ) - - -def test_calculate_max_drawdown(testdatadir): - filename = testdatadir / "backtest_results/backtest-result.json" - bt_data = load_backtest_data(filename) - drawdown = calculate_max_drawdown(bt_data, value_col="profit_abs") - assert isinstance(drawdown.relative_account_drawdown, float) - assert pytest.approx(drawdown.relative_account_drawdown) == 0.29753914 - assert isinstance(drawdown.high_date, Timestamp) - assert isinstance(drawdown.low_date, Timestamp) - assert isinstance(drawdown.high_value, float) - assert isinstance(drawdown.low_value, float) - assert drawdown.high_date == Timestamp("2018-01-16 19:30:00", tz="UTC") - assert drawdown.low_date == Timestamp("2018-01-16 22:25:00", tz="UTC") - - underwater = calculate_underwater(bt_data) - assert isinstance(underwater, DataFrame) - - with pytest.raises(ValueError, match=r"Trade dataframe empty\."): - calculate_max_drawdown(DataFrame()) - - with pytest.raises(ValueError, match=r"Trade dataframe empty\."): - calculate_underwater(DataFrame()) - - -def test_calculate_csum(testdatadir): - filename = testdatadir / "backtest_results/backtest-result.json" - bt_data = load_backtest_data(filename) - csum_min, csum_max = calculate_csum(bt_data) - - assert isinstance(csum_min, float) - assert isinstance(csum_max, float) - assert csum_min < csum_max - assert csum_min < 0.0001 - assert csum_max > 0.0002 - csum_min1, csum_max1 = calculate_csum(bt_data, 5) - - assert csum_min1 == csum_min + 5 - assert csum_max1 == csum_max + 5 - - with pytest.raises(ValueError, match=r"Trade dataframe empty\."): - csum_min, csum_max = calculate_csum(DataFrame()) - - -def test_calculate_expectancy(testdatadir): - filename = testdatadir / "backtest_results/backtest-result.json" - bt_data = load_backtest_data(filename) - - expectancy, expectancy_ratio = calculate_expectancy(DataFrame()) - assert expectancy == 0.0 - assert expectancy_ratio == 100 - - expectancy, expectancy_ratio = calculate_expectancy(bt_data) - assert isinstance(expectancy, float) - assert isinstance(expectancy_ratio, float) - assert pytest.approx(expectancy) == 5.820687070932315e-06 - assert pytest.approx(expectancy_ratio) == 0.07151374226574791 - - data = {"profit_abs": [100, 200, 50, -150, 300, -100, 80, -30]} - df = DataFrame(data) - expectancy, expectancy_ratio = calculate_expectancy(df) - - assert pytest.approx(expectancy) == 56.25 - assert pytest.approx(expectancy_ratio) == 0.60267857 - - -def test_calculate_sortino(testdatadir): - filename = testdatadir / "backtest_results/backtest-result.json" - bt_data = load_backtest_data(filename) - - sortino = calculate_sortino(DataFrame(), None, None, 0) - assert sortino == 0.0 - - sortino = calculate_sortino( - bt_data, - bt_data["open_date"].min(), - bt_data["close_date"].max(), - 0.01, - ) - assert isinstance(sortino, float) - assert pytest.approx(sortino) == 35.17722 - - -def test_calculate_sharpe(testdatadir): - filename = testdatadir / "backtest_results/backtest-result.json" - bt_data = load_backtest_data(filename) - - sharpe = calculate_sharpe(DataFrame(), None, None, 0) - assert sharpe == 0.0 - - sharpe = calculate_sharpe( - bt_data, - bt_data["open_date"].min(), - bt_data["close_date"].max(), - 0.01, - ) - assert isinstance(sharpe, float) - assert pytest.approx(sharpe) == 44.5078669 - - -def test_calculate_calmar(testdatadir): - filename = testdatadir / "backtest_results/backtest-result.json" - bt_data = load_backtest_data(filename) - - calmar = calculate_calmar(DataFrame(), None, None, 0) - assert calmar == 0.0 - - calmar = calculate_calmar( - bt_data, - bt_data["open_date"].min(), - bt_data["close_date"].max(), - 0.01, - ) - assert isinstance(calmar, float) - assert pytest.approx(calmar) == 559.040508 - - -def test_calculate_sqn(testdatadir): - filename = testdatadir / "backtest_results/backtest-result.json" - bt_data = load_backtest_data(filename) - - sqn = calculate_sqn(DataFrame(), 0) - assert sqn == 0.0 - - sqn = calculate_sqn( - bt_data, - 0.01, - ) - assert isinstance(sqn, float) - assert pytest.approx(sqn) == 3.2991 - - -@pytest.mark.parametrize( - "profits,starting_balance,expected_sqn,description", - [ - ([1.0, -0.5, 2.0, -1.0, 0.5, 1.5, -0.5, 1.0], 100, 1.3229, "Mixed profits/losses"), - ([], 100, 0.0, "Empty dataframe"), - ([1.0, 0.5, 2.0, 1.5, 0.8], 100, 4.3657, "All winning trades"), - ([-1.0, -0.5, -2.0, -1.5, -0.8], 100, -4.3657, "All losing trades"), - ([1.0], 100, -100, "Single trade"), - ], -) -def test_calculate_sqn_cases(profits, starting_balance, expected_sqn, description): - """ - Test SQN calculation with various scenarios: - """ - trades = DataFrame({"profit_abs": profits}) - sqn = calculate_sqn(trades, starting_balance=starting_balance) - - assert isinstance(sqn, float) - assert pytest.approx(sqn, rel=1e-4) == expected_sqn - - -@pytest.mark.parametrize( - "start,end,days, expected", - [ - (64900, 176000, 3 * 365, 0.3945), - (64900, 176000, 365, 1.7119), - (1000, 1000, 365, 0.0), - (1000, 1500, 365, 0.5), - (1000, 1500, 100, 3.3927), # sub year - (0.01000000, 0.01762792, 120, 4.6087), # sub year BTC values - (1000, 1010, 0, 0.0), # zero days - (-100, 100, 365, 0.0), # negative starting balance - ], -) -def test_calculate_cagr(start, end, days, expected): - assert round(calculate_cagr(days, start, end), 4) == expected - - -def test_calculate_max_drawdown2(): - values = [ - 0.011580, - 0.010048, - 0.011340, - 0.012161, - 0.010416, - 0.010009, - 0.020024, - -0.024662, - -0.022350, - 0.020496, - -0.029859, - -0.030511, - 0.010041, - 0.010872, - -0.025782, - 0.010400, - 0.012374, - 0.012467, - 0.114741, - 0.010303, - 0.010088, - -0.033961, - 0.010680, - 0.010886, - -0.029274, - 0.011178, - 0.010693, - 0.010711, - ] - - dates = [dt_utc(2020, 1, 1) + timedelta(days=i) for i in range(len(values))] - df = DataFrame(zip(values, dates, strict=False), columns=["profit", "open_date"]) - # sort by profit and reset index - df = df.sort_values("profit").reset_index(drop=True) - df1 = df.copy() - drawdown = calculate_max_drawdown( - df, date_col="open_date", starting_balance=0.2, value_col="profit" - ) - # Ensure df has not been altered. - assert df.equals(df1) - - assert isinstance(drawdown.drawdown_abs, float) - assert isinstance(drawdown.relative_account_drawdown, float) - # High must be before low - assert drawdown.high_date < drawdown.low_date - # High value must be higher than low value - assert drawdown.high_value > drawdown.low_value - assert drawdown.drawdown_abs == 0.091755 - assert pytest.approx(drawdown.relative_account_drawdown) == 0.32129575 - - df = DataFrame(zip(values[:5], dates[:5], strict=False), columns=["profit", "open_date"]) - # No losing trade ... - drawdown = calculate_max_drawdown(df, date_col="open_date", value_col="profit") - assert drawdown.drawdown_abs == 0.0 - assert drawdown.low_value == 0.0 - assert drawdown.current_high_value >= 0.0 - assert drawdown.current_drawdown_abs == 0.0 - - df1 = DataFrame(zip(values[:5], dates[:5], strict=False), columns=["profit", "open_date"]) - df1.loc[:, "profit"] = df1["profit"] * -1 - # No winning trade ... - drawdown = calculate_max_drawdown(df1, date_col="open_date", value_col="profit") - assert drawdown.drawdown_abs == 0.055545 - assert drawdown.high_value == 0.0 - assert drawdown.current_high_value == 0.0 - assert drawdown.current_drawdown_abs == 0.055545 - - -@pytest.mark.parametrize( - "profits,relative,highd,lowdays,result,result_rel", - [ - ([0.0, -500.0, 500.0, 10000.0, -1000.0], False, 3, 4, 1000.0, 0.090909), - ([0.0, -500.0, 500.0, 10000.0, -1000.0], True, 0, 1, 500.0, 0.5), - ], -) -def test_calculate_max_drawdown_abs(profits, relative, highd, lowdays, result, result_rel): - """ - Test case from issue https://github.com/freqtrade/freqtrade/issues/6655 - [1000, 500, 1000, 11000, 10000] # absolute results - [1000, 50%, 0%, 0%, ~9%] # Relative drawdowns - """ - init_date = datetime(2020, 1, 1, tzinfo=UTC) - dates = [init_date + timedelta(days=i) for i in range(len(profits))] - df = DataFrame(zip(profits, dates, strict=False), columns=["profit_abs", "open_date"]) - # sort by profit and reset index - df = df.sort_values("profit_abs").reset_index(drop=True) - df1 = df.copy() - drawdown = calculate_max_drawdown( - df, date_col="open_date", starting_balance=1000, relative=relative - ) - # Ensure df has not been altered. - assert df.equals(df1) - - assert isinstance(drawdown.drawdown_abs, float) - assert isinstance(drawdown.relative_account_drawdown, float) - assert drawdown.high_date == init_date + timedelta(days=highd) - assert drawdown.low_date == init_date + timedelta(days=lowdays) - - # High must be before low - assert drawdown.high_date < drawdown.low_date - # High value must be higher than low value - 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") @@ -649,3 +255,56 @@ def test_load_file_from_zip(tmp_path): with pytest.raises(ValueError, match=r"File .* not found in zip.*"): load_file_from_zip(zip_file, "testfile55.txt") + + +def test_get_backtest_market_change(tmp_path): + df = DataFrame( + { + "date": [dt_utc(2020, 1, 1), dt_utc(2020, 1, 2)], + "price": [100.0, 110.0], + } + ) + feather_file = tmp_path / "backtest-result_market_change.feather" + df.to_feather(feather_file) + + direct_df = get_backtest_market_change(feather_file) + assert isinstance(direct_df, DataFrame) + assert "__date_ts" in direct_df.columns + assert direct_df.loc[0, "__date_ts"] == int(df.loc[0, "date"].timestamp() * 1000) + + no_ts_df = get_backtest_market_change(feather_file, include_ts=False) + assert "__date_ts" not in no_ts_df.columns + + zip_file = tmp_path / "backtest-result.zip" + with ZipFile(zip_file, "w") as zipf: + zipf.write(feather_file, arcname=f"{zip_file.stem}_market_change.feather") + + zipped_df = get_backtest_market_change(zip_file) + assert isinstance(zipped_df, DataFrame) + assert zipped_df.loc[0, "__date_ts"] == int(df.loc[0, "date"].timestamp() * 1000) + assert list(zipped_df["price"]) == [100.0, 110.0] + + +def test_get_backtest_wallet_change(tmp_path): + df = DataFrame( + { + "date": [dt_utc(2020, 1, 1), dt_utc(2020, 1, 2)], + "balance": [1.0, 1.1], + "rate": [1.0, 1.1], + } + ) + wallet_feather = tmp_path / "backtest-result_TestStrategy_wallet.feather" + df.to_feather(wallet_feather) + + zip_file = tmp_path / "backtest-result.zip" + with ZipFile(zip_file, "w") as zipf: + zipf.write(wallet_feather, arcname=wallet_feather.name) + + wallet_df = get_backtest_wallet_change(zip_file, "TestStrategy") + assert isinstance(wallet_df, DataFrame) + assert "__date_ts" in wallet_df.columns + assert wallet_df.loc[0, "__date_ts"] == int(df.loc[0, "date"].timestamp() * 1000) + assert list(wallet_df["balance"]) == [1.0, 1.1] + + assert get_backtest_wallet_change(tmp_path / "backtest-result.feather", "TestStrategy") is None + assert get_backtest_wallet_change(zip_file, "UnknownStrategy") is None diff --git a/tests/data/test_converter.py b/tests/data/test_converter.py index 835f5a861..4946741aa 100644 --- a/tests/data/test_converter.py +++ b/tests/data/test_converter.py @@ -207,10 +207,13 @@ def test_ohlcv_to_dataframe_multi(timeframe): data1 = data.copy() if timeframe in ("1M", "3M", "1y"): - data1.loc[:, "date"] = data1.loc[:, "date"] + pd.to_timedelta("1w") + data1.loc[:, "date"] = data1.loc[:, "date"] + pd.to_timedelta("1W") else: # Shift by half a timeframe - data1.loc[:, "date"] = data1.loc[:, "date"] + (pd.to_timedelta(timeframe) / 2) + timeframe_f = ( + timeframe.upper() if timeframe.endswith("d") or timeframe.endswith("w") else timeframe + ) + data1.loc[:, "date"] = data1.loc[:, "date"] + (pd.to_timedelta(timeframe_f) / 2) df2 = ohlcv_to_dataframe(data1, timeframe, "UNITTEST/USDT") assert len(df2) == len(data) - 1 diff --git a/tests/data/test_metrics.py b/tests/data/test_metrics.py new file mode 100644 index 000000000..242700eef --- /dev/null +++ b/tests/data/test_metrics.py @@ -0,0 +1,567 @@ +from datetime import UTC, datetime, timedelta + +import numpy as np +import pytest +from pandas import DataFrame, DateOffset, Timestamp, to_datetime + +from freqtrade.configuration import TimeRange +from freqtrade.data.btanalysis import ( + load_backtest_data, +) +from freqtrade.data.history import load_data, load_pair_history +from freqtrade.data.metrics import ( + calculate_cagr, + calculate_calmar, + calculate_calmar_from_balance, + calculate_csum, + calculate_expectancy, + calculate_market_change, + calculate_max_drawdown, + calculate_max_drawdown_from_balance, + calculate_sharpe, + calculate_sharpe_from_balance, + calculate_sortino, + calculate_sortino_from_balance, + calculate_sqn, + calculate_underwater, + combine_dataframes_with_mean, + combined_dataframes_with_rel_mean, + create_cum_profit, +) +from freqtrade.util import dt_utc + + +def test_calculate_market_change(testdatadir): + pairs = ["ETH/BTC", "ADA/BTC"] + data = load_data(datadir=testdatadir, pairs=pairs, timeframe="5m") + result = calculate_market_change(data) + assert isinstance(result, float) + assert pytest.approx(result) == 0.01100002 + + result = calculate_market_change(data, min_date=dt_utc(2018, 1, 20)) + assert isinstance(result, float) + assert pytest.approx(result) == 0.0375149 + + # Move min-date after the last date + result = calculate_market_change(data, min_date=dt_utc(2018, 2, 20)) + assert pytest.approx(result) == 0.0 + + +def test_combine_dataframes_with_mean(testdatadir): + pairs = ["ETH/BTC", "ADA/BTC"] + data = load_data(datadir=testdatadir, pairs=pairs, timeframe="5m") + df = combine_dataframes_with_mean(data) + assert isinstance(df, DataFrame) + assert "ETH/BTC" in df.columns + assert "ADA/BTC" in df.columns + assert "mean" in df.columns + + +def test_combined_dataframes_with_rel_mean(testdatadir): + pairs = ["BTC/USDT", "XRP/USDT"] + data = load_data(datadir=testdatadir, pairs=pairs, timeframe="5m") + df = combined_dataframes_with_rel_mean( + data, + fromdt=data["BTC/USDT"].at[0, "date"], + todt=data["BTC/USDT"].at[data["BTC/USDT"].index[-1], "date"], + ) + assert isinstance(df, DataFrame) + assert "BTC/USDT" not in df.columns + assert "XRP/USDT" not in df.columns + assert "mean" in df.columns + assert "rel_mean" in df.columns + assert "count" in df.columns + assert df.iloc[0]["count"] == 2 + assert df.iloc[-1]["count"] == 2 + assert len(df) < len(data["BTC/USDT"]) + assert df["rel_mean"].between(-0.5, 0.5).all() + + +def test_combine_dataframes_with_mean_no_data(testdatadir): + pairs = ["ETH/BTC", "ADA/BTC"] + data = load_data(datadir=testdatadir, pairs=pairs, timeframe="6m") + with pytest.raises(ValueError, match=r"No data provided\."): + combine_dataframes_with_mean(data) + + +def test_create_cum_profit(testdatadir): + filename = testdatadir / "backtest_results/backtest-result.json" + bt_data = load_backtest_data(filename) + timerange = TimeRange.parse_timerange("20180110-20180112") + + df = load_pair_history(pair="TRX/BTC", timeframe="5m", datadir=testdatadir, timerange=timerange) + + cum_profits = create_cum_profit( + df.set_index("date"), bt_data[bt_data["pair"] == "TRX/BTC"], "cum_profits", timeframe="5m" + ) + assert "cum_profits" in cum_profits.columns + assert cum_profits.iloc[0]["cum_profits"] == 0 + assert pytest.approx(cum_profits.iloc[-1]["cum_profits"]) == 9.0225563e-05 + + +def test_create_cum_profit1(testdatadir): + filename = testdatadir / "backtest_results/backtest-result.json" + bt_data = load_backtest_data(filename) + # Move close-time to "off" the candle, to make sure the logic still works + bt_data["close_date"] = bt_data.loc[:, "close_date"] + DateOffset(seconds=20) + timerange = TimeRange.parse_timerange("20180110-20180112") + + df = load_pair_history(pair="TRX/BTC", timeframe="5m", datadir=testdatadir, timerange=timerange) + + cum_profits = create_cum_profit( + df.set_index("date"), bt_data[bt_data["pair"] == "TRX/BTC"], "cum_profits", timeframe="5m" + ) + assert "cum_profits" in cum_profits.columns + assert cum_profits.iloc[0]["cum_profits"] == 0 + assert pytest.approx(cum_profits.iloc[-1]["cum_profits"]) == 9.0225563e-05 + + with pytest.raises(ValueError, match=r"Trade dataframe empty\."): + create_cum_profit( + df.set_index("date"), + bt_data[bt_data["pair"] == "NOTAPAIR"], + "cum_profits", + timeframe="5m", + ) + + +def test_calculate_max_drawdown(testdatadir): + filename = testdatadir / "backtest_results/backtest-result.json" + bt_data = load_backtest_data(filename) + drawdown = calculate_max_drawdown(bt_data, value_col="profit_abs") + assert isinstance(drawdown.relative_account_drawdown, float) + assert pytest.approx(drawdown.relative_account_drawdown) == 0.29753914 + assert isinstance(drawdown.high_date, Timestamp) + assert isinstance(drawdown.low_date, Timestamp) + assert isinstance(drawdown.high_value, float) + assert isinstance(drawdown.low_value, float) + assert drawdown.high_date == Timestamp("2018-01-16 19:30:00", tz="UTC") + assert drawdown.low_date == Timestamp("2018-01-16 22:25:00", tz="UTC") + + underwater = calculate_underwater(bt_data) + assert isinstance(underwater, DataFrame) + + with pytest.raises(ValueError, match=r"Trade dataframe empty\."): + calculate_max_drawdown(DataFrame()) + + with pytest.raises(ValueError, match=r"Trade dataframe empty\."): + calculate_underwater(DataFrame()) + + +def test_calculate_max_drawdown_from_balance(): + balance_history = DataFrame( + { + "date": to_datetime( + [ + "2025-01-01 00:00:00+00:00", + "2025-01-01 12:00:00+00:00", + "2025-01-01 18:00:00+00:00", + "2025-01-04 00:00:00+00:00", + ], + utc=True, + ), + "total_quote": [100.0, 120.0, 80.0, 110.0], + } + ) + + drawdown = calculate_max_drawdown_from_balance(balance_history) + assert isinstance(drawdown.relative_account_drawdown, float) + assert pytest.approx(drawdown.relative_account_drawdown) == 1 / 3 + assert pytest.approx(drawdown.drawdown_abs) == 40 + assert pytest.approx(drawdown.current_high_value) == 20 + assert pytest.approx(drawdown.low_value) == -20 + assert pytest.approx(drawdown.high_value) == 20 + + assert drawdown.high_date == Timestamp("2025-01-01 12:00:00", tz="UTC") + assert drawdown.low_date == Timestamp("2025-01-01 18:00:00", tz="UTC") + + +def test_calculate_max_drawdown_from_balance_empty_or_short(): + with pytest.raises(ValueError, match=r"Balance-history dataframe empty\."): + calculate_max_drawdown_from_balance(DataFrame()) + + one_point = DataFrame( + { + "date": to_datetime(["2025-01-01 00:00:00+00:00"], utc=True), + "total_quote": [100.0], + } + ) + with pytest.raises(ValueError, match=r"Balance-history dataframe empty\."): + calculate_max_drawdown_from_balance(one_point) + + +def test_calculate_csum(testdatadir): + filename = testdatadir / "backtest_results/backtest-result.json" + bt_data = load_backtest_data(filename) + csum_min, csum_max = calculate_csum(bt_data) + + assert isinstance(csum_min, float) + assert isinstance(csum_max, float) + assert csum_min < csum_max + assert csum_min < 0.0001 + assert csum_max > 0.0002 + csum_min1, csum_max1 = calculate_csum(bt_data, 5) + + assert csum_min1 == csum_min + 5 + assert csum_max1 == csum_max + 5 + + with pytest.raises(ValueError, match=r"Trade dataframe empty\."): + csum_min, csum_max = calculate_csum(DataFrame()) + + +def test_calculate_expectancy(testdatadir): + filename = testdatadir / "backtest_results/backtest-result.json" + bt_data = load_backtest_data(filename) + + expectancy, expectancy_ratio = calculate_expectancy(DataFrame()) + assert expectancy == 0.0 + assert expectancy_ratio == 100 + + expectancy, expectancy_ratio = calculate_expectancy(bt_data) + assert isinstance(expectancy, float) + assert isinstance(expectancy_ratio, float) + assert pytest.approx(expectancy) == 5.820687070932315e-06 + assert pytest.approx(expectancy_ratio) == 0.07151374226574791 + + data = {"profit_abs": [100, 200, 50, -150, 300, -100, 80, -30]} + df = DataFrame(data) + expectancy, expectancy_ratio = calculate_expectancy(df) + + assert pytest.approx(expectancy) == 56.25 + assert pytest.approx(expectancy_ratio) == 0.60267857 + + +def test_calculate_sortino(testdatadir): + filename = testdatadir / "backtest_results/backtest-result.json" + bt_data = load_backtest_data(filename) + + sortino = calculate_sortino(DataFrame(), None, None, 0) + assert sortino == 0.0 + + sortino = calculate_sortino( + bt_data, + bt_data["open_date"].min(), + bt_data["close_date"].max(), + 0.01, + ) + assert isinstance(sortino, float) + assert pytest.approx(sortino) == 35.17722 + + +def test_calculate_sortino_from_balance(): + balance_history = DataFrame( + { + "date": to_datetime( + [ + "2025-01-01 00:00:00+00:00", + "2025-01-02 00:00:00+00:00", + "2025-01-03 00:00:00+00:00", + "2025-01-04 00:00:00+00:00", + "2025-01-05 00:00:00+00:00", + ], + utc=True, + ), + "total_quote": [100.0, 110.0, 104.5, 125.4, 112.86], + } + ) + + sortino = calculate_sortino_from_balance(balance_history) + expected_returns = np.array([0.1, -0.05, 0.2, -0.1]) + expected_sortino = expected_returns.mean() / np.std(expected_returns[expected_returns < 0]) + expected_sortino *= np.sqrt(365) + + assert isinstance(sortino, float) + assert pytest.approx(sortino) == expected_sortino + # Explicit assert + assert pytest.approx(sortino) == 28.6574597 + + +def test_calculate_sortino_from_balance_empty_or_no_downside(): + assert calculate_sortino_from_balance(DataFrame()) == 0.0 + + positive_balance_history = DataFrame( + { + "date": to_datetime( + [ + "2025-01-01 00:00:00+00:00", + "2025-01-02 00:00:00+00:00", + "2025-01-03 00:00:00+00:00", + ], + utc=True, + ), + "total_quote": [100.0, 110.0, 121.0], + } + ) + assert calculate_sortino_from_balance(positive_balance_history) == -100 + + +def test_calculate_sharpe(testdatadir): + filename = testdatadir / "backtest_results/backtest-result.json" + bt_data = load_backtest_data(filename) + + sharpe = calculate_sharpe(DataFrame(), None, None, 0) + assert sharpe == 0.0 + + sharpe = calculate_sharpe( + bt_data, + bt_data["open_date"].min(), + bt_data["close_date"].max(), + 0.01, + ) + assert isinstance(sharpe, float) + assert pytest.approx(sharpe) == 44.5078669 + + +def test_calculate_sharpe_from_balance(): + balance_history = DataFrame( + { + "date": to_datetime( + [ + "2025-01-01 00:00:00+00:00", + "2025-01-02 00:00:00+00:00", + "2025-01-03 00:00:00+00:00", + "2025-01-04 00:00:00+00:00", + ], + utc=True, + ), + "total_quote": [100.0, 110.0, 104.5, 125.4], + } + ) + + sharpe = calculate_sharpe_from_balance(balance_history) + expected_returns = np.array([0.1, -0.05, 0.2]) + expected_sharpe = expected_returns.mean() / expected_returns.std() * np.sqrt(365) + + assert isinstance(sharpe, float) + assert pytest.approx(sharpe) == expected_sharpe + + +def test_calculate_sharpe_from_balance_empty_or_flat(): + assert calculate_sharpe_from_balance(DataFrame()) == 0.0 + + flat_balance_history = DataFrame( + { + "date": to_datetime( + ["2025-01-01 00:00:00+00:00", "2025-01-02 00:00:00+00:00"], + utc=True, + ), + "total_quote": [100.0, 100.0], + } + ) + assert calculate_sharpe_from_balance(flat_balance_history) == -100 + + +def test_calculate_calmar(testdatadir): + filename = testdatadir / "backtest_results/backtest-result.json" + bt_data = load_backtest_data(filename) + + calmar = calculate_calmar(DataFrame(), None, None, 0) + assert calmar == 0.0 + + calmar = calculate_calmar( + bt_data, + bt_data["open_date"].min(), + bt_data["close_date"].max(), + 0.01, + ) + assert isinstance(calmar, float) + assert pytest.approx(calmar) == 559.040508 + + +def test_calculate_calmar_from_balance(): + balance_history = DataFrame( + { + "date": to_datetime( + [ + "2025-01-01 00:00:00+00:00", + "2025-01-01 12:00:00+00:00", + "2025-01-01 18:00:00+00:00", + "2025-01-04 00:00:00+00:00", + ], + utc=True, + ), + "total_quote": [100.0, 120.0, 80.0, 110.0], + } + ) + + calmar = calculate_calmar_from_balance(balance_history) + expected_returns_mean = ((110.0 - 100.0) / 100.0) / 3 * 100 + expected_calmar = expected_returns_mean / (1 / 3) * np.sqrt(365) + + assert isinstance(calmar, float) + assert pytest.approx(calmar) == expected_calmar + + +def test_calculate_calmar_from_balance_empty_or_flat(): + assert calculate_calmar_from_balance(DataFrame()) == 0.0 + + flat_balance_history = DataFrame( + { + "date": to_datetime( + ["2025-01-01 00:00:00+00:00", "2025-01-02 00:00:00+00:00"], + utc=True, + ), + "total_quote": [100.0, 100.0], + } + ) + assert calculate_calmar_from_balance(flat_balance_history) == -100 + + +def test_calculate_sqn(testdatadir): + filename = testdatadir / "backtest_results/backtest-result.json" + bt_data = load_backtest_data(filename) + + sqn = calculate_sqn(DataFrame(), 0) + assert sqn == 0.0 + + sqn = calculate_sqn( + bt_data, + 0.01, + ) + assert isinstance(sqn, float) + assert pytest.approx(sqn) == 3.2991 + + +@pytest.mark.parametrize( + "profits,starting_balance,expected_sqn,description", + [ + ([1.0, -0.5, 2.0, -1.0, 0.5, 1.5, -0.5, 1.0], 100, 1.3229, "Mixed profits/losses"), + ([], 100, 0.0, "Empty dataframe"), + ([1.0, 0.5, 2.0, 1.5, 0.8], 100, 4.3657, "All winning trades"), + ([-1.0, -0.5, -2.0, -1.5, -0.8], 100, -4.3657, "All losing trades"), + ([1.0], 100, -100, "Single trade"), + ], +) +def test_calculate_sqn_cases(profits, starting_balance, expected_sqn, description): + """ + Test SQN calculation with various scenarios: + """ + trades = DataFrame({"profit_abs": profits}) + sqn = calculate_sqn(trades, starting_balance=starting_balance) + + assert isinstance(sqn, float) + assert pytest.approx(sqn, rel=1e-4) == expected_sqn + + +@pytest.mark.parametrize( + "start,end,days, expected", + [ + (64900, 176000, 3 * 365, 0.3945), + (64900, 176000, 365, 1.7119), + (1000, 1000, 365, 0.0), + (1000, 1500, 365, 0.5), + (1000, 1500, 100, 3.3927), # sub year + (0.01000000, 0.01762792, 120, 4.6087), # sub year BTC values + (1000, 1010, 0, 0.0), # zero days + (-100, 100, 365, 0.0), # negative starting balance + ], +) +def test_calculate_cagr(start, end, days, expected): + assert round(calculate_cagr(days, start, end), 4) == expected + + +def test_calculate_max_drawdown2(): + values = [ + 0.011580, + 0.010048, + 0.011340, + 0.012161, + 0.010416, + 0.010009, + 0.020024, + -0.024662, + -0.022350, + 0.020496, + -0.029859, + -0.030511, + 0.010041, + 0.010872, + -0.025782, + 0.010400, + 0.012374, + 0.012467, + 0.114741, + 0.010303, + 0.010088, + -0.033961, + 0.010680, + 0.010886, + -0.029274, + 0.011178, + 0.010693, + 0.010711, + ] + + dates = [dt_utc(2020, 1, 1) + timedelta(days=i) for i in range(len(values))] + df = DataFrame(zip(values, dates, strict=False), columns=["profit", "open_date"]) + # sort by profit and reset index + df = df.sort_values("profit").reset_index(drop=True) + df1 = df.copy() + drawdown = calculate_max_drawdown( + df, date_col="open_date", starting_balance=0.2, value_col="profit" + ) + # Ensure df has not been altered. + assert df.equals(df1) + + assert isinstance(drawdown.drawdown_abs, float) + assert isinstance(drawdown.relative_account_drawdown, float) + # High must be before low + assert drawdown.high_date < drawdown.low_date + # High value must be higher than low value + assert drawdown.high_value > drawdown.low_value + assert drawdown.drawdown_abs == 0.091755 + assert pytest.approx(drawdown.relative_account_drawdown) == 0.32129575 + + df = DataFrame(zip(values[:5], dates[:5], strict=False), columns=["profit", "open_date"]) + # No losing trade ... + drawdown = calculate_max_drawdown(df, date_col="open_date", value_col="profit") + assert drawdown.drawdown_abs == 0.0 + assert drawdown.low_value == 0.0 + assert drawdown.current_high_value >= 0.0 + assert drawdown.current_drawdown_abs == 0.0 + + df1 = DataFrame(zip(values[:5], dates[:5], strict=False), columns=["profit", "open_date"]) + df1.loc[:, "profit"] = df1["profit"] * -1 + # No winning trade ... + drawdown = calculate_max_drawdown(df1, date_col="open_date", value_col="profit") + assert drawdown.drawdown_abs == 0.055545 + assert drawdown.high_value == 0.0 + assert drawdown.current_high_value == 0.0 + assert drawdown.current_drawdown_abs == 0.055545 + + +@pytest.mark.parametrize( + "profits,relative,highd,lowdays,result,result_rel", + [ + ([0.0, -500.0, 500.0, 10000.0, -1000.0], False, 3, 4, 1000.0, 0.090909), + ([0.0, -500.0, 500.0, 10000.0, -1000.0], True, 0, 1, 500.0, 0.5), + ], +) +def test_calculate_max_drawdown_abs(profits, relative, highd, lowdays, result, result_rel): + """ + Test case from issue https://github.com/freqtrade/freqtrade/issues/6655 + [1000, 500, 1000, 11000, 10000] # absolute results + [1000, 50%, 0%, 0%, ~9%] # Relative drawdowns + """ + init_date = datetime(2020, 1, 1, tzinfo=UTC) + dates = [init_date + timedelta(days=i) for i in range(len(profits))] + df = DataFrame(zip(profits, dates, strict=False), columns=["profit_abs", "open_date"]) + # sort by profit and reset index + df = df.sort_values("profit_abs").reset_index(drop=True) + df1 = df.copy() + drawdown = calculate_max_drawdown( + df, date_col="open_date", starting_balance=1000, relative=relative + ) + # Ensure df has not been altered. + assert df.equals(df1) + + assert isinstance(drawdown.drawdown_abs, float) + assert isinstance(drawdown.relative_account_drawdown, float) + assert drawdown.high_date == init_date + timedelta(days=highd) + assert drawdown.low_date == init_date + timedelta(days=lowdays) + + # High must be before low + assert drawdown.high_date < drawdown.low_date + # High value must be higher than low value + assert drawdown.high_value > drawdown.low_value + assert drawdown.drawdown_abs == result + assert pytest.approx(drawdown.relative_account_drawdown) == result_rel diff --git a/tests/data/test_trade_parallelism.py b/tests/data/test_trade_parallelism.py new file mode 100644 index 000000000..266c59446 --- /dev/null +++ b/tests/data/test_trade_parallelism.py @@ -0,0 +1,210 @@ +from datetime import timedelta + +import pytest +from pandas import DataFrame, Timestamp + +from freqtrade.data.btanalysis import ( + analyze_trade_parallelism, + load_backtest_data, +) +from freqtrade.data.btanalysis.trade_parallelism import balance_distribution_over_time +from freqtrade.util import dt_utc + + +def test_analyze_trade_parallelism(testdatadir): + filename = testdatadir / "backtest_results/backtest-result.json" + bt_data = load_backtest_data(filename) + + res = analyze_trade_parallelism(bt_data, "5m") + assert isinstance(res, DataFrame) + assert "open_trades" in res.columns + assert res["open_trades"].max() == 3 + assert res["open_trades"].min() == 0 + + +@pytest.mark.parametrize("is_short", [False, True]) +def test_balance_distribution_over_time(is_short): + """ + Test balance_distribution_over_time for both long and short trades. + """ + # Create a minimal trades DataFrame with 4 trades over time + # Base dates for trades + start_date = dt_utc(2023, 1, 1) + base_date = start_date + timedelta(hours=15) + stake_currency = "USDT" + start_balance = 1000.0 + fee = 0.001 # 0.1% fee + + # Create trades spanning different time periods + trades_data = { + "pair": ["BTC/USDT", "ETH/USDT", "XRP/USDT", "LTC/USDT"], + "stake_amount": [100.0, 150.0, 80.0, 120.0], + "open_date": [ + base_date, + base_date + timedelta(hours=2), + base_date + timedelta(hours=5), + base_date + timedelta(hours=8), + ], + "close_date": [ + base_date + timedelta(hours=3), + base_date + timedelta(hours=6), + base_date + timedelta(hours=9), + base_date + timedelta(hours=12), + ], + "open_rate": [40000.0, 2000.0, 0.5, 100.0], + "close_rate": [41000.0, 2100.0, 0.52, 105.0], + "fee_open": [fee, fee, fee, fee], + "fee_close": [fee, fee, fee, fee], + "is_short": [is_short, is_short, is_short, is_short], + "leverage": [1.0, 1.0, 1.0, 1.0], + "orders": [ + # Trade 1: BTC/USDT - entry at 40000, exit at 41000 + [ + { + "amount": 0.0025, # 100 / 40000 + "filled": 0.0025, + "safe_price": 40000.0, + "ft_order_side": "sell" if is_short else "buy", + "order_filled_timestamp": int(base_date.timestamp() * 1000), + "ft_is_entry": True, + }, + { + "amount": 0.0025, + "filled": 0.0025, + "safe_price": 41000.0, + "ft_order_side": "buy" if is_short else "sell", + "order_filled_timestamp": int( + (base_date + timedelta(hours=3)).timestamp() * 1000 + ), + "ft_is_entry": False, + }, + ], + # Trade 2: ETH/USDT - entry at 2000, exit at 2100 + [ + { + "amount": 0.075, # 150 / 2000 + "filled": 0.075, + "safe_price": 2000.0, + "ft_order_side": "sell" if is_short else "buy", + "order_filled_timestamp": int( + (base_date + timedelta(hours=2)).timestamp() * 1000 + ), + "ft_is_entry": True, + }, + { + "amount": 0.075, + "filled": 0.075, + "safe_price": 2100.0, + "ft_order_side": "buy" if is_short else "sell", + "order_filled_timestamp": int( + (base_date + timedelta(hours=6)).timestamp() * 1000 + ), + "ft_is_entry": False, + }, + ], + # Trade 3: XRP/USDT - entry at 0.5, exit at 0.52 + [ + { + "amount": 160.0, # 80 / 0.5 + "filled": 160.0, + "safe_price": 0.5, + "ft_order_side": "sell" if is_short else "buy", + "order_filled_timestamp": int( + (base_date + timedelta(hours=5)).timestamp() * 1000 + ), + "ft_is_entry": True, + }, + { + "amount": 160.0, + "filled": 160.0, + "safe_price": 0.52, + "ft_order_side": "buy" if is_short else "sell", + "order_filled_timestamp": int( + (base_date + timedelta(hours=9)).timestamp() * 1000 + ), + "ft_is_entry": False, + }, + ], + # Trade 4: LTC/USDT - entry at 100, exit at 105 + [ + { + "amount": 1.2, # 120 / 100 + "filled": 1.2, + "safe_price": 100.0, + "ft_order_side": "sell" if is_short else "buy", + "order_filled_timestamp": int( + (base_date + timedelta(hours=8)).timestamp() * 1000 + ), + "ft_is_entry": True, + }, + { + "amount": 1.2, + "filled": 1.2, + "safe_price": 105.0, + "ft_order_side": "buy" if is_short else "sell", + "order_filled_timestamp": int( + (base_date + timedelta(hours=12)).timestamp() * 1000 + ), + "ft_is_entry": False, + }, + ], + ], + } + + trades_df = DataFrame(trades_data) + pairlist = ["BTC/USDT", "ETH/USDT", "XRP/USDT", "LTC/USDT"] + + min_date = start_date + max_date = start_date + timedelta(hours=35) + + result = balance_distribution_over_time( + trades=trades_df, + min_date=min_date, + max_date=max_date, + timeframe="1h", + stake_currency=stake_currency, + start_balance=start_balance, + pairlist=pairlist, + ) + + # Verify basic structure + assert isinstance(result, DataFrame) + assert stake_currency in result.columns + for pair in pairlist: + assert pair in result.columns + assert f"{pair}_leverage" in result.columns + assert f"{pair}_is_short" in result.columns + assert f"{pair}_collateral" in result.columns + + # Verify the index is a DatetimeIndex + assert isinstance(result.index, Timestamp.__class__.__bases__[0]) + + # Verify we have entries over the full time period (36h) + assert len(result) == 36 + + # First trade opens 15h after the start date + assert result.iloc[0][stake_currency] == 1000 + expected_first_balance = start_balance - (100.0 + 100.0 * fee) + assert result.iloc[15][stake_currency] == pytest.approx(expected_first_balance) + + # Check that pair columns have non-zero values during trade periods + # Trade 1 (BTC/USDT) is open from hour 15 to hour 18 + # At hour 16, BTC/USDT should have position + btc_during_trade = result.loc[base_date + timedelta(hours=1), "BTC/USDT"] + assert btc_during_trade > 0, "Trade should have positive position during open period" + + # After Trade 1 closes at hour 3, BTC/USDT position should be 0 + btc_after_close = result.loc[base_date + timedelta(hours=4) :, "BTC/USDT"] + assert all(btc_after_close == 0), "Position should be 0 after trade closes" + + # Final stake currency should reflect all trades' cash flows minus fees + final_balance = result.iloc[-1][stake_currency] + + # Verify the balance changed (trades had effect) + assert final_balance != start_balance, "Balance should change after trading" + + # Since all exit prices > entry prices, exits return more cash than entries spent + # This means final balance > start balance for long trades and < start balance for short trades + assert (final_balance > start_balance) if not is_short else (final_balance < start_balance), ( + "Balance increases for long and decreases for short trades" + ) diff --git a/tests/exchange/test_binance_public_data.py b/tests/exchange/test_binance_public_data.py index ab299321b..98d3864d3 100644 --- a/tests/exchange/test_binance_public_data.py +++ b/tests/exchange/test_binance_public_data.py @@ -69,7 +69,7 @@ def make_response_from_url(start_date, end_date): "taker_buy_quote_volume,ignore" ) df = pd.DataFrame(columns=cols.split(","), dtype=float) - df["open_time"] = date_col.astype("int64") // 10**6 + df["open_time"] = date_col.as_unit("ms").astype("int64") df["open"] = df["high"] = df["low"] = df["close"] = df["volume"] = 1.0 return df diff --git a/tests/exchange/test_bitget.py b/tests/exchange/test_bitget.py index b4dafb0ae..28059ead8 100644 --- a/tests/exchange/test_bitget.py +++ b/tests/exchange/test_bitget.py @@ -5,7 +5,7 @@ from unittest.mock import MagicMock, PropertyMock import pytest from freqtrade.enums import CandleType, MarginMode, RunMode, TradingMode -from freqtrade.exceptions import OperationalException, RetryableOrderError +from freqtrade.exceptions import InvalidOrderException, OperationalException, RetryableOrderError from freqtrade.exchange.common import API_RETRY_COUNT from freqtrade.util import dt_now, dt_ts, dt_utc from tests.conftest import EXMS, get_patched_exchange @@ -77,6 +77,41 @@ def test_fetch_stoploss_order_bitget_exceptions(default_conf_usdt, mocker): ) +@pytest.mark.usefixtures("init_persistence") +def test_cancel_stoploss_order_bitget(default_conf_usdt, mocker): + default_conf_usdt["dry_run"] = False + api_mock = MagicMock() + + exchange = get_patched_exchange(mocker, default_conf_usdt, api_mock, exchange="bitget") + + # Spot scenario + exchange.cancel_order = MagicMock(return_value={"id": "1234"}) + assert exchange.cancel_stoploss_order("1234", "ETH/USDT", {}) == {"id": "1234"} + assert exchange.cancel_order.call_count == 1 + exchange.cancel_order.assert_called_once_with("1234", "ETH/USDT", {"stop": True}) + + # Futures scenario + default_conf_usdt["trading_mode"] = TradingMode.FUTURES + default_conf_usdt["margin_mode"] = MarginMode.ISOLATED + exchange = get_patched_exchange(mocker, default_conf_usdt, api_mock, exchange="bitget") + exchange.cancel_order = MagicMock(return_value={"id": "1234"}) + assert exchange.cancel_stoploss_order("1234", "ETH/USDT:USDT", {}) == {"id": "1234"} + assert exchange.cancel_order.call_count == 1 + exchange.cancel_order.assert_called_once_with( + "1234", "ETH/USDT:USDT", {"stop": True, "planType": "pos_loss"} + ) + + exchange.cancel_order = MagicMock( + side_effect=[InvalidOrderException("API error"), {"id": "1234"}] + ) + assert exchange.cancel_stoploss_order("1234", "ETH/USDT:USDT", {}) == {"id": "1234"} + assert exchange.cancel_order.call_count == 2 + exchange.cancel_order.assert_any_call( + "1234", "ETH/USDT:USDT", {"stop": True, "planType": "pos_loss"} + ) + exchange.cancel_order.assert_any_call("1234", "ETH/USDT:USDT", {"stop": True}) + + def test_bitget_ohlcv_candle_limit(mocker, default_conf_usdt): # This test is also a live test - so we're sure our limits are correct. api_mock = MagicMock() @@ -183,7 +218,7 @@ def test__lev_prep_bitget(default_conf, mocker): exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="bitget") exchange._lev_prep("BTC/USDC:USDC", 3.2, "buy") - assert api_mock.set_margin_mode.call_count == 0 + assert api_mock.set_margin_mode.call_count == 1 assert api_mock.set_leverage.call_count == 1 api_mock.set_leverage.assert_called_with(symbol="BTC/USDC:USDC", leverage=3.2) @@ -191,7 +226,7 @@ def test__lev_prep_bitget(default_conf, mocker): exchange._lev_prep("BTC/USDC:USDC", 19.99, "sell") - assert api_mock.set_margin_mode.call_count == 0 + assert api_mock.set_margin_mode.call_count == 1 assert api_mock.set_leverage.call_count == 1 api_mock.set_leverage.assert_called_with(symbol="BTC/USDC:USDC", leverage=19.99) diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 7458a4a5b..187a5f64e 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -1,5 +1,6 @@ import copy import logging +import re from copy import deepcopy from datetime import UTC, datetime, timedelta from random import randint @@ -348,6 +349,22 @@ def test_validate_freqai_compat(default_conf, mocker, caplog): ex.validate_freqai(default_conf) +def test_validate_demo_trading(default_conf_usdt, mocker, caplog): + # Test - nothing enabled so nothing happens + ex = get_patched_exchange(mocker, default_conf_usdt, exchange="kraken") + ex.validate_demo_trading(default_conf_usdt["exchange"]) + + default_conf_usdt["exchange"]["demo_trading"] = True + with pytest.raises(ConfigurationError, match=r"Demo trading is not supported for .*"): + ex.validate_demo_trading(default_conf_usdt["exchange"]) + + msg = r"Demo trading enabled for .*" + assert not log_has_re(msg, caplog) + ex_bybit = get_patched_exchange(mocker, default_conf_usdt, exchange="bybit") + ex_bybit.validate_demo_trading(default_conf_usdt["exchange"]) + assert log_has_re(msg, caplog) + + @pytest.mark.parametrize( "price,precision_mode,precision,expected", [ @@ -1061,6 +1078,24 @@ def test_create_dry_run_order(default_conf, mocker, side, exchange_name, leverag assert order["cost"] == 1 * 200 +def test_create_dry_run_order_id_unique_with_same_timestamp(default_conf, mocker, time_machine): + exchange = get_patched_exchange(mocker, default_conf) + + time_machine.move_to("2026-04-27T04:49:57.438232Z", tick=False) + order1 = exchange.create_dry_run_order( + pair="ETH/USDT", ordertype="limit", side="sell", amount=1, rate=2.05, leverage=1.0 + ) + order2 = exchange.create_dry_run_order( + pair="ETH/USDT", ordertype="limit", side="sell", amount=1, rate=2.05, leverage=1.0 + ) + + assert order1["id"] != order2["id"] + assert re.match( + r"^dry_run_sell_ETH/USDT_[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[0-9a-f]{4}-[0-9a-f]{12}$", + order1["id"], + ) + + @pytest.mark.parametrize( "side,is_short,order_reason", [ @@ -4296,12 +4331,29 @@ def test_fetch_order_or_stoploss_order(default_conf, mocker): @pytest.mark.parametrize("exchange_name", EXCHANGES) -def test_name(default_conf, mocker, exchange_name): - exchange = get_patched_exchange(mocker, default_conf, exchange=exchange_name) +def test_name(default_conf_usdt, mocker, exchange_name): + # exchange = get_patched_exchange(mocker, default_conf_usdt, exchange=exchange_name) + api_mock = MagicMock() + api_mock.name = exchange_name.title() + api_mock.id = exchange_name + mocker.patch(f"{EXMS}._init_ccxt", MagicMock(return_value=api_mock)) + mocker.patch(f"{EXMS}._load_async_markets") + # mocker.patch(f"{EXMS}.validate_timeframes") + # mocker.patch(f"{EXMS}.validate_stakecurrency") + # mocker.patch(f"{EXMS}.validate_pricing") + default_conf_usdt["exchange"]["name"] = "exchange_name" + exchange = ExchangeResolver.load_exchange(default_conf_usdt, validate=False) assert exchange.name == exchange_name.title() assert exchange.id == exchange_name + default_conf_usdt["exchange"]["demo_trading"] = True + + exchange_demo = ExchangeResolver.load_exchange(default_conf_usdt, validate=False) + + assert exchange_demo.name == f"{exchange_name.title()} (Demo)" + assert exchange_demo.id == f"{exchange_name}_demo" + @pytest.mark.parametrize( "trading_mode,amount", diff --git a/tests/exchange/test_exchange_ws.py b/tests/exchange/test_exchange_ws.py index 2cc4873fe..315f3ef5f 100644 --- a/tests/exchange/test_exchange_ws.py +++ b/tests/exchange/test_exchange_ws.py @@ -5,9 +5,11 @@ from datetime import timedelta from time import sleep from unittest.mock import AsyncMock, MagicMock +import pytest from ccxt import NotSupported from freqtrade.enums import CandleType +from freqtrade.exceptions import TemporaryError from freqtrade.exchange.exchange_ws import ExchangeWS from ft_client.test_client.test_rest_client import log_has_re @@ -26,8 +28,8 @@ def test_exchangews_init(mocker): assert exchange_ws._background_tasks == set() assert exchange_ws._klines_watching == set() assert exchange_ws._klines_scheduled == set() - assert exchange_ws.klines_last_refresh == {} - assert exchange_ws.klines_last_request == {} + assert exchange_ws._klines_last_refresh == {} + assert exchange_ws._klines_last_request == {} # Cleanup exchange_ws.cleanup() @@ -49,6 +51,84 @@ def test_exchangews_cleanup_error(mocker, caplog): exchange_ws.cleanup() +def test_exchangews_reset_connections_timeout_and_exception(mocker, caplog): + config = MagicMock() + ccxt_object = MagicMock() + mocker.patch("freqtrade.exchange.exchange_ws.ExchangeWS._start_forever", MagicMock()) + + exchange_ws = ExchangeWS(config, ccxt_object) + exchange_ws._loop = MagicMock() + exchange_ws._loop.is_closed.return_value = False + + timeout_future = MagicMock() + timeout_future.result.side_effect = TimeoutError("timed out") + + error_future = MagicMock() + error_future.result.side_effect = RuntimeError("broken future") + + def fake_run_coroutine_threadsafe(coro, loop): + # Avoid coroutine warnings since we don't execute it in this unit test. + coro.close() + fake_run_coroutine_threadsafe.calls += 1 + return timeout_future if fake_run_coroutine_threadsafe.calls == 1 else error_future + + fake_run_coroutine_threadsafe.calls = 0 + + mock_run = mocker.patch( + "freqtrade.exchange.exchange_ws.asyncio.run_coroutine_threadsafe", + side_effect=fake_run_coroutine_threadsafe, + ) + + exchange_ws.reset_connections() + assert log_has_re("Timed out while resetting websocket connections", caplog) + assert log_has_re("Resetting exchange WS connections", caplog) + assert mock_run.call_count == 1 + + exchange_ws.reset_connections(cleanup=True) + + assert mock_run.call_count == 2 + assert log_has_re("Exception while resetting websocket connections", caplog) + assert log_has_re("Cleaning up exchange WS connections", caplog) + + exchange_ws.cleanup() + + +def test_exchangews_cleanup_thread_timeout_warning(mocker, caplog): + config = MagicMock() + ccxt_object = MagicMock() + mocker.patch("freqtrade.exchange.exchange_ws.ExchangeWS._start_forever", MagicMock()) + + exchange_ws = ExchangeWS(config, ccxt_object) + exchange_ws._loop = MagicMock() + exchange_ws._loop.is_closed.return_value = True + + thread_mock = MagicMock() + thread_mock.is_alive.return_value = True + exchange_ws._thread = thread_mock + + exchange_ws.cleanup() + + thread_mock.join.assert_called_once_with(timeout=5) + assert log_has_re("Websocket loop thread did not stop within timeout", caplog) + + +def test_exchangews_schedule_ohlcv_loop_not_ready(mocker, caplog): + config = MagicMock() + ccxt_object = MagicMock() + mocker.patch("freqtrade.exchange.exchange_ws.ExchangeWS._start_forever", MagicMock()) + run_threadsafe = mocker.patch("freqtrade.exchange.exchange_ws.asyncio.run_coroutine_threadsafe") + + exchange_ws = ExchangeWS(config, ccxt_object) + exchange_ws.schedule_ohlcv("ETH/BTC", "1m", CandleType.SPOT) + + assert exchange_ws._klines_watching == set() + assert exchange_ws._klines_last_request == {} + assert run_threadsafe.call_count == 0 + assert log_has_re("Websocket loop not ready. Could not schedule ETH/BTC, 1m", caplog) + + exchange_ws.cleanup() + + def patch_eventloop_threading(exchange): init_event = threading.Event() @@ -87,6 +167,7 @@ async def test_exchangews_ohlcv(mocker, time_machine, caplog): ccxt_object.un_watch_ohlcv_for_symbols = AsyncMock(side_effect=[NotSupported, ValueError]) ccxt_object.watch_ohlcv = AsyncMock(side_effect=controlled_sleeper) + ccxt_object.has = {"unWatchOHLCVForSymbols": True} ccxt_object.close = AsyncMock() time_machine.move_to("2024-11-01 01:00:02 +00:00") @@ -180,7 +261,7 @@ async def test_exchangews_get_ohlcv(mocker, caplog): mocker.patch("freqtrade.exchange.exchange_ws.ExchangeWS._start_forever", MagicMock()) exchange_ws = ExchangeWS(config, ccxt_object) - exchange_ws.klines_last_refresh = { + exchange_ws._klines_last_refresh = { ("ETH/USDT", "1m", CandleType.SPOT): 1635840120000, ("ETH/USDT", "5m", CandleType.SPOT): 1635840600000, } @@ -209,7 +290,7 @@ async def test_exchangews_get_ohlcv(mocker, caplog): # Change "received" times to be before the candle starts. # This should trigger the "time sync" warning. - exchange_ws.klines_last_refresh = { + exchange_ws._klines_last_refresh = { ("ETH/USDT", "1m", CandleType.SPOT): 1635840110000, ("ETH/USDT", "5m", CandleType.SPOT): 1635840600000, } @@ -228,3 +309,143 @@ async def test_exchangews_get_ohlcv(mocker, caplog): assert log_has_re(msg, caplog) exchange_ws.cleanup() + + +async def test_exchangews_get_ohlcv_missing_refresh_date(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], + ] + } + } + mocker.patch("freqtrade.exchange.exchange_ws.ExchangeWS._start_forever", MagicMock()) + + exchange_ws = ExchangeWS(config, ccxt_object) + exchange_ws._klines_last_refresh = {} + + # No refresh-date entry should not raise KeyError. + resp = await exchange_ws.get_ohlcv("ETH/USDT", "1m", CandleType.SPOT, 1635840120000) + assert resp[0] == "ETH/USDT" + assert resp[1] == "1m" + assert resp[4] is True + assert not log_has_re(r".*Candle date > last refresh.*", caplog) + + exchange_ws.cleanup() + + +def test_exchangews_ohlcvs_deepcopy_and_retry(mocker): + config = MagicMock() + ccxt_object = MagicMock() + ccxt_object.ohlcvs = { + "ETH/USDT": { + "1m": [[1, 2, 3, 4, 5, 6]], + } + } + mocker.patch("freqtrade.exchange.exchange_ws.ExchangeWS._start_forever", MagicMock()) + + exchange_ws = ExchangeWS(config, ccxt_object) + + call_count = {"count": 0} + + def deepcopy_side_effect(value): + call_count["count"] += 1 + if call_count["count"] < 3: + raise RuntimeError("copy failed") + return [candle.copy() for candle in value] + + mocker.patch("freqtrade.exchange.exchange_ws.deepcopy", deepcopy_side_effect) + + result = exchange_ws.ohlcvs("ETH/USDT", "1m") + + assert call_count["count"] == 3 + assert result == [[1, 2, 3, 4, 5, 6]] + assert result is not ccxt_object.ohlcvs["ETH/USDT"]["1m"] + + # Fail all the time + mocker.patch("freqtrade.exchange.exchange_ws.deepcopy", side_effect=RuntimeError("copy failed")) + with pytest.raises(TemporaryError, match=r"Error deepcopying: copy failed"): + exchange_ws.ohlcvs("ETH/USDT", "1m") + + exchange_ws.cleanup() + + +def test_exchangews_get_ohlcv_with_refresh(mocker): + config = MagicMock() + ccxt_object = MagicMock() + mocker.patch("freqtrade.exchange.exchange_ws.ExchangeWS._start_forever", MagicMock()) + + exchange_ws = ExchangeWS(config, ccxt_object) + ohlcvs_mock = mocker.patch.object( + exchange_ws, "ohlcvs", return_value=[[10, 11, 12, 13, 14, 15]] + ) + + paircomb = ("ETH/USDT", "1m", CandleType.SPOT) + exchange_ws._klines_last_refresh[paircomb] = 123456789 + + candles, refresh = exchange_ws.get_ohlcv_with_refresh("ETH/USDT", "1m", CandleType.SPOT) + + ohlcvs_mock.assert_called_once_with("ETH/USDT", "1m") + assert candles == [[10, 11, 12, 13, 14, 15]] + assert refresh == 123456789 + + candles, refresh = exchange_ws.get_ohlcv_with_refresh("ETH/USDT", "5m", CandleType.SPOT) + assert candles == [[10, 11, 12, 13, 14, 15]] + assert refresh == 0 + + exchange_ws.cleanup() + + +def test_exchangews_continuous_stopped_task_exception(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], + ] + } + } + mocker.patch("freqtrade.exchange.exchange_ws.ExchangeWS._start_forever", MagicMock()) + + exchange_ws = ExchangeWS(config, ccxt_object) + exchange_ws._loop = MagicMock() + exchange_ws._loop.is_closed.return_value = False + + paircomb = ("ETH/USDT", "1m", CandleType.SPOT) + exchange_ws._klines_scheduled.add(paircomb) + exchange_ws._klines_last_refresh[paircomb] = 1 + + task = MagicMock() + task.cancelled.return_value = False + task.result.side_effect = RuntimeError("unexpected") + exchange_ws._background_tasks.add(task) + + completed_future = MagicMock() + completed_future.result.return_value = None + + def side_effect(coro, loop): + coro.close() + return completed_future + + run_threadsafe = mocker.patch( + "freqtrade.exchange.exchange_ws.asyncio.run_coroutine_threadsafe", + side_effect=side_effect, + ) + + exchange_ws._continuous_stopped(task, "ETH/USDT", "1m", CandleType.SPOT) + + assert task not in exchange_ws._background_tasks + assert paircomb not in exchange_ws._klines_scheduled + assert paircomb not in exchange_ws._klines_last_refresh + assert ccxt_object.ohlcvs["ETH/USDT"].get("1m") is None + assert run_threadsafe.call_count == 1 + assert log_has_re("Unhandled exception in watch task callback for ETH/USDT, 1m", caplog) + + exchange_ws.cleanup() diff --git a/tests/exchange_online/conftest.py b/tests/exchange_online/conftest.py index ba04598cd..50d2fe116 100644 --- a/tests/exchange_online/conftest.py +++ b/tests/exchange_online/conftest.py @@ -603,6 +603,7 @@ EXCHANGES: dict[str, TestExchangeOnlineSetup] = { "status": "closed", "type": "market", "amount": 0.0004, + "filled": 0.0004, "side": "sell", "triggerPrice": None, "stopPrice": None, @@ -646,6 +647,8 @@ EXCHANGES: dict[str, TestExchangeOnlineSetup] = { "price": None, "status": "open", "amount": 0.0004, + # TODO: filled should be 0, not None. + "filled": None, "side": "buy", "triggerPrice": 71641.0, "stopPrice": 71641.0, @@ -681,8 +684,7 @@ EXCHANGES: dict[str, TestExchangeOnlineSetup] = { "datetime": "2026-03-21T07:32:21.555Z", "price": None, "status": "canceled", - # TODO: filled should be 0, not None. - "filled": None, + "filled": 0.0, }, }, ], diff --git a/tests/exchange_online/test_ccxt_compat.py b/tests/exchange_online/test_ccxt_compat.py index c44bb216a..556d5edf6 100644 --- a/tests/exchange_online/test_ccxt_compat.py +++ b/tests/exchange_online/test_ccxt_compat.py @@ -287,7 +287,7 @@ class TestCCXTExchange: # Check if last-timeframe is within the last 2 intervals now = datetime.now(UTC) - timedelta(minutes=(timeframe_to_minutes(timeframe) * 2)) assert exch.klines(pair_tf).iloc[-1]["date"] >= timeframe_to_prev_date(timeframe, now) - assert exch.klines(pair_tf)["date"].astype(int).iloc[0] // 1e6 == since_ms + assert exch.klines(pair_tf)["date"].dt.as_unit("ms").astype("int64").iloc[0] == since_ms def _ccxt__async_get_candle_history( self, exchange, pair: str, timeframe: str, candle_type: CandleType, factor: float = 0.9 diff --git a/tests/optimize/test_backtesting.py b/tests/optimize/test_backtesting.py index e051e6b3c..95250e7a7 100644 --- a/tests/optimize/test_backtesting.py +++ b/tests/optimize/test_backtesting.py @@ -757,10 +757,12 @@ def test_backtest__check_trade_exit(default_conf, mocker) -> None: def test_backtest_one(default_conf, mocker, testdatadir) -> None: default_conf["use_exit_signal"] = False default_conf["max_open_trades"] = 10 + default_conf["runmode"] = RunMode.BACKTEST patch_exchange(mocker) 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}.get_pair_base_currency", lambda _, x: x.split("/")[0]) backtesting = Backtesting(default_conf) backtesting._set_strategy(backtesting.strategylist[0]) pair = "UNITTEST/BTC" @@ -858,6 +860,9 @@ def test_backtest_one(default_conf, mocker, testdatadir) -> None: "funding_fees": [0.0, 0.0], } ) + # TODO: pandas3 - create correctly above ?!? + expected["open_date"] = expected["open_date"].astype("datetime64[ms, UTC]") + expected["close_date"] = expected["close_date"].astype("datetime64[ms, UTC]") pd.testing.assert_frame_equal(results, expected) assert "orders" in results.columns data_pair = processed[pair] @@ -875,13 +880,23 @@ def test_backtest_one(default_conf, mocker, testdatadir) -> None: ln1.iloc[0]["low"], 6 ) < round(t["close_rate"], 6) < round(ln1.iloc[0]["high"], 6) + wallet_summary = result["wallet_summary"] + assert isinstance(wallet_summary, pd.DataFrame) + assert len(wallet_summary) == 255 + unique_currencies = wallet_summary["currency"].value_counts() + assert unique_currencies["BTC"] == 200 + assert unique_currencies["UNITTEST"] == 55 + @pytest.mark.parametrize("use_detail", [True, False]) def test_backtest_one_detail(default_conf_usdt, mocker, testdatadir, use_detail) -> None: default_conf_usdt["use_exit_signal"] = False + default_conf_usdt["runmode"] = RunMode.BACKTEST patch_exchange(mocker) 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}.get_pair_base_currency", lambda _, x: x.split("/")[0]) + default_conf_usdt["unfilledtimeout"] = { "entry": 11, "exit": 30, @@ -968,6 +983,12 @@ def test_backtest_one_detail(default_conf_usdt, mocker, testdatadir, use_detail) ) assert late_entry > 0 + wallet_summary = result["wallet_summary"] + assert isinstance(wallet_summary, pd.DataFrame) + assert len(wallet_summary) == 591 if use_detail else 597 + unique_currencies = wallet_summary["currency"].value_counts() + assert unique_currencies["USDT"] == 576 + assert unique_currencies["XRP"] == 15 if use_detail else 21 @pytest.mark.parametrize( diff --git a/tests/optimize/test_backtesting_adjust_position.py b/tests/optimize/test_backtesting_adjust_position.py index 33fd87d83..f698173c9 100644 --- a/tests/optimize/test_backtesting_adjust_position.py +++ b/tests/optimize/test_backtesting_adjust_position.py @@ -83,6 +83,9 @@ def test_backtest_position_adjustment(default_conf, fee, mocker, testdatadir) -> "funding_fees": [0.0, 0.0], } ) + # TODO: pandas3 - create correctly above ?!? + expected["open_date"] = expected["open_date"].astype("datetime64[ms, UTC]") + expected["close_date"] = expected["close_date"].astype("datetime64[ms, UTC]") results_no = results.drop(columns=["orders"]) pd.testing.assert_frame_equal(results_no, expected, check_exact=True) diff --git a/tests/optimize/test_optimize_reports.py b/tests/optimize/test_optimize_reports.py index b37080f43..fcf5abffd 100644 --- a/tests/optimize/test_optimize_reports.py +++ b/tests/optimize/test_optimize_reports.py @@ -28,6 +28,7 @@ from freqtrade.optimize.optimize_reports import ( generate_trading_stats, show_sorted_pairlist, store_backtest_results, + text_table_add_metrics, text_table_bt_results, text_table_strategy, ) @@ -36,6 +37,7 @@ from freqtrade.optimize.optimize_reports.optimize_reports import ( _get_resample_from_period, calc_streak, generate_tag_metrics, + generate_wallet_stats, ) from freqtrade.resolvers.strategy_resolver import StrategyResolver from freqtrade.util import dt_ts, format_duration @@ -616,6 +618,63 @@ def test_text_table_strategy(testdatadir, capsys): ) +def test_generate_wallet_stats_extended_metrics(): + wallet_df = pd.DataFrame( + { + "date": [ + dt_utc(2025, 1, 1, 0, 0, 0), + dt_utc(2025, 1, 1, 12, 0, 0), + dt_utc(2025, 1, 1, 18, 0, 0), + dt_utc(2025, 1, 3, 0, 0, 0), + ], + "currency": ["BTC", "BTC", "BTC", "BTC"], + "rate": [1.0, 1.0, 1.0, 1.0], + "balance": [100.0, 120.0, 80.0, 110.0], + } + ) + + stats = generate_wallet_stats(wallet_df, "BTC") + + assert "sharpe" in stats + assert "sortino" in stats + assert "calmar" in stats + assert "max_drawdown_account" in stats + assert "max_drawdown_abs" in stats + assert pytest.approx(stats["max_drawdown_account"]) == 1 / 3 + assert stats["drawdown_start"] == "2025-01-01 12:00:00" + assert stats["drawdown_end"] == "2025-01-01 18:00:00" + + +def test_text_table_add_metrics_shows_wallet_ratios(testdatadir, capsys): + filename = testdatadir / "backtest_results/backtest-result.json" + bt_data = load_backtest_stats(filename) + strat_results = next(iter(bt_data["strategy"].values())) + strat_results["wallet_stats"] = { + "low_balance": 0.95, + "high_balance": 1.12, + "low_date": "2025-01-01 18:00:00", + "high_date": "2025-01-01 12:00:00", + "sharpe": 1.23, + "sortino": 2.34, + "calmar": 3.45, + "max_drawdown_account": 0.12, + "max_relative_drawdown": 0.15, + "max_drawdown_abs": 0.05, + "drawdown_start": "2025-01-01 12:00:00", + "drawdown_end": "2025-01-01 18:00:00", + "max_drawdown_high": 1.12, + "max_drawdown_low": 0.95, + } + + text_table_add_metrics(strat_results) + text = capsys.readouterr().out + + assert "Sharpe (daily wallet balance)" in text + assert "Sortino (daily wallet balance)" in text + assert "Calmar (daily wallet balance)" in text + assert "Max % of account underwater (balance)" in text + + def test_generate_periodic_breakdown_stats(testdatadir): filename = testdatadir / "backtest_results/backtest-result.json" bt_data = load_backtest_data(filename).to_dict(orient="records") @@ -654,7 +713,7 @@ def test_generate_periodic_breakdown_stats(testdatadir): def test__get_resample_from_period(): - assert _get_resample_from_period("day") == "1d" + assert _get_resample_from_period("day") == "1D" assert _get_resample_from_period("week") == "1W-MON" assert _get_resample_from_period("month") == "1ME" assert _get_resample_from_period("weekday") == "weekday" diff --git a/tests/persistence/test_db_migration.py b/tests/persistence/test_db_migration.py new file mode 100644 index 000000000..5cd75f038 --- /dev/null +++ b/tests/persistence/test_db_migration.py @@ -0,0 +1,69 @@ +from unittest.mock import MagicMock + +from freqtrade.persistence.base import ModelBase +from freqtrade.persistence.custom_data import _CustomData +from freqtrade.persistence.db_migration import migrate_db +from freqtrade.persistence.key_value_store import _KeyValueStoreModel +from freqtrade.persistence.pairlock import PairLock +from freqtrade.persistence.trade_model import Trade +from freqtrade.persistence.wallet_history import WalletHistory + + +def test_migrate_db_detail(mocker): + # Expected models to be migrated based on the registered models + expected_models = {mapper.class_.__name__ for mapper in ModelBase.registry.mappers} + session_target = MagicMock() + + order = MagicMock() + trade = MagicMock(orders=[order]) + pairlock = MagicMock() + kv = MagicMock() + custom_data = MagicMock() + wallet_history = MagicMock() + + kv_session = MagicMock() + kv_session.scalars.return_value = [kv] + custom_data_session = MagicMock() + custom_data_session.scalars.return_value = [custom_data] + wallet_history_session = MagicMock() + wallet_history_session.scalars.return_value = [wallet_history] + + mocker.patch.object(Trade, "get_trades", return_value=[trade]) + mocker.patch.object(PairLock, "get_all_locks", return_value=[pairlock]) + mocker.patch.object(_KeyValueStoreModel, "session", kv_session, create=True) + mocker.patch.object(_CustomData, "session", custom_data_session, create=True) + mocker.patch.object(WalletHistory, "session", wallet_history_session, create=True) + + make_transient_mock = mocker.patch("freqtrade.persistence.db_migration.make_transient") + set_sequence_ids_mock = mocker.patch("freqtrade.persistence.db_migration.set_sequence_ids") + + # max ids for Trade, Order, PairLock, KeyValueStore, CustomData, WalletHistory + session_target.scalar.side_effect = [10, 11, 12, 13, 14, 15] + session_target.get_bind.return_value = "bind" + + migrate_db(session_target) + + assert session_target.add.call_count == 5 + # Order objects are linked to trades, so they are not added explicitly + + assert session_target.add.call_count == len(expected_models) - 1 + session_target.add.assert_any_call(trade) + session_target.add.assert_any_call(pairlock) + session_target.add.assert_any_call(kv) + session_target.add.assert_any_call(custom_data) + session_target.add.assert_any_call(wallet_history) + + assert session_target.commit.call_count == 5 + assert make_transient_mock.call_count == 6 + make_transient_mock.assert_any_call(trade) + make_transient_mock.assert_any_call(order) + + set_sequence_ids_mock.assert_called_once_with( + "bind", + trade_id=11, + order_id=12, + pairlock_id=13, + kv_id=14, + custom_data_id=15, + wallet_history_id=16, + ) diff --git a/tests/persistence/test_migrations.py b/tests/persistence/test_migrations.py index 2118193fa..9c2212486 100644 --- a/tests/persistence/test_migrations.py +++ b/tests/persistence/test_migrations.py @@ -383,11 +383,19 @@ def test_migrate_set_sequence_ids(): ) engine.name = "postgresql" - set_sequence_ids(engine, 22, 55, 5, 3, 1) + set_sequence_ids( + engine, + order_id=22, + trade_id=55, + pairlock_id=5, + kv_id=3, + custom_data_id=10, + wallet_history_id=15, + ) # begin called once and connection.execute invoked for each provided sequence id assert engine.begin.call_count == 1 - assert conn.execute.call_count == 5 + assert conn.execute.call_count == 6 assert ( conn.execute.call_args_list[0][0][0].text == "ALTER SEQUENCE orders_id_seq RESTART WITH 22" ) @@ -404,7 +412,12 @@ def test_migrate_set_sequence_ids(): ) assert ( conn.execute.call_args_list[4][0][0].text - == "ALTER SEQUENCE trade_custom_data_id_seq RESTART WITH 1" + == "ALTER SEQUENCE trade_custom_data_id_seq RESTART WITH 10" + ) + + assert ( + conn.execute.call_args_list[5][0][0].text + == "ALTER SEQUENCE wallet_history_id_seq RESTART WITH 15" ) engine.reset_mock() diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index 5d43e912f..9d2c4bed8 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -2850,10 +2850,10 @@ def test_backtesting_modes( def test_DelistFilter_error(whitelist_conf) -> None: whitelist_conf["pairlists"] = [{"method": "StaticPairList"}, {"method": "DelistFilter"}] exchange_mock = MagicMock() - exchange_mock._ft_has = {"has_delisting": False} + exchange_mock.get_option = MagicMock(return_value=False) with pytest.raises( OperationalException, - match=r"DelistFilter doesn't support this exchange and trading mode combination\.", + match=r"DelistFilter doesn't support .* in .* mode\.", ): PairListManager(exchange_mock, whitelist_conf, MagicMock()) diff --git a/tests/plugins/test_pairlocks.py b/tests/plugins/test_pairlocks.py index 678355f1c..729691bf5 100644 --- a/tests/plugins/test_pairlocks.py +++ b/tests/plugins/test_pairlocks.py @@ -158,3 +158,36 @@ def test_PairLocks_reason(use_db): PairLocks.reset_locks() PairLocks.use_db = True + + +@pytest.mark.parametrize("use_db", (False, True)) +@pytest.mark.usefixtures("init_persistence") +def test_PairLocks_no_duplicates(use_db, time_machine): + PairLocks.timeframe = "5m" + PairLocks.use_db = use_db + PairLocks.reset_locks() + # No lock should be present + assert len(PairLocks.get_all_locks()) == 0 + time_machine.move_to("2026-01-05 20:00:05 +00:00", tick=False) + + assert PairLocks.use_db == use_db + PairLocks.lock_pair("XRP/USDT", dt_now() + timedelta(minutes=4), "TestLock1") + assert len(PairLocks.get_all_locks()) == 1 + + PairLocks.lock_pair("XRP/USDT", dt_now() + timedelta(minutes=4), "TestLock1") + assert len(PairLocks.get_all_locks()) == 1 + + # Different Reason - should create a new lock + PairLocks.lock_pair("XRP/USDT", dt_now() + timedelta(minutes=4), "TestLock2") + assert len(PairLocks.get_all_locks()) == 2 + + # Different end-time - should create a new lock + PairLocks.lock_pair("XRP/USDT", dt_now() + timedelta(minutes=5), "TestLock1") + assert len(PairLocks.get_all_locks()) == 3 + + # Different side - should create a new lock + PairLocks.lock_pair("XRP/USDT", dt_now() + timedelta(minutes=4), "TestLock1", side="long") + assert len(PairLocks.get_all_locks()) == 4 + + PairLocks.reset_locks() + PairLocks.use_db = True diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index fe82e9904..86084ad5f 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -1434,8 +1434,8 @@ def test_rpc_add_and_delete_lock(mocker, default_conf): pair = "ETH/BTC" rpc._rpc_add_lock(pair, datetime.now(UTC) + timedelta(minutes=4), "", "*") - rpc._rpc_add_lock(pair, datetime.now(UTC) + timedelta(minutes=5), "", "*") - rpc._rpc_add_lock(pair, datetime.now(UTC) + timedelta(minutes=10), "", "*") + rpc._rpc_add_lock(pair, datetime.now(UTC) + timedelta(minutes=20), "", "*") + rpc._rpc_add_lock(pair, datetime.now(UTC) + timedelta(minutes=50), "", "*") locks = rpc._rpc_locks() assert locks["lock_count"] == 3 diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 1247d4c02..63a1d6efd 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -7,8 +7,10 @@ import logging import time from copy import deepcopy from datetime import UTC, datetime, timedelta +from io import BytesIO from pathlib import Path from unittest.mock import ANY, MagicMock, PropertyMock, patch +from zipfile import ZipFile import pandas as pd import pytest @@ -1434,6 +1436,41 @@ def test_api_stats(botclient, mocker, ticker, fee, markets, is_short): assert "draws" in rc.json()["durations"] +@pytest.mark.parametrize("is_short", [True, False]) +def test_api_historic_balance(botclient, mocker, ticker, fee, markets, is_short): + ftbot, client = botclient + patch_get_signal(ftbot, enter_long=not is_short, enter_short=is_short) + mocker.patch.multiple( + EXMS, + get_balances=MagicMock(return_value=ticker), + fetch_ticker=ticker, + get_fee=fee, + markets=PropertyMock(return_value=markets), + ) + + rc = client_get(client, f"{BASE_URI}/historic_balance") + assert_response(rc, 200) + resp = rc.json() + assert "columns" in resp + assert "data" in resp + assert "length" in resp + assert "capture_start_ts" in resp + assert resp["length"] == 0 + + ftbot.wallets.record_wallet_state() + + rc = client_get(client, f"{BASE_URI}/historic_balance") + assert_response(rc, 200) + resp1 = rc.json() + assert "columns" in resp1 + assert "data" in resp1 + assert "length" in resp1 + assert "capture_start_ts" in resp1 + assert resp1["length"] == 1 + assert "__date_ts" in resp1["columns"] + assert "total_quote" in resp1["columns"] + + def test_api_performance(botclient, fee): ftbot, client = botclient patch_get_signal(ftbot) @@ -3275,7 +3312,7 @@ def test_api_patch_backtest_history_entry(botclient, tmp_path: Path): assert fileres[CURRENT_TEST_STRATEGY]["notes"] == "FooBar" -def test_api_patch_backtest_market_change(botclient, tmp_path: Path): +def test_api_backtest_market_change(botclient, tmp_path: Path): ftbot, client = botclient # Create a temporary directory and file @@ -3313,6 +3350,55 @@ def test_api_patch_backtest_market_change(botclient, tmp_path: Path): ] +def test_api_backtest_wallets(botclient, tmp_path: Path): + ftbot, client = botclient + + # Create a temporary directory and file + bt_results_base = tmp_path / "backtest_results" + bt_results_base.mkdir() + zip_file = bt_results_base / "backtest_15.zip" + with ZipFile(zip_file, "w") as zipf: + wallet_df = pd.DataFrame( + { + "date": [ + "2018-01-01T00:00:00Z", + "2018-01-01T00:00:00Z", + "2018-01-01T00:05:00Z", + "2018-01-01T00:05:00Z", + ], + "currency": ["ETH", "BTC", "ETH", "BTC"], + "rate": [2000, 60_000, 2001, 60_001], + "balance": [0.5, 0.25, 0.5, 0.25], + } + ) + wallet_df["date"] = pd.to_datetime(wallet_df["date"]) + wallet_buf = BytesIO() + wallet_df.reset_index().to_feather(wallet_buf, compression_level=9, compression="lz4") + wallet_buf.seek(0) + zipf.writestr("backtest_15_SampleStrategy_wallet.feather", wallet_buf.read()) + + # Wrong basedirectory + rc = client_get(client, f"{BASE_URI}/backtest/history/randomFile.json/SampleStrategy/wallet") + assert_response(rc, 503) + + ftbot.config["user_data_dir"] = tmp_path + ftbot.config["runmode"] = RunMode.WEBSERVER + + # Nonexisting file - fails "is_file_in_dir" check + rc = client_get(client, f"{BASE_URI}/backtest/history/randomFile.json/SampleStrategy/wallet") + assert_response(rc, 400) + + rc = client_get(client, f"{BASE_URI}/backtest/history/backtest_15/SampleStrategy/wallet") + assert_response(rc, 200) + result = rc.json() + assert result["length"] == 2 + assert result["columns"] == ["date", "__date_ts", "total_quote"] + assert result["data"] == [ + ["2018-01-01T00:00:00Z", 1514764800000, 16000.0], + ["2018-01-01T00:05:00Z", 1514765100000, 16000.75], + ] + + def test_health(botclient): _ftbot, client = botclient diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index f64c2c3cb..4ddc35e88 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -22,6 +22,7 @@ from freqtrade.strategy.parameters import ( ) from freqtrade.strategy.strategy_validation import StrategyResultValidator from freqtrade.util import dt_now +from freqtrade.util.datetime_helpers import dt_now_no_micro from tests.conftest import CURRENT_TEST_STRATEGY, TRADE_SIDES, log_has, log_has_re from .strats.strategy_test_v3 import StrategyTestV3 @@ -33,7 +34,7 @@ _STRATEGY.dp = DataProvider({}, None, None) def test_returns_latest_signal(ohlcv_history): - ohlcv_history.loc[1, "date"] = dt_now() + ohlcv_history.loc[1, "date"] = dt_now_no_micro() # Take a copy to correctly modify the call mocked_history = ohlcv_history.copy() mocked_history["enter_long"] = 0 @@ -160,7 +161,7 @@ def test_get_signal_exception_valueerror(mocker, caplog, ohlcv_history): def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history): # default_conf defines a 5m interval. we check interval * 2 + 5m # this is necessary as the last candle is removed (partial candles) by default - ohlcv_history.loc[1, "date"] = dt_now() - timedelta(minutes=16) + ohlcv_history.loc[1, "date"] = dt_now_no_micro() - timedelta(minutes=16) # Take a copy to correctly modify the call mocked_history = ohlcv_history.copy() mocked_history["exit_long"] = 0 @@ -179,7 +180,7 @@ def test_get_signal_old_dataframe(default_conf, mocker, caplog, ohlcv_history): def test_get_signal_no_sell_column(default_conf, mocker, caplog, ohlcv_history): # default_conf defines a 5m interval. we check interval * 2 + 5m # this is necessary as the last candle is removed (partial candles) by default - ohlcv_history.loc[1, "date"] = dt_now() + ohlcv_history.loc[1, "date"] = dt_now_no_micro() # Take a copy to correctly modify the call mocked_history = ohlcv_history.copy() # Intentionally don't set sell column @@ -223,7 +224,7 @@ def test_ignore_expired_candle(default_conf): def test_assert_df_raise(mocker, caplog, ohlcv_history): - ohlcv_history.loc[1, "date"] = dt_now() - timedelta(minutes=16) + ohlcv_history.loc[1, "date"] = dt_now_no_micro() - timedelta(minutes=16) # Take a copy to correctly modify the call mocked_history = ohlcv_history.copy() mocked_history["sell"] = 0 @@ -1040,6 +1041,7 @@ def test_auto_hyperopt_interface_loadparams(default_conf, mocker, caplog): ], ) def test_pandas_warning_direct(ohlcv_history, function, raises, recwarn): + recwarn.clear() df = _STRATEGY.populate_indicators(ohlcv_history, {"pair": "ETH/BTC"}) if raises: assert len(recwarn) == 1 @@ -1053,6 +1055,7 @@ def test_pandas_warning_direct(ohlcv_history, function, raises, recwarn): def test_pandas_warning_through_analyze_pair(ohlcv_history, mocker, recwarn): + recwarn.clear() mocker.patch.object(_STRATEGY.dp, "ohlcv", return_value=ohlcv_history) _STRATEGY.analyze_pair("ETH/BTC") assert len(recwarn) == 0, f"warnings: {', '.join(str(w) for w in recwarn.list)}" diff --git a/tests/test_configuration.py b/tests/test_configuration.py index ba54e7b52..e58a7176a 100644 --- a/tests/test_configuration.py +++ b/tests/test_configuration.py @@ -1045,6 +1045,22 @@ def test__validate_orderflow(default_conf) -> None: validate_config_consistency(conf) +def test__validate_demo_trading(default_conf_usdt) -> None: + conf = deepcopy(default_conf_usdt) + validate_config_consistency(conf) + # explicitly set dry-run to clarify intent + conf["dry_run"] = True + conf["exchange"]["demo_trading"] = True + + with pytest.raises( + ConfigurationError, + match=r"Demo trading cannot be used together with dry_run\.", + ): + validate_config_consistency(conf) + conf["dry_run"] = False + validate_config_consistency(conf) + + def test_validate_edge_removal(default_conf): default_conf["edge"] = { "enabled": True, diff --git a/tests/test_wallets.py b/tests/test_wallets.py index a7f83ebf0..20861108a 100644 --- a/tests/test_wallets.py +++ b/tests/test_wallets.py @@ -7,12 +7,14 @@ from sqlalchemy import select from freqtrade.constants import UNLIMITED_STAKE_AMOUNT from freqtrade.exceptions import DependencyException -from freqtrade.persistence import Trade +from freqtrade.persistence import Trade, WalletHistory +from freqtrade.wallets import PositionWallet, Wallet from tests.conftest import ( EXMS, create_mock_trades, create_mock_trades_usdt, get_patched_freqtradebot, + log_has_re, patch_wallet, ) @@ -607,3 +609,81 @@ def test_dry_run_wallet_initialization(mocker, default_conf_usdt, config, wallet pytest.approx(freqtrade.wallets._wallets[stake_currency].free) == wallets[stake_currency]["free"] - 100.0 ) + + +@pytest.mark.usefixtures("init_persistence") +def test_record_wallet_state_stores_wallet_history(mocker, default_conf_usdt): + freqtrade = get_patched_freqtradebot(mocker, default_conf_usdt) + stake_currency = default_conf_usdt["stake_currency"] + freqtrade.wallets._wallets = { + stake_currency: Wallet(stake_currency, free=100.0, used=50, total=150), + "BTC": Wallet("BTC", free=2.0, used=1.0, total=3.0), + } + freqtrade.wallets._positions = { + "ETH/USDT:USDT": PositionWallet( + symbol="ETH/USDT:USDT", + position=0.8, + collateral=1.0, + leverage=3.0, + side="long", + ) + } + + conversion_rates = {stake_currency: 1.0, "BTC": 70000, "ETH": 2500.1} + mocker.patch.object( + freqtrade.exchange, + "get_conversion_rate", + side_effect=lambda currency, *args, **kwargs: conversion_rates.get(currency, 1.0), + ) + mocker.patch( + "freqtrade.persistence.trade_model.Trade.get_open_trades", + return_value=[ + MagicMock(pair="ETH/USDT:USDT", safe_base_currency="ETH"), + ], + ) + + freqtrade.wallets.record_wallet_state() + + wallet_entries = WalletHistory.session.query(WalletHistory).all() + assert len(wallet_entries) == 3 + assert "total_quote" in repr(wallet_entries[0]) + assert "WalletHistory(" in repr(wallet_entries[0]) + + records_by_currency = {entry.currency: entry for entry in wallet_entries} + assert records_by_currency[stake_currency].balance == 149 + assert records_by_currency[stake_currency].rate == 1.0 + assert records_by_currency["BTC"].rate == 70000 + assert records_by_currency["BTC"].balance == 3 + assert not records_by_currency["BTC"].bot_managed + assert records_by_currency["ETH/USDT:USDT"].balance == 0.8 + assert records_by_currency["ETH/USDT:USDT"].rate == 2500.1 + assert records_by_currency["ETH/USDT:USDT"].bot_managed is True + + +@pytest.mark.usefixtures("init_persistence") +def test_record_wallet_state_stores_wallet_history_error(mocker, default_conf, caplog): + freqtrade = get_patched_freqtradebot(mocker, default_conf) + stake_currency = default_conf["stake_currency"] + freqtrade.wallets._wallets = { + stake_currency: Wallet(stake_currency, free=1.0, used=0.5, total=1.5), + "ETH": Wallet("ETH", free=2.0, used=1.0, total=3.0), + } + freqtrade.wallets._positions = { + "ETH/BTC": PositionWallet( + symbol="ETH/BTC", + position=0.8, + collateral=1.0, + leverage=3.0, + side="long", + ) + } + + # Mock bulk_save_objects to raise an exception + mocker.patch.object( + WalletHistory.session, "bulk_save_objects", side_effect=Exception("DB Error") + ) + freqtrade.wallets.record_wallet_state() + + assert log_has_re(r"Error saving wallet balance records: .*", caplog) + wallet_entries = WalletHistory.session.query(WalletHistory).all() + assert len(wallet_entries) == 0 diff --git a/tests/util/test_datetime_helpers.py b/tests/util/test_datetime_helpers.py index 9069b60c5..babe5b7b9 100644 --- a/tests/util/test_datetime_helpers.py +++ b/tests/util/test_datetime_helpers.py @@ -6,7 +6,9 @@ import time_machine from freqtrade.util import ( dt_floor_day, dt_from_ts, + dt_humanize_delta, dt_now, + dt_now_no_micro, dt_ts, dt_ts_def, dt_ts_none, @@ -16,15 +18,17 @@ from freqtrade.util import ( format_ms_time_det, shorten_date, ) -from freqtrade.util.datetime_helpers import dt_humanize_delta def test_dt_now(): - with time_machine.travel("2021-09-01 05:01:00 +00:00", tick=False) as t: + with time_machine.travel("2021-09-01 05:01:00.123 +00:00", tick=False) as t: now = datetime.now(UTC) assert dt_now() == now assert dt_ts() == int(now.timestamp() * 1000) assert dt_ts(now) == int(now.timestamp() * 1000) + assert dt_now().microsecond != 0.0 + assert dt_now_no_micro().microsecond == 0.0 + assert dt_now_no_micro() == now.replace(microsecond=0) t.shift(timedelta(hours=5)) assert dt_now() >= now diff --git a/tests/util/test_historic_wallets_migration.py b/tests/util/test_historic_wallets_migration.py new file mode 100644 index 000000000..e4a078201 --- /dev/null +++ b/tests/util/test_historic_wallets_migration.py @@ -0,0 +1,500 @@ +from datetime import datetime, timedelta +from unittest.mock import MagicMock + +import pandas as pd +import pytest + +from freqtrade.enums import CandleType +from freqtrade.persistence import KeyValueStore, Order, Trade, WalletHistory +from freqtrade.util import dt_now, dt_utc +from freqtrade.util.migrations.migrate_wallet_history import ( + _migrate_wallet_history, + _prepare_balance_distribution, + migrate_wallet_history, +) +from tests.conftest import EXMS, generate_test_data, get_patched_exchange, log_has_re + + +def create_closed_mock_trade(fee, pair: str, open_date: datetime, close_date: datetime): + """Create a closed trade for wallet history testing.""" + trade = Trade( + pair=pair, + stake_amount=100.0, + amount=10.0, + amount_requested=10.0, + fee_open=fee.return_value, + fee_close=fee.return_value, + open_rate=10.0, + close_rate=11.0, + close_profit=0.1, + close_profit_abs=9.5, + exchange="binance", + is_open=False, + strategy="TestStrategy", + timeframe=5, + open_date=open_date, + close_date=close_date, + is_short=False, + ) + order_entry = Order( + ft_order_side="buy", + ft_pair=pair, + ft_is_open=False, + ft_amount=10.0, + ft_price=10.0, + order_id=f"order_{pair}_entry", + status="closed", + symbol=pair, + order_type="limit", + side="buy", + price=10.0, + average=10.0, + amount=10.0, + filled=10.0, + remaining=0.0, + order_date=open_date, + order_filled_date=open_date, + ) + + order_exit = Order( + ft_order_side="sell", + ft_pair=pair, + ft_is_open=False, + ft_amount=10.0, + ft_price=11.0, + order_id=f"order_{pair}_exit", + status="closed", + symbol=pair, + order_type="limit", + side="sell", + price=11.0, + average=11.0, + amount=10.0, + filled=10.0, + remaining=0.0, + order_date=close_date, + order_filled_date=close_date, + ) + + trade.orders.append(order_entry) + trade.orders.append(order_exit) + return trade + + +@pytest.mark.usefixtures("init_persistence") +def test_migrate_wallet_history_skips_when_no_ohlcv_history(mocker, default_conf_usdt): + """Test that migration is skipped when exchange doesn't support OHLCV history.""" + exchange = MagicMock() + exchange.get_option.return_value = False # ohlcv_has_history = False + + migrate_mock = mocker.patch( + "freqtrade.util.migrations.migrate_wallet_history._migrate_wallet_history" + ) + + migrate_wallet_history(default_conf_usdt, exchange, 1000.0) + + # Should return early without setting the migration flag + assert KeyValueStore.get_int_value("wallet_history_migration") is None + assert not migrate_mock.called + + +@pytest.mark.usefixtures("init_persistence") +def test_migrate_wallet_history_skips_when_already_migrated(mocker, default_conf_usdt): + """Test that migration is skipped if already completed.""" + exchange = MagicMock() + exchange.get_option.return_value = True + + migrate_mock = mocker.patch( + "freqtrade.util.migrations.migrate_wallet_history._migrate_wallet_history" + ) + + # Set migration as already completed + KeyValueStore.store_value("wallet_history_migration", 1) + + migrate_wallet_history(default_conf_usdt, exchange, 1000.0) + # Should not call any migration logic + assert KeyValueStore.get_int_value("wallet_history_migration") == 1 + assert not migrate_mock.called + + +@pytest.mark.usefixtures("init_persistence") +def test_migrate_wallet_history_no_trades(default_conf_usdt): + """Test migration with no trades in database.""" + exchange = MagicMock() + exchange.get_option.return_value = True + + # Set bot_start_time + KeyValueStore.store_value("bot_start_time", dt_now() - timedelta(days=5)) + + migrate_wallet_history(default_conf_usdt, exchange, 1000.0) + + # Should complete migration (flag set) but no wallet entries + assert KeyValueStore.get_int_value("wallet_history_migration") == 1 + assert WalletHistory.session.query(WalletHistory).count() == 0 + + +@pytest.mark.usefixtures("init_persistence") +def test_migrate_wallet_history_with_trades(default_conf_usdt, fee, time_machine, markets): + """Test migration with trades creates wallet history entries.""" + start_time = dt_utc(2024, 1, 10, 12, 0, 0) + time_machine.move_to(start_time, tick=False) + + # Bot started 10 days ago + bot_start = start_time - timedelta(days=10) + KeyValueStore.store_value("bot_start_time", bot_start) + + # Create mock trades with dates within the range + trade_open = start_time - timedelta(days=5) + trade_close = start_time - timedelta(days=3) + trade1 = create_closed_mock_trade( + fee, + "ETH/USDT", + open_date=trade_open, + close_date=trade_close, + ) + Trade.session.add(trade1) + Trade.commit() + + # Generate mock OHLCV data starting from bot_start + candle_type = default_conf_usdt.get("candle_type_def", CandleType.SPOT) + ohlcv_df = generate_test_data("1d", size=15, start=bot_start.strftime("%Y-%m-%d")) + ohlcv_data = {("ETH/USDT", "1d", candle_type): ohlcv_df} + + exchange = MagicMock() + exchange.get_option.return_value = True + exchange.markets = markets + exchange.refresh_latest_ohlcv.return_value = ohlcv_data + exchange.get_pair_base_currency = MagicMock(side_effect=lambda pair: markets.get(pair)["base"]) + + migrate_wallet_history(default_conf_usdt, exchange, 1000.0) + + # Should complete migration + assert KeyValueStore.get_int_value("wallet_history_migration") == 1 + + # Should have created wallet history entries + wallet_entries = WalletHistory.session.query(WalletHistory).all() + assert len(wallet_entries) > 0 + + +@pytest.mark.usefixtures("init_persistence") +def test_migrate_wallet_history_with_multiple_pairs(default_conf_usdt, fee, time_machine, markets): + """Test migration with multiple trading pairs.""" + start_time = dt_utc(2024, 1, 15, 12, 0, 0) + time_machine.move_to(start_time, tick=False) + + # Bot started 15 days ago + bot_start = start_time - timedelta(days=15) + KeyValueStore.store_value("bot_start_time", bot_start) + + # Create mock trades for multiple pairs within the date range + trade1 = create_closed_mock_trade( + fee, + "ETH/USDT", + open_date=start_time - timedelta(days=10), + close_date=start_time - timedelta(days=6), + ) + trade2 = create_closed_mock_trade( + fee, + "BTC/USDT", + open_date=start_time - timedelta(days=7), + close_date=start_time - timedelta(days=5), + ) + Trade.session.add(trade1) + Trade.session.add(trade2) + Trade.commit() + + # Generate mock OHLCV data for both pairs starting from bot_start + candle_type = default_conf_usdt.get("candle_type_def", CandleType.SPOT) + ohlcv_data = {} + ohlcv_data[("ETH/USDT", "1d", candle_type)] = generate_test_data( + "1d", size=20, start=bot_start.strftime("%Y-%m-%d"), base=1500 + ) + + ohlcv_data[("BTC/USDT", "1d", candle_type)] = generate_test_data( + "1d", size=20, start=bot_start.strftime("%Y-%m-%d"), base=30000 + ) + + exchange = MagicMock() + exchange.get_option.return_value = True + exchange.markets = markets + exchange.refresh_latest_ohlcv.return_value = ohlcv_data + exchange.get_pair_base_currency = MagicMock(side_effect=lambda pair: markets.get(pair)["base"]) + + migrate_wallet_history(default_conf_usdt, exchange, 1000.0) + + # Should complete migration + assert KeyValueStore.get_int_value("wallet_history_migration") == 1 + + # Should have wallet history entries + wallet_entries = WalletHistory.session.query(WalletHistory).all() + assert len(wallet_entries) > 0 + + # Check that stake currency (USDT) entries exist + usdt_entries = [e for e in wallet_entries if e.currency == "USDT"] + assert len(usdt_entries) > 0 + assert len(wallet_entries) > len(usdt_entries) + + # Stake currency should have price = 1.0 + for entry in usdt_entries: + assert entry.rate == 1.0 + + eth_entries = [e for e in wallet_entries if e.currency == "ETH"] + btc_entries = [e for e in wallet_entries if e.currency == "BTC"] + assert len(eth_entries) == 4 + assert len(btc_entries) == 2 + assert all(entry.rate and entry.rate > 1400 and entry.rate < 1600 for entry in eth_entries) + assert all(entry.rate and entry.rate > 29000 and entry.rate < 31000 for entry in btc_entries) + assert all(entry.balance == 10 for entry in btc_entries) + + +@pytest.mark.usefixtures("init_persistence") +def test_migrate_wallet_history_pair_not_in_markets( + default_conf_usdt, caplog, fee, time_machine, markets +): + """Test migration handles pairs that are not in exchange markets.""" + start_time = dt_utc(2024, 1, 10, 12, 0, 0) + time_machine.move_to(start_time, tick=False) + + # Bot started 10 days ago + bot_start = start_time - timedelta(days=10) + KeyValueStore.store_value("bot_start_time", bot_start) + + # Create a trade with a pair that won't be in markets + trade1 = create_closed_mock_trade( + fee, + "UNKNOWN/USDT", + open_date=start_time - timedelta(days=5), + close_date=start_time - timedelta(days=3), + ) + Trade.session.add(trade1) + Trade.commit() + + exchange = MagicMock() + exchange.get_option.return_value = True + exchange.markets = markets + exchange.refresh_latest_ohlcv.return_value = {} + + migrate_wallet_history(default_conf_usdt, exchange, 1000.0) + assert log_has_re("No OHLCV data available for .*", caplog) + + +@pytest.mark.usefixtures("init_persistence") +def test_migrate_wallet_history_stores_migration_date( + default_conf_usdt, fee, time_machine, markets +): + """Test that migration stores the migration date.""" + start_time = dt_utc(2024, 1, 10, 12, 0, 0) + time_machine.move_to(start_time, tick=False) + + # Bot started 10 days ago + bot_start = start_time - timedelta(days=10) + KeyValueStore.store_value("bot_start_time", bot_start) + + # Create a trade + trade1 = create_closed_mock_trade( + fee, + "ETH/USDT", + open_date=start_time - timedelta(days=5), + close_date=start_time - timedelta(days=3), + ) + Trade.session.add(trade1) + Trade.commit() + + candle_type = default_conf_usdt.get("candle_type_def", CandleType.SPOT) + ohlcv_data = { + ("ETH/USDT", "1d", candle_type): generate_test_data( + "1d", size=15, start=bot_start.strftime("%Y-%m-%d") + ) + } + + exchange = MagicMock() + exchange.get_option.return_value = True + exchange.markets = markets + exchange.refresh_latest_ohlcv.return_value = ohlcv_data + + migrate_wallet_history(default_conf_usdt, exchange, 1000.0) + + # Check migration date is stored + migration_date = KeyValueStore.get_datetime_value("wallet_history_migration_date") + assert migration_date is not None + + +@pytest.mark.usefixtures("init_persistence") +def test_internal_migrate_wallet_history_empty_trades(default_conf_usdt, time_machine): + """Test _migrate_wallet_history returns early when no trades exist.""" + start_time = dt_utc(2024, 1, 1, 12, 0, 0) + time_machine.move_to(start_time, tick=False) + + # Set bot_start_time + KeyValueStore.store_value("bot_start_time", start_time - timedelta(days=5)) + + exchange = MagicMock() + exchange.get_option.return_value = True + exchange.markets = {} + exchange.refresh_latest_ohlcv.return_value = {} + + # Call internal function directly with no trades + _migrate_wallet_history(default_conf_usdt, exchange, 1000.0) + + # refresh_latest_ohlcv should not be called when there are no trades + exchange.refresh_latest_ohlcv.assert_not_called() + + +@pytest.mark.usefixtures("init_persistence") +def test_migrate_wallet_history_with_patched_exchange(mocker, default_conf_usdt, fee, time_machine): + """Test migration using get_patched_exchange helper.""" + start_time = dt_utc(2024, 1, 10, 12, 0, 0) + time_machine.move_to(start_time, tick=False) + + # Bot started 10 days ago + bot_start = start_time - timedelta(days=10) + KeyValueStore.store_value("bot_start_time", bot_start) + + # Create a trade + trade1 = create_closed_mock_trade( + fee, + "ETH/USDT", + open_date=start_time - timedelta(days=5), + close_date=start_time - timedelta(days=3), + ) + Trade.session.add(trade1) + Trade.commit() + + # Generate mock OHLCV data starting from bot_start + candle_type = default_conf_usdt.get("candle_type_def", CandleType.SPOT) + ohlcv_df = generate_test_data("1d", size=15, start=bot_start.strftime("%Y-%m-%d")) + ohlcv_data = {("ETH/USDT", "1d", candle_type): ohlcv_df} + + # Mock exchange methods + mocker.patch.multiple( + EXMS, + get_option=MagicMock(return_value=True), + refresh_latest_ohlcv=MagicMock(return_value=ohlcv_data), + ) + + exchange = get_patched_exchange(mocker, default_conf_usdt) + + migrate_wallet_history(default_conf_usdt, exchange, 1000.0) + + # Should complete migration + assert KeyValueStore.get_int_value("wallet_history_migration") == 1 + + +@pytest.mark.usefixtures("init_persistence") +def test_migrate_wallet_history_db_error_handling( + mocker, default_conf_usdt, fee, time_machine, markets +): + """Test that database errors are handled gracefully.""" + start_time = dt_utc(2024, 1, 10, 12, 0, 0) + time_machine.move_to(start_time, tick=False) + + # Bot started 10 days ago + bot_start = start_time - timedelta(days=10) + KeyValueStore.store_value("bot_start_time", bot_start) + + # Create a trade + trade1 = create_closed_mock_trade( + fee, + "ETH/USDT", + open_date=start_time - timedelta(days=5), + close_date=start_time - timedelta(days=3), + ) + Trade.session.add(trade1) + Trade.commit() + + candle_type = default_conf_usdt.get("candle_type_def", CandleType.SPOT) + ohlcv_data = { + ("ETH/USDT", "1d", candle_type): generate_test_data( + "1d", size=15, start=bot_start.strftime("%Y-%m-%d") + ) + } + + exchange = MagicMock() + exchange.get_option.return_value = True + exchange.markets = markets + exchange.refresh_latest_ohlcv.return_value = ohlcv_data + + # Mock bulk_save_objects to raise an exception + mocker.patch.object( + WalletHistory.session, "bulk_save_objects", side_effect=Exception("DB Error") + ) + + # Should not raise exception, but handle error gracefully + migrate_wallet_history(default_conf_usdt, exchange, 1000.0) + + # Migration flag should still be set even after error in _migrate + assert KeyValueStore.get_int_value("wallet_history_migration") == 1 + + +@pytest.mark.usefixtures("init_persistence") +def test__prepare_balance_distribution(default_conf_usdt, fee, time_machine, markets): + """Test migration with multiple trading pairs.""" + start_time = dt_utc(2024, 1, 15, 12, 0, 0) + time_machine.move_to(start_time, tick=False) + + # Bot started 15 days ago + bot_start = start_time - timedelta(days=15) + KeyValueStore.store_value("bot_start_time", bot_start) + + # Create mock trades for multiple pairs within the date range + trade1 = create_closed_mock_trade( + fee, + "ETH/USDT", + open_date=start_time - timedelta(days=10), + close_date=start_time - timedelta(days=6), + ) + trade2 = create_closed_mock_trade( + fee, + "BTC/USDT", + open_date=start_time - timedelta(days=7), + close_date=start_time - timedelta(days=5), + ) + Trade.session.add(trade1) + Trade.session.add(trade2) + Trade.commit() + + # Generate mock OHLCV data for both pairs starting from bot_start + candle_type = default_conf_usdt.get("candle_type_def", CandleType.SPOT) + ohlcv_data = {} + ohlcv_data[("ETH/USDT", "1d", candle_type)] = generate_test_data( + "1d", size=20, start=bot_start.strftime("%Y-%m-%d"), base=1500 + ) + + ohlcv_data[("BTC/USDT", "1d", candle_type)] = generate_test_data( + "1d", size=20, start=bot_start.strftime("%Y-%m-%d"), base=30000 + ) + + exchange = MagicMock() + exchange.get_option.return_value = True + exchange.markets = markets + exchange.refresh_latest_ohlcv.return_value = ohlcv_data + + balance_dist, pairlist_valid = _prepare_balance_distribution( + default_conf_usdt, exchange, 1000.0 + ) + assert not balance_dist.empty + assert len(pairlist_valid) == 2 + assert "ETH/USDT" in pairlist_valid + assert "BTC/USDT" in pairlist_valid + + assert len(balance_dist) == 16 # 16 days from bot_start to now + assert balance_dist["USDT"].iloc[0] == 1000.0 + assert pd.isna(balance_dist["USDT"]).sum() == 0 + + assert all( + col in balance_dist.columns + for col in [ + "USDT", + "ETH/USDT", + "ETH/USDT_collateral", + "ETH/USDT_leverage", + "BTC/USDT", + "BTC/USDT_collateral", + "BTC/USDT_leverage", + "ETH/USDT_open", + "BTC/USDT_open", + "ETH/USDT_value", + "BTC/USDT_value", + "total_value", + ] + )