diff --git a/.github/actions/docker-tags/action.yml b/.github/actions/docker-tags/action.yml index 1a563aade..a0b8e4243 100644 --- a/.github/actions/docker-tags/action.yml +++ b/.github/actions/docker-tags/action.yml @@ -46,8 +46,9 @@ runs: id: tags env: BRANCH_NAME_INPUT: ${{ github.event.inputs.branch_name }} + EVENT_NAME: ${{ github.event_name }} run: | - if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + if [ "${EVENT_NAME}" = "workflow_dispatch" ]; then BRANCH_NAME="${BRANCH_NAME_INPUT}" else BRANCH_NAME="${GITHUB_REF##*/}" diff --git a/.github/workflows/binance-lev-tier-update.yml b/.github/workflows/binance-lev-tier-update.yml index bf24ec1bf..914f238dc 100644 --- a/.github/workflows/binance-lev-tier-update.yml +++ b/.github/workflows/binance-lev-tier-update.yml @@ -6,20 +6,25 @@ on: # on demand workflow_dispatch: +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true + permissions: contents: read jobs: auto-update: + name: "Auto Update Binance Leverage Tiers" runs-on: ubuntu-latest environment: name: develop steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/setup-python@v6 + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: "3.12" diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index b29e941fa..773261d8d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,8 +16,8 @@ on: concurrency: group: "${{ github.workflow }}-${{ github.ref }}-${{ github.event_name }}" cancel-in-progress: true -permissions: - repository-projects: read +permissions: {} + jobs: tests: name: "Tests and Linting" @@ -28,17 +28,17 @@ jobs: python-version: ["3.11", "3.12", "3.13", "3.14"] steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: ${{ matrix.python-version }} - name: Install uv - uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6 + uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7.2.0 with: activate-environment: true enable-cache: true @@ -178,12 +178,12 @@ jobs: name: "Mypy Version Check" runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 #v6.1.0 with: python-version: "3.12" @@ -196,11 +196,11 @@ jobs: name: "Pre-commit checks" runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/setup-python@v6 + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: "3.12" - uses: pre-commit/action@2c7b3805fd2a0fd8c1884dcaebf91fc102a13ecd # v3.0.1 @@ -209,7 +209,7 @@ jobs: name: "Documentation build" runs-on: ubuntu-22.04 steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -218,7 +218,7 @@ jobs: ./tests/test_docs.sh - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: "3.12" @@ -241,17 +241,17 @@ jobs: name: "Tests and Linting - Online tests" runs-on: ubuntu-24.04 steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: "3.12" - name: Install uv - uses: astral-sh/setup-uv@681c641aba71e4a1c380be3ab5e12ad51f415867 # v7.1.6 + uses: astral-sh/setup-uv@61cb8a9741eeb8a550a1b8544337180c0fc8476b # v7.2.0 with: activate-environment: true enable-cache: true @@ -275,6 +275,7 @@ jobs: # Notify only once - when CI completes (and after deploy) in case it's successful notify-complete: + name: "Notify CI Completion" needs: [ build, build-linux-online @@ -282,8 +283,6 @@ jobs: runs-on: ubuntu-22.04 # Discord notification can't handle schedule events if: github.event_name != 'schedule' && github.repository == 'freqtrade/freqtrade' - permissions: - repository-projects: read steps: - name: Check user permission @@ -321,12 +320,12 @@ jobs: with: jobs: ${{ toJSON(needs) }} - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: "3.12" @@ -336,7 +335,7 @@ jobs: python -m build --sdist --wheel - name: Upload artifacts πŸ“¦ - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.1.0 with: name: freqtrade-build path: | @@ -349,7 +348,7 @@ jobs: python -m build --sdist --wheel ft_client - name: Upload artifacts πŸ“¦ - uses: actions/upload-artifact@v6 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.1.0 with: name: freqtrade-client-build path: | @@ -365,15 +364,15 @@ jobs: name: testpypi url: https://test.pypi.org/p/freqtrade permissions: - id-token: write + id-token: write # Needed for pypa/gh-action-pypi-publish steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Download artifact πŸ“¦ - uses: actions/download-artifact@v7 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: pattern: freqtrade*-build path: dist @@ -394,15 +393,15 @@ jobs: name: pypi url: https://pypi.org/p/freqtrade permissions: - id-token: write + id-token: write # Needed for pypa/gh-action-pypi-publish steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Download artifact πŸ“¦ - uses: actions/download-artifact@v7 + uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0 with: pattern: freqtrade*-build path: dist @@ -420,7 +419,7 @@ jobs: if: (github.event_name == 'push' || github.event_name == 'schedule' || github.event_name == 'release') && github.repository == 'freqtrade/freqtrade' uses: ./.github/workflows/docker-build.yml permissions: - packages: write + packages: write # Needed to push package versions contents: read secrets: DOCKER_PASSWORD: ${{ secrets.DOCKER_PASSWORD }} @@ -434,6 +433,6 @@ jobs: # Only run on push, schedule, or release events if: (github.event_name == 'push' || github.event_name == 'schedule') && github.repository == 'freqtrade/freqtrade' permissions: - packages: write + packages: write # Needed to delete package versions with: package_name: 'freqtrade' diff --git a/.github/workflows/deploy-docs.yml b/.github/workflows/deploy-docs.yml index 33272efa6..26859a77c 100644 --- a/.github/workflows/deploy-docs.yml +++ b/.github/workflows/deploy-docs.yml @@ -11,6 +11,9 @@ on: # disable permissions for all of the available permissions permissions: {} +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true jobs: build-docs: @@ -19,12 +22,12 @@ jobs: name: Deploy Docs through mike runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: true - name: Set up Python - uses: actions/setup-python@v6 + uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: '3.12' diff --git a/.github/workflows/devcontainer-build.yml b/.github/workflows/devcontainer-build.yml index 485ff30ed..efaa0a4a5 100644 --- a/.github/workflows/devcontainer-build.yml +++ b/.github/workflows/devcontainer-build.yml @@ -17,14 +17,17 @@ concurrency: group: "${{ github.workflow }}" cancel-in-progress: true +permissions: + contents: read jobs: build-and-push: + name: "Build and Push Devcontainer Image" permissions: - packages: write + packages: write # Needed to push package versions runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Login to GitHub Container Registry @@ -40,3 +43,14 @@ jobs: imageName: ghcr.io/${{ github.repository }}-devcontainer cacheFrom: ghcr.io/${{ github.repository }}-devcontainer push: always + + + packages-cleanup: + name: "Docker Package Cleanup" + uses: ./.github/workflows/packages-cleanup.yml + # Only run on push, schedule, or release events + if: (github.event_name == 'push' || github.event_name == 'schedule') && github.repository == 'freqtrade/freqtrade' + permissions: + packages: write # Needed to delete package versions + with: + package_name: 'freqtrade-devcontainer' diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml index e105c9d21..0e6e82908 100644 --- a/.github/workflows/docker-build.yml +++ b/.github/workflows/docker-build.yml @@ -17,6 +17,10 @@ on: default: 'develop' type: string +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + permissions: contents: read @@ -33,7 +37,7 @@ jobs: if: github.repository == 'freqtrade/freqtrade' steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -159,14 +163,14 @@ jobs: deploy-arm: name: "Deploy Docker ARM64" permissions: - packages: write + packages: write # Needed to push package versions needs: [ deploy-docker ] # Only run on 64bit machines runs-on: [self-hosted, linux, ARM64] if: github.repository == 'freqtrade/freqtrade' steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false diff --git a/.github/workflows/docker-update-readme.yml b/.github/workflows/docker-update-readme.yml index 7f3a75280..208bd0289 100644 --- a/.github/workflows/docker-update-readme.yml +++ b/.github/workflows/docker-update-readme.yml @@ -4,14 +4,19 @@ on: branches: - stable +concurrency: + group: ${{ github.workflow }} + cancel-in-progress: true + # disable permissions for all of the available permissions permissions: {} jobs: dockerHubDescription: + name: "Update Docker Hub Description" runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false diff --git a/.github/workflows/packages-cleanup.yml b/.github/workflows/packages-cleanup.yml index 9d31504e9..76b252df4 100644 --- a/.github/workflows/packages-cleanup.yml +++ b/.github/workflows/packages-cleanup.yml @@ -25,20 +25,26 @@ on: default: true type: boolean +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: false + env: PACKAGE_NAME: "freqtrade" +permissions: {} + jobs: deploy-docker: name: "Delete Packages" runs-on: ubuntu-24.04 if: github.repository == 'freqtrade/freqtrade' permissions: - packages: write + packages: write # Needed to delete package versions steps: - name: "Delete untagged Package Versions" - uses: actions/delete-package-versions@v5 + uses: actions/delete-package-versions@e5bc658cc4c965c472efe991f8beea3981499c55 # v5.0.0 with: package-name: ${{ inputs.package_name || env.PACKAGE_NAME }} package-type: 'container' diff --git a/.github/workflows/pre-commit-update.yml b/.github/workflows/pre-commit-update.yml index 31a0f8c5a..97cc935a9 100644 --- a/.github/workflows/pre-commit-update.yml +++ b/.github/workflows/pre-commit-update.yml @@ -9,15 +9,20 @@ on: permissions: contents: read +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + jobs: auto-update: + name: Auto-update pre-commit hooks runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.1 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - - uses: actions/setup-python@v6 + - uses: actions/setup-python@83679a892e2d95755f2dac6acb0bfd1e9ac5d548 # v6.1.0 with: python-version: "3.12" diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml deleted file mode 100644 index cec2469b8..000000000 --- a/.github/workflows/zizmor.yml +++ /dev/null @@ -1,30 +0,0 @@ -name: GitHub Actions Security Analysis with zizmor 🌈 - -on: - push: - branches: - - develop - - stable - pull_request: - branches: - - develop - - stable - -permissions: {} - -jobs: - zizmor: - name: Run zizmor 🌈 - runs-on: ubuntu-latest - permissions: - security-events: write - # contents: read # only needed for private repos - # actions: read # only needed for private repos - steps: - - name: Checkout repository - uses: actions/checkout@v6.0.1 - with: - persist-credentials: false - - - name: Run zizmor 🌈 - uses: zizmorcore/zizmor-action@e639db99335bc9038abc0e066dfcd72e23d26fb4 # v0.3.0 diff --git a/.github/workflows/zizmor_action.yml b/.github/workflows/zizmor_action.yml new file mode 100644 index 000000000..476bfcbe7 --- /dev/null +++ b/.github/workflows/zizmor_action.yml @@ -0,0 +1,34 @@ +name: GitHub Actions Security Analysis with zizmor 🌈 + +on: + push: + branches: + - develop + - stable + pull_request: + branches: + - develop + - stable + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: false + +permissions: {} + +jobs: + zizmor: + name: Run zizmor 🌈 + runs-on: ubuntu-latest + permissions: + security-events: write # Required for upload-sarif (used by zizmor-action) to upload SARIF files. + # contents: read # Only needed for private repos. Needed to clone the repo. + # actions: read # Only needed for private repos. Needed for upload-sarif to read workflow run info. + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: Run zizmor 🌈 + uses: zizmorcore/zizmor-action@135698455da5c3b3e55f73f4419e481ab68cdd95 # v0.4.1 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 09b08f0cc..2c9793951 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -28,10 +28,10 @@ repos: additional_dependencies: - types-cachetools==6.2.0.20251022 - types-filelock==3.2.7 - - types-requests==2.32.4.20250913 + - types-requests==2.32.4.20260107 - types-tabulate==0.9.0.20241207 - types-python-dateutil==2.9.0.20251115 - - scipy-stubs==1.16.3.3 + - scipy-stubs==1.17.0.1 - SQLAlchemy==2.0.45 # stages: [push] @@ -44,7 +44,7 @@ repos: - repo: https://github.com/charliermarsh/ruff-pre-commit # Ruff version. - rev: 'v0.14.10' + rev: 'v0.14.14' hooks: - id: ruff - id: ruff-format @@ -83,6 +83,6 @@ repos: # Ensure github actions remain safe - repo: https://github.com/woodruffw/zizmor-pre-commit - rev: v1.19.0 + rev: v1.22.0 hooks: - id: zizmor diff --git a/Dockerfile b/Dockerfile index 5a1d54493..138775c4b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM python:3.13.11-slim-bookworm AS base +FROM python:3.13.11-slim-trixie AS base # Setup env ENV LANG=C.UTF-8 diff --git a/README.md b/README.md index cc02d41ae..7ea9263f2 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,8 @@ hesitate to read the source code and understand the mechanism of this bot. Please read the [exchange-specific notes](docs/exchanges.md) to learn about special configurations that maybe needed for each exchange. +### Supported Spot Exchanges + - [X] [Binance](https://www.binance.com/) - [X] [BingX](https://bingx.com/invite/0EM9RX) - [X] [Bitget](https://www.bitget.com/) @@ -39,7 +41,7 @@ Please read the [exchange-specific notes](docs/exchanges.md) to learn about spec - [X] [MyOKX](https://okx.com/) (OKX EEA) - [ ] [potentially many others](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_ -### Supported Futures Exchanges (experimental) +### Supported Futures Exchanges - [X] [Binance](https://www.binance.com/) - [X] [Bitget](https://www.bitget.com/) diff --git a/build_helpers/pyarrow-22.0.0-cp311-cp311-linux_armv7l.whl b/build_helpers/pyarrow-23.0.0-cp311-cp311-linux_armv7l.whl similarity index 64% rename from build_helpers/pyarrow-22.0.0-cp311-cp311-linux_armv7l.whl rename to build_helpers/pyarrow-23.0.0-cp311-cp311-linux_armv7l.whl index 57d06a96d..ddfc88add 100644 Binary files a/build_helpers/pyarrow-22.0.0-cp311-cp311-linux_armv7l.whl and b/build_helpers/pyarrow-23.0.0-cp311-cp311-linux_armv7l.whl differ diff --git a/docs/advanced-backtesting.md b/docs/advanced-backtesting.md index 617ea7e3d..b0b77c391 100644 --- a/docs/advanced-backtesting.md +++ b/docs/advanced-backtesting.md @@ -41,7 +41,7 @@ ranging from the simplest (0) to the most detailed per pair, per buy and per sel * 1: profit summaries grouped by enter_tag * 2: profit summaries grouped by enter_tag and exit_tag * 3: profit summaries grouped by pair and enter_tag -* 4: profit summaries grouped by pair, enter_ and exit_tag (this can get quite large) +* 4: profit summaries grouped by pair, enter_tag and exit_tag (this can get quite large) * 5: profit summaries grouped by exit_tag More options are available by running with the `-h` option. @@ -52,11 +52,10 @@ By default, `backtesting-analysis` processes the most recent backtest results in If you want to analyze results from an earlier backtest, use the `--backtest-filename` option to specify the desired file. This lets you revisit and re-analyze historical backtest outputs at any time by providing the filename of the relevant backtest result: ``` bash -freqtrade backtesting-analysis -c --timeframe --strategy --timerange --export signals --backtest-filename backtest-result-2025-03-05_20-38-34.zip +freqtrade backtesting -c --strategy --timerange --export signals --backtest-filename backtest-result-2025-03-05_20-38-34.zip ``` -You should see some output similar to below in the logs with the name of the timestamped -filename that was exported: +You should see some output similar to below in the logs with the name of the timestamped filename that was exported: ``` 2022-06-14 16:28:32,698 - freqtrade.misc - INFO - dumping json to "mystrat_backtest-2022-06-14_16-28-32.json" @@ -64,14 +63,14 @@ filename that was exported: You can then use that filename in `backtesting-analysis`: -``` -freqtrade backtesting-analysis -c --backtest-filename=mystrat_backtest-2022-06-14_16-28-32.json +``` bash +freqtrade backtesting-analysis -c --backtest-filename=backtest-result-2025-03-05_20-38-34.zip ``` To use a result from a different results directory, you can use `--backtest-directory` to specify the directory ``` bash -freqtrade backtesting-analysis -c --backtest-directory custom_results/ --backtest-filename mystrat_backtest-2022-06-14_16-28-32.json +freqtrade backtesting-analysis -c --backtest-directory custom_results/ --backtest-filename backtest-result-2025-03-05_20-38-34.zip ``` ### Tuning the buy tags and sell tags to display @@ -85,7 +84,7 @@ To show only certain buy and sell tags in the displayed output, use the followin For example: -```bash +``` bash freqtrade backtesting-analysis -c --analysis-groups 0 2 --enter-reason-list enter_tag_a enter_tag_b --exit-reason-list roi custom_exit_tag_a stop_loss ``` @@ -96,7 +95,7 @@ values present on signal candles to allow fine-grained investigation and tuning indicators. To print out a column for a given set of indicators, use the `--indicator-list` option: -```bash +``` bash freqtrade backtesting-analysis -c --analysis-groups 0 2 --enter-reason-list enter_tag_a enter_tag_b --exit-reason-list roi custom_exit_tag_a stop_loss --indicator-list rsi rsi_1h bb_lowerband ema_9 macd macdsignal ``` @@ -108,24 +107,24 @@ output. The indicator values will be displayed for both entry and exit points. If `--indicator-list all` is specified, only the indicators at the entry point will be shown to avoid excessively large lists, which could occur depending on the strategy. -There are a range of candle and trade-related fields that are included in the analysis so are +There are a range of candle and trade-related fields that are included in the analysis so are automatically accessible by including them on the indicator-list, and these include: -- **open_date :** trade open datetime -- **close_date :** trade close datetime -- **min_rate :** minimum price seen throughout the position -- **max_rate :** maximum price seen throughout the position -- **open :** signal candle open price -- **close :** signal candle close price -- **high :** signal candle high price -- **low :** signal candle low price -- **volume :** signal candle volume -- **profit_ratio :** trade profit ratio -- **profit_abs :** absolute profit return of the trade +* **open_date :** trade open datetime +* **close_date :** trade close datetime +* **min_rate :** minimum price seen throughout the position +* **max_rate :** maximum price seen throughout the position +* **open :** signal candle open price +* **close :** signal candle close price +* **high :** signal candle high price +* **low :** signal candle low price +* **volume :** signal candle volume +* **profit_ratio :** trade profit ratio +* **profit_abs :** absolute profit return of the trade #### Sample Output for Indicator Values -```bash +``` bash freqtrade backtesting-analysis -c user_data/config.json --analysis-groups 0 --indicator-list chikou_span tenkan_sen ``` @@ -158,13 +157,13 @@ The `--indicator-list` option, by default, displays indicator values for both en Example: Display indicator values at entry signals: -```bash +``` bash freqtrade backtesting-analysis -c user_data/config.json --analysis-groups 0 --indicator-list chikou_span tenkan_sen --entry-only ``` Example: Display indicator values at exit signals: -```bash +``` bash freqtrade backtesting-analysis -c user_data/config.json --analysis-groups 0 --indicator-list chikou_span tenkan_sen --exit-only ``` @@ -181,7 +180,7 @@ To show only trades between dates within your backtested timerange, supply the u For example, if your backtest timerange was `20220101-20221231` but you only want to output trades in January: -```bash +``` bash freqtrade backtesting-analysis -c --timerange 20220101-20220201 ``` @@ -189,7 +188,7 @@ freqtrade backtesting-analysis -c --timerange 20220101-20220201 Use the `--rejected-signals` option to print out rejected signals. -```bash +``` bash freqtrade backtesting-analysis -c --rejected-signals ``` @@ -198,13 +197,13 @@ freqtrade backtesting-analysis -c --rejected-signals Some of the tabular outputs can become large, so printing them out to the terminal is not preferable. Use the `--analysis-to-csv` option to disable printing out of tables to standard out and write them to CSV files. -```bash +``` bash freqtrade backtesting-analysis -c --analysis-to-csv ``` By default this will write one file per output table you specified in the `backtesting-analysis` command, e.g. -```bash +``` bash freqtrade backtesting-analysis -c --analysis-to-csv --rejected-signals --analysis-groups 0 1 ``` @@ -216,6 +215,6 @@ This will write to `user_data/backtest_results`: To override where the files will be written, also specify the `--analysis-csv-path` option. -```bash +``` bash freqtrade backtesting-analysis -c --analysis-to-csv --analysis-csv-path another/data/path/ ``` diff --git a/docs/advanced-hyperopt.md b/docs/advanced-hyperopt.md index 1f727398b..8b33b6d91 100644 --- a/docs/advanced-hyperopt.md +++ b/docs/advanced-hyperopt.md @@ -133,7 +133,7 @@ class MyAwesomeStrategy(IStrategy): ] # Define a custom max_open_trades space - def max_open_trades_space(self) -> List[Dimension]: + def max_open_trades_space() -> List[Dimension]: return [ Integer(-1, 10, name='max_open_trades'), ] @@ -142,7 +142,7 @@ class MyAwesomeStrategy(IStrategy): !!! Note All overrides are optional and can be mixed/matched as necessary. -### Dynamic parameters +## Dynamic parameters Parameters can also be defined dynamically, but must be available to the instance once the [`bot_start()` callback](strategy-callbacks.md#bot-start) has been called. @@ -159,7 +159,7 @@ class MyAwesomeStrategy(IStrategy): !!! Warning Parameters created this way will not show up in the `list-strategies` parameter count. -### Overriding Base estimator +## Overriding Base estimator You can define your own optuna sampler for Hyperopt by implementing `generate_estimator()` in the Hyperopt subclass. @@ -208,7 +208,6 @@ Some research will be necessary to find additional Samplers (from optunahub) for Obviously the same approach will work for all other Samplers optuna supports. - ## Space options For the additional spaces, scikit-optimize (in combination with Freqtrade) provides the following space types: diff --git a/docs/deprecated.md b/docs/deprecated.md index 1b2835ea2..00c7b7a62 100644 --- a/docs/deprecated.md +++ b/docs/deprecated.md @@ -120,8 +120,8 @@ The script below should serve as an example - you may need to adjust the timefra ``` bash # Cleanup no longer needed data -rm user_data/data//futures/*-mark-* -rm user_data/data//futures/*-funding_rate-* +rm user_data/data//futures/*-mark* +rm user_data/data//futures/*-funding_rate* # download new data (only required once to fix the mark and funding fee data) freqtrade download-data -t 1h --trading-mode futures --candle-types funding_rate mark [...] --timerange diff --git a/docs/developer.md b/docs/developer.md index d470bf8b2..f86c75d19 100644 --- a/docs/developer.md +++ b/docs/developer.md @@ -432,7 +432,6 @@ freqtrade download-data --timerange 20250625-20250801 --config tests/testdata/co freqtrade backtesting --config tests/testdata/config.tests.usdt.json -s SampleStrategy --userdir user_data_bttest/ --cache none --timerange 20250701-20250801 ``` - ## Continuous integration This documents some decisions taken for the CI Pipeline. @@ -464,10 +463,10 @@ git checkout -b new_release Determine if crucial bugfixes have been made between this commit and the current state, and eventually cherry-pick these. * Merge the release branch (stable) into this branch. -* Edit `freqtrade/__init__.py` and add the version matching the current date (for example `2019.7` for July 2019). Minor versions can be `2019.7.1` should we need to do a second release that month. Version numbers must follow allowed versions from PEP0440 to avoid failures pushing to pypi. +* Edit `freqtrade/__init__.py` and add the version matching the current date (for example `2025.7` for July 2025). Minor versions can be `2025.7.1` should we need to do a second release that month. Version numbers must follow allowed versions from PEP0440 to avoid failures pushing to pypi. * Commit this part. * Push that branch to the remote and create a PR against the **stable branch**. -* Update develop version to next version following the pattern `2019.8-dev`. +* Update develop version to next version following the pattern `2025.8-dev`. ### Create changelog from git commits diff --git a/docs/exchanges.md b/docs/exchanges.md index 358242fe9..55ca8a556 100644 --- a/docs/exchanges.md +++ b/docs/exchanges.md @@ -428,6 +428,36 @@ Your balance and trades will now be used from your vault / subaccount - and no l The Hyperliquid API does not provide historic data beyond the single call to fetch current data, so downloading data is not possible, as the downloaded data would not constitute proper historic data. +### HIP-3 DEXes + +Hyperliquid supports HIP-3 decentralized exchanges (DEXes), which are independent exchanges built on top of the Hyperliquid infrastructure. +These DEXes operate similarly to the main Hyperliquid exchange but are community-created and managed. + +To trade on HIP-3 DEXes with Freqtrade, you need to add them to your configuration using the `hip3_dexes` parameter: + +```json +"exchange": { + "name": "hyperliquid", + "walletAddress": "your_master_wallet_address", + "privateKey": "your_api_private_key", + "hip3_dexes": ["dex_name_1", "dex_name_2"] +} +``` + +Replace `"dex_name_1"` and `"dex_name_2"` with the actual names of the HIP-3 DEXes you want to trade on (e.g. `vntl` and `xyz`). + +!!! Warning "Performance and Rate Limit Impact" + Each HIP-3 DEX you add significantly impacts bot performance and rate limits. + + * **Additional API Calls**: For each HIP-3 DEX configured, Freqtrade needs to make additional API calls. + * **Rate Limit Pressure**: Additional API calls contribute to Hyperliquid's strict rate limits. With multiple DEXes, you may hit rate limits faster, or rather, slow down bot operations due to enforced delays. + + Please only add HIP-3 DEXes that you actively trade on. Monitor your logs for rate limit warnings or signs of slowed operations, and adjust your configuration accordingly. + Different HIP-3 DEXes may also use different quote currencies - so make sure to only add DEXes that are compatible with your stake currency to avoid unnecessary delays. + +!!! Note + HIP-3 DEXes share the same wallet and free amount of collateral as your main Hyperliquid account. Trades on different DEXes will affect your overall account balance and margin. + ## Bitvavo If your account is required to use an operatorId, you can set it in the configuration file as follows: diff --git a/docs/faq.md b/docs/faq.md index f326b188a..aea0030ad 100644 --- a/docs/faq.md +++ b/docs/faq.md @@ -2,7 +2,7 @@ ## Supported Markets -Freqtrade supports spot trading, as well as (isolated) 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-experimental) for an up-to-date list of supported exchanges. ### Can my bot open short positions? @@ -29,6 +29,13 @@ You can however use the [`adjust_trade_position()` callback](strategy-callbacks. Backtesting provides an option for this in `--eps` - however this is only there to highlight "hidden" signals, and will not work in live. +### Does freqtrade support sandbox accounts? + +No, but you can use dry-run mode to simulate trading without risking real funds. + +Sandbox markets are separate, simulated markets - which are not suitable to test your strategy in a realistic environment. +These markets usually have different order books, liquidity and trading behaviour (usually with very few participants) - which makes them unsuitable for realistic tests of your strategy. + ### The bot does not start Running the bot with `freqtrade trade --config config.json` shows the output `freqtrade: command not found`. diff --git a/docs/includes/release_template.md b/docs/includes/release_template.md index 87a3564da..c991235e3 100644 --- a/docs/includes/release_template.md +++ b/docs/includes/release_template.md @@ -1,28 +1,28 @@ -## Highlighted changes +# Highlighted changes - ... -### How to update +## How to update As always, you can update your bot using one of the following commands: -#### docker-compose +### docker-compose ```bash docker-compose pull docker-compose up -d ``` -#### Installation via setup script +### Installation via setup script -``` +``` bash # Deactivate venv and run ./setup.sh --update ``` -#### Plain native installation +### Plain native installation -``` +``` bash git pull pip install -U -r requirements.txt ``` diff --git a/docs/index.md b/docs/index.md index e09e483f5..b33d88772 100644 --- a/docs/index.md +++ b/docs/index.md @@ -37,6 +37,8 @@ Freqtrade is a free and open source crypto trading bot written in Python. It is Please read the [exchange specific notes](exchanges.md) to learn about eventual, special configurations needed for each exchange. +### Supported Spot Exchanges + - [X] [Binance](https://www.binance.com/) - [X] [BingX](https://bingx.com/invite/0EM9RX) - [X] [Bitget](https://www.bitget.com/) @@ -50,7 +52,7 @@ Please read the [exchange specific notes](exchanges.md) to learn about eventual, - [X] [MyOKX](https://okx.com/) (OKX EEA) - [ ] [potentially many others through ccxt](https://github.com/ccxt/ccxt/). _(We cannot guarantee they will work)_ -### Supported Futures Exchanges (experimental) +### Supported Futures Exchanges - [X] [Binance](https://www.binance.com/) - [X] [Bitget](https://www.bitget.com/) diff --git a/docs/installation.md b/docs/installation.md index 583d3969a..da594cfe1 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -9,15 +9,25 @@ The freqtrade documentation describes various ways to install freqtrade * [Manual Installation](#manual-installation) * [Installation with Conda](#installation-with-conda) -Please consider using the prebuilt [docker images](docker_quickstart.md) to get started quickly while evaluating how freqtrade works. +Please consider using the prebuilt [docker images](docker_quickstart.md) to get started quickly. + +!!! Note "Updating" + Keeping freqtrade updated is important to [ensure ongoing compatibility](updating.md#why-update) with exchange API's. + Please refer to the [updating guide](updating.md) for details on how to update your installation. + +!!! Note "Windows users" + We **strongly** recommend that Windows users use [Docker](docker_quickstart.md) as this will work much easier and smoother (also more secure). + + If that is not possible, try using the Windows Linux subsystem (WSL) - for which the Ubuntu/Linux instructions will work. + If you really want to install freqtrade natively on Windows, best use the [`./setup.ps1` installation script](#use-setupps1-windows). + + Please also make sure to use the 64bit version of Python, as 32bit versions have severe memory limitations, which can negatively impact your experience with backtesting/hyperopt. ------ ## Information -For Windows installation, please use the [windows installation guide](windows_installation.md). - -The easiest way to install and run Freqtrade is to clone the bot Github repository and then run the `./setup.sh` script, if it's available for your platform. +The easiest way to install and run Freqtrade is to clone the bot Github repository and then run the `./setup.sh` (`./setup.ps1` for Windows) script, if it's available for your platform. !!! Note "Version considerations" When cloning the repository the default working branch has the name `develop`. This branch contains all last features (can be considered as relatively stable, thanks to automated tests). @@ -152,20 +162,9 @@ If you are on Debian, Ubuntu or MacOS, freqtrade provides the script to install ./setup.sh -i ``` -### Activate your virtual environment +#### Other options of /setup.sh script -Each time you open a new terminal, you must run `source .venv/bin/activate` to activate your virtual environment. - -```bash -# activate virtual environment -source ./.venv/bin/activate -``` - -[You are now ready](#you-are-ready) to run the bot. - -### Other options of /setup.sh script - -You can as well update, configure and reset the codebase of your bot with `./script.sh` +You can also update, configure and reset the codebase of your bot with `./setup.sh` ```bash # --update, Command git pull to update. @@ -194,6 +193,34 @@ This option will pull the last version of your current branch and update your vi This option will hard reset your branch (only if you are on either `stable` or `develop`) and recreate your virtualenv. ``` +#### Activate your virtual environment + +Each time you open a new terminal, you must run `source .venv/bin/activate` to activate your virtual environment. + +```bash +# activate virtual environment +source ./.venv/bin/activate +``` + +### Use ./setup.ps1 (Windows) + +The script will ask you a few questions to determine which parts should be installed. + +```powershell +Set-ExecutionPolicy -ExecutionPolicy Bypass +cd freqtrade +. .\setup.ps1 +``` + +#### Activate your virtual environment (Windows) + +```powershell +# activate virtual environment +. .\.venv\Scripts\Activate.ps1 +``` + +[You are now ready](#you-are-ready) to run the bot. + ----- ## Manual Installation @@ -337,7 +364,7 @@ conda deactivate Happy trading! ------ +------ ## You are ready @@ -394,3 +421,15 @@ open /Library/Developer/CommandLineTools/Packages/macOS_SDK_headers_for_macOS_10 ``` If this file is inexistent, then you're probably on a different version of MacOS, so you may need to consult the internet for specific resolution details. + +### Windows Installation error + +```bash +error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools +``` + +Unfortunately, many packages requiring compilation don't provide a pre-built wheel. It is therefore mandatory to have a C/C++ compiler installed and available for your python environment to use. + +You can download the Visual C++ build tools from [the Visual Studio website](https://visualstudio.microsoft.com/visual-cpp-build-tools/) and install "Desktop development with C++" in it's default configuration. Unfortunately, this is a heavy download / dependency so you might want to consider WSL2 or [docker compose](docker_quickstart.md) first. + +![Windows installation](assets/windows_install.png) diff --git a/docs/leverage.md b/docs/leverage.md index c96dd714f..2236bbff5 100644 --- a/docs/leverage.md +++ b/docs/leverage.md @@ -1,8 +1,5 @@ # Trading with Leverage -!!! Warning "Beta feature" - This feature is still in it's testing phase. Should you notice something you think is wrong please let us know via Discord or via Github Issue. - !!! Note "Multiple bots on one account" You can't run 2 bots on the same account with leverage. For leveraged / margin trading, freqtrade assumes it's the only user of the account, and all liquidation levels are calculated based on this assumption. @@ -55,7 +52,7 @@ Perpetual swaps (also known as Perpetual Futures) are contracts traded at a pric In addition to the gains/losses from the change in price of the futures contract, traders also exchange _funding fees_, which are gains/losses worth an amount that is derived from the difference in price between the futures contract and the underlying asset. The difference in price between a futures contract and the underlying asset varies between exchanges. To trade in futures markets, you'll have to set `trading_mode` to "futures". -You will also have to pick a "margin mode" (explanation below) - with freqtrade currently only supporting isolated margin. +You will also have to pick a "margin mode" (explanation below). ``` json "trading_mode": "futures", diff --git a/docs/requirements-docs.txt b/docs/requirements-docs.txt index d9a07258b..89d011f63 100644 --- a/docs/requirements-docs.txt +++ b/docs/requirements-docs.txt @@ -1,7 +1,7 @@ markdown==3.10 mkdocs==1.6.1 -mkdocs-material==9.7.0 +mkdocs-material==9.7.1 mdx_truly_sane_lists==1.3 -pymdown-extensions==10.19.1 +pymdown-extensions==10.20 jinja2==3.1.6 mike==2.1.3 diff --git a/docs/strategy-callbacks.md b/docs/strategy-callbacks.md index c24857612..ed98708ff 100644 --- a/docs/strategy-callbacks.md +++ b/docs/strategy-callbacks.md @@ -1292,6 +1292,22 @@ Currently two types of annotations are supported, `area` and `line`. } ``` +#### Point + +``` json +{ + "type": "point", // Type of the annotation, currently only "point" is supported + "x": "2024-01-01 15:00:00", // Start date of the point + "y": 94000.2, // Price / y axis value + "color": "", + "z_level": 5, // z-level, higher values are drawn on top of lower values. Positions relative to the Chart elements need to be set in freqUI. + "label": "some label", + "size": 2, // Optional, line width in pixels. Defaults to 10 + "symbol": "circle", // Optional, can be "circle", "rect", "roundRect", "triangle", "pin", "arrow", "none". + +} +``` + The below example will mark the chart with areas for the hours 8 and 15, with a grey color, highlighting the market open and close hours. This is obviously a very basic example. diff --git a/docs/updating.md b/docs/updating.md index 5ffd9bd2c..ae9135af9 100644 --- a/docs/updating.md +++ b/docs/updating.md @@ -6,6 +6,12 @@ To update your freqtrade installation, please use one of the below methods, corr Breaking changes / changed behavior will be documented in the changelog that is posted alongside every release. For the develop branch, please follow PR's to avoid being surprised by changes. +## Why update? + +Keeping your bot updated not only ensures that you have the latest features and improvements, but is a requirement to keep your bot running smoothly. +Freqtrade is heavily dependent on the underlying exchange API's, which change pretty frequently if considered across exchanges. +To ensure ongoing compatibility, please make sure to update your bot regularly. + ## Docker !!! Note "Legacy installations using the `master` image" @@ -38,7 +44,12 @@ pip install -e . freqtrade install-ui ``` -### Problems updating +## Problems updating -Update-problems usually come missing dependencies (you didn't follow the above instructions) - or from updated dependencies, which fail to install (for example TA-lib). -Please refer to the corresponding installation sections (common problems linked below) +Update-problems usually come missing dependencies (you didn't follow the above instructions) - or from dependencies which fail to install. +We try to make sure that heavy dependencies have wheels available for major platforms, but sometimes this is not possible. + +Please refer to the corresponding installation sections (common problem sections linked below). + +[Common installation problems](installation.md#troubleshooting) +[Common installation problems - windows](installation.md#windows-installation-error) diff --git a/docs/windows_installation.md b/docs/windows_installation.md deleted file mode 100644 index 51facfb58..000000000 --- a/docs/windows_installation.md +++ /dev/null @@ -1,54 +0,0 @@ -# Windows installation - -We **strongly** recommend that Windows users use [Docker](docker_quickstart.md) as this will work much easier and smoother (also more secure). - -If that is not possible, try using the Windows Linux subsystem (WSL) - for which the Ubuntu instructions should work. -Otherwise, please follow the instructions below. - -All instructions assume that python 3.11+ is installed and available. - -## Clone the git repository - -First of all clone the repository by running: - -``` powershell -git clone https://github.com/freqtrade/freqtrade.git -``` - -Now, choose your installation method, either automatically via script (recommended) or manually following the corresponding instructions. - -## Install freqtrade automatically - -### Run the installation script - -The script will ask you a few questions to determine which parts should be installed. - -```powershell -Set-ExecutionPolicy -ExecutionPolicy Bypass -cd freqtrade -. .\setup.ps1 -``` - -## Install freqtrade manually - -!!! Note "64bit Python version" - Please make sure to use 64bit Windows and 64bit Python to avoid problems with backtesting or hyperopt due to the memory constraints 32bit applications have under Windows. - 32bit python versions are no longer supported under Windows. - -!!! Hint - Using the [Anaconda Distribution](https://www.anaconda.com/distribution/) under Windows can greatly help with installation problems. Check out the [Anaconda installation section](installation.md#installation-with-conda) in the documentation for more information. - - -### Error during installation on Windows - -``` bash -error: Microsoft Visual C++ 14.0 is required. Get it with "Microsoft Visual C++ Build Tools": http://landinghub.visualstudio.com/visual-cpp-build-tools -``` - -Unfortunately, many packages requiring compilation don't provide a pre-built wheel. It is therefore mandatory to have a C/C++ compiler installed and available for your python environment to use. - -You can download the Visual C++ build tools from [here](https://visualstudio.microsoft.com/visual-cpp-build-tools/) and install "Desktop development with C++" in it's default configuration. Unfortunately, this is a heavy download / dependency so you might want to consider WSL2 or [docker compose](docker_quickstart.md) first. - -![Windows installation](assets/windows_install.png) - ---- diff --git a/freqtrade/__init__.py b/freqtrade/__init__.py index ae55129aa..252597c9e 100644 --- a/freqtrade/__init__.py +++ b/freqtrade/__init__.py @@ -1,6 +1,6 @@ """Freqtrade bot""" -__version__ = "2025.12" +__version__ = "2026.1" if "dev" in __version__: from pathlib import Path diff --git a/freqtrade/commands/arguments.py b/freqtrade/commands/arguments.py index f84822ac8..17c3702b2 100755 --- a/freqtrade/commands/arguments.py +++ b/freqtrade/commands/arguments.py @@ -103,7 +103,13 @@ ARGS_BACKTEST_SHOW = [ "backtest_breakdown", ] -ARGS_LIST_EXCHANGES = ["print_one_column", "list_exchanges_all", "trading_mode", "dex_exchanges"] +ARGS_LIST_EXCHANGES = [ + "print_one_column", + "list_exchanges_all", + "trading_mode", + "dex_exchanges", + "list_exchanges_futures_options", +] ARGS_LIST_TIMEFRAMES = ["exchange", "print_one_column", "trading_mode"] diff --git a/freqtrade/commands/cli_options.py b/freqtrade/commands/cli_options.py index 9fc5f2e11..8a035b6a0 100755 --- a/freqtrade/commands/cli_options.py +++ b/freqtrade/commands/cli_options.py @@ -2,7 +2,7 @@ Definition of cli arguments used in arguments.py """ -from argparse import ArgumentTypeError +from argparse import SUPPRESS, ArgumentTypeError from freqtrade import constants from freqtrade.constants import ( @@ -388,6 +388,13 @@ AVAILABLE_CLI_OPTIONS = { help="Print only DEX exchanges.", action="store_true", ), + "list_exchanges_futures_options": Arg( + "--ccxt-show-futures-options-exchanges", + help=SUPPRESS, + # Show compatibility with ccxt for futures functionality + # Doesn't show in help as it's an internal/debug option. + action="store_true", + ), # List pairs / markets "list_pairs_all": Arg( "-a", diff --git a/freqtrade/commands/list_commands.py b/freqtrade/commands/list_commands.py index a918afae3..8fc592381 100644 --- a/freqtrade/commands/list_commands.py +++ b/freqtrade/commands/list_commands.py @@ -38,13 +38,15 @@ def start_list_exchanges(args: dict[str, Any]) -> None: else: available_exchanges = [e for e in available_exchanges if e["valid"] is not False] title = f"Exchanges available for Freqtrade ({len(available_exchanges)} exchanges):" - + show_fut_reasons = args.get("list_exchanges_futures_options", False) table = Table(title=title) table.add_column("Exchange Name") table.add_column("Class Name") table.add_column("Markets") table.add_column("Reason") + if show_fut_reasons: + table.add_column("Futures Reason") trading_mode = args.get("trading_mode", None) dex_only = args.get("dex_exchanges", False) @@ -78,12 +80,14 @@ def start_list_exchanges(args: dict[str, Any]) -> None: if exchange["dex"]: trade_modes = Text("DEX: ") + trade_modes trade_modes.stylize("bold", 0, 3) + futcol = [] if not show_fut_reasons else [exchange["comment_futures"]] table.add_row( name, classname, trade_modes, exchange["comment"], + *futcol, style=None if exchange["valid"] else "red", ) # table.add_row(*[exchange[header] for header in headers]) diff --git a/freqtrade/configuration/load_config.py b/freqtrade/configuration/load_config.py index e18707a99..d3bc91002 100644 --- a/freqtrade/configuration/load_config.py +++ b/freqtrade/configuration/load_config.py @@ -30,7 +30,7 @@ def log_config_error_range(path: str, errmsg: str) -> str: offsetlist = re.findall(r"(?<=Parse\serror\sat\soffset\s)\d+", errmsg) if offsetlist: offset = int(offsetlist[0]) - text = Path(path).read_text() + text = Path(path).read_text(encoding="utf-8") # Fetch an offset of 80 characters around the error line subtext = text[offset - min(80, offset) : offset + 80] segments = subtext.split("\n") diff --git a/freqtrade/data/metrics.py b/freqtrade/data/metrics.py index cf59c330a..6d4e405cb 100644 --- a/freqtrade/data/metrics.py +++ b/freqtrade/data/metrics.py @@ -334,7 +334,10 @@ def calculate_expectancy(trades: pd.DataFrame) -> tuple[float, float]: def calculate_sortino( - trades: pd.DataFrame, min_date: datetime, max_date: datetime, starting_balance: float + trades: pd.DataFrame, + min_date: datetime | None, + max_date: datetime | None, + starting_balance: float, ) -> float: """ Calculate sortino @@ -362,7 +365,10 @@ def calculate_sortino( def calculate_sharpe( - trades: pd.DataFrame, min_date: datetime, max_date: datetime, starting_balance: float + trades: pd.DataFrame, + min_date: datetime | None, + max_date: datetime | None, + starting_balance: float, ) -> float: """ Calculate sharpe @@ -389,7 +395,10 @@ def calculate_sharpe( def calculate_calmar( - trades: pd.DataFrame, min_date: datetime, max_date: datetime, starting_balance: float + trades: pd.DataFrame, + min_date: datetime | None, + max_date: datetime | None, + starting_balance: float, ) -> float: """ Calculate calmar diff --git a/freqtrade/exchange/binance.py b/freqtrade/exchange/binance.py index 40de29470..aa1aa3e68 100644 --- a/freqtrade/exchange/binance.py +++ b/freqtrade/exchange/binance.py @@ -68,6 +68,7 @@ class Binance(Exchange): "BFUSD": "USDT", }, } + _can_use_data_download_fast = True _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [ (TradingMode.SPOT, MarginMode.NONE), @@ -181,7 +182,8 @@ class Binance(Exchange): return DataFrame(columns=DEFAULT_DATAFRAME_COLUMNS) if ( - self._config["exchange"].get("only_from_ccxt", False) + not self._can_use_data_download_fast + or self._config["exchange"].get("only_from_ccxt", False) or # only download timeframes with significant improvements, # otherwise fall back to rest API @@ -405,7 +407,10 @@ class Binance(Exchange): ) -> tuple[str, list[list]]: logger.info(f"Fetching trades for {pair} from Binance, {from_id=}, {since=}, {until=}") - if not self._config["exchange"].get("only_from_ccxt", False): + if ( + not self._config["exchange"].get("only_from_ccxt", False) + and self._can_use_data_download_fast + ): if from_id is None or not since: trades = await self._api_async.fetch_trades( pair, @@ -569,3 +574,5 @@ class Binanceus(Binance): _supported_trading_mode_margin_pairs: list[tuple[TradingMode, MarginMode]] = [ (TradingMode.SPOT, MarginMode.NONE), ] + # binance vision does not have data for binanceus + _can_use_data_download_fast = False diff --git a/freqtrade/exchange/binance_leverage_tiers.json b/freqtrade/exchange/binance_leverage_tiers.json index b4d316b5b..4d003cf68 100644 --- a/freqtrade/exchange/binance_leverage_tiers.json +++ b/freqtrade/exchange/binance_leverage_tiers.json @@ -3449,13 +3449,13 @@ "symbol": "42/USDT:USDT", "currency": "USDT", "minNotional": 100000.0, - "maxNotional": 250000.0, + "maxNotional": 200000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { "bracket": "4", "initialLeverage": "3", - "notionalCap": "250000", + "notionalCap": "200000", "notionalFloor": "100000", "maintMarginRatio": "0.1667", "cum": "5920.0" @@ -3465,34 +3465,34 @@ "tier": 5.0, "symbol": "42/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 2500000.0, + "minNotional": 200000.0, + "maxNotional": 300000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "5", "initialLeverage": "2", - "notionalCap": "2500000", - "notionalFloor": "250000", + "notionalCap": "300000", + "notionalFloor": "200000", "maintMarginRatio": "0.25", - "cum": "26745.0" + "cum": "22580.0" } }, { "tier": 6.0, "symbol": "42/USDT:USDT", "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 300000.0, + "maxNotional": 350000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "6", "initialLeverage": "1", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "350000", + "notionalFloor": "300000", "maintMarginRatio": "0.5", - "cum": "651745.0" + "cum": "97580.0" } } ], @@ -4450,14 +4450,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maintenanceMarginRate": 0.03, + "maxLeverage": 20.0, "info": { "bracket": "1", - "initialLeverage": "25", + "initialLeverage": "20", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.02", + "maintMarginRatio": "0.03", "cum": "0.0" } }, @@ -4466,54 +4466,37 @@ "symbol": "ACT/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": "25.0" + "maintMarginRatio": "0.05", + "cum": "100.0" } }, { "tier": 3.0, "symbol": "ACT/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": "ACT/USDT:USDT", - "currency": "USDT", - "minNotional": 20000.0, + "minNotional": 15000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "4", + "bracket": "3", "initialLeverage": "5", "notionalCap": "50000", - "notionalFloor": "20000", + "notionalFloor": "15000", "maintMarginRatio": "0.1", - "cum": "1275.0" + "cum": "850.0" } }, { - "tier": 5.0, + "tier": 4.0, "symbol": "ACT/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, @@ -4521,16 +4504,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": "2100.0" } }, { - "tier": 6.0, + "tier": 5.0, "symbol": "ACT/USDT:USDT", "currency": "USDT", "minNotional": 100000.0, @@ -4538,16 +4521,16 @@ "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { - "bracket": "6", + "bracket": "5", "initialLeverage": "3", "notionalCap": "250000", "notionalFloor": "100000", "maintMarginRatio": "0.1667", - "cum": "6695.0" + "cum": "6270.0" } }, { - "tier": 7.0, + "tier": 6.0, "symbol": "ACT/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, @@ -4555,16 +4538,16 @@ "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "7", + "bracket": "6", "initialLeverage": "2", "notionalCap": "2500000", "notionalFloor": "250000", "maintMarginRatio": "0.25", - "cum": "27520.0" + "cum": "27095.0" } }, { - "tier": 8.0, + "tier": 7.0, "symbol": "ACT/USDT:USDT", "currency": "USDT", "minNotional": 2500000.0, @@ -4572,12 +4555,116 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "7", "initialLeverage": "1", "notionalCap": "5000000", "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "652520.0" + "cum": "652095.0" + } + } + ], + "ACU/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "ACU/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, + "info": { + "bracket": "1", + "initialLeverage": "10", + "notionalCap": "10000", + "notionalFloor": "0", + "maintMarginRatio": "0.05", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "ACU/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": "500.0" + } + }, + { + "tier": 3.0, + "symbol": "ACU/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": "1750.0" + } + }, + { + "tier": 4.0, + "symbol": "ACU/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": "5920.0" + } + }, + { + "tier": 5.0, + "symbol": "ACU/USDT:USDT", + "currency": "USDT", + "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": "ACU/USDT:USDT", + "currency": "USDT", + "minNotional": 2500000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "6", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "2500000", + "maintMarginRatio": "0.5", + "cum": "651745.0" } } ], @@ -6190,15 +6277,15 @@ "symbol": "AIA/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 3000.0, - "maintenanceMarginRate": 0.03, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "1", "initialLeverage": "20", - "notionalCap": "3000", + "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.03", + "maintMarginRatio": "0.025", "cum": "0.0" } }, @@ -6206,24 +6293,24 @@ "tier": 2.0, "symbol": "AIA/USDT:USDT", "currency": "USDT", - "minNotional": 3000.0, - "maxNotional": 15000.0, + "minNotional": 5000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "2", "initialLeverage": "10", - "notionalCap": "15000", - "notionalFloor": "3000", + "notionalCap": "10000", + "notionalFloor": "5000", "maintMarginRatio": "0.05", - "cum": "60.0" + "cum": "125.0" } }, { "tier": 3.0, "symbol": "AIA/USDT:USDT", "currency": "USDT", - "minNotional": 15000.0, + "minNotional": 10000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, @@ -6231,9 +6318,9 @@ "bracket": "3", "initialLeverage": "5", "notionalCap": "50000", - "notionalFloor": "15000", + "notionalFloor": "10000", "maintMarginRatio": "0.1", - "cum": "810.0" + "cum": "625.0" } }, { @@ -6241,67 +6328,67 @@ "symbol": "AIA/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 150000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "4", "initialLeverage": "4", - "notionalCap": "150000", + "notionalCap": "100000", "notionalFloor": "50000", "maintMarginRatio": "0.125", - "cum": "2060.0" + "cum": "1875.0" } }, { "tier": 5.0, "symbol": "AIA/USDT:USDT", "currency": "USDT", - "minNotional": 150000.0, - "maxNotional": 1000000.0, + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { "bracket": "5", "initialLeverage": "3", - "notionalCap": "1000000", - "notionalFloor": "150000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1667", - "cum": "8315.0" + "cum": "6045.0" } }, { "tier": 6.0, "symbol": "AIA/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 2500000.0, + "minNotional": 250000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "6", "initialLeverage": "2", - "notionalCap": "2500000", - "notionalFloor": "1000000", + "notionalCap": "500000", + "notionalFloor": "250000", "maintMarginRatio": "0.25", - "cum": "91615.0" + "cum": "26870.0" } }, { "tier": 7.0, "symbol": "AIA/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": "716615.0" + "cum": "151870.0" } } ], @@ -7157,14 +7244,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.02", "cum": "0.0" } }, @@ -7182,7 +7269,7 @@ "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.025", - "cum": "50.0" + "cum": "25.0" } }, { @@ -7199,7 +7286,7 @@ "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.05", - "cum": "300.0" + "cum": "275.0" } }, { @@ -7216,7 +7303,7 @@ "notionalCap": "50000", "notionalFloor": "20000", "maintMarginRatio": "0.1", - "cum": "1300.0" + "cum": "1275.0" } }, { @@ -7224,67 +7311,67 @@ "symbol": "ALCH/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 250000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "250000", + "notionalCap": "100000", "notionalFloor": "50000", "maintMarginRatio": "0.125", - "cum": "2550.0" + "cum": "2525.0" } }, { "tier": 6.0, "symbol": "ALCH/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { "bracket": "6", "initialLeverage": "3", - "notionalCap": "1000000", - "notionalFloor": "250000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1667", - "cum": "12975.0" + "cum": "6695.0" } }, { "tier": 7.0, "symbol": "ALCH/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 7500000.0, + "minNotional": 250000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "7500000", - "notionalFloor": "1000000", + "notionalCap": "2500000", + "notionalFloor": "250000", "maintMarginRatio": "0.25", - "cum": "96275.0" + "cum": "27520.0" } }, { "tier": 8.0, "symbol": "ALCH/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "1971275.0" + "cum": "652520.0" } } ], @@ -7811,14 +7898,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.02", "cum": "0.0" } }, @@ -7828,14 +7915,14 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", - "maintMarginRatio": "0.02", + "maintMarginRatio": "0.025", "cum": "25.0" } }, @@ -7844,67 +7931,67 @@ "symbol": "ALLO/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "3", - "initialLeverage": "20", - "notionalCap": "25000", + "initialLeverage": "10", + "notionalCap": "20000", "notionalFloor": "10000", - "maintMarginRatio": "0.025", - "cum": "75.0" + "maintMarginRatio": "0.05", + "cum": "275.0" } }, { "tier": 4.0, "symbol": "ALLO/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 62500.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 20000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "4", - "initialLeverage": "10", - "notionalCap": "62500", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" + "initialLeverage": "5", + "notionalCap": "50000", + "notionalFloor": "20000", + "maintMarginRatio": "0.1", + "cum": "1275.0" } }, { "tier": 5.0, "symbol": "ALLO/USDT:USDT", "currency": "USDT", - "minNotional": 62500.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "5", - "initialLeverage": "5", - "notionalCap": "125000", - "notionalFloor": "62500", - "maintMarginRatio": "0.1", - "cum": "3825.0" + "initialLeverage": "4", + "notionalCap": "100000", + "notionalFloor": "50000", + "maintMarginRatio": "0.125", + "cum": "2525.0" } }, { "tier": 6.0, "symbol": "ALLO/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, + "minNotional": 100000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": "6", - "initialLeverage": "4", + "initialLeverage": "3", "notionalCap": "250000", - "notionalFloor": "125000", - "maintMarginRatio": "0.125", - "cum": "6950.0" + "notionalFloor": "100000", + "maintMarginRatio": "0.1667", + "cum": "6695.0" } }, { @@ -7912,50 +7999,33 @@ "symbol": "ALLO/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 4500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "maxNotional": 2500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": "7", - "initialLeverage": "3", - "notionalCap": "4500000", + "initialLeverage": "2", + "notionalCap": "2500000", "notionalFloor": "250000", - "maintMarginRatio": "0.1667", - "cum": "17375.0" + "maintMarginRatio": "0.25", + "cum": "27520.0" } }, { "tier": 8.0, "symbol": "ALLO/USDT:USDT", "currency": "USDT", - "minNotional": 4500000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": "8", - "initialLeverage": "2", - "notionalCap": "7500000", - "notionalFloor": "4500000", - "maintMarginRatio": "0.25", - "cum": "392225.0" - } - }, - { - "tier": 9.0, - "symbol": "ALLO/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "9", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "2267225.0" + "cum": "652520.0" } } ], @@ -8328,13 +8398,13 @@ "symbol": "ALT/USDT:USDT", "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 12500.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "12500", + "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.02", "cum": "25.0" @@ -8344,75 +8414,75 @@ "tier": 3.0, "symbol": "ALT/USDT:USDT", "currency": "USDT", - "minNotional": 12500.0, - "maxNotional": 27500.0, + "minNotional": 10000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "3", "initialLeverage": "20", - "notionalCap": "27500", - "notionalFloor": "12500", + "notionalCap": "25000", + "notionalFloor": "10000", "maintMarginRatio": "0.025", - "cum": "87.5" + "cum": "75.0" } }, { "tier": 4.0, "symbol": "ALT/USDT:USDT", "currency": "USDT", - "minNotional": 27500.0, - "maxNotional": 70000.0, + "minNotional": 25000.0, + "maxNotional": 62500.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "70000", - "notionalFloor": "27500", + "notionalCap": "62500", + "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "775.0" + "cum": "700.0" } }, { "tier": 5.0, "symbol": "ALT/USDT:USDT", "currency": "USDT", - "minNotional": 70000.0, - "maxNotional": 140000.0, + "minNotional": 62500.0, + "maxNotional": 125000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "140000", - "notionalFloor": "70000", + "notionalCap": "125000", + "notionalFloor": "62500", "maintMarginRatio": "0.1", - "cum": "4275.0" + "cum": "3825.0" } }, { "tier": 6.0, "symbol": "ALT/USDT:USDT", "currency": "USDT", - "minNotional": 140000.0, - "maxNotional": 300000.0, + "minNotional": 125000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "300000", - "notionalFloor": "140000", + "notionalCap": "250000", + "notionalFloor": "125000", "maintMarginRatio": "0.125", - "cum": "7775.0" + "cum": "6950.0" } }, { "tier": 7.0, "symbol": "ALT/USDT:USDT", "currency": "USDT", - "minNotional": 300000.0, + "minNotional": 250000.0, "maxNotional": 500000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, @@ -8420,9 +8490,9 @@ "bracket": "7", "initialLeverage": "3", "notionalCap": "500000", - "notionalFloor": "300000", + "notionalFloor": "250000", "maintMarginRatio": "0.1667", - "cum": "20285.0" + "cum": "17375.0" } }, { @@ -8439,7 +8509,7 @@ "notionalCap": "7500000", "notionalFloor": "500000", "maintMarginRatio": "0.25", - "cum": "61935.0" + "cum": "59025.0" } }, { @@ -8456,7 +8526,7 @@ "notionalCap": "12500000", "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "1936935.0" + "cum": "1934025.0" } } ], @@ -10000,14 +10070,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.02", "cum": "0.0" } }, @@ -10025,7 +10095,7 @@ "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.025", - "cum": "50.0" + "cum": "25.0" } }, { @@ -10042,7 +10112,7 @@ "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.05", - "cum": "300.0" + "cum": "275.0" } }, { @@ -10059,7 +10129,7 @@ "notionalCap": "50000", "notionalFloor": "20000", "maintMarginRatio": "0.1", - "cum": "1300.0" + "cum": "1275.0" } }, { @@ -10067,67 +10137,67 @@ "symbol": "ARC/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 250000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "250000", + "notionalCap": "100000", "notionalFloor": "50000", "maintMarginRatio": "0.125", - "cum": "2550.0" + "cum": "2525.0" } }, { "tier": 6.0, "symbol": "ARC/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 2000000.0, + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { "bracket": "6", "initialLeverage": "3", - "notionalCap": "2000000", - "notionalFloor": "250000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1667", - "cum": "12975.0" + "cum": "6695.0" } }, { "tier": 7.0, "symbol": "ARC/USDT:USDT", "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 7500000.0, + "minNotional": 250000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "7500000", - "notionalFloor": "2000000", + "notionalCap": "2500000", + "notionalFloor": "250000", "maintMarginRatio": "0.25", - "cum": "179575.0" + "cum": "27520.0" } }, { "tier": 8.0, "symbol": "ARC/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "2054575.0" + "cum": "652520.0" } } ], @@ -10999,13 +11069,13 @@ "symbol": "ASTR/USDT:USDT", "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 12500.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "12500", + "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.02", "cum": "25.0" @@ -11015,41 +11085,41 @@ "tier": 3.0, "symbol": "ASTR/USDT:USDT", "currency": "USDT", - "minNotional": 12500.0, - "maxNotional": 27500.0, + "minNotional": 10000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "3", "initialLeverage": "20", - "notionalCap": "27500", - "notionalFloor": "12500", + "notionalCap": "25000", + "notionalFloor": "10000", "maintMarginRatio": "0.025", - "cum": "87.5" + "cum": "75.0" } }, { "tier": 4.0, "symbol": "ASTR/USDT:USDT", "currency": "USDT", - "minNotional": 27500.0, - "maxNotional": 70000.0, + "minNotional": 25000.0, + "maxNotional": 62500.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "70000", - "notionalFloor": "27500", + "notionalCap": "62500", + "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "775.0" + "cum": "700.0" } }, { "tier": 5.0, "symbol": "ASTR/USDT:USDT", "currency": "USDT", - "minNotional": 70000.0, + "minNotional": 62500.0, "maxNotional": 125000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, @@ -11057,9 +11127,9 @@ "bracket": "5", "initialLeverage": "5", "notionalCap": "125000", - "notionalFloor": "70000", + "notionalFloor": "62500", "maintMarginRatio": "0.1", - "cum": "4275.0" + "cum": "3825.0" } }, { @@ -11076,7 +11146,7 @@ "notionalCap": "250000", "notionalFloor": "125000", "maintMarginRatio": "0.125", - "cum": "7400.0" + "cum": "6950.0" } }, { @@ -11093,7 +11163,7 @@ "notionalCap": "500000", "notionalFloor": "250000", "maintMarginRatio": "0.1667", - "cum": "17825.0" + "cum": "17375.0" } }, { @@ -11110,7 +11180,7 @@ "notionalCap": "7500000", "notionalFloor": "500000", "maintMarginRatio": "0.25", - "cum": "59475.0" + "cum": "59025.0" } }, { @@ -11127,7 +11197,7 @@ "notionalCap": "12500000", "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "1934475.0" + "cum": "1934025.0" } } ], @@ -13221,14 +13291,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" } }, @@ -13238,14 +13308,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" } }, @@ -13255,14 +13325,14 @@ "currency": "USDT", "minNotional": 10000.0, "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "3", - "initialLeverage": "25", + "initialLeverage": "20", "notionalCap": "25000", "notionalFloor": "10000", - "maintMarginRatio": "0.02", + "maintMarginRatio": "0.025", "cum": "75.0" } }, @@ -13271,33 +13341,33 @@ "symbol": "B/USDT:USDT", "currency": "USDT", "minNotional": 25000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 62500.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "4", - "initialLeverage": "20", - "notionalCap": "50000", + "initialLeverage": "10", + "notionalCap": "62500", "notionalFloor": "25000", - "maintMarginRatio": "0.025", - "cum": "200.0" + "maintMarginRatio": "0.05", + "cum": "700.0" } }, { "tier": 5.0, "symbol": "B/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, + "minNotional": 62500.0, "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "5", - "initialLeverage": "10", + "initialLeverage": "5", "notionalCap": "125000", - "notionalFloor": "50000", - "maintMarginRatio": "0.05", - "cum": "1450.0" + "notionalFloor": "62500", + "maintMarginRatio": "0.1", + "cum": "3825.0" } }, { @@ -13306,15 +13376,15 @@ "currency": "USDT", "minNotional": 125000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "6", - "initialLeverage": "5", + "initialLeverage": "4", "notionalCap": "250000", "notionalFloor": "125000", - "maintMarginRatio": "0.1", - "cum": "7700.0" + "maintMarginRatio": "0.125", + "cum": "6950.0" } }, { @@ -13323,15 +13393,15 @@ "currency": "USDT", "minNotional": 250000.0, "maxNotional": 500000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": "7", - "initialLeverage": "4", + "initialLeverage": "3", "notionalCap": "500000", "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "13950.0" + "maintMarginRatio": "0.1667", + "cum": "17375.0" } }, { @@ -13339,37 +13409,20 @@ "symbol": "B/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": "B/USDT:USDT", - "currency": "USDT", - "minNotional": 1000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "9", + "bracket": "8", "initialLeverage": "2", "notionalCap": "7500000", - "notionalFloor": "1000000", + "notionalFloor": "500000", "maintMarginRatio": "0.25", - "cum": "118100.0" + "cum": "59025.0" } }, { - "tier": 10.0, + "tier": 9.0, "symbol": "B/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -13377,12 +13430,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "10", + "bracket": "9", "initialLeverage": "1", "notionalCap": "12500000", "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "1993100.0" + "cum": "1934025.0" } } ], @@ -14911,14 +14964,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.02", "cum": "0.0" } }, @@ -14928,14 +14981,14 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", - "maintMarginRatio": "0.02", + "maintMarginRatio": "0.025", "cum": "25.0" } }, @@ -14944,67 +14997,67 @@ "symbol": "BARD/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "3", - "initialLeverage": "20", - "notionalCap": "25000", + "initialLeverage": "10", + "notionalCap": "20000", "notionalFloor": "10000", - "maintMarginRatio": "0.025", - "cum": "75.0" + "maintMarginRatio": "0.05", + "cum": "275.0" } }, { "tier": 4.0, "symbol": "BARD/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 62500.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 20000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "4", - "initialLeverage": "10", - "notionalCap": "62500", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" + "initialLeverage": "5", + "notionalCap": "50000", + "notionalFloor": "20000", + "maintMarginRatio": "0.1", + "cum": "1275.0" } }, { "tier": 5.0, "symbol": "BARD/USDT:USDT", "currency": "USDT", - "minNotional": 62500.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "5", - "initialLeverage": "5", - "notionalCap": "125000", - "notionalFloor": "62500", - "maintMarginRatio": "0.1", - "cum": "3825.0" + "initialLeverage": "4", + "notionalCap": "100000", + "notionalFloor": "50000", + "maintMarginRatio": "0.125", + "cum": "2525.0" } }, { "tier": 6.0, "symbol": "BARD/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, + "minNotional": 100000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": "6", - "initialLeverage": "4", + "initialLeverage": "3", "notionalCap": "250000", - "notionalFloor": "125000", - "maintMarginRatio": "0.125", - "cum": "6950.0" + "notionalFloor": "100000", + "maintMarginRatio": "0.1667", + "cum": "6695.0" } }, { @@ -15012,50 +15065,33 @@ "symbol": "BARD/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 2000000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "maxNotional": 2500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": "7", - "initialLeverage": "3", - "notionalCap": "2000000", + "initialLeverage": "2", + "notionalCap": "2500000", "notionalFloor": "250000", - "maintMarginRatio": "0.1667", - "cum": "17375.0" + "maintMarginRatio": "0.25", + "cum": "27520.0" } }, { "tier": 8.0, "symbol": "BARD/USDT:USDT", "currency": "USDT", - "minNotional": 2000000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": "8", - "initialLeverage": "2", - "notionalCap": "7500000", - "notionalFloor": "2000000", - "maintMarginRatio": "0.25", - "cum": "183975.0" - } - }, - { - "tier": 9.0, - "symbol": "BARD/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "9", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "2058975.0" + "cum": "652520.0" } } ], @@ -19471,6 +19507,161 @@ } } ], + "BREV/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "BREV/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": "BREV/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": "BREV/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": "BREV/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": "BREV/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": "BREV/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": "BREV/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": "BREV/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": "BREV/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" + } + } + ], "BROCCOLI714/USDT:USDT": [ { "tier": 1.0, @@ -20401,144 +20592,6 @@ } } ], - "BTC/USDT:USDT-251226": [ - { - "tier": 1.0, - "symbol": "BTC/USDT:USDT-251226", - "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-251226", - "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-251226", - "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-251226", - "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-251226", - "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-251226", - "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-251226", - "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-251226", - "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-260327": [ { "tier": 1.0, @@ -20677,6 +20730,144 @@ } } ], + "BTC/USDT:USDT-260626": [ + { + "tier": 1.0, + "symbol": "BTC/USDT:USDT-260626", + "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-260626", + "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-260626", + "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-260626", + "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-260626", + "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-260626", + "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-260626", + "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-260626", + "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, @@ -23407,13 +23598,13 @@ "symbol": "CKB/USDT:USDT", "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 12500.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "12500", + "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.02", "cum": "25.0" @@ -23423,41 +23614,41 @@ "tier": 3.0, "symbol": "CKB/USDT:USDT", "currency": "USDT", - "minNotional": 12500.0, - "maxNotional": 27500.0, + "minNotional": 10000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "3", "initialLeverage": "20", - "notionalCap": "27500", - "notionalFloor": "12500", + "notionalCap": "25000", + "notionalFloor": "10000", "maintMarginRatio": "0.025", - "cum": "87.5" + "cum": "75.0" } }, { "tier": 4.0, "symbol": "CKB/USDT:USDT", "currency": "USDT", - "minNotional": 27500.0, - "maxNotional": 70000.0, + "minNotional": 25000.0, + "maxNotional": 62500.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "70000", - "notionalFloor": "27500", + "notionalCap": "62500", + "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "775.0" + "cum": "700.0" } }, { "tier": 5.0, "symbol": "CKB/USDT:USDT", "currency": "USDT", - "minNotional": 70000.0, + "minNotional": 62500.0, "maxNotional": 125000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, @@ -23465,9 +23656,9 @@ "bracket": "5", "initialLeverage": "5", "notionalCap": "125000", - "notionalFloor": "70000", + "notionalFloor": "62500", "maintMarginRatio": "0.1", - "cum": "4275.0" + "cum": "3825.0" } }, { @@ -23484,7 +23675,7 @@ "notionalCap": "250000", "notionalFloor": "125000", "maintMarginRatio": "0.125", - "cum": "7400.0" + "cum": "6950.0" } }, { @@ -23501,7 +23692,7 @@ "notionalCap": "500000", "notionalFloor": "250000", "maintMarginRatio": "0.1667", - "cum": "17825.0" + "cum": "17375.0" } }, { @@ -23518,7 +23709,7 @@ "notionalCap": "7500000", "notionalFloor": "500000", "maintMarginRatio": "0.25", - "cum": "59475.0" + "cum": "59025.0" } }, { @@ -23535,7 +23726,7 @@ "notionalCap": "12500000", "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "1934475.0" + "cum": "1934025.0" } } ], @@ -23546,14 +23737,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.02", "cum": "0.0" } }, @@ -23571,7 +23762,7 @@ "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.025", - "cum": "50.0" + "cum": "25.0" } }, { @@ -23588,7 +23779,7 @@ "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.05", - "cum": "300.0" + "cum": "275.0" } }, { @@ -23605,7 +23796,7 @@ "notionalCap": "50000", "notionalFloor": "20000", "maintMarginRatio": "0.1", - "cum": "1300.0" + "cum": "1275.0" } }, { @@ -23613,67 +23804,67 @@ "symbol": "CLANKER/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 250000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "250000", + "notionalCap": "100000", "notionalFloor": "50000", "maintMarginRatio": "0.125", - "cum": "2550.0" + "cum": "2525.0" } }, { "tier": 6.0, "symbol": "CLANKER/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { "bracket": "6", "initialLeverage": "3", - "notionalCap": "1000000", - "notionalFloor": "250000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1667", - "cum": "12975.0" + "cum": "6695.0" } }, { "tier": 7.0, "symbol": "CLANKER/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 7500000.0, + "minNotional": 250000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "7500000", - "notionalFloor": "1000000", + "notionalCap": "2500000", + "notionalFloor": "250000", "maintMarginRatio": "0.25", - "cum": "96275.0" + "cum": "27520.0" } }, { "tier": 8.0, "symbol": "CLANKER/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "1971275.0" + "cum": "652520.0" } } ], @@ -23919,6 +24110,127 @@ } } ], + "COLLECT/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "COLLECT/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": "COLLECT/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": "COLLECT/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": "COLLECT/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": "COLLECT/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": "COLLECT/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": "COLLECT/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" + } + } + ], "COMBO/USDT:USDT": [ { "tier": 1.0, @@ -24030,14 +24342,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "1", - "initialLeverage": "10", + "initialLeverage": "5", "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.05", + "maintMarginRatio": "0.1", "cum": "0.0" } }, @@ -24046,84 +24358,67 @@ "symbol": "COMMON/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 60000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "2", - "initialLeverage": "5", - "notionalCap": "60000", + "initialLeverage": "4", + "notionalCap": "50000", "notionalFloor": "10000", - "maintMarginRatio": "0.1", - "cum": "500.0" + "maintMarginRatio": "0.125", + "cum": "250.0" } }, { "tier": 3.0, "symbol": "COMMON/USDT:USDT", "currency": "USDT", - "minNotional": 60000.0, - "maxNotional": 70000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 50000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": "3", - "initialLeverage": "4", - "notionalCap": "70000", - "notionalFloor": "60000", - "maintMarginRatio": "0.125", - "cum": "2000.0" + "initialLeverage": "3", + "notionalCap": "500000", + "notionalFloor": "50000", + "maintMarginRatio": "0.1667", + "cum": "2335.0" } }, { "tier": 4.0, "symbol": "COMMON/USDT:USDT", "currency": "USDT", - "minNotional": 70000.0, - "maxNotional": 700000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "minNotional": 500000.0, + "maxNotional": 600000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": "4", - "initialLeverage": "3", - "notionalCap": "700000", - "notionalFloor": "70000", - "maintMarginRatio": "0.1667", - "cum": "4919.0" + "initialLeverage": "2", + "notionalCap": "600000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "43985.0" } }, { "tier": 5.0, "symbol": "COMMON/USDT:USDT", "currency": "USDT", - "minNotional": 700000.0, - "maxNotional": 2500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": "5", - "initialLeverage": "2", - "notionalCap": "2500000", - "notionalFloor": "700000", - "maintMarginRatio": "0.25", - "cum": "63229.0" - } - }, - { - "tier": 6.0, - "symbol": "COMMON/USDT:USDT", - "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 600000.0, + "maxNotional": 650000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "6", + "bracket": "5", "initialLeverage": "1", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "650000", + "notionalFloor": "600000", "maintMarginRatio": "0.5", - "cum": "688229.0" + "cum": "193985.0" } } ], @@ -25650,14 +25945,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "1", - "initialLeverage": "10", + "initialLeverage": "5", "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.05", + "maintMarginRatio": "0.1", "cum": "0.0" } }, @@ -25667,15 +25962,15 @@ "currency": "USDT", "minNotional": 10000.0, "maxNotional": 50000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "2", - "initialLeverage": "5", + "initialLeverage": "4", "notionalCap": "50000", "notionalFloor": "10000", - "maintMarginRatio": "0.1", - "cum": "500.0" + "maintMarginRatio": "0.125", + "cum": "250.0" } }, { @@ -25683,67 +25978,50 @@ "symbol": "CUDIS/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": "3", - "initialLeverage": "4", - "notionalCap": "100000", + "initialLeverage": "3", + "notionalCap": "250000", "notionalFloor": "50000", - "maintMarginRatio": "0.125", - "cum": "1750.0" + "maintMarginRatio": "0.1667", + "cum": "2335.0" } }, { "tier": 4.0, "symbol": "CUDIS/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "minNotional": 250000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": "4", - "initialLeverage": "3", - "notionalCap": "250000", - "notionalFloor": "100000", - "maintMarginRatio": "0.1667", - "cum": "5920.0" + "initialLeverage": "2", + "notionalCap": "300000", + "notionalFloor": "250000", + "maintMarginRatio": "0.25", + "cum": "23160.0" } }, { "tier": 5.0, "symbol": "CUDIS/USDT:USDT", "currency": "USDT", - "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": "CUDIS/USDT:USDT", - "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 300000.0, + "maxNotional": 500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "6", + "bracket": "5", "initialLeverage": "1", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "500000", + "notionalFloor": "300000", "maintMarginRatio": "0.5", - "cum": "651745.0" + "cum": "98160.0" } } ], @@ -26925,13 +27203,13 @@ "symbol": "DEEP/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 50000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "3", "initialLeverage": "20", - "notionalCap": "50000", + "notionalCap": "25000", "notionalFloor": "10000", "maintMarginRatio": "0.025", "cum": "75.0" @@ -26941,58 +27219,58 @@ "tier": 4.0, "symbol": "DEEP/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 125000.0, + "minNotional": 25000.0, + "maxNotional": 62500.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "125000", - "notionalFloor": "50000", + "notionalCap": "62500", + "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "1325.0" + "cum": "700.0" } }, { "tier": 5.0, "symbol": "DEEP/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, + "minNotional": 62500.0, + "maxNotional": 125000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "125000", + "notionalCap": "125000", + "notionalFloor": "62500", "maintMarginRatio": "0.1", - "cum": "7575.0" + "cum": "3825.0" } }, { "tier": 6.0, "symbol": "DEEP/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 400000.0, + "minNotional": 125000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "400000", - "notionalFloor": "250000", + "notionalCap": "250000", + "notionalFloor": "125000", "maintMarginRatio": "0.125", - "cum": "13825.0" + "cum": "6950.0" } }, { "tier": 7.0, "symbol": "DEEP/USDT:USDT", "currency": "USDT", - "minNotional": 400000.0, + "minNotional": 250000.0, "maxNotional": 500000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, @@ -27000,9 +27278,9 @@ "bracket": "7", "initialLeverage": "3", "notionalCap": "500000", - "notionalFloor": "400000", + "notionalFloor": "250000", "maintMarginRatio": "0.1667", - "cum": "30505.0" + "cum": "17375.0" } }, { @@ -27019,7 +27297,7 @@ "notionalCap": "7500000", "notionalFloor": "500000", "maintMarginRatio": "0.25", - "cum": "72155.0" + "cum": "59025.0" } }, { @@ -27036,7 +27314,7 @@ "notionalCap": "12500000", "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "1947155.0" + "cum": "1934025.0" } } ], @@ -27564,13 +27842,13 @@ "symbol": "DEXE/USDT:USDT", "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 20000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "20000", + "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.02", "cum": "25.0" @@ -27580,75 +27858,75 @@ "tier": 3.0, "symbol": "DEXE/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 50000.0, + "minNotional": 10000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "3", "initialLeverage": "20", - "notionalCap": "50000", - "notionalFloor": "20000", + "notionalCap": "25000", + "notionalFloor": "10000", "maintMarginRatio": "0.025", - "cum": "125.0" + "cum": "75.0" } }, { "tier": 4.0, "symbol": "DEXE/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 125000.0, + "minNotional": 25000.0, + "maxNotional": 62500.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "125000", - "notionalFloor": "50000", + "notionalCap": "62500", + "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "1375.0" + "cum": "700.0" } }, { "tier": 5.0, "symbol": "DEXE/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, + "minNotional": 62500.0, + "maxNotional": 125000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "125000", + "notionalCap": "125000", + "notionalFloor": "62500", "maintMarginRatio": "0.1", - "cum": "7625.0" + "cum": "3825.0" } }, { "tier": 6.0, "symbol": "DEXE/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, + "minNotional": 125000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "500000", - "notionalFloor": "250000", + "notionalCap": "250000", + "notionalFloor": "125000", "maintMarginRatio": "0.125", - "cum": "13875.0" + "cum": "6950.0" } }, { "tier": 7.0, "symbol": "DEXE/USDT:USDT", "currency": "USDT", - "minNotional": 500000.0, + "minNotional": 250000.0, "maxNotional": 500000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, @@ -27656,9 +27934,9 @@ "bracket": "7", "initialLeverage": "3", "notionalCap": "500000", - "notionalFloor": "500000", + "notionalFloor": "250000", "maintMarginRatio": "0.1667", - "cum": "34725.0" + "cum": "17375.0" } }, { @@ -27675,7 +27953,7 @@ "notionalCap": "7500000", "notionalFloor": "500000", "maintMarginRatio": "0.25", - "cum": "76375.0" + "cum": "59025.0" } }, { @@ -27692,7 +27970,7 @@ "notionalCap": "12500000", "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "1951375.0" + "cum": "1934025.0" } } ], @@ -30556,6 +30834,127 @@ } } ], + "ELSA/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "ELSA/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": "ELSA/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": "ELSA/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": "ELSA/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": "ELSA/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": "ELSA/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": "ELSA/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" + } + } + ], "ENA/USDC:USDC": [ { "tier": 1.0, @@ -31493,14 +31892,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "1", - "initialLeverage": "10", + "initialLeverage": "5", "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.05", + "maintMarginRatio": "0.1", "cum": "0.0" } }, @@ -31510,15 +31909,15 @@ "currency": "USDT", "minNotional": 10000.0, "maxNotional": 50000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "2", - "initialLeverage": "5", + "initialLeverage": "4", "notionalCap": "50000", "notionalFloor": "10000", - "maintMarginRatio": "0.1", - "cum": "500.0" + "maintMarginRatio": "0.125", + "cum": "250.0" } }, { @@ -31526,67 +31925,50 @@ "symbol": "EPT/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 100000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": "3", - "initialLeverage": "4", - "notionalCap": "100000", + "initialLeverage": "3", + "notionalCap": "250000", "notionalFloor": "50000", - "maintMarginRatio": "0.125", - "cum": "1750.0" + "maintMarginRatio": "0.1667", + "cum": "2335.0" } }, { "tier": 4.0, "symbol": "EPT/USDT:USDT", "currency": "USDT", - "minNotional": 100000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "minNotional": 250000.0, + "maxNotional": 300000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": "4", - "initialLeverage": "3", - "notionalCap": "250000", - "notionalFloor": "100000", - "maintMarginRatio": "0.1667", - "cum": "5920.0" + "initialLeverage": "2", + "notionalCap": "300000", + "notionalFloor": "250000", + "maintMarginRatio": "0.25", + "cum": "23160.0" } }, { "tier": 5.0, "symbol": "EPT/USDT:USDT", "currency": "USDT", - "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": "EPT/USDT:USDT", - "currency": "USDT", - "minNotional": 2500000.0, - "maxNotional": 5000000.0, + "minNotional": 300000.0, + "maxNotional": 350000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "6", + "bracket": "5", "initialLeverage": "1", - "notionalCap": "5000000", - "notionalFloor": "2500000", + "notionalCap": "350000", + "notionalFloor": "300000", "maintMarginRatio": "0.5", - "cum": "651745.0" + "cum": "98160.0" } } ], @@ -32656,144 +33038,6 @@ } } ], - "ETH/USDT:USDT-251226": [ - { - "tier": 1.0, - "symbol": "ETH/USDT:USDT-251226", - "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-251226", - "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-251226", - "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-251226", - "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-251226", - "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-251226", - "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-251226", - "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-251226", - "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-260327": [ { "tier": 1.0, @@ -32932,6 +33176,144 @@ } } ], + "ETH/USDT:USDT-260626": [ + { + "tier": 1.0, + "symbol": "ETH/USDT:USDT-260626", + "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-260626", + "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-260626", + "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-260626", + "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-260626", + "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-260626", + "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-260626", + "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-260626", + "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, @@ -34654,6 +35036,127 @@ } } ], + "FIGHT/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "FIGHT/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": "FIGHT/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": "FIGHT/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": "FIGHT/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": "FIGHT/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": "FIGHT/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": "FIGHT/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" + } + } + ], "FIL/USDC:USDC": [ { "tier": 1.0, @@ -35437,15 +35940,15 @@ "symbol": "FLOW/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.01, - "maxLeverage": 75.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "1", - "initialLeverage": "75", - "notionalCap": "5000", + "initialLeverage": "10", + "notionalCap": "50000", "notionalFloor": "0", - "maintMarginRatio": "0.01", + "maintMarginRatio": "0.05", "cum": "0.0" } }, @@ -35453,153 +35956,85 @@ "tier": 2.0, "symbol": "FLOW/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "minNotional": 50000.0, + "maxNotional": 200000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "2", - "initialLeverage": "50", - "notionalCap": "10000", - "notionalFloor": "5000", - "maintMarginRatio": "0.015", - "cum": "25.0" + "initialLeverage": "5", + "notionalCap": "200000", + "notionalFloor": "50000", + "maintMarginRatio": "0.1", + "cum": "2500.0" } }, { "tier": 3.0, "symbol": "FLOW/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "minNotional": 200000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "3", - "initialLeverage": "25", - "notionalCap": "25000", - "notionalFloor": "10000", - "maintMarginRatio": "0.02", - "cum": "75.0" + "initialLeverage": "4", + "notionalCap": "400000", + "notionalFloor": "200000", + "maintMarginRatio": "0.125", + "cum": "7500.0" } }, { "tier": 4.0, "symbol": "FLOW/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 400000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": "4", - "initialLeverage": "20", - "notionalCap": "50000", - "notionalFloor": "25000", - "maintMarginRatio": "0.025", - "cum": "200.0" + "initialLeverage": "3", + "notionalCap": "500000", + "notionalFloor": "400000", + "maintMarginRatio": "0.1667", + "cum": "24180.0" } }, { "tier": 5.0, "symbol": "FLOW/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 500000.0, + "maxNotional": 800000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": "5", - "initialLeverage": "10", - "notionalCap": "125000", - "notionalFloor": "50000", - "maintMarginRatio": "0.05", - "cum": "1450.0" + "initialLeverage": "2", + "notionalCap": "800000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "65830.0" } }, { "tier": 6.0, "symbol": "FLOW/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, - "info": { - "bracket": "6", - "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "125000", - "maintMarginRatio": "0.1", - "cum": "7700.0" - } - }, - { - "tier": 7.0, - "symbol": "FLOW/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": "FLOW/USDT:USDT", - "currency": "USDT", - "minNotional": 500000.0, + "minNotional": 800000.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": "FLOW/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": "FLOW/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "10", + "bracket": "6", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "1000000", + "notionalFloor": "800000", "maintMarginRatio": "0.5", - "cum": "1993100.0" + "cum": "265830.0" } } ], @@ -35913,6 +36348,161 @@ } } ], + "FOGO/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "FOGO/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": "FOGO/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": "FOGO/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": "FOGO/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": "FOGO/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": "FOGO/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": "FOGO/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": "FOGO/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": "FOGO/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" + } + } + ], "FOLKS/USDT:USDT": [ { "tier": 1.0, @@ -36344,6 +36934,161 @@ } } ], + "FRAX/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "FRAX/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": "FRAX/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": "FRAX/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": "FRAX/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": "FRAX/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": "FRAX/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": "FRAX/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": "FRAX/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": "FRAX/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" + } + } + ], "FTM/USDT:USDT": [ { "tier": 1.0, @@ -36609,15 +37354,15 @@ "symbol": "FUN/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 500.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "500", + "initialLeverage": "20", + "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.025", "cum": "0.0" } }, @@ -36625,72 +37370,38 @@ "tier": 2.0, "symbol": "FUN/USDT:USDT", "currency": "USDT", - "minNotional": 500.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 40.0, + "minNotional": 5000.0, + "maxNotional": 15000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "2", - "initialLeverage": "40", - "notionalCap": "5000", - "notionalFloor": "500", - "maintMarginRatio": "0.02", - "cum": "2.5" + "initialLeverage": "10", + "notionalCap": "15000", + "notionalFloor": "5000", + "maintMarginRatio": "0.05", + "cum": "125.0" } }, { "tier": 3.0, "symbol": "FUN/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, - "info": { - "bracket": "3", - "initialLeverage": "20", - "notionalCap": "10000", - "notionalFloor": "5000", - "maintMarginRatio": "0.025", - "cum": "27.5" - } - }, - { - "tier": 4.0, - "symbol": "FUN/USDT:USDT", - "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, - "info": { - "bracket": "4", - "initialLeverage": "10", - "notionalCap": "25000", - "notionalFloor": "10000", - "maintMarginRatio": "0.05", - "cum": "277.5" - } - }, - { - "tier": 5.0, - "symbol": "FUN/USDT:USDT", - "currency": "USDT", - "minNotional": 25000.0, + "minNotional": 15000.0, "maxNotional": 50000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "5", + "bracket": "3", "initialLeverage": "5", "notionalCap": "50000", - "notionalFloor": "25000", + "notionalFloor": "15000", "maintMarginRatio": "0.1", - "cum": "1527.5" + "cum": "875.0" } }, { - "tier": 6.0, + "tier": 4.0, "symbol": "FUN/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, @@ -36698,16 +37409,16 @@ "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "6", + "bracket": "4", "initialLeverage": "4", "notionalCap": "100000", "notionalFloor": "50000", "maintMarginRatio": "0.125", - "cum": "2777.5" + "cum": "2125.0" } }, { - "tier": 7.0, + "tier": 5.0, "symbol": "FUN/USDT:USDT", "currency": "USDT", "minNotional": 100000.0, @@ -36715,16 +37426,16 @@ "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { - "bracket": "7", + "bracket": "5", "initialLeverage": "3", "notionalCap": "250000", "notionalFloor": "100000", "maintMarginRatio": "0.1667", - "cum": "6947.5" + "cum": "6295.0" } }, { - "tier": 8.0, + "tier": 6.0, "symbol": "FUN/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, @@ -36732,16 +37443,16 @@ "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "8", + "bracket": "6", "initialLeverage": "2", "notionalCap": "2500000", "notionalFloor": "250000", "maintMarginRatio": "0.25", - "cum": "27772.5" + "cum": "27120.0" } }, { - "tier": 9.0, + "tier": 7.0, "symbol": "FUN/USDT:USDT", "currency": "USDT", "minNotional": 2500000.0, @@ -36749,12 +37460,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "9", + "bracket": "7", "initialLeverage": "1", "notionalCap": "5000000", "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "652772.5" + "cum": "652120.0" } } ], @@ -36764,15 +37475,15 @@ "symbol": "FXS/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 15000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "1", - "initialLeverage": "20", - "notionalCap": "10000", + "initialLeverage": "10", + "notionalCap": "15000", "notionalFloor": "0", - "maintMarginRatio": "0.025", + "maintMarginRatio": "0.05", "cum": "0.0" } }, @@ -36780,55 +37491,38 @@ "tier": 2.0, "symbol": "FXS/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 15000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "2", - "initialLeverage": "10", - "notionalCap": "20000", - "notionalFloor": "10000", - "maintMarginRatio": "0.05", - "cum": "250.0" + "initialLeverage": "5", + "notionalCap": "50000", + "notionalFloor": "15000", + "maintMarginRatio": "0.1", + "cum": "750.0" } }, { "tier": 3.0, "symbol": "FXS/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 60000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, - "info": { - "bracket": "3", - "initialLeverage": "5", - "notionalCap": "60000", - "notionalFloor": "20000", - "maintMarginRatio": "0.1", - "cum": "1250.0" - } - }, - { - "tier": 4.0, - "symbol": "FXS/USDT:USDT", - "currency": "USDT", - "minNotional": 60000.0, + "minNotional": 50000.0, "maxNotional": 150000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "4", + "bracket": "3", "initialLeverage": "4", "notionalCap": "150000", - "notionalFloor": "60000", + "notionalFloor": "50000", "maintMarginRatio": "0.125", - "cum": "2750.0" + "cum": "2000.0" } }, { - "tier": 5.0, + "tier": 4.0, "symbol": "FXS/USDT:USDT", "currency": "USDT", "minNotional": 150000.0, @@ -36836,16 +37530,16 @@ "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { - "bracket": "5", + "bracket": "4", "initialLeverage": "3", "notionalCap": "250000", "notionalFloor": "150000", "maintMarginRatio": "0.1667", - "cum": "9005.0" + "cum": "8255.0" } }, { - "tier": 6.0, + "tier": 5.0, "symbol": "FXS/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, @@ -36853,16 +37547,16 @@ "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "6", + "bracket": "5", "initialLeverage": "2", "notionalCap": "2500000", "notionalFloor": "250000", "maintMarginRatio": "0.25", - "cum": "29830.0" + "cum": "29080.0" } }, { - "tier": 7.0, + "tier": 6.0, "symbol": "FXS/USDT:USDT", "currency": "USDT", "minNotional": 2500000.0, @@ -36870,12 +37564,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "7", + "bracket": "6", "initialLeverage": "1", "notionalCap": "5000000", "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "654830.0" + "cum": "654080.0" } } ], @@ -37832,13 +38526,13 @@ "symbol": "GLM/USDT:USDT", "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 12500.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "12500", + "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.02", "cum": "25.0" @@ -37848,41 +38542,41 @@ "tier": 3.0, "symbol": "GLM/USDT:USDT", "currency": "USDT", - "minNotional": 12500.0, - "maxNotional": 27500.0, + "minNotional": 10000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "3", "initialLeverage": "20", - "notionalCap": "27500", - "notionalFloor": "12500", + "notionalCap": "25000", + "notionalFloor": "10000", "maintMarginRatio": "0.025", - "cum": "87.5" + "cum": "75.0" } }, { "tier": 4.0, "symbol": "GLM/USDT:USDT", "currency": "USDT", - "minNotional": 27500.0, - "maxNotional": 70000.0, + "minNotional": 25000.0, + "maxNotional": 62500.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "70000", - "notionalFloor": "27500", + "notionalCap": "62500", + "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "775.0" + "cum": "700.0" } }, { "tier": 5.0, "symbol": "GLM/USDT:USDT", "currency": "USDT", - "minNotional": 70000.0, + "minNotional": 62500.0, "maxNotional": 125000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, @@ -37890,9 +38584,9 @@ "bracket": "5", "initialLeverage": "5", "notionalCap": "125000", - "notionalFloor": "70000", + "notionalFloor": "62500", "maintMarginRatio": "0.1", - "cum": "4275.0" + "cum": "3825.0" } }, { @@ -37909,7 +38603,7 @@ "notionalCap": "250000", "notionalFloor": "125000", "maintMarginRatio": "0.125", - "cum": "7400.0" + "cum": "6950.0" } }, { @@ -37926,7 +38620,7 @@ "notionalCap": "500000", "notionalFloor": "250000", "maintMarginRatio": "0.1667", - "cum": "17825.0" + "cum": "17375.0" } }, { @@ -37943,7 +38637,7 @@ "notionalCap": "7500000", "notionalFloor": "500000", "maintMarginRatio": "0.25", - "cum": "59475.0" + "cum": "59025.0" } }, { @@ -37960,7 +38654,7 @@ "notionalCap": "12500000", "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "1934475.0" + "cum": "1934025.0" } } ], @@ -45829,14 +46523,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.02", "cum": "0.0" } }, @@ -45846,14 +46540,14 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", - "maintMarginRatio": "0.02", + "maintMarginRatio": "0.025", "cum": "25.0" } }, @@ -45862,67 +46556,67 @@ "symbol": "JOE/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "3", - "initialLeverage": "20", - "notionalCap": "25000", + "initialLeverage": "10", + "notionalCap": "20000", "notionalFloor": "10000", - "maintMarginRatio": "0.025", - "cum": "75.0" + "maintMarginRatio": "0.05", + "cum": "275.0" } }, { "tier": 4.0, "symbol": "JOE/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 62500.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 20000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "4", - "initialLeverage": "10", - "notionalCap": "62500", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" + "initialLeverage": "5", + "notionalCap": "50000", + "notionalFloor": "20000", + "maintMarginRatio": "0.1", + "cum": "1275.0" } }, { "tier": 5.0, "symbol": "JOE/USDT:USDT", "currency": "USDT", - "minNotional": 62500.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "5", - "initialLeverage": "5", - "notionalCap": "125000", - "notionalFloor": "62500", - "maintMarginRatio": "0.1", - "cum": "3825.0" + "initialLeverage": "4", + "notionalCap": "100000", + "notionalFloor": "50000", + "maintMarginRatio": "0.125", + "cum": "2525.0" } }, { "tier": 6.0, "symbol": "JOE/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, + "minNotional": 100000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": "6", - "initialLeverage": "4", + "initialLeverage": "3", "notionalCap": "250000", - "notionalFloor": "125000", - "maintMarginRatio": "0.125", - "cum": "6950.0" + "notionalFloor": "100000", + "maintMarginRatio": "0.1667", + "cum": "6695.0" } }, { @@ -45930,50 +46624,33 @@ "symbol": "JOE/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "maxNotional": 2500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": "7", - "initialLeverage": "3", - "notionalCap": "500000", + "initialLeverage": "2", + "notionalCap": "2500000", "notionalFloor": "250000", - "maintMarginRatio": "0.1667", - "cum": "17375.0" + "maintMarginRatio": "0.25", + "cum": "27520.0" } }, { "tier": 8.0, "symbol": "JOE/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": "JOE/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "9", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "1934025.0" + "cum": "652520.0" } } ], @@ -47878,14 +48555,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.02", "cum": "0.0" } }, @@ -47895,14 +48572,14 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", - "maintMarginRatio": "0.02", + "maintMarginRatio": "0.025", "cum": "25.0" } }, @@ -47911,67 +48588,67 @@ "symbol": "KITE/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "3", - "initialLeverage": "20", - "notionalCap": "25000", + "initialLeverage": "10", + "notionalCap": "20000", "notionalFloor": "10000", - "maintMarginRatio": "0.025", - "cum": "75.0" + "maintMarginRatio": "0.05", + "cum": "275.0" } }, { "tier": 4.0, "symbol": "KITE/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 62500.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 20000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "4", - "initialLeverage": "10", - "notionalCap": "62500", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" + "initialLeverage": "5", + "notionalCap": "50000", + "notionalFloor": "20000", + "maintMarginRatio": "0.1", + "cum": "1275.0" } }, { "tier": 5.0, "symbol": "KITE/USDT:USDT", "currency": "USDT", - "minNotional": 62500.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "5", - "initialLeverage": "5", - "notionalCap": "125000", - "notionalFloor": "62500", - "maintMarginRatio": "0.1", - "cum": "3825.0" + "initialLeverage": "4", + "notionalCap": "100000", + "notionalFloor": "50000", + "maintMarginRatio": "0.125", + "cum": "2525.0" } }, { "tier": 6.0, "symbol": "KITE/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, + "minNotional": 100000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": "6", - "initialLeverage": "4", + "initialLeverage": "3", "notionalCap": "250000", - "notionalFloor": "125000", - "maintMarginRatio": "0.125", - "cum": "6950.0" + "notionalFloor": "100000", + "maintMarginRatio": "0.1667", + "cum": "6695.0" } }, { @@ -47979,50 +48656,33 @@ "symbol": "KITE/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "maxNotional": 2500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": "7", - "initialLeverage": "3", - "notionalCap": "500000", + "initialLeverage": "2", + "notionalCap": "2500000", "notionalFloor": "250000", - "maintMarginRatio": "0.1667", - "cum": "17375.0" + "maintMarginRatio": "0.25", + "cum": "27520.0" } }, { "tier": 8.0, "symbol": "KITE/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": "KITE/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "9", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "1934025.0" + "cum": "652520.0" } } ], @@ -49514,15 +50174,15 @@ "symbol": "LIGHT/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "20", + "notionalCap": "10000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.025", "cum": "0.0" } }, @@ -49530,38 +50190,21 @@ "tier": 2.0, "symbol": "LIGHT/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": "LIGHT/USDT:USDT", - "currency": "USDT", "minNotional": 10000.0, "maxNotional": 20000.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { - "bracket": "3", + "bracket": "2", "initialLeverage": "10", "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.05", - "cum": "300.0" + "cum": "250.0" } }, { - "tier": 4.0, + "tier": 3.0, "symbol": "LIGHT/USDT:USDT", "currency": "USDT", "minNotional": 20000.0, @@ -49569,16 +50212,16 @@ "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { - "bracket": "4", + "bracket": "3", "initialLeverage": "5", "notionalCap": "50000", "notionalFloor": "20000", "maintMarginRatio": "0.1", - "cum": "1300.0" + "cum": "1250.0" } }, { - "tier": 5.0, + "tier": 4.0, "symbol": "LIGHT/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, @@ -49586,50 +50229,50 @@ "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "5", + "bracket": "4", "initialLeverage": "4", "notionalCap": "250000", "notionalFloor": "50000", "maintMarginRatio": "0.125", - "cum": "2550.0" + "cum": "2500.0" + } + }, + { + "tier": 5.0, + "symbol": "LIGHT/USDT:USDT", + "currency": "USDT", + "minNotional": 250000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": "5", + "initialLeverage": "3", + "notionalCap": "500000", + "notionalFloor": "250000", + "maintMarginRatio": "0.1667", + "cum": "12925.0" } }, { "tier": 6.0, "symbol": "LIGHT/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": "6", - "initialLeverage": "3", - "notionalCap": "1000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.1667", - "cum": "12975.0" - } - }, - { - "tier": 7.0, - "symbol": "LIGHT/USDT:USDT", - "currency": "USDT", - "minNotional": 1000000.0, + "minNotional": 500000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "7", + "bracket": "6", "initialLeverage": "2", "notionalCap": "7500000", - "notionalFloor": "1000000", + "notionalFloor": "500000", "maintMarginRatio": "0.25", - "cum": "96275.0" + "cum": "54575.0" } }, { - "tier": 8.0, + "tier": 7.0, "symbol": "LIGHT/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -49637,12 +50280,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "7", "initialLeverage": "1", "notionalCap": "12500000", "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "1971275.0" + "cum": "1929575.0" } } ], @@ -52557,6 +53200,127 @@ } } ], + "MAGMA/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "MAGMA/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": "MAGMA/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": "MAGMA/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": "MAGMA/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": "MAGMA/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": "MAGMA/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": "MAGMA/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" + } + } + ], "MANA/USDT:USDT": [ { "tier": 1.0, @@ -59955,14 +60719,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" } }, @@ -59972,15 +60736,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" } }, { @@ -59988,33 +60752,33 @@ "symbol": "NXPC/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": "NXPC/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" } }, { @@ -60022,88 +60786,54 @@ "symbol": "NXPC/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": "NXPC/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": "NXPC/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": "NXPC/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": "NXPC/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": "NXPC/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -60111,12 +60841,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" } } ], @@ -60335,14 +61065,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.02", "cum": "0.0" } }, @@ -60352,14 +61082,14 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", - "maintMarginRatio": "0.02", + "maintMarginRatio": "0.025", "cum": "25.0" } }, @@ -60368,118 +61098,101 @@ "symbol": "OG/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "3", - "initialLeverage": "20", - "notionalCap": "50000", + "initialLeverage": "10", + "notionalCap": "20000", "notionalFloor": "10000", - "maintMarginRatio": "0.025", - "cum": "75.0" + "maintMarginRatio": "0.05", + "cum": "275.0" } }, { "tier": 4.0, "symbol": "OG/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 20000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "4", - "initialLeverage": "10", - "notionalCap": "125000", - "notionalFloor": "50000", - "maintMarginRatio": "0.05", - "cum": "1325.0" + "initialLeverage": "5", + "notionalCap": "50000", + "notionalFloor": "20000", + "maintMarginRatio": "0.1", + "cum": "1275.0" } }, { "tier": 5.0, "symbol": "OG/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "5", - "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "125000", - "maintMarginRatio": "0.1", - "cum": "7575.0" + "initialLeverage": "4", + "notionalCap": "100000", + "notionalFloor": "50000", + "maintMarginRatio": "0.125", + "cum": "2525.0" } }, { "tier": 6.0, "symbol": "OG/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 400000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": "6", - "initialLeverage": "4", - "notionalCap": "400000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "13825.0" + "initialLeverage": "3", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1667", + "cum": "6695.0" } }, { "tier": 7.0, "symbol": "OG/USDT:USDT", "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "minNotional": 250000.0, + "maxNotional": 2500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": "7", - "initialLeverage": "3", - "notionalCap": "500000", - "notionalFloor": "400000", - "maintMarginRatio": "0.1667", - "cum": "30505.0" + "initialLeverage": "2", + "notionalCap": "2500000", + "notionalFloor": "250000", + "maintMarginRatio": "0.25", + "cum": "27520.0" } }, { "tier": 8.0, "symbol": "OG/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": "72155.0" - } - }, - { - "tier": 9.0, - "symbol": "OG/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "9", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "1947155.0" + "cum": "652520.0" } } ], @@ -70965,13 +71678,13 @@ "symbol": "RIVER/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, - "maxNotional": 7500000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "7500000", + "notionalCap": "2500000", "notionalFloor": "500000", "maintMarginRatio": "0.25", "cum": "54625.0" @@ -70981,17 +71694,17 @@ "tier": 8.0, "symbol": "RIVER/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "1929625.0" + "cum": "679625.0" } } ], @@ -72912,13 +73625,13 @@ "symbol": "SAHARA/USDT:USDT", "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 25000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "25000", + "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.02", "cum": "25.0" @@ -72928,92 +73641,92 @@ "tier": 3.0, "symbol": "SAHARA/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 50000.0, + "minNotional": 10000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "3", "initialLeverage": "20", - "notionalCap": "50000", - "notionalFloor": "25000", + "notionalCap": "25000", + "notionalFloor": "10000", "maintMarginRatio": "0.025", - "cum": "150.0" + "cum": "75.0" } }, { "tier": 4.0, "symbol": "SAHARA/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 125000.0, + "minNotional": 25000.0, + "maxNotional": 62500.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "125000", - "notionalFloor": "50000", + "notionalCap": "62500", + "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "1400.0" + "cum": "700.0" } }, { "tier": 5.0, "symbol": "SAHARA/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, + "minNotional": 62500.0, + "maxNotional": 125000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "125000", + "notionalCap": "125000", + "notionalFloor": "62500", "maintMarginRatio": "0.1", - "cum": "7650.0" + "cum": "3825.0" } }, { "tier": 6.0, "symbol": "SAHARA/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, + "minNotional": 125000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "1000000", - "notionalFloor": "250000", + "notionalCap": "250000", + "notionalFloor": "125000", "maintMarginRatio": "0.125", - "cum": "13900.0" + "cum": "6950.0" } }, { "tier": 7.0, "symbol": "SAHARA/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 4500000.0, + "minNotional": 250000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { "bracket": "7", "initialLeverage": "3", - "notionalCap": "4500000", - "notionalFloor": "1000000", + "notionalCap": "5000000", + "notionalFloor": "250000", "maintMarginRatio": "0.1667", - "cum": "55600.0" + "cum": "17375.0" } }, { "tier": 8.0, "symbol": "SAHARA/USDT:USDT", "currency": "USDT", - "minNotional": 4500000.0, + "minNotional": 5000000.0, "maxNotional": 7500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, @@ -73021,9 +73734,9 @@ "bracket": "8", "initialLeverage": "2", "notionalCap": "7500000", - "notionalFloor": "4500000", + "notionalFloor": "5000000", "maintMarginRatio": "0.25", - "cum": "430450.0" + "cum": "433875.0" } }, { @@ -73040,7 +73753,7 @@ "notionalCap": "12000000", "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "2305450.0" + "cum": "2308875.0" } } ], @@ -75129,6 +75842,127 @@ } } ], + "SKR/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "SKR/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": "SKR/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": "SKR/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": "SKR/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": "SKR/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": "SKR/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": "SKR/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" + } + } + ], "SKY/USDT:USDT": [ { "tier": 1.0, @@ -75291,14 +76125,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.02", "cum": "0.0" } }, @@ -75316,7 +76150,7 @@ "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.025", - "cum": "50.0" + "cum": "25.0" } }, { @@ -75333,7 +76167,7 @@ "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.05", - "cum": "300.0" + "cum": "275.0" } }, { @@ -75350,7 +76184,7 @@ "notionalCap": "50000", "notionalFloor": "20000", "maintMarginRatio": "0.1", - "cum": "1300.0" + "cum": "1275.0" } }, { @@ -75358,67 +76192,67 @@ "symbol": "SKYAI/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 250000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "250000", + "notionalCap": "100000", "notionalFloor": "50000", "maintMarginRatio": "0.125", - "cum": "2550.0" + "cum": "2525.0" } }, { "tier": 6.0, "symbol": "SKYAI/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { "bracket": "6", "initialLeverage": "3", - "notionalCap": "500000", - "notionalFloor": "250000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1667", - "cum": "12975.0" + "cum": "6695.0" } }, { "tier": 7.0, "symbol": "SKYAI/USDT:USDT", "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 7500000.0, + "minNotional": 250000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "7500000", - "notionalFloor": "500000", + "notionalCap": "2500000", + "notionalFloor": "250000", "maintMarginRatio": "0.25", - "cum": "54625.0" + "cum": "27520.0" } }, { "tier": 8.0, "symbol": "SKYAI/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "1929625.0" + "cum": "652520.0" } } ], @@ -76325,14 +77159,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.02", "cum": "0.0" } }, @@ -76342,14 +77176,14 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", - "maintMarginRatio": "0.02", + "maintMarginRatio": "0.025", "cum": "25.0" } }, @@ -76358,67 +77192,67 @@ "symbol": "SOLV/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "3", - "initialLeverage": "20", - "notionalCap": "25000", + "initialLeverage": "10", + "notionalCap": "20000", "notionalFloor": "10000", - "maintMarginRatio": "0.025", - "cum": "75.0" + "maintMarginRatio": "0.05", + "cum": "275.0" } }, { "tier": 4.0, "symbol": "SOLV/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 62500.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 20000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "4", - "initialLeverage": "10", - "notionalCap": "62500", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" + "initialLeverage": "5", + "notionalCap": "50000", + "notionalFloor": "20000", + "maintMarginRatio": "0.1", + "cum": "1275.0" } }, { "tier": 5.0, "symbol": "SOLV/USDT:USDT", "currency": "USDT", - "minNotional": 62500.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "5", - "initialLeverage": "5", - "notionalCap": "125000", - "notionalFloor": "62500", - "maintMarginRatio": "0.1", - "cum": "3825.0" + "initialLeverage": "4", + "notionalCap": "100000", + "notionalFloor": "50000", + "maintMarginRatio": "0.125", + "cum": "2525.0" } }, { "tier": 6.0, "symbol": "SOLV/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, + "minNotional": 100000.0, "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": "6", - "initialLeverage": "4", + "initialLeverage": "3", "notionalCap": "250000", - "notionalFloor": "125000", - "maintMarginRatio": "0.125", - "cum": "6950.0" + "notionalFloor": "100000", + "maintMarginRatio": "0.1667", + "cum": "6695.0" } }, { @@ -76426,50 +77260,33 @@ "symbol": "SOLV/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "maxNotional": 2500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": "7", - "initialLeverage": "3", - "notionalCap": "500000", + "initialLeverage": "2", + "notionalCap": "2500000", "notionalFloor": "250000", - "maintMarginRatio": "0.1667", - "cum": "17375.0" + "maintMarginRatio": "0.25", + "cum": "27520.0" } }, { "tier": 8.0, "symbol": "SOLV/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": "SOLV/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "9", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "1934025.0" + "cum": "652520.0" } } ], @@ -77059,6 +77876,127 @@ } } ], + "SPACE/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "SPACE/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": "SPACE/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": "SPACE/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": "SPACE/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": "SPACE/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": "SPACE/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": "SPACE/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" + } + } + ], "SPELL/USDT:USDT": [ { "tier": 1.0, @@ -77403,6 +78341,127 @@ } } ], + "SPORTFUN/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "SPORTFUN/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": "SPORTFUN/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": "SPORTFUN/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": "SPORTFUN/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": "SPORTFUN/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": "SPORTFUN/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": "SPORTFUN/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" + } + } + ], "SPX/USDT:USDT": [ { "tier": 1.0, @@ -78599,14 +79658,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.02", "cum": "0.0" } }, @@ -78624,7 +79683,7 @@ "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.025", - "cum": "50.0" + "cum": "25.0" } }, { @@ -78641,7 +79700,7 @@ "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.05", - "cum": "300.0" + "cum": "275.0" } }, { @@ -78658,7 +79717,7 @@ "notionalCap": "50000", "notionalFloor": "20000", "maintMarginRatio": "0.1", - "cum": "1300.0" + "cum": "1275.0" } }, { @@ -78666,67 +79725,67 @@ "symbol": "STO/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 250000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "250000", + "notionalCap": "100000", "notionalFloor": "50000", "maintMarginRatio": "0.125", - "cum": "2550.0" + "cum": "2525.0" } }, { "tier": 6.0, "symbol": "STO/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { "bracket": "6", "initialLeverage": "3", - "notionalCap": "1000000", - "notionalFloor": "250000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1667", - "cum": "12975.0" + "cum": "6695.0" } }, { "tier": 7.0, "symbol": "STO/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 7500000.0, + "minNotional": 250000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "7500000", - "notionalFloor": "1000000", + "notionalCap": "2500000", + "notionalFloor": "250000", "maintMarginRatio": "0.25", - "cum": "96275.0" + "cum": "27520.0" } }, { "tier": 8.0, "symbol": "STO/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "1971275.0" + "cum": "652520.0" } } ], @@ -79804,13 +80863,13 @@ "symbol": "SUN/USDT:USDT", "currency": "USDT", "minNotional": 5000.0, - "maxNotional": 15000.0, + "maxNotional": 10000.0, "maintenanceMarginRate": 0.02, "maxLeverage": 25.0, "info": { "bracket": "2", "initialLeverage": "25", - "notionalCap": "15000", + "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.02", "cum": "25.0" @@ -79820,75 +80879,75 @@ "tier": 3.0, "symbol": "SUN/USDT:USDT", "currency": "USDT", - "minNotional": 15000.0, - "maxNotional": 30000.0, + "minNotional": 10000.0, + "maxNotional": 25000.0, "maintenanceMarginRate": 0.025, "maxLeverage": 20.0, "info": { "bracket": "3", "initialLeverage": "20", - "notionalCap": "30000", - "notionalFloor": "15000", + "notionalCap": "25000", + "notionalFloor": "10000", "maintMarginRatio": "0.025", - "cum": "100.0" + "cum": "75.0" } }, { "tier": 4.0, "symbol": "SUN/USDT:USDT", "currency": "USDT", - "minNotional": 30000.0, - "maxNotional": 80000.0, + "minNotional": 25000.0, + "maxNotional": 62500.0, "maintenanceMarginRate": 0.05, "maxLeverage": 10.0, "info": { "bracket": "4", "initialLeverage": "10", - "notionalCap": "80000", - "notionalFloor": "30000", + "notionalCap": "62500", + "notionalFloor": "25000", "maintMarginRatio": "0.05", - "cum": "850.0" + "cum": "700.0" } }, { "tier": 5.0, "symbol": "SUN/USDT:USDT", "currency": "USDT", - "minNotional": 80000.0, - "maxNotional": 150000.0, + "minNotional": 62500.0, + "maxNotional": 125000.0, "maintenanceMarginRate": 0.1, "maxLeverage": 5.0, "info": { "bracket": "5", "initialLeverage": "5", - "notionalCap": "150000", - "notionalFloor": "80000", + "notionalCap": "125000", + "notionalFloor": "62500", "maintMarginRatio": "0.1", - "cum": "4850.0" + "cum": "3825.0" } }, { "tier": 6.0, "symbol": "SUN/USDT:USDT", "currency": "USDT", - "minNotional": 150000.0, - "maxNotional": 300000.0, + "minNotional": 125000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "6", "initialLeverage": "4", - "notionalCap": "300000", - "notionalFloor": "150000", + "notionalCap": "250000", + "notionalFloor": "125000", "maintMarginRatio": "0.125", - "cum": "8600.0" + "cum": "6950.0" } }, { "tier": 7.0, "symbol": "SUN/USDT:USDT", "currency": "USDT", - "minNotional": 300000.0, + "minNotional": 250000.0, "maxNotional": 500000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, @@ -79896,9 +80955,9 @@ "bracket": "7", "initialLeverage": "3", "notionalCap": "500000", - "notionalFloor": "300000", + "notionalFloor": "250000", "maintMarginRatio": "0.1667", - "cum": "21110.0" + "cum": "17375.0" } }, { @@ -79915,7 +80974,7 @@ "notionalCap": "7500000", "notionalFloor": "500000", "maintMarginRatio": "0.25", - "cum": "62760.0" + "cum": "59025.0" } }, { @@ -79932,7 +80991,7 @@ "notionalCap": "12500000", "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "1937760.0" + "cum": "1934025.0" } } ], @@ -80650,14 +81709,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.02", "cum": "0.0" } }, @@ -80667,14 +81726,14 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", - "maintMarginRatio": "0.02", + "maintMarginRatio": "0.025", "cum": "25.0" } }, @@ -80683,118 +81742,101 @@ "symbol": "SXT/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "3", - "initialLeverage": "20", - "notionalCap": "50000", + "initialLeverage": "10", + "notionalCap": "20000", "notionalFloor": "10000", - "maintMarginRatio": "0.025", - "cum": "75.0" + "maintMarginRatio": "0.05", + "cum": "275.0" } }, { "tier": 4.0, "symbol": "SXT/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 20000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "4", - "initialLeverage": "10", - "notionalCap": "125000", - "notionalFloor": "50000", - "maintMarginRatio": "0.05", - "cum": "1325.0" + "initialLeverage": "5", + "notionalCap": "50000", + "notionalFloor": "20000", + "maintMarginRatio": "0.1", + "cum": "1275.0" } }, { "tier": 5.0, "symbol": "SXT/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 50000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "5", - "initialLeverage": "5", - "notionalCap": "250000", - "notionalFloor": "125000", - "maintMarginRatio": "0.1", - "cum": "7575.0" + "initialLeverage": "4", + "notionalCap": "100000", + "notionalFloor": "50000", + "maintMarginRatio": "0.125", + "cum": "2525.0" } }, { "tier": 6.0, "symbol": "SXT/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 400000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 100000.0, + "maxNotional": 250000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, "info": { "bracket": "6", - "initialLeverage": "4", - "notionalCap": "400000", - "notionalFloor": "250000", - "maintMarginRatio": "0.125", - "cum": "13825.0" + "initialLeverage": "3", + "notionalCap": "250000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1667", + "cum": "6695.0" } }, { "tier": 7.0, "symbol": "SXT/USDT:USDT", "currency": "USDT", - "minNotional": 400000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "minNotional": 250000.0, + "maxNotional": 2500000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, "info": { "bracket": "7", - "initialLeverage": "3", - "notionalCap": "500000", - "notionalFloor": "400000", - "maintMarginRatio": "0.1667", - "cum": "30505.0" + "initialLeverage": "2", + "notionalCap": "2500000", + "notionalFloor": "250000", + "maintMarginRatio": "0.25", + "cum": "27520.0" } }, { "tier": 8.0, "symbol": "SXT/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": "72155.0" - } - }, - { - "tier": 9.0, - "symbol": "SXT/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "9", + "bracket": "8", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "1947155.0" + "cum": "652520.0" } } ], @@ -81841,14 +82883,14 @@ "currency": "USDT", "minNotional": 0.0, "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "1", - "initialLeverage": "50", + "initialLeverage": "25", "notionalCap": "5000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.02", "cum": "0.0" } }, @@ -81866,7 +82908,7 @@ "notionalCap": "10000", "notionalFloor": "5000", "maintMarginRatio": "0.025", - "cum": "50.0" + "cum": "25.0" } }, { @@ -81883,7 +82925,7 @@ "notionalCap": "20000", "notionalFloor": "10000", "maintMarginRatio": "0.05", - "cum": "300.0" + "cum": "275.0" } }, { @@ -81900,7 +82942,7 @@ "notionalCap": "50000", "notionalFloor": "20000", "maintMarginRatio": "0.1", - "cum": "1300.0" + "cum": "1275.0" } }, { @@ -81908,67 +82950,67 @@ "symbol": "TAKE/USDT:USDT", "currency": "USDT", "minNotional": 50000.0, - "maxNotional": 250000.0, + "maxNotional": 100000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { "bracket": "5", "initialLeverage": "4", - "notionalCap": "250000", + "notionalCap": "100000", "notionalFloor": "50000", "maintMarginRatio": "0.125", - "cum": "2550.0" + "cum": "2525.0" } }, { "tier": 6.0, "symbol": "TAKE/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 1000000.0, + "minNotional": 100000.0, + "maxNotional": 250000.0, "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { "bracket": "6", "initialLeverage": "3", - "notionalCap": "1000000", - "notionalFloor": "250000", + "notionalCap": "250000", + "notionalFloor": "100000", "maintMarginRatio": "0.1667", - "cum": "12975.0" + "cum": "6695.0" } }, { "tier": 7.0, "symbol": "TAKE/USDT:USDT", "currency": "USDT", - "minNotional": 1000000.0, - "maxNotional": 7500000.0, + "minNotional": 250000.0, + "maxNotional": 2500000.0, "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { "bracket": "7", "initialLeverage": "2", - "notionalCap": "7500000", - "notionalFloor": "1000000", + "notionalCap": "2500000", + "notionalFloor": "250000", "maintMarginRatio": "0.25", - "cum": "96275.0" + "cum": "27520.0" } }, { "tier": 8.0, "symbol": "TAKE/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "minNotional": 2500000.0, + "maxNotional": 5000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { "bracket": "8", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "5000000", + "notionalFloor": "2500000", "maintMarginRatio": "0.5", - "cum": "1971275.0" + "cum": "652520.0" } } ], @@ -85263,6 +86305,93 @@ } } ], + "TSLA/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "TSLA/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 10000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, + "info": { + "bracket": "1", + "initialLeverage": "5", + "notionalCap": "10000", + "notionalFloor": "0", + "maintMarginRatio": "0.1", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "TSLA/USDT:USDT", + "currency": "USDT", + "minNotional": 10000.0, + "maxNotional": 100000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, + "info": { + "bracket": "2", + "initialLeverage": "4", + "notionalCap": "100000", + "notionalFloor": "10000", + "maintMarginRatio": "0.125", + "cum": "250.0" + } + }, + { + "tier": 3.0, + "symbol": "TSLA/USDT:USDT", + "currency": "USDT", + "minNotional": 100000.0, + "maxNotional": 500000.0, + "maintenanceMarginRate": 0.1667, + "maxLeverage": 3.0, + "info": { + "bracket": "3", + "initialLeverage": "3", + "notionalCap": "500000", + "notionalFloor": "100000", + "maintMarginRatio": "0.1667", + "cum": "4420.0" + } + }, + { + "tier": 4.0, + "symbol": "TSLA/USDT:USDT", + "currency": "USDT", + "minNotional": 500000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.25, + "maxLeverage": 2.0, + "info": { + "bracket": "4", + "initialLeverage": "2", + "notionalCap": "8000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "46070.0" + } + }, + { + "tier": 5.0, + "symbol": "TSLA/USDT:USDT", + "currency": "USDT", + "minNotional": 8000000.0, + "maxNotional": 15000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "5", + "initialLeverage": "1", + "notionalCap": "15000000", + "notionalFloor": "8000000", + "maintMarginRatio": "0.5", + "cum": "2046070.0" + } + } + ], "TST/USDT:USDT": [ { "tier": 1.0, @@ -86200,15 +87329,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", - "maintMarginRatio": "0.02", - "cum": "25.0" + "maintMarginRatio": "0.025", + "cum": "50.0" } }, { @@ -86216,71 +87345,54 @@ "symbol": "UB/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "3", - "initialLeverage": "20", - "notionalCap": "25000", + "initialLeverage": "10", + "notionalCap": "20000", "notionalFloor": "10000", - "maintMarginRatio": "0.025", - "cum": "75.0" + "maintMarginRatio": "0.05", + "cum": "300.0" } }, { "tier": 4.0, "symbol": "UB/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 62500.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 20000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "4", - "initialLeverage": "10", - "notionalCap": "62500", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" + "initialLeverage": "5", + "notionalCap": "50000", + "notionalFloor": "20000", + "maintMarginRatio": "0.1", + "cum": "1300.0" } }, { "tier": 5.0, "symbol": "UB/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": "UB/USDT:USDT", - "currency": "USDT", - "minNotional": 125000.0, + "minNotional": 50000.0, "maxNotional": 250000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "6", + "bracket": "5", "initialLeverage": "4", "notionalCap": "250000", - "notionalFloor": "125000", + "notionalFloor": "50000", "maintMarginRatio": "0.125", - "cum": "6950.0" + "cum": "2550.0" } }, { - "tier": 7.0, + "tier": 6.0, "symbol": "UB/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, @@ -86288,16 +87400,16 @@ "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { - "bracket": "7", + "bracket": "6", "initialLeverage": "3", "notionalCap": "500000", "notionalFloor": "250000", "maintMarginRatio": "0.1667", - "cum": "17375.0" + "cum": "12975.0" } }, { - "tier": 8.0, + "tier": 7.0, "symbol": "UB/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, @@ -86305,16 +87417,16 @@ "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "8", + "bracket": "7", "initialLeverage": "2", "notionalCap": "7500000", "notionalFloor": "500000", "maintMarginRatio": "0.25", - "cum": "59025.0" + "cum": "54625.0" } }, { - "tier": 9.0, + "tier": 8.0, "symbol": "UB/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -86322,12 +87434,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "9", + "bracket": "8", "initialLeverage": "1", "notionalCap": "12500000", "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "1934025.0" + "cum": "1929625.0" } } ], @@ -87234,15 +88346,15 @@ "currency": "USDT", "minNotional": 5000.0, "maxNotional": 10000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "2", - "initialLeverage": "25", + "initialLeverage": "20", "notionalCap": "10000", "notionalFloor": "5000", - "maintMarginRatio": "0.02", - "cum": "25.0" + "maintMarginRatio": "0.025", + "cum": "50.0" } }, { @@ -87250,71 +88362,54 @@ "symbol": "USELESS/USDT:USDT", "currency": "USDT", "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "3", - "initialLeverage": "20", - "notionalCap": "25000", + "initialLeverage": "10", + "notionalCap": "20000", "notionalFloor": "10000", - "maintMarginRatio": "0.025", - "cum": "75.0" + "maintMarginRatio": "0.05", + "cum": "300.0" } }, { "tier": 4.0, "symbol": "USELESS/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 62500.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 20000.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "4", - "initialLeverage": "10", - "notionalCap": "62500", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" + "initialLeverage": "5", + "notionalCap": "50000", + "notionalFloor": "20000", + "maintMarginRatio": "0.1", + "cum": "1300.0" } }, { "tier": 5.0, "symbol": "USELESS/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": "USELESS/USDT:USDT", - "currency": "USDT", - "minNotional": 125000.0, + "minNotional": 50000.0, "maxNotional": 250000.0, "maintenanceMarginRate": 0.125, "maxLeverage": 4.0, "info": { - "bracket": "6", + "bracket": "5", "initialLeverage": "4", "notionalCap": "250000", - "notionalFloor": "125000", + "notionalFloor": "50000", "maintMarginRatio": "0.125", - "cum": "6950.0" + "cum": "2550.0" } }, { - "tier": 7.0, + "tier": 6.0, "symbol": "USELESS/USDT:USDT", "currency": "USDT", "minNotional": 250000.0, @@ -87322,16 +88417,16 @@ "maintenanceMarginRate": 0.1667, "maxLeverage": 3.0, "info": { - "bracket": "7", + "bracket": "6", "initialLeverage": "3", "notionalCap": "500000", "notionalFloor": "250000", "maintMarginRatio": "0.1667", - "cum": "17375.0" + "cum": "12975.0" } }, { - "tier": 8.0, + "tier": 7.0, "symbol": "USELESS/USDT:USDT", "currency": "USDT", "minNotional": 500000.0, @@ -87339,16 +88434,16 @@ "maintenanceMarginRate": 0.25, "maxLeverage": 2.0, "info": { - "bracket": "8", + "bracket": "7", "initialLeverage": "2", "notionalCap": "7500000", "notionalFloor": "500000", "maintMarginRatio": "0.25", - "cum": "59025.0" + "cum": "54625.0" } }, { - "tier": 9.0, + "tier": 8.0, "symbol": "USELESS/USDT:USDT", "currency": "USDT", "minNotional": 7500000.0, @@ -87356,12 +88451,12 @@ "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "9", + "bracket": "8", "initialLeverage": "1", "notionalCap": "12500000", "notionalFloor": "7500000", "maintMarginRatio": "0.5", - "cum": "1934025.0" + "cum": "1929625.0" } } ], @@ -91656,6 +92751,178 @@ } } ], + "XAG/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "XAG/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": "XAG/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": "XAG/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": "XAG/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": "XAG/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": "XAG/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": "XAG/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": "XAG/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": "XAG/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": "XAG/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" + } + } + ], "XAI/USDT:USDT": [ { "tier": 1.0, @@ -91955,15 +93222,15 @@ "symbol": "XAU/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 50000.0, + "maintenanceMarginRate": 0.005, + "maxLeverage": 100.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "100", + "notionalCap": "50000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.005", "cum": "0.0" } }, @@ -91971,136 +93238,153 @@ "tier": 2.0, "symbol": "XAU/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.02, - "maxLeverage": 25.0, + "minNotional": 50000.0, + "maxNotional": 400000.0, + "maintenanceMarginRate": 0.0065, + "maxLeverage": 75.0, "info": { "bracket": "2", - "initialLeverage": "25", - "notionalCap": "10000", - "notionalFloor": "5000", - "maintMarginRatio": "0.02", - "cum": "25.0" + "initialLeverage": "75", + "notionalCap": "400000", + "notionalFloor": "50000", + "maintMarginRatio": "0.0065", + "cum": "75.0" } }, { "tier": 3.0, "symbol": "XAU/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 25000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 400000.0, + "maxNotional": 1000000.0, + "maintenanceMarginRate": 0.01, + "maxLeverage": 50.0, "info": { "bracket": "3", - "initialLeverage": "20", - "notionalCap": "25000", - "notionalFloor": "10000", - "maintMarginRatio": "0.025", - "cum": "75.0" + "initialLeverage": "50", + "notionalCap": "1000000", + "notionalFloor": "400000", + "maintMarginRatio": "0.01", + "cum": "1475.0" } }, { "tier": 4.0, "symbol": "XAU/USDT:USDT", "currency": "USDT", - "minNotional": 25000.0, - "maxNotional": 62500.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 1000000.0, + "maxNotional": 4000000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, "info": { "bracket": "4", - "initialLeverage": "10", - "notionalCap": "62500", - "notionalFloor": "25000", - "maintMarginRatio": "0.05", - "cum": "700.0" + "initialLeverage": "25", + "notionalCap": "4000000", + "notionalFloor": "1000000", + "maintMarginRatio": "0.02", + "cum": "11475.0" } }, { "tier": 5.0, "symbol": "XAU/USDT:USDT", "currency": "USDT", - "minNotional": 62500.0, - "maxNotional": 125000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 4000000.0, + "maxNotional": 8000000.0, + "maintenanceMarginRate": 0.025, + "maxLeverage": 20.0, "info": { "bracket": "5", - "initialLeverage": "5", - "notionalCap": "125000", - "notionalFloor": "62500", - "maintMarginRatio": "0.1", - "cum": "3825.0" + "initialLeverage": "20", + "notionalCap": "8000000", + "notionalFloor": "4000000", + "maintMarginRatio": "0.025", + "cum": "31475.0" } }, { "tier": 6.0, "symbol": "XAU/USDT:USDT", "currency": "USDT", - "minNotional": 125000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 8000000.0, + "maxNotional": 40000000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "6", - "initialLeverage": "4", - "notionalCap": "250000", - "notionalFloor": "125000", - "maintMarginRatio": "0.125", - "cum": "6950.0" + "initialLeverage": "10", + "notionalCap": "40000000", + "notionalFloor": "8000000", + "maintMarginRatio": "0.05", + "cum": "231475.0" } }, { "tier": 7.0, "symbol": "XAU/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 500000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, + "minNotional": 40000000.0, + "maxNotional": 80000000.0, + "maintenanceMarginRate": 0.1, + "maxLeverage": 5.0, "info": { "bracket": "7", - "initialLeverage": "3", - "notionalCap": "500000", - "notionalFloor": "250000", - "maintMarginRatio": "0.1667", - "cum": "17375.0" + "initialLeverage": "5", + "notionalCap": "80000000", + "notionalFloor": "40000000", + "maintMarginRatio": "0.1", + "cum": "2231475.0" } }, { "tier": 8.0, "symbol": "XAU/USDT:USDT", "currency": "USDT", - "minNotional": 500000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, + "minNotional": 80000000.0, + "maxNotional": 100000000.0, + "maintenanceMarginRate": 0.125, + "maxLeverage": 4.0, "info": { "bracket": "8", - "initialLeverage": "2", - "notionalCap": "7500000", - "notionalFloor": "500000", - "maintMarginRatio": "0.25", - "cum": "59025.0" + "initialLeverage": "4", + "notionalCap": "100000000", + "notionalFloor": "80000000", + "maintMarginRatio": "0.125", + "cum": "4231475.0" } }, { "tier": 9.0, "symbol": "XAU/USDT:USDT", "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "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": "XAU/USDT:USDT", + "currency": "USDT", + "minNotional": 200000000.0, + "maxNotional": 400000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "9", + "bracket": "10", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "400000000", + "notionalFloor": "200000000", "maintMarginRatio": "0.5", - "cum": "1934025.0" + "cum": "66731475.0" } } ], @@ -94533,6 +95817,144 @@ } } ], + "ZAMA/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "ZAMA/USDT:USDT", + "currency": "USDT", + "minNotional": 0.0, + "maxNotional": 5000.0, + "maintenanceMarginRate": 0.02, + "maxLeverage": 25.0, + "info": { + "bracket": "1", + "initialLeverage": "25", + "notionalCap": "5000", + "notionalFloor": "0", + "maintMarginRatio": "0.02", + "cum": "0.0" + } + }, + { + "tier": 2.0, + "symbol": "ZAMA/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": "ZAMA/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": "275.0" + } + }, + { + "tier": 4.0, + "symbol": "ZAMA/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": "1525.0" + } + }, + { + "tier": 5.0, + "symbol": "ZAMA/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": "2775.0" + } + }, + { + "tier": 6.0, + "symbol": "ZAMA/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": "ZAMA/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": "ZAMA/USDT:USDT", + "currency": "USDT", + "minNotional": 2500000.0, + "maxNotional": 5000000.0, + "maintenanceMarginRate": 0.5, + "maxLeverage": 1.0, + "info": { + "bracket": "8", + "initialLeverage": "1", + "notionalCap": "5000000", + "notionalFloor": "2500000", + "maintMarginRatio": "0.5", + "cum": "652770.0" + } + } + ], "ZBT/USDT:USDT": [ { "tier": 1.0, @@ -95210,15 +96632,15 @@ "symbol": "ZEREBRO/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" } }, @@ -95226,119 +96648,85 @@ "tier": 2.0, "symbol": "ZEREBRO/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 15000.0, + "maxNotional": 60000.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": "60000", + "notionalFloor": "15000", + "maintMarginRatio": "0.1", + "cum": "750.0" } }, { "tier": 3.0, "symbol": "ZEREBRO/USDT:USDT", "currency": "USDT", - "minNotional": 10000.0, - "maxNotional": 20000.0, - "maintenanceMarginRate": 0.05, - "maxLeverage": 10.0, + "minNotional": 60000.0, + "maxNotional": 250000.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": "250000", + "notionalFloor": "60000", + "maintMarginRatio": "0.125", + "cum": "2250.0" } }, { "tier": 4.0, "symbol": "ZEREBRO/USDT:USDT", "currency": "USDT", - "minNotional": 20000.0, - "maxNotional": 50000.0, - "maintenanceMarginRate": 0.1, - "maxLeverage": 5.0, + "minNotional": 250000.0, + "maxNotional": 500000.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": "500000", + "notionalFloor": "250000", + "maintMarginRatio": "0.1667", + "cum": "12675.0" } }, { "tier": 5.0, "symbol": "ZEREBRO/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 250000.0, - "maintenanceMarginRate": 0.125, - "maxLeverage": 4.0, + "minNotional": 500000.0, + "maxNotional": 2000000.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": "2000000", + "notionalFloor": "500000", + "maintMarginRatio": "0.25", + "cum": "54325.0" } }, { "tier": 6.0, "symbol": "ZEREBRO/USDT:USDT", "currency": "USDT", - "minNotional": 250000.0, - "maxNotional": 2000000.0, - "maintenanceMarginRate": 0.1667, - "maxLeverage": 3.0, - "info": { - "bracket": "6", - "initialLeverage": "3", - "notionalCap": "2000000", - "notionalFloor": "250000", - "maintMarginRatio": "0.1667", - "cum": "12975.0" - } - }, - { - "tier": 7.0, - "symbol": "ZEREBRO/USDT:USDT", - "currency": "USDT", "minNotional": 2000000.0, - "maxNotional": 7500000.0, - "maintenanceMarginRate": 0.25, - "maxLeverage": 2.0, - "info": { - "bracket": "7", - "initialLeverage": "2", - "notionalCap": "7500000", - "notionalFloor": "2000000", - "maintMarginRatio": "0.25", - "cum": "179575.0" - } - }, - { - "tier": 8.0, - "symbol": "ZEREBRO/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "maxNotional": 3000000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "6", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "3000000", + "notionalFloor": "2000000", "maintMarginRatio": "0.5", - "cum": "2054575.0" + "cum": "554325.0" } } ], @@ -96433,15 +97821,15 @@ "symbol": "ZRC/USDT:USDT", "currency": "USDT", "minNotional": 0.0, - "maxNotional": 5000.0, - "maintenanceMarginRate": 0.015, - "maxLeverage": 50.0, + "maxNotional": 20000.0, + "maintenanceMarginRate": 0.05, + "maxLeverage": 10.0, "info": { "bracket": "1", - "initialLeverage": "50", - "notionalCap": "5000", + "initialLeverage": "10", + "notionalCap": "20000", "notionalFloor": "0", - "maintMarginRatio": "0.015", + "maintMarginRatio": "0.05", "cum": "0.0" } }, @@ -96449,119 +97837,85 @@ "tier": 2.0, "symbol": "ZRC/USDT:USDT", "currency": "USDT", - "minNotional": 5000.0, - "maxNotional": 10000.0, - "maintenanceMarginRate": 0.025, - "maxLeverage": 20.0, + "minNotional": 20000.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": "20000", + "maintMarginRatio": "0.1", + "cum": "1000.0" } }, { "tier": 3.0, "symbol": "ZRC/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": "2250.0" } }, { "tier": 4.0, "symbol": "ZRC/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": "6420.0" } }, { "tier": 5.0, "symbol": "ZRC/USDT:USDT", "currency": "USDT", - "minNotional": 50000.0, - "maxNotional": 250000.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": "250000", - "notionalFloor": "50000", - "maintMarginRatio": "0.125", - "cum": "2550.0" + "initialLeverage": "2", + "notionalCap": "500000", + "notionalFloor": "250000", + "maintMarginRatio": "0.25", + "cum": "27245.0" } }, { "tier": 6.0, "symbol": "ZRC/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": "ZRC/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": "ZRC/USDT:USDT", - "currency": "USDT", - "minNotional": 7500000.0, - "maxNotional": 12500000.0, + "maxNotional": 800000.0, "maintenanceMarginRate": 0.5, "maxLeverage": 1.0, "info": { - "bracket": "8", + "bracket": "6", "initialLeverage": "1", - "notionalCap": "12500000", - "notionalFloor": "7500000", + "notionalCap": "800000", + "notionalFloor": "500000", "maintMarginRatio": "0.5", - "cum": "1929625.0" + "cum": "152245.0" } } ], @@ -97046,5 +98400,126 @@ "cum": "1932375.0" } } + ], + "\u6211\u8e0f\u9a6c\u6765\u4e86/USDT:USDT": [ + { + "tier": 1.0, + "symbol": "\u6211\u8e0f\u9a6c\u6765\u4e86/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": "\u6211\u8e0f\u9a6c\u6765\u4e86/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": "\u6211\u8e0f\u9a6c\u6765\u4e86/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": "\u6211\u8e0f\u9a6c\u6765\u4e86/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": "\u6211\u8e0f\u9a6c\u6765\u4e86/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": "\u6211\u8e0f\u9a6c\u6765\u4e86/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": "\u6211\u8e0f\u9a6c\u6765\u4e86/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" + } + } ] } \ No newline at end of file diff --git a/freqtrade/exchange/check_exchange.py b/freqtrade/exchange/check_exchange.py index 1402e40cc..583868744 100644 --- a/freqtrade/exchange/check_exchange.py +++ b/freqtrade/exchange/check_exchange.py @@ -47,14 +47,16 @@ def check_exchange(config: Config, check_for_bad: bool = True) -> bool: f"{', '.join(available_exchanges())}" ) - valid, reason, _ = validate_exchange(exchange) + valid, reason, _, _ = validate_exchange(exchange) 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/common.py b/freqtrade/exchange/common.py index 61206de92..7f53c39ef 100644 --- a/freqtrade/exchange/common.py +++ b/freqtrade/exchange/common.py @@ -36,12 +36,13 @@ API_RETRY_COUNT = 4 API_FETCH_ORDER_RETRY_COUNT = 5 BAD_EXCHANGES = { - "bitmex": "Various reasons.", - "probit": "Requires additional, regular calls to `signIn()`.", - "poloniex": "Does not provide fetch_order endpoint to fetch both open and closed orders.", - "kucoinfutures": "Unsupported futures exchange.", - "poloniexfutures": "Unsupported futures exchange.", - "binancecoinm": "Unsupported futures exchange.", + "bitmex": "Various reasons", + "probit": "Requires additional, regular calls to `signIn()`", + "poloniex": "Does not provide fetch_order endpoint to fetch both open and closed orders", + "krakenfutures": "Unsupported futures exchange", + "kucoinfutures": "Unsupported futures exchange", + "poloniexfutures": "Unsupported futures exchange", + "binancecoinm": "Unsupported futures exchange", } MAP_EXCHANGE_CHILDCLASS = { @@ -78,31 +79,35 @@ EXCHANGE_HAS_REQUIRED: dict[str, list[str]] = { "fetchOHLCV": [], } -EXCHANGE_HAS_OPTIONAL = [ +EXCHANGE_HAS_OPTIONAL: dict[str, list[str]] = { # Private - "fetchMyTrades", # Trades for order - fee detection - "createLimitOrder", - "createMarketOrder", # Either OR for orders - # 'setLeverage', # Margin/Futures trading - # 'setMarginMode', # Margin/Futures trading - # 'fetchFundingHistory', # Futures trading + "fetchMyTrades": [], # Trades for order - fee detection + "createLimitOrder": [], + "createMarketOrder": [], # Either OR for orders # Public - "fetchOrderBook", - "fetchL2OrderBook", - "fetchTicker", # OR for pricing - "fetchTickers", # For volumepairlist? - "fetchTrades", # Downloading trades data - # 'fetchFundingRateHistory', # Futures trading - # 'fetchPositions', # Futures trading - # 'fetchLeverageTiers', # Futures initialization - # 'fetchMarketLeverageTiers', # Futures initialization - # 'fetchOpenOrders', 'fetchClosedOrders', # 'fetchOrders', # Refinding balance... - # "fetchPremiumIndexOHLCV", # Futures additional data - # "fetchMarkOHLCV", # Futures additional data - # "fetchIndexOHLCV", # Futures additional data + "fetchOrderBook": [], + "fetchL2OrderBook": [], + "fetchTicker": [], # OR for pricing + "fetchTickers": [], # For volumepairlist? + "fetchTrades": [], # Downloading trades data + "fetchOrders": ["fetchOpenOrders", "fetchClosedOrders"], # , # Refinding balance... # ccxt.pro - "watchOHLCV", -] + "watchOHLCV": [], +} + +EXCHANGE_HAS_OPTIONAL_FUTURES: dict[str, list[str]] = { + # private + "setLeverage": [], # Margin/Futures trading + "setMarginMode": [], # Margin/Futures trading + "fetchFundingHistory": [], # Futures trading + # Public + "fetchFundingRateHistory": [], # Futures trading + "fetchPositions": [], # Futures trading + "fetchLeverageTiers": ["fetchMarketLeverageTiers"], # Futures initialization + "fetchMarkOHLCV": [], + "fetchIndexOHLCV": [], # Futures additional data + "fetchPremiumIndexOHLCV": [], +} def calculate_backoff(retrycount, max_retries): diff --git a/freqtrade/exchange/exchange.py b/freqtrade/exchange/exchange.py index 605d6721a..ac19696fa 100644 --- a/freqtrade/exchange/exchange.py +++ b/freqtrade/exchange/exchange.py @@ -73,6 +73,7 @@ from freqtrade.exchange.exchange_types import ( CcxtPosition, FtHas, FundingRate, + LeverageTier, OHLCVResponse, OrderBook, Ticker, @@ -105,7 +106,6 @@ from freqtrade.misc import ( file_dump_json, file_load_json, safe_value_fallback, - safe_value_fallback2, ) from freqtrade.util import FtTTLCache, PeriodicCache, dt_from_ts, dt_now from freqtrade.util.datetime_helpers import dt_humanize_delta, dt_ts, format_ms_time @@ -195,29 +195,30 @@ class Exchange: self._exchange_ws: ExchangeWS | None = None self._markets: dict = {} self._trading_fees: dict[str, Any] = {} - self._leverage_tiers: dict[str, list[dict]] = {} + self._leverage_tiers: dict[str, list[LeverageTier]] = {} # Lock event loop. This is necessary to avoid race-conditions when using force* commands # Due to funding fee fetching. self._loop_lock = Lock() self.loop = self._init_async_loop() - self._config: Config = {} + self._config: Config = config # Leverage properties self.trading_mode: TradingMode = TradingMode( - config.get("trading_mode", self._supported_trading_mode_margin_pairs[0][0]) + self._config.get("trading_mode", self._supported_trading_mode_margin_pairs[0][0]) ) self.margin_mode: MarginMode = MarginMode( - MarginMode(config.get("margin_mode")) - if config.get("margin_mode") + MarginMode(self._config.get("margin_mode")) + if self._config.get("margin_mode") else self._supported_trading_mode_margin_pairs[0][1] ) - config["trading_mode"] = self.trading_mode - config["margin_mode"] = self.margin_mode - config["candle_type_def"] = CandleType.get_default(self.trading_mode) - self._config.update(config) - self.liquidation_buffer = config.get("liquidation_buffer", 0.05) + self._config["trading_mode"] = self.trading_mode + self._config["margin_mode"] = self.margin_mode + self._config["candle_type_def"] = CandleType.get_default(self.trading_mode) + self.liquidation_buffer = self._config.get("liquidation_buffer", 0.05) - exchange_conf: ExchangeConfig = exchange_config if exchange_config else config["exchange"] + exchange_conf: ExchangeConfig = ( + exchange_config if exchange_config else self._config["exchange"] + ) # Deep merge ft_has with default ft_has options # Must be called before ft_has is used. @@ -248,14 +249,14 @@ class Exchange: # Holds all open sell orders for dry_run self._dry_run_open_orders: dict[str, Any] = {} - if config["dry_run"]: + if self._config["dry_run"]: logger.info("Instance is running with dry_run enabled") logger.info(f"Using CCXT {ccxt.__version__}") # Don't remove exchange credentials for dry-run or if always_require_api_keys is set remove_exchange_credentials( exchange_conf, - not self._ft_has["always_require_api_keys"] and config.get("dry_run", False), + not self._ft_has["always_require_api_keys"] and self._config.get("dry_run", False), ) self.log_responses = exchange_conf.get("log_responses", False) @@ -296,7 +297,7 @@ class Exchange: if validate: # Initial markets load self.reload_markets(True, load_leverage_tiers=False) - self.validate_config(config) + self.validate_config(self._config) if self.trading_mode != TradingMode.SPOT and load_leverage_tiers: self.fill_leverage_tiers() @@ -480,7 +481,7 @@ class Exchange: def _log_exchange_response(self, endpoint: str, response, *, add_info=None) -> None: """Log exchange responses""" if self.log_responses: - add_info_str = "" if add_info is None else f" {add_info}: " + add_info_str = "" if add_info is None else f"{add_info}: " logger.info(f"API {endpoint}: {add_info_str}{response}") def ohlcv_candle_limit( @@ -1827,16 +1828,16 @@ class Exchange: return order @retrier - def get_balances(self) -> CcxtBalances: + def get_balances(self, params: dict | None = None) -> CcxtBalances: try: - balances = self._api.fetch_balance() + balances = self._api.fetch_balance(params or {}) # Remove additional info from ccxt results balances.pop("info", None) balances.pop("free", None) balances.pop("total", None) balances.pop("used", None) - self._log_exchange_response("fetch_balance", balances) + self._log_exchange_response("fetch_balance", balances, add_info=params) return balances except ccxt.DDoSProtection as e: raise DDosProtection(e) from e @@ -1848,7 +1849,9 @@ class Exchange: raise OperationalException(e) from e @retrier - def fetch_positions(self, pair: str | None = None) -> list[CcxtPosition]: + def fetch_positions( + self, pair: str | None = None, params: dict | None = None + ) -> list[CcxtPosition]: """ Fetch positions from the exchange. If no pair is given, all positions are returned. @@ -1860,7 +1863,7 @@ class Exchange: symbols = None if pair: symbols = [pair] - positions: list[CcxtPosition] = self._api.fetch_positions(symbols) + positions: list[CcxtPosition] = self._api.fetch_positions(symbols, params=params or {}) self._log_exchange_response("fetch_positions", positions) return positions except ccxt.DDoSProtection as e: @@ -2060,12 +2063,13 @@ class Exchange: """ return self._config["stake_currency"] - def get_conversion_rate(self, coin: str, currency: str) -> float | None: + def get_conversion_rate(self, coin: str, currency: str, *, cached=True) -> float | None: """ Quick and cached way to get conversion rate one currency to the other. Can then be used as "rate * amount" to convert between currencies. :param coin: Coin to convert :param currency: Currency to convert to + :param cached: Allow cached tickers, default True :returns: Conversion rate from coin to currency :raises: ExchangeErrors """ @@ -2076,13 +2080,13 @@ class Exchange: currency = proxy_currency if coin == currency: return 1.0 - tickers = self.get_tickers(cached=True) + tickers = self.get_tickers(cached=cached) try: for pair in self.get_valid_pair_combination(coin, currency): ticker: Ticker | None = tickers.get(pair, None) if not ticker: tickers_other: Tickers = self.get_tickers( - cached=True, + cached=cached, market_type=( TradingMode.SPOT if self.trading_mode != TradingMode.SPOT @@ -2091,7 +2095,7 @@ class Exchange: ) ticker = tickers_other.get(pair, None) if ticker: - rate: float | None = safe_value_fallback2(ticker, ticker, "last", "ask", None) + rate: float | None = safe_value_fallback(ticker, "last", "ask", None) if rate and pair.startswith(currency) and not pair.endswith(currency): rate = 1.0 / rate return rate @@ -2391,6 +2395,16 @@ class Exchange: raise OperationalException(e) from e def get_order_id_conditional(self, order: CcxtOrder) -> str: + """ + Return order id or id_stop (for conditional orders) based on exchange settings + + :param order: ccxt order dict + :return: correct order id + """ + if self.get_option("stoploss_query_requires_stop_flag") and ( + order["type"] in ("stoploss", "stop") + ): + return safe_value_fallback(order, "id_stop", "id") return order["id"] @retrier @@ -2550,7 +2564,13 @@ class Exchange: ) ) logger.debug(f"Downloaded data for {pair} from ccxt with length {len(data)}.") - return ohlcv_to_dataframe(data, timeframe, pair, fill_missing=False, drop_incomplete=True) + # funding_rates are always complete, so never need to be dropped. + drop_incomplete = ( + self._ohlcv_partial_candle if candle_type != CandleType.FUNDING_RATE else False + ) + return ohlcv_to_dataframe( + data, timeframe, pair, fill_missing=False, drop_incomplete=drop_incomplete + ) async def _async_get_historic_ohlcv( self, @@ -3602,7 +3622,7 @@ class Exchange: pair_tiers.append(self.parse_leverage_tier(tier)) self._leverage_tiers[pair] = pair_tiers - def parse_leverage_tier(self, tier) -> dict: + def parse_leverage_tier(self, tier) -> LeverageTier: info = tier.get("info", {}) return { "minNotional": tier["minNotional"], @@ -3643,7 +3663,11 @@ class Exchange: for tier in pair_tiers: # Adjust notional by leverage to do a proper comparison min_stake = tier["minNotional"] / (prior_max_lev or tier["maxLeverage"]) - max_stake = tier["maxNotional"] / tier["maxLeverage"] + max_stake = ( + tier["maxNotional"] / tier["maxLeverage"] + if tier["maxNotional"] is not None + else float("inf") + ) prior_max_lev = tier["maxLeverage"] if min_stake <= stake_amount <= max_stake: return tier["maxLeverage"] diff --git a/freqtrade/exchange/exchange_types.py b/freqtrade/exchange/exchange_types.py index fd5faecf7..842bc7c14 100644 --- a/freqtrade/exchange/exchange_types.py +++ b/freqtrade/exchange/exchange_types.py @@ -115,5 +115,27 @@ class CcxtPosition(TypedDict): CcxtOrder = dict[str, Any] + +class LeverageTier(TypedDict): + """ + Represents a single leverage tier returned by the exchange. + + Attributes: + minNotional: Minimum notional value (quote currency) for which this tier applies. + maxNotional: Maximum notional value (quote currency) for which this tier applies. + When ``maxNotional`` is ``None``, the tier is unbounded on the upper side, + i.e. there is no maximum notional limit for this tier + maintenanceMarginRate: Maintenance margin rate for this tier (fraction, e.g. 0.005 for 0.5%) + maxLeverage: Maximum leverage allowed for this tier + maintAmt: Optional fixed maintenance margin amount, if provided by the exchange + """ + + minNotional: float + maxNotional: float | None + maintenanceMarginRate: float + maxLeverage: float + maintAmt: float | None + + # pair, timeframe, candleType, OHLCV, drop last?, OHLCVResponse = tuple[str, str, CandleType, list, bool] diff --git a/freqtrade/exchange/exchange_utils.py b/freqtrade/exchange/exchange_utils.py index ddb57b123..43d7acbd6 100644 --- a/freqtrade/exchange/exchange_utils.py +++ b/freqtrade/exchange/exchange_utils.py @@ -22,6 +22,7 @@ from ccxt import ( from freqtrade.exchange.common import ( BAD_EXCHANGES, EXCHANGE_HAS_OPTIONAL, + EXCHANGE_HAS_OPTIONAL_FUTURES, EXCHANGE_HAS_REQUIRED, MAP_EXCHANGE_CHILDCLASS, SUPPORTED_EXCHANGES, @@ -53,7 +54,22 @@ def available_exchanges(ccxt_module: CcxtModuleType | None = None) -> list[str]: return [x for x in exchanges if validate_exchange(x)[0]] -def validate_exchange(exchange: str) -> tuple[bool, str, ccxt.Exchange | None]: +def _exchange_has_helper(ex_mod: ccxt.Exchange, required: dict[str, list[str]]) -> list[str]: + """ + Checks availability of methods (or their replacement)s in ex_mod.has + :param ex_mod: ccxt Exchange module + :param required: dict of required methods, with possible replacement methods as list + :return: list of missing required methods + """ + return [ + k + for k, v in required.items() + if ex_mod.has.get(k) is not True + and (len(v) == 0 or not (all(ex_mod.has.get(x) for x in v))) + ] + + +def validate_exchange(exchange: str) -> tuple[bool, str, str, ccxt.Exchange | None]: """ returns: can_use, reason, exchange_object with Reason including both missing and missing_opt @@ -64,36 +80,38 @@ def validate_exchange(exchange: str) -> tuple[bool, str, ccxt.Exchange | None]: ex_mod = getattr(ccxt.async_support, exchange.lower())() if not ex_mod or not ex_mod.has: - return False, "", None + return False, "", "", None result = True - reason = "" - missing = [ - k - for k, v in EXCHANGE_HAS_REQUIRED.items() - if ex_mod.has.get(k) is not True and not (all(ex_mod.has.get(x) for x in v)) - ] + reasons = [] + reasons_fut = "" + missing = _exchange_has_helper(ex_mod, EXCHANGE_HAS_REQUIRED) if missing: result = False - reason += f"missing: {', '.join(missing)}" + reasons.append(f"missing: {', '.join(missing)}") - missing_opt = [k for k in EXCHANGE_HAS_OPTIONAL if not ex_mod.has.get(k)] + missing_opt = _exchange_has_helper(ex_mod, EXCHANGE_HAS_OPTIONAL) + + missing_futures = _exchange_has_helper(ex_mod, EXCHANGE_HAS_OPTIONAL_FUTURES) if exchange.lower() in BAD_EXCHANGES: result = False - reason = BAD_EXCHANGES.get(exchange.lower(), "") + reasons.append(BAD_EXCHANGES.get(exchange.lower(), "")) if missing_opt: - reason += f"{'. ' if reason else ''}missing opt: {', '.join(missing_opt)}. " + reasons.append(f"missing opt: {', '.join(missing_opt)}") - return result, reason, ex_mod + if missing_futures: + reasons_fut = f"missing futures opt: {', '.join(missing_futures)}" + + return result, "; ".join(reasons), reasons_fut, ex_mod def _build_exchange_list_entry( exchange_name: str, exchangeClasses: dict[str, Any] ) -> ValidExchangesType: exchange_name = exchange_name.lower() - valid, comment, ex_mod = validate_exchange(exchange_name) + valid, comment, comment_fut, ex_mod = validate_exchange(exchange_name) mapped_exchange_name = MAP_EXCHANGE_CHILDCLASS.get(exchange_name, exchange_name).lower() is_alias = getattr(ex_mod, "alias", False) result: ValidExchangesType = { @@ -102,6 +120,7 @@ def _build_exchange_list_entry( "valid": valid, "supported": mapped_exchange_name in SUPPORTED_EXCHANGES and not is_alias, "comment": comment, + "comment_futures": comment_fut, "dex": getattr(ex_mod, "dex", False), "is_alias": is_alias, "alias_for": inspect.getmro(ex_mod.__class__)[1]().id diff --git a/freqtrade/exchange/gate.py b/freqtrade/exchange/gate.py index 585dddad5..e48cf3af6 100644 --- a/freqtrade/exchange/gate.py +++ b/freqtrade/exchange/gate.py @@ -10,8 +10,7 @@ from freqtrade.enums import MarginMode, PriceType, TradingMode from freqtrade.exceptions import DDosProtection, OperationalException, TemporaryError from freqtrade.exchange import Exchange from freqtrade.exchange.common import retrier -from freqtrade.exchange.exchange_types import CcxtOrder, FtHas -from freqtrade.misc import safe_value_fallback2 +from freqtrade.exchange.exchange_types import FtHas logger = logging.getLogger(__name__) @@ -132,6 +131,3 @@ class Gate(Exchange): "rate": pair_fees[takerOrMaker], } return trades - - def get_order_id_conditional(self, order: CcxtOrder) -> str: - return safe_value_fallback2(order, order, "id_stop", "id") diff --git a/freqtrade/exchange/hyperliquid.py b/freqtrade/exchange/hyperliquid.py index f5e6fb7ee..4a9e1547b 100644 --- a/freqtrade/exchange/hyperliquid.py +++ b/freqtrade/exchange/hyperliquid.py @@ -7,9 +7,10 @@ from typing import Any from freqtrade.constants import BuySell from freqtrade.enums import MarginMode, TradingMode -from freqtrade.exceptions import ExchangeError, OperationalException +from freqtrade.enums.runmode import NON_UTIL_MODES +from freqtrade.exceptions import ConfigurationError, ExchangeError, OperationalException from freqtrade.exchange import Exchange -from freqtrade.exchange.exchange_types import CcxtOrder, FtHas +from freqtrade.exchange.exchange_types import CcxtBalances, CcxtOrder, CcxtPosition, FtHas from freqtrade.util.datetime_helpers import dt_from_ts @@ -57,12 +58,108 @@ class Hyperliquid(Exchange): config.update(super()._ccxt_config) return config + def _get_configured_hip3_dexes(self) -> list[str]: + """Get list of configured HIP-3 DEXes.""" + return self._config.get("exchange", {}).get("hip3_dexes", []) + + def validate_config(self, config: dict) -> None: + """Validate HIP-3 configuration at bot startup.""" + super().validate_config(config) + configured = self._get_configured_hip3_dexes() + if not configured or not self.markets: + return + if self.trading_mode != TradingMode.FUTURES: + if configured: + raise ConfigurationError( + "HIP-3 DEXes are only supported in FUTURES trading mode. " + "Please update your configuration!" + ) + return + if configured and self.margin_mode != MarginMode.ISOLATED: + raise ConfigurationError( + "HIP-3 DEXes require 'isolated' margin mode. " + f"Current margin mode: '{self.margin_mode.value}'. " + "Please update your configuration!" + ) + + available = { + m.get("info", {}).get("dex") + for m in self.get_markets( + quote_currencies=[self._config["stake_currency"]], + tradable_only=True, + active_only=True, + ).values() + if m.get("info", {}).get("hip3") + } + available.discard(None) + + invalid = set(configured) - available + if invalid: + raise ConfigurationError( + f"Invalid HIP-3 DEXes configured: {sorted(invalid)}. " + f"Available DEXes matching your stake currency ({self._config['stake_currency']}): " + f"{sorted(available)}. " + f"Check your 'hip3_dexes' configuration!" + ) + def market_is_tradable(self, market: dict[str, Any]) -> bool: + """Check if market is tradable, including HIP-3 markets.""" parent_check = super().market_is_tradable(market) - # Exclude hip3 markets for now - which have the format XYZ:GOOGL/USDT:USDT - - # and XYZ:GOOGL as base - return parent_check and ":" not in market["base"] + market_info = market.get("info", {}) + if market_info.get("hip3") and self._config["runmode"] in NON_UTIL_MODES: + configured = self._get_configured_hip3_dexes() + if not configured: + return False + + market_dex = market_info.get("dex") + return parent_check and market_dex in configured + + return parent_check + + def get_balances(self, params: dict | None = None) -> CcxtBalances: + """Fetch balances from default DEX and HIP-3 DEXes needed by tradable pairs. + This override is not absolutely necessary and is only there for correct used / total values + which are however not used by Freqtrade in futures mode at the moment. + """ + balances = super().get_balances() + dexes = self._get_configured_hip3_dexes() + for dex in dexes: + try: + dex_balance = super().get_balances(params={"dex": dex}) + + for currency, amount_info in dex_balance.items(): + if currency in ["info", "free", "used", "total", "datetime", "timestamp"]: + continue + + if currency not in balances: + balances[currency] = amount_info + else: + balances[currency]["free"] += amount_info["free"] + balances[currency]["used"] += amount_info["used"] + balances[currency]["total"] += amount_info["total"] + + except Exception as e: + logger.error(f"Could not fetch balance for HIP-3 DEX '{dex}': {e}") + + if dexes: + self._log_exchange_response("fetch_balance", balances, add_info="combined") + return balances + + def fetch_positions( + self, pair: str | None = None, params: dict | None = None + ) -> list[CcxtPosition]: + """Fetch positions from default DEX and HIP-3 DEXes needed by tradable pairs.""" + positions = super().fetch_positions(pair) + dexes = self._get_configured_hip3_dexes() + for dex in dexes: + try: + positions.extend(super().fetch_positions(pair, params={"dex": dex})) + except Exception as e: + logger.error(f"Could not fetch positions from HIP-3 DEX '{dex}': {e}") + if dexes: + self._log_exchange_response("fetch_positions", positions, add_info="combined") + return positions def get_max_leverage(self, pair: str, stake_amount: float | None) -> float: # There are no leverage tiers diff --git a/freqtrade/exchange/kraken.py b/freqtrade/exchange/kraken.py index 60efdf954..949f02530 100644 --- a/freqtrade/exchange/kraken.py +++ b/freqtrade/exchange/kraken.py @@ -70,7 +70,7 @@ class Kraken(Exchange): return consolidated @retrier - def get_balances(self) -> CcxtBalances: + def get_balances(self, params: dict | None = None) -> CcxtBalances: if self._config["dry_run"]: return {} diff --git a/freqtrade/exchange/okx.py b/freqtrade/exchange/okx.py index 4640d5647..df7f23299 100644 --- a/freqtrade/exchange/okx.py +++ b/freqtrade/exchange/okx.py @@ -14,7 +14,6 @@ from freqtrade.exceptions import ( from freqtrade.exchange import Exchange from freqtrade.exchange.common import API_RETRY_COUNT, retrier from freqtrade.exchange.exchange_types import CcxtOrder, FtHas -from freqtrade.misc import safe_value_fallback2 from freqtrade.util import dt_now, dt_ts @@ -183,7 +182,10 @@ class Okx(Exchange): return float("inf") pair_tiers = self._leverage_tiers[pair] - return pair_tiers[-1]["maxNotional"] / leverage + last_max_notional = pair_tiers[-1]["maxNotional"] + if last_max_notional is None: + return float("inf") + return last_max_notional / leverage def _get_stop_params(self, side: BuySell, ordertype: str, stop_price: float) -> dict: params = super()._get_stop_params(side, ordertype, stop_price) @@ -259,11 +261,6 @@ class Okx(Exchange): raise OperationalException(e) from e raise RetryableOrderError(f"StoplossOrder not found (pair: {pair} id: {order_id}).") - def get_order_id_conditional(self, order: CcxtOrder) -> str: - if order.get("type", "") == "stop": - return safe_value_fallback2(order, order, "id_stop", "id") - return order["id"] - def _fetch_orders_emulate(self, pair: str, since_ms: int) -> list[CcxtOrder]: orders = [] diff --git a/freqtrade/freqai/freqai_interface.py b/freqtrade/freqai/freqai_interface.py index b4f37adf1..1ba58d3e8 100644 --- a/freqtrade/freqai/freqai_interface.py +++ b/freqtrade/freqai/freqai_interface.py @@ -227,6 +227,10 @@ class IFreqaiModel(ABC): """ while not self._stop_event.is_set(): time.sleep(1) + + if not self.train_queue: + continue + pair = self.train_queue[0] # ensure pair is available in dp diff --git a/freqtrade/ft_types/plot_annotation_type.py b/freqtrade/ft_types/plot_annotation_type.py index 27717b4cd..13247b266 100644 --- a/freqtrade/ft_types/plot_annotation_type.py +++ b/freqtrade/ft_types/plot_annotation_type.py @@ -6,25 +6,36 @@ from typing_extensions import TypedDict class _BaseAnnotationType(TypedDict, total=False): - start: str | datetime - end: str | datetime - y_start: float - y_end: float color: str label: str z_level: int -class AreaAnnotationType(_BaseAnnotationType, total=False): +class _Base2DAnnotationType(_BaseAnnotationType, total=False): + start: str | datetime + end: str | datetime + y_start: float + y_end: float + + +class AreaAnnotationType(_Base2DAnnotationType, total=False): type: Required[Literal["area"]] -class LineAnnotationType(_BaseAnnotationType, total=False): +class LineAnnotationType(_Base2DAnnotationType, total=False): type: Required[Literal["line"]] width: int line_style: Literal["solid", "dashed", "dotted"] -AnnotationType = AreaAnnotationType | LineAnnotationType +class PointAnnotationType(_BaseAnnotationType, total=False): + type: Required[Literal["point"]] + x: str | datetime + y: float + size: int + shape: Literal["circle", "rect", "roundRect", "triangle", "pin", "arrow", "none"] + + +AnnotationType = AreaAnnotationType | LineAnnotationType | PointAnnotationType AnnotationTypeTA: TypeAdapter[AnnotationType] = TypeAdapter(AnnotationType) diff --git a/freqtrade/ft_types/valid_exchanges_type.py b/freqtrade/ft_types/valid_exchanges_type.py index d6b93f4b2..2d0e6ae2f 100644 --- a/freqtrade/ft_types/valid_exchanges_type.py +++ b/freqtrade/ft_types/valid_exchanges_type.py @@ -14,6 +14,7 @@ class ValidExchangesType(TypedDict): valid: bool supported: bool comment: str + comment_futures: str dex: bool is_alias: bool alias_for: str | None diff --git a/freqtrade/optimize/backtesting.py b/freqtrade/optimize/backtesting.py index e3e340a1b..8cbdd0dea 100644 --- a/freqtrade/optimize/backtesting.py +++ b/freqtrade/optimize/backtesting.py @@ -439,6 +439,8 @@ class Backtesting: PairLocks.reset_locks() Trade.reset_trades() CustomDataWrapper.reset_custom_data() + # Ensure logging is disabled in other processes during hyperopt + LoggingMixin.show_output = False self.rejected_trades = 0 self.timedout_entry_orders = 0 self.timedout_exit_orders = 0 diff --git a/freqtrade/optimize/hyperopt/hyperopt_logger.py b/freqtrade/optimize/hyperopt/hyperopt_logger.py index d6940ee3a..e4f0df0bc 100644 --- a/freqtrade/optimize/hyperopt/hyperopt_logger.py +++ b/freqtrade/optimize/hyperopt/hyperopt_logger.py @@ -21,6 +21,11 @@ def logging_mp_setup(log_queue: Queue, verbosity: int): root = logging.getLogger() root.setLevel(verbosity) root.addHandler(h) + # Disable freqtrade logging outside of the main process + # This only leaves logging from the strategy (unless it's prefixed with "freqtrade.") + # and eventually from other libraries. + if verbosity > logging.DEBUG: + logging.getLogger("freqtrade").setLevel(logging.WARNING) def logging_mp_handle(q: Queue): diff --git a/freqtrade/optimize/hyperopt/hyperopt_optimizer.py b/freqtrade/optimize/hyperopt/hyperopt_optimizer.py index 3d515b27f..516af1377 100644 --- a/freqtrade/optimize/hyperopt/hyperopt_optimizer.py +++ b/freqtrade/optimize/hyperopt/hyperopt_optimizer.py @@ -6,7 +6,7 @@ and will be sent to the hyperopt worker processes. import logging import sys import warnings -from datetime import UTC, datetime +from datetime import datetime from multiprocessing import Manager from pathlib import Path from typing import Any @@ -42,6 +42,7 @@ from freqtrade.optimize.space import ( ft_IntDistribution, ) from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver +from freqtrade.util import dt_now from freqtrade.util.dry_run_wallet import get_dry_run_wallet @@ -269,7 +270,7 @@ class HyperOptimizer: Keep this function as optimized as possible! """ HyperoptStateContainer.set_state(HyperoptState.OPTIMIZE) - backtest_start_time = datetime.now(UTC) + backtest_start_time = dt_now() for attr_name, attr in self.backtesting.strategy.enumerate_parameters(): if attr.in_space and attr.optimize: @@ -320,7 +321,7 @@ class HyperOptimizer: bt_results = self.backtesting.backtest( processed=processed, start_date=self.min_date, end_date=self.max_date ) - backtest_end_time = datetime.now(UTC) + backtest_end_time = dt_now() bt_results.update( { "backtest_start_time": int(backtest_start_time.timestamp()), diff --git a/freqtrade/plugins/pairlist/PercentChangePairList.py b/freqtrade/plugins/pairlist/PercentChangePairList.py index 3138856c8..9deefdfd4 100644 --- a/freqtrade/plugins/pairlist/PercentChangePairList.py +++ b/freqtrade/plugins/pairlist/PercentChangePairList.py @@ -53,7 +53,7 @@ class PercentChangePairList(IPairList): self._sort_direction: str | None = self._pairlistconfig.get("sort_direction", "desc") self._def_candletype = self._config["candle_type_def"] - if (self._lookback_days > 0) & (self._lookback_period > 0): + if (self._lookback_days > 0) and (self._lookback_period > 0): raise OperationalException( "Ambiguous configuration: lookback_days and lookback_period both set in pairlist " "config. Please set lookback_days only or lookback_period and lookback_timeframe " @@ -70,7 +70,7 @@ class PercentChangePairList(IPairList): _tf_in_sec = self._tf_in_min * 60 # whether to use range lookback or not - self._use_range = (self._tf_in_min > 0) & (self._lookback_period > 0) + self._use_range = (self._tf_in_min > 0) and (self._lookback_period > 0) if self._use_range & (self._refresh_period < _tf_in_sec): raise OperationalException( @@ -84,9 +84,9 @@ class PercentChangePairList(IPairList): and self._exchange.get_option("tickers_have_percentage") ): raise OperationalException( - "Exchange does not support dynamic whitelist in this configuration. " - "Please edit your config and either remove PercentChangePairList, " - "or switch to using candles. and restart the bot." + f"Exchange {self._exchange.name} does not support dynamic whitelist in this " + "configuration. Please edit your config and either remove PercentChangePairList, " + "or switch to using candles and restart the bot." ) candle_limit = self._exchange.ohlcv_candle_limit( diff --git a/freqtrade/plugins/pairlist/VolumePairList.py b/freqtrade/plugins/pairlist/VolumePairList.py index 7d5ce3468..b03688039 100644 --- a/freqtrade/plugins/pairlist/VolumePairList.py +++ b/freqtrade/plugins/pairlist/VolumePairList.py @@ -8,7 +8,7 @@ import logging from datetime import timedelta from typing import Any, Literal -from freqtrade.constants import ListPairsWithTimeframes +from freqtrade.constants import DOCS_LINK, ListPairsWithTimeframes from freqtrade.exceptions import OperationalException from freqtrade.exchange import timeframe_to_minutes, timeframe_to_prev_date from freqtrade.exchange.exchange_types import Tickers @@ -35,19 +35,19 @@ class VolumePairList(IPairList): 'for "pairlist.config.number_assets"' ) - self._stake_currency = self._config["stake_currency"] - self._number_pairs = self._pairlistconfig["number_assets"] + self._stake_currency: str = self._config["stake_currency"] + self._number_pairs: int = self._pairlistconfig["number_assets"] self._sort_key: Literal["quoteVolume"] = self._pairlistconfig.get("sort_key", "quoteVolume") - self._min_value = self._pairlistconfig.get("min_value", 0) - self._max_value = self._pairlistconfig.get("max_value", None) + self._min_value: float | None = self._pairlistconfig.get("min_value", 0) + self._max_value: float | None = self._pairlistconfig.get("max_value", None) self._refresh_period = self._pairlistconfig.get("refresh_period", 1800) self._pair_cache: FtTTLCache = FtTTLCache(maxsize=1, ttl=self._refresh_period) - self._lookback_days = self._pairlistconfig.get("lookback_days", 0) - self._lookback_timeframe = self._pairlistconfig.get("lookback_timeframe", "1d") - self._lookback_period = self._pairlistconfig.get("lookback_period", 0) + self._lookback_days: int = self._pairlistconfig.get("lookback_days", 0) + self._lookback_timeframe: str = self._pairlistconfig.get("lookback_timeframe", "1d") + self._lookback_period: int = self._pairlistconfig.get("lookback_period", 0) self._def_candletype = self._config["candle_type_def"] - if (self._lookback_days > 0) & (self._lookback_period > 0): + if (self._lookback_days > 0) and (self._lookback_period > 0): raise OperationalException( "Ambiguous configuration: lookback_days and lookback_period both set in pairlist " "config. Please set lookback_days only or lookback_period and lookback_timeframe " @@ -64,9 +64,9 @@ class VolumePairList(IPairList): _tf_in_sec = self._tf_in_min * 60 # whether to use range lookback or not - self._use_range = (self._tf_in_min > 0) & (self._lookback_period > 0) + self._use_range = (self._tf_in_min > 0) and (self._lookback_period > 0) - if self._use_range & (self._refresh_period < _tf_in_sec): + if self._use_range and (self._refresh_period < _tf_in_sec): raise OperationalException( f"Refresh period of {self._refresh_period} seconds is smaller than one " f"timeframe of {self._lookback_timeframe}. Please adjust refresh_period " @@ -78,9 +78,11 @@ class VolumePairList(IPairList): and self._exchange.get_option("tickers_have_quoteVolume") ): raise OperationalException( - "Exchange does not support dynamic whitelist in this configuration. " - "Please edit your config and either remove Volumepairlist, " - "or switch to using candles. and restart the bot." + f"Exchange {self._exchange.name} does not support dynamic whitelist in this " + "configuration. Please edit your config and either remove Volumepairlist, " + "or switch to using candles and restart the bot. " + f"You can find more information about this in the documentation under " + f"{DOCS_LINK}/plugins/#volumepairlist-advanced-mode ." ) if not self._validate_keys(self._sort_key): @@ -297,7 +299,7 @@ class VolumePairList(IPairList): # Tickers mode - filter based on incoming pairlist. filtered_tickers = [v for k, v in tickers.items() if k in pairlist] - if self._min_value > 0: + if self._min_value and self._min_value > 0: filtered_tickers = [v for v in filtered_tickers if v[self._sort_key] > self._min_value] if self._max_value is not None: filtered_tickers = [v for v in filtered_tickers if v[self._sort_key] < self._max_value] diff --git a/freqtrade/plugins/pairlistmanager.py b/freqtrade/plugins/pairlistmanager.py index 8f7eff61f..e8cfdd581 100644 --- a/freqtrade/plugins/pairlistmanager.py +++ b/freqtrade/plugins/pairlistmanager.py @@ -51,8 +51,8 @@ class PairListManager(LoggingMixin): invalid = ". ".join([p.name for p in self._pairlist_handlers if p.needstickers]) raise OperationalException( - "Exchange does not support fetchTickers, therefore the following pairlists " - "cannot be used. Please edit your config and restart the bot.\n" + f"Exchange {self._exchange.name} does not support fetchTickers, therefore the " + "following pairlists cannot be used. Please edit your config and restart the bot.\n" f"{invalid}." ) diff --git a/freqtrade/resolvers/iresolver.py b/freqtrade/resolvers/iresolver.py index b437166e7..fc372b2f2 100644 --- a/freqtrade/resolvers/iresolver.py +++ b/freqtrade/resolvers/iresolver.py @@ -139,7 +139,7 @@ class IResolver: :return: object class """ logger.debug(f"Searching for {cls.object_type.__name__} {object_name} in '{directory}'") - for entry in directory.iterdir(): + for entry in sorted(directory.iterdir()): # Only consider python files if entry.suffix != ".py": logger.debug("Ignoring %s", entry) @@ -148,7 +148,7 @@ class IResolver: logger.debug("Ignoring broken symlink %s", entry) continue module_path = entry.resolve() - if entry.read_text().find(f"class {object_name}(") == -1: + if entry.read_text(encoding="utf-8").find(f"class {object_name}(") == -1: logger.debug(f"Skipping {module_path} as it does not contain class {object_name}.") continue diff --git a/freqtrade/rpc/api_server/api_background_tasks.py b/freqtrade/rpc/api_server/api_background_tasks.py index b41828d6f..daa844d7b 100644 --- a/freqtrade/rpc/api_server/api_background_tasks.py +++ b/freqtrade/rpc/api_server/api_background_tasks.py @@ -13,7 +13,7 @@ logger = logging.getLogger(__name__) router = APIRouter() -@router.get("/background", response_model=list[BackgroundTaskStatus], tags=["webserver"]) +@router.get("/background", response_model=list[BackgroundTaskStatus]) def background_job_list(): return [ { @@ -29,7 +29,7 @@ def background_job_list(): ] -@router.get("/background/{jobid}", response_model=BackgroundTaskStatus, tags=["webserver"]) +@router.get("/background/{jobid}", response_model=BackgroundTaskStatus) def background_job(jobid: str): if not (job := ApiBG.jobs.get(jobid)): raise HTTPException(status_code=404, detail="Job not found.") diff --git a/freqtrade/rpc/api_server/api_backtest.py b/freqtrade/rpc/api_server/api_backtest.py index d038f5f6a..485c74119 100644 --- a/freqtrade/rpc/api_server/api_backtest.py +++ b/freqtrade/rpc/api_server/api_backtest.py @@ -125,7 +125,7 @@ def __run_backtest_bg(btconfig: Config): ApiBG.bgtask_running = False -@router.post("/backtest", response_model=BacktestResponse, tags=["webserver", "backtest"]) +@router.post("/backtest", response_model=BacktestResponse) async def api_start_backtest( bt_settings: BacktestRequest, background_tasks: BackgroundTasks, config=Depends(get_config) ): @@ -168,7 +168,7 @@ async def api_start_backtest( } -@router.get("/backtest", response_model=BacktestResponse, tags=["webserver", "backtest"]) +@router.get("/backtest", response_model=BacktestResponse) def api_get_backtest(): """ Get backtesting result. @@ -215,7 +215,7 @@ def api_get_backtest(): } -@router.delete("/backtest", response_model=BacktestResponse, tags=["webserver", "backtest"]) +@router.delete("/backtest", response_model=BacktestResponse) def api_delete_backtest(): """Reset backtesting""" if ApiBG.bgtask_running: @@ -242,7 +242,7 @@ def api_delete_backtest(): } -@router.get("/backtest/abort", response_model=BacktestResponse, tags=["webserver", "backtest"]) +@router.get("/backtest/abort", response_model=BacktestResponse) def api_backtest_abort(): if not ApiBG.bgtask_running: return { @@ -262,17 +262,13 @@ def api_backtest_abort(): } -@router.get( - "/backtest/history", response_model=list[BacktestHistoryEntry], tags=["webserver", "backtest"] -) +@router.get("/backtest/history", response_model=list[BacktestHistoryEntry]) def api_backtest_history(config=Depends(get_config)): # Get backtest result history, read from metadata files return get_backtest_resultlist(config["user_data_dir"] / "backtest_results") -@router.get( - "/backtest/history/result", response_model=BacktestResponse, tags=["webserver", "backtest"] -) +@router.get("/backtest/history/result", response_model=BacktestResponse) def api_backtest_history_result(filename: str, strategy: str, config=Depends(get_config)): # Get backtest result history, read from metadata files bt_results_base: Path = config["user_data_dir"] / "backtest_results" @@ -299,11 +295,7 @@ def api_backtest_history_result(filename: str, strategy: str, config=Depends(get } -@router.delete( - "/backtest/history/{file}", - response_model=list[BacktestHistoryEntry], - tags=["webserver", "backtest"], -) +@router.delete("/backtest/history/{file}", response_model=list[BacktestHistoryEntry]) def api_delete_backtest_history_entry(file: str, config=Depends(get_config)): # Get backtest result history, read from metadata files bt_results_base: Path = config["user_data_dir"] / "backtest_results" @@ -319,11 +311,7 @@ def api_delete_backtest_history_entry(file: str, config=Depends(get_config)): return get_backtest_resultlist(config["user_data_dir"] / "backtest_results") -@router.patch( - "/backtest/history/{file}", - response_model=list[BacktestHistoryEntry], - tags=["webserver", "backtest"], -) +@router.patch("/backtest/history/{file}", response_model=list[BacktestHistoryEntry]) def api_update_backtest_history_entry( file: str, body: BacktestMetadataUpdate, config=Depends(get_config) ): @@ -346,11 +334,7 @@ def api_update_backtest_history_entry( return get_backtest_result(file_abs) -@router.get( - "/backtest/history/{file}/market_change", - response_model=BacktestMarketChange, - tags=["webserver", "backtest"], -) +@router.get("/backtest/history/{file}/market_change", response_model=BacktestMarketChange) def api_get_backtest_market_change(file: str, config=Depends(get_config)): bt_results_base: Path = config["user_data_dir"] / "backtest_results" for fn in ( diff --git a/freqtrade/rpc/api_server/api_download_data.py b/freqtrade/rpc/api_server/api_download_data.py index b755474c9..aff6ec157 100644 --- a/freqtrade/rpc/api_server/api_download_data.py +++ b/freqtrade/rpc/api_server/api_download_data.py @@ -17,7 +17,7 @@ from freqtrade.util.progress_tracker import get_progress_tracker logger = logging.getLogger(__name__) # Private API, protected by authentication and webserver_mode dependency -router = APIRouter(tags=["download-data", "webserver"]) +router = APIRouter() def __run_download(job_id: str, config_loc: Config): diff --git a/freqtrade/rpc/api_server/api_pair_history.py b/freqtrade/rpc/api_server/api_pair_history.py index 31fd86f46..1409c7672 100644 --- a/freqtrade/rpc/api_server/api_pair_history.py +++ b/freqtrade/rpc/api_server/api_pair_history.py @@ -15,7 +15,7 @@ logger = logging.getLogger(__name__) router = APIRouter() -@router.get("/pair_history", response_model=PairHistory, tags=["candle data"]) +@router.get("/pair_history", response_model=PairHistory, tags=["Candle data"]) def pair_history( pair: str, timeframe: str, @@ -43,7 +43,7 @@ def pair_history( raise HTTPException(status_code=502, detail=str(e)) -@router.post("/pair_history", response_model=PairHistory, tags=["candle data"]) +@router.post("/pair_history", response_model=PairHistory, tags=["Candle data"]) def pair_history_filtered(payload: PairHistoryRequest, config=Depends(get_config)): # The initial call to this endpoint can be slow, as it may need to initialize # the exchange class. diff --git a/freqtrade/rpc/api_server/api_pairlists.py b/freqtrade/rpc/api_server/api_pairlists.py index 094b9f8e2..4cca65ab9 100644 --- a/freqtrade/rpc/api_server/api_pairlists.py +++ b/freqtrade/rpc/api_server/api_pairlists.py @@ -25,9 +25,7 @@ logger = logging.getLogger(__name__) router = APIRouter() -@router.get( - "/pairlists/available", response_model=PairListsResponse, tags=["pairlists", "webserver"] -) +@router.get("/pairlists/available", response_model=PairListsResponse) def list_pairlists(config=Depends(get_config)): from freqtrade.resolvers import PairListResolver @@ -72,7 +70,7 @@ def __run_pairlist(job_id: str, config_loc: Config): ApiBG.pairlist_running = False -@router.post("/pairlists/evaluate", response_model=BgJobStarted, tags=["pairlists", "webserver"]) +@router.post("/pairlists/evaluate", response_model=BgJobStarted) def pairlists_evaluate( payload: PairListsPayload, background_tasks: BackgroundTasks, config=Depends(get_config) ): @@ -126,11 +124,7 @@ def handleExchangePayload(payload: ExchangeModePayloadMixin, config_loc: Config) config_loc["margin_mode"] = payload.margin_mode -@router.get( - "/pairlists/evaluate/{jobid}", - response_model=WhitelistEvaluateResponse, - tags=["pairlists", "webserver"], -) +@router.get("/pairlists/evaluate/{jobid}", response_model=WhitelistEvaluateResponse) def pairlists_evaluate_get(jobid: str): if not (job := ApiBG.jobs.get(jobid)): raise HTTPException(status_code=404, detail="Job not found.") diff --git a/freqtrade/rpc/api_server/api_schemas.py b/freqtrade/rpc/api_server/api_schemas.py index 0dcd95edf..1783c9ebf 100644 --- a/freqtrade/rpc/api_server/api_schemas.py +++ b/freqtrade/rpc/api_server/api_schemas.py @@ -157,6 +157,11 @@ class Profit(BaseModel): winrate: float expectancy: float expectancy_ratio: float + sharpe: float + sortino: float + sqn: float + calmar: float + cagr: float max_drawdown: float max_drawdown_abs: float max_drawdown_start: str diff --git a/freqtrade/rpc/api_server/api_trading.py b/freqtrade/rpc/api_server/api_trading.py new file mode 100644 index 000000000..3ec7a08b3 --- /dev/null +++ b/freqtrade/rpc/api_server/api_trading.py @@ -0,0 +1,337 @@ +import logging + +from fastapi import APIRouter, Depends, Query +from fastapi.exceptions import HTTPException + +from freqtrade.enums import TradingMode +from freqtrade.rpc import RPC +from freqtrade.rpc.api_server.api_schemas import ( + Balances, + BlacklistPayload, + BlacklistResponse, + Count, + DailyWeeklyMonthly, + DeleteLockRequest, + DeleteTrade, + Entry, + Exit, + ForceEnterPayload, + ForceEnterResponse, + ForceExitPayload, + ListCustomData, + Locks, + LocksPayload, + MixTag, + OpenTradeSchema, + PairCandlesRequest, + PairHistory, + PerformanceEntry, + Profit, + ProfitAll, + ResultMsg, + Stats, + StatusMsg, + WhitelistResponse, +) +from freqtrade.rpc.api_server.deps import get_config, get_rpc +from freqtrade.rpc.rpc import RPCException + + +logger = logging.getLogger(__name__) + +router = APIRouter() + + +@router.get("/balance", response_model=Balances, tags=["Trading-info"]) +def balance(rpc: RPC = Depends(get_rpc), config=Depends(get_config)): + """Account Balances""" + return rpc._rpc_balance( + config["stake_currency"], + config.get("fiat_display_currency", ""), + ) + + +@router.get("/count", response_model=Count, tags=["Trading-info"]) +def count(rpc: RPC = Depends(get_rpc)): + return rpc._rpc_count() + + +@router.get("/entries", response_model=list[Entry], tags=["Trading-info"]) +def entries(pair: str | None = None, rpc: RPC = Depends(get_rpc)): + return rpc._rpc_enter_tag_performance(pair) + + +@router.get("/exits", response_model=list[Exit], tags=["Trading-info"]) +def exits(pair: str | None = None, rpc: RPC = Depends(get_rpc)): + return rpc._rpc_exit_reason_performance(pair) + + +@router.get("/mix_tags", response_model=list[MixTag], tags=["Trading-info"]) +def mix_tags(pair: str | None = None, rpc: RPC = Depends(get_rpc)): + return rpc._rpc_mix_tag_performance(pair) + + +@router.get("/performance", response_model=list[PerformanceEntry], tags=["Trading-info"]) +def performance(rpc: RPC = Depends(get_rpc)): + return rpc._rpc_performance() + + +@router.get("/profit", response_model=Profit, tags=["Trading-info"]) +def profit(rpc: RPC = Depends(get_rpc), config=Depends(get_config)): + return rpc._rpc_trade_statistics(config["stake_currency"], config.get("fiat_display_currency")) + + +@router.get("/profit_all", response_model=ProfitAll, tags=["Trading-info"]) +def profit_all(rpc: RPC = Depends(get_rpc), config=Depends(get_config)): + response = { + "all": rpc._rpc_trade_statistics( + config["stake_currency"], config.get("fiat_display_currency") + ), + } + if config.get("trading_mode", TradingMode.SPOT) != TradingMode.SPOT: + response["long"] = rpc._rpc_trade_statistics( + config["stake_currency"], config.get("fiat_display_currency"), direction="long" + ) + response["short"] = rpc._rpc_trade_statistics( + config["stake_currency"], config.get("fiat_display_currency"), direction="short" + ) + + return response + + +@router.get("/stats", response_model=Stats, tags=["Trading-info"]) +def stats(rpc: RPC = Depends(get_rpc)): + return rpc._rpc_stats() + + +@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"), + rpc: RPC = Depends(get_rpc), + config=Depends(get_config), +): + return rpc._rpc_timeunit_profit( + timescale, config["stake_currency"], config.get("fiat_display_currency", "") + ) + + +@router.get("/weekly", response_model=DailyWeeklyMonthly, tags=["Trading-info"]) +def weekly( + timescale: int = Query(4, ge=1, description="Number of weeks to fetch data for"), + rpc: RPC = Depends(get_rpc), + config=Depends(get_config), +): + return rpc._rpc_timeunit_profit( + timescale, config["stake_currency"], config.get("fiat_display_currency", ""), "weeks" + ) + + +@router.get("/monthly", response_model=DailyWeeklyMonthly, tags=["Trading-info"]) +def monthly( + timescale: int = Query(3, ge=1, description="Number of months to fetch data for"), + rpc: RPC = Depends(get_rpc), + config=Depends(get_config), +): + return rpc._rpc_timeunit_profit( + timescale, config["stake_currency"], config.get("fiat_display_currency", ""), "months" + ) + + +@router.get("/status", response_model=list[OpenTradeSchema], tags=["Trading-info"]) +def status(rpc: RPC = Depends(get_rpc)): + try: + return rpc._rpc_trade_status() + except RPCException: + return [] + + +# Using the responsemodel here will cause a ~100% increase in response time (from 1s to 2s) +# on big databases. Correct response model: response_model=TradeResponse, +@router.get("/trades", tags=["Trading-info", "Trades"]) +def trades( + limit: int = Query(500, ge=1, description="Maximum number of different trades to return data"), + offset: int = Query(0, ge=0, description="Number of trades to skip for pagination"), + order_by_id: bool = Query( + True, description="Sort trades by id (default: True). If False, sorts by latest timestamp" + ), + rpc: RPC = Depends(get_rpc), +): + return rpc._rpc_trade_history(limit, offset=offset, order_by_id=order_by_id) + + +@router.get("/trade/{tradeid}", response_model=OpenTradeSchema, tags=["Trades"]) +def trade(tradeid: int = 0, rpc: RPC = Depends(get_rpc)): + try: + return rpc._rpc_trade_status([tradeid])[0] + except (RPCException, KeyError): + raise HTTPException(status_code=404, detail="Trade not found.") + + +@router.delete("/trades/{tradeid}", response_model=DeleteTrade, tags=["Trades"]) +def trades_delete(tradeid: int, rpc: RPC = Depends(get_rpc)): + return rpc._rpc_delete(tradeid) + + +@router.delete("/trades/{tradeid}/open-order", response_model=OpenTradeSchema, tags=["Trades"]) +def trade_cancel_open_order(tradeid: int, rpc: RPC = Depends(get_rpc)): + rpc._rpc_cancel_open_order(tradeid) + return rpc._rpc_trade_status([tradeid])[0] + + +@router.post("/trades/{tradeid}/reload", response_model=OpenTradeSchema, tags=["Trades"]) +def trade_reload(tradeid: int, rpc: RPC = Depends(get_rpc)): + rpc._rpc_reload_trade_from_exchange(tradeid) + return rpc._rpc_trade_status([tradeid])[0] + + +@router.get("/trades/open/custom-data", response_model=list[ListCustomData], tags=["Trades"]) +def list_open_trades_custom_data( + key: str | None = Query(None, description="Optional key to filter data"), + limit: int = Query(100, ge=1, description="Maximum number of different trades to return data"), + offset: int = Query(0, ge=0, description="Number of trades to skip for pagination"), + rpc: RPC = Depends(get_rpc), +): + """ + Fetch custom data for all open trades. + If a key is provided, it will be used to filter data accordingly. + Pagination is implemented via the `limit` and `offset` parameters. + """ + try: + return rpc._rpc_list_custom_data(key=key, limit=limit, offset=offset) + except RPCException as e: + raise HTTPException(status_code=404, detail=str(e)) + + +@router.get("/trades/{trade_id}/custom-data", response_model=list[ListCustomData], tags=["Trades"]) +def list_custom_data(trade_id: int, key: str | None = Query(None), rpc: RPC = Depends(get_rpc)): + """ + Fetch custom data for a specific trade. + If a key is provided, it will be used to filter data accordingly. + """ + try: + return rpc._rpc_list_custom_data(trade_id, key=key) + except RPCException as e: + raise HTTPException(status_code=404, detail=str(e)) + + +# /forcebuy is deprecated with short addition. use /forceentry instead +@router.post("/forceenter", response_model=ForceEnterResponse, tags=["Trades"]) +@router.post( + "/forcebuy", + response_model=ForceEnterResponse, + tags=["Trades"], + summary="(deprecated) Please use /forceenter instead", +) +def force_entry(payload: ForceEnterPayload, rpc: RPC = Depends(get_rpc)): + ordertype = payload.ordertype.value if payload.ordertype else None + + trade = rpc._rpc_force_entry( + payload.pair, + payload.price, + order_side=payload.side, + order_type=ordertype, + stake_amount=payload.stakeamount, + enter_tag=payload.entry_tag or "force_entry", + leverage=payload.leverage, + ) + + if trade: + return ForceEnterResponse.model_validate(trade.to_json()) + else: + return ForceEnterResponse.model_validate( + {"status": f"Error entering {payload.side} trade for pair {payload.pair}."} + ) + + +# /forcesell is deprecated with short addition. use /forceexit instead +@router.post("/forceexit", response_model=ResultMsg, tags=["Trades"]) +@router.post( + "/forcesell", + response_model=ResultMsg, + tags=["Trades"], + summary="(deprecated) Please use /forceexit instead", +) +def forceexit(payload: ForceExitPayload, rpc: RPC = Depends(get_rpc)): + ordertype = payload.ordertype.value if payload.ordertype else None + return rpc._rpc_force_exit( + str(payload.tradeid), ordertype, amount=payload.amount, price=payload.price + ) + + +@router.get("/blacklist", response_model=BlacklistResponse, tags=["Trading-info", "Pairlist"]) +def blacklist(rpc: RPC = Depends(get_rpc)): + return rpc._rpc_blacklist() + + +@router.post("/blacklist", response_model=BlacklistResponse, tags=["Pairlist"]) +def blacklist_post(payload: BlacklistPayload, rpc: RPC = Depends(get_rpc)): + return rpc._rpc_blacklist(payload.blacklist) + + +@router.delete("/blacklist", response_model=BlacklistResponse, tags=["Pairlist"]) +def blacklist_delete(pairs_to_delete: list[str] = Query([]), rpc: RPC = Depends(get_rpc)): + """Provide a list of pairs to delete from the blacklist""" + + return rpc._rpc_blacklist_delete(pairs_to_delete) + + +@router.get("/whitelist", response_model=WhitelistResponse, tags=["Trading-info", "Pairlist"]) +def whitelist(rpc: RPC = Depends(get_rpc)): + return rpc._rpc_whitelist() + + +@router.get("/locks", response_model=Locks, tags=["Trading-info", "Locks"]) +def locks(rpc: RPC = Depends(get_rpc)): + return rpc._rpc_locks() + + +@router.delete("/locks/{lockid}", response_model=Locks, tags=["Locks"]) +def delete_lock(lockid: int, rpc: RPC = Depends(get_rpc)): + return rpc._rpc_delete_lock(lockid=lockid) + + +@router.post("/locks/delete", response_model=Locks, tags=["Locks"]) +def delete_lock_pair(payload: DeleteLockRequest, rpc: RPC = Depends(get_rpc)): + return rpc._rpc_delete_lock(lockid=payload.lockid, pair=payload.pair) + + +@router.post("/locks", response_model=Locks, tags=["Locks"]) +def add_locks(payload: list[LocksPayload], rpc: RPC = Depends(get_rpc)): + for lock in payload: + rpc._rpc_add_lock(lock.pair, lock.until, lock.reason, lock.side) + return rpc._rpc_locks() + + +@router.post("/start", response_model=StatusMsg, tags=["Bot-control"]) +def start(rpc: RPC = Depends(get_rpc)): + return rpc._rpc_start() + + +@router.post("/stop", response_model=StatusMsg, tags=["Bot-control"]) +def stop(rpc: RPC = Depends(get_rpc)): + return rpc._rpc_stop() + + +@router.post("/pause", response_model=StatusMsg, tags=["Bot-control"]) +@router.post("/stopentry", response_model=StatusMsg, tags=["Bot-control"]) +@router.post("/stopbuy", response_model=StatusMsg, tags=["Bot-control"]) +def pause(rpc: RPC = Depends(get_rpc)): + return rpc._rpc_pause() + + +@router.post("/reload_config", response_model=StatusMsg, tags=["Bot-control"]) +def reload_config(rpc: RPC = Depends(get_rpc)): + return rpc._rpc_reload_config() + + +@router.get("/pair_candles", response_model=PairHistory, tags=["Candle data"]) +def pair_candles(pair: str, timeframe: str, limit: int | None = None, rpc: RPC = Depends(get_rpc)): + return rpc._rpc_analysed_dataframe(pair, timeframe, limit, None) + + +@router.post("/pair_candles", response_model=PairHistory, tags=["Candle data"]) +def pair_candles_filtered(payload: PairCandlesRequest, rpc: RPC = Depends(get_rpc)): + # Advanced pair_candles endpoint with column filtering + return rpc._rpc_analysed_dataframe( + payload.pair, payload.timeframe, payload.limit, payload.columns + ) diff --git a/freqtrade/rpc/api_server/api_v1.py b/freqtrade/rpc/api_server/api_v1.py index 39c8e9821..b15942e2e 100644 --- a/freqtrade/rpc/api_server/api_v1.py +++ b/freqtrade/rpc/api_server/api_v1.py @@ -6,53 +6,19 @@ from fastapi import APIRouter, Depends, Query from fastapi.exceptions import HTTPException from freqtrade import __version__ -from freqtrade.data.history import get_datahandler -from freqtrade.enums import CandleType, RunMode, State, TradingMode -from freqtrade.exceptions import OperationalException +from freqtrade.enums import RunMode, State from freqtrade.rpc import RPC from freqtrade.rpc.api_server.api_pairlists import handleExchangePayload from freqtrade.rpc.api_server.api_schemas import ( - AvailablePairs, - Balances, - BlacklistPayload, - BlacklistResponse, - Count, - DailyWeeklyMonthly, - DeleteLockRequest, - DeleteTrade, - Entry, - ExchangeListResponse, - Exit, - ForceEnterPayload, - ForceEnterResponse, - ForceExitPayload, - FreqAIModelListResponse, Health, - HyperoptLossListResponse, - ListCustomData, - Locks, - LocksPayload, Logs, MarketRequest, MarketResponse, - MixTag, - OpenTradeSchema, - PairCandlesRequest, - PairHistory, - PerformanceEntry, Ping, PlotConfig, - Profit, - ProfitAll, - ResultMsg, ShowConfig, - Stats, - StatusMsg, - StrategyListResponse, - StrategyResponse, SysInfo, Version, - WhitelistResponse, ) from freqtrade.rpc.api_server.deps import get_config, get_exchange, get_rpc, get_rpc_optional from freqtrade.rpc.rpc import RPCException @@ -101,191 +67,19 @@ router_public = APIRouter() router = APIRouter() -@router_public.get("/ping", response_model=Ping) +@router_public.get("/ping", response_model=Ping, tags=["Info"]) def ping(): """simple ping""" return {"status": "pong"} -@router.get("/version", response_model=Version, tags=["info"]) +@router.get("/version", response_model=Version, tags=["Info"]) def version(): """Bot Version info""" return {"version": __version__} -@router.get("/balance", response_model=Balances, tags=["info"]) -def balance(rpc: RPC = Depends(get_rpc), config=Depends(get_config)): - """Account Balances""" - return rpc._rpc_balance( - config["stake_currency"], - config.get("fiat_display_currency", ""), - ) - - -@router.get("/count", response_model=Count, tags=["info"]) -def count(rpc: RPC = Depends(get_rpc)): - return rpc._rpc_count() - - -@router.get("/entries", response_model=list[Entry], tags=["info"]) -def entries(pair: str | None = None, rpc: RPC = Depends(get_rpc)): - return rpc._rpc_enter_tag_performance(pair) - - -@router.get("/exits", response_model=list[Exit], tags=["info"]) -def exits(pair: str | None = None, rpc: RPC = Depends(get_rpc)): - return rpc._rpc_exit_reason_performance(pair) - - -@router.get("/mix_tags", response_model=list[MixTag], tags=["info"]) -def mix_tags(pair: str | None = None, rpc: RPC = Depends(get_rpc)): - return rpc._rpc_mix_tag_performance(pair) - - -@router.get("/performance", response_model=list[PerformanceEntry], tags=["info"]) -def performance(rpc: RPC = Depends(get_rpc)): - return rpc._rpc_performance() - - -@router.get("/profit", response_model=Profit, tags=["info"]) -def profit(rpc: RPC = Depends(get_rpc), config=Depends(get_config)): - return rpc._rpc_trade_statistics(config["stake_currency"], config.get("fiat_display_currency")) - - -@router.get("/profit_all", response_model=ProfitAll, tags=["info"]) -def profit_all(rpc: RPC = Depends(get_rpc), config=Depends(get_config)): - response = { - "all": rpc._rpc_trade_statistics( - config["stake_currency"], config.get("fiat_display_currency") - ), - } - if config.get("trading_mode", TradingMode.SPOT) != TradingMode.SPOT: - response["long"] = rpc._rpc_trade_statistics( - config["stake_currency"], config.get("fiat_display_currency"), direction="long" - ) - response["short"] = rpc._rpc_trade_statistics( - config["stake_currency"], config.get("fiat_display_currency"), direction="short" - ) - - return response - - -@router.get("/stats", response_model=Stats, tags=["info"]) -def stats(rpc: RPC = Depends(get_rpc)): - return rpc._rpc_stats() - - -@router.get("/daily", response_model=DailyWeeklyMonthly, tags=["info"]) -def daily( - timescale: int = Query(7, ge=1, description="Number of days to fetch data for"), - rpc: RPC = Depends(get_rpc), - config=Depends(get_config), -): - return rpc._rpc_timeunit_profit( - timescale, config["stake_currency"], config.get("fiat_display_currency", "") - ) - - -@router.get("/weekly", response_model=DailyWeeklyMonthly, tags=["info"]) -def weekly( - timescale: int = Query(4, ge=1, description="Number of weeks to fetch data for"), - rpc: RPC = Depends(get_rpc), - config=Depends(get_config), -): - return rpc._rpc_timeunit_profit( - timescale, config["stake_currency"], config.get("fiat_display_currency", ""), "weeks" - ) - - -@router.get("/monthly", response_model=DailyWeeklyMonthly, tags=["info"]) -def monthly( - timescale: int = Query(3, ge=1, description="Number of months to fetch data for"), - rpc: RPC = Depends(get_rpc), - config=Depends(get_config), -): - return rpc._rpc_timeunit_profit( - timescale, config["stake_currency"], config.get("fiat_display_currency", ""), "months" - ) - - -@router.get("/status", response_model=list[OpenTradeSchema], tags=["info"]) -def status(rpc: RPC = Depends(get_rpc)): - try: - return rpc._rpc_trade_status() - except RPCException: - return [] - - -# Using the responsemodel here will cause a ~100% increase in response time (from 1s to 2s) -# on big databases. Correct response model: response_model=TradeResponse, -@router.get("/trades", tags=["info", "trading"]) -def trades( - limit: int = Query(500, ge=1, description="Maximum number of different trades to return data"), - offset: int = Query(0, ge=0, description="Number of trades to skip for pagination"), - order_by_id: bool = Query( - True, description="Sort trades by id (default: True). If False, sorts by latest timestamp" - ), - rpc: RPC = Depends(get_rpc), -): - return rpc._rpc_trade_history(limit, offset=offset, order_by_id=order_by_id) - - -@router.get("/trade/{tradeid}", response_model=OpenTradeSchema, tags=["info", "trading"]) -def trade(tradeid: int = 0, rpc: RPC = Depends(get_rpc)): - try: - return rpc._rpc_trade_status([tradeid])[0] - except (RPCException, KeyError): - raise HTTPException(status_code=404, detail="Trade not found.") - - -@router.delete("/trades/{tradeid}", response_model=DeleteTrade, tags=["info", "trading"]) -def trades_delete(tradeid: int, rpc: RPC = Depends(get_rpc)): - return rpc._rpc_delete(tradeid) - - -@router.delete("/trades/{tradeid}/open-order", response_model=OpenTradeSchema, tags=["trading"]) -def trade_cancel_open_order(tradeid: int, rpc: RPC = Depends(get_rpc)): - rpc._rpc_cancel_open_order(tradeid) - return rpc._rpc_trade_status([tradeid])[0] - - -@router.post("/trades/{tradeid}/reload", response_model=OpenTradeSchema, tags=["trading"]) -def trade_reload(tradeid: int, rpc: RPC = Depends(get_rpc)): - rpc._rpc_reload_trade_from_exchange(tradeid) - return rpc._rpc_trade_status([tradeid])[0] - - -@router.get("/trades/open/custom-data", response_model=list[ListCustomData], tags=["trading"]) -def list_open_trades_custom_data( - key: str | None = Query(None, description="Optional key to filter data"), - limit: int = Query(100, ge=1, description="Maximum number of different trades to return data"), - offset: int = Query(0, ge=0, description="Number of trades to skip for pagination"), - rpc: RPC = Depends(get_rpc), -): - """ - Fetch custom data for all open trades. - If a key is provided, it will be used to filter data accordingly. - Pagination is implemented via the `limit` and `offset` parameters. - """ - try: - return rpc._rpc_list_custom_data(key=key, limit=limit, offset=offset) - except RPCException as e: - raise HTTPException(status_code=404, detail=str(e)) - - -@router.get("/trades/{trade_id}/custom-data", response_model=list[ListCustomData], tags=["trading"]) -def list_custom_data(trade_id: int, key: str | None = Query(None), rpc: RPC = Depends(get_rpc)): - """ - Fetch custom data for a specific trade. - If a key is provided, it will be used to filter data accordingly. - """ - try: - return rpc._rpc_list_custom_data(trade_id, key=key) - except RPCException as e: - raise HTTPException(status_code=404, detail=str(e)) - - -@router.get("/show_config", response_model=ShowConfig, tags=["info"]) +@router.get("/show_config", response_model=ShowConfig, tags=["Info"]) def show_config(rpc: RPC | None = Depends(get_rpc_optional), config=Depends(get_config)): state: State | str = "" strategy_version = None @@ -297,125 +91,12 @@ def show_config(rpc: RPC | None = Depends(get_rpc_optional), config=Depends(get_ return resp -# /forcebuy is deprecated with short addition. use /forceentry instead -@router.post("/forceenter", response_model=ForceEnterResponse, tags=["trading"]) -@router.post("/forcebuy", response_model=ForceEnterResponse, tags=["trading"]) -def force_entry(payload: ForceEnterPayload, rpc: RPC = Depends(get_rpc)): - ordertype = payload.ordertype.value if payload.ordertype else None - - trade = rpc._rpc_force_entry( - payload.pair, - payload.price, - order_side=payload.side, - order_type=ordertype, - stake_amount=payload.stakeamount, - enter_tag=payload.entry_tag or "force_entry", - leverage=payload.leverage, - ) - - if trade: - return ForceEnterResponse.model_validate(trade.to_json()) - else: - return ForceEnterResponse.model_validate( - {"status": f"Error entering {payload.side} trade for pair {payload.pair}."} - ) - - -# /forcesell is deprecated with short addition. use /forceexit instead -@router.post("/forceexit", response_model=ResultMsg, tags=["trading"]) -@router.post("/forcesell", response_model=ResultMsg, tags=["trading"]) -def forceexit(payload: ForceExitPayload, rpc: RPC = Depends(get_rpc)): - ordertype = payload.ordertype.value if payload.ordertype else None - return rpc._rpc_force_exit( - str(payload.tradeid), ordertype, amount=payload.amount, price=payload.price - ) - - -@router.get("/blacklist", response_model=BlacklistResponse, tags=["info", "pairlist"]) -def blacklist(rpc: RPC = Depends(get_rpc)): - return rpc._rpc_blacklist() - - -@router.post("/blacklist", response_model=BlacklistResponse, tags=["info", "pairlist"]) -def blacklist_post(payload: BlacklistPayload, rpc: RPC = Depends(get_rpc)): - return rpc._rpc_blacklist(payload.blacklist) - - -@router.delete("/blacklist", response_model=BlacklistResponse, tags=["info", "pairlist"]) -def blacklist_delete(pairs_to_delete: list[str] = Query([]), rpc: RPC = Depends(get_rpc)): - """Provide a list of pairs to delete from the blacklist""" - - return rpc._rpc_blacklist_delete(pairs_to_delete) - - -@router.get("/whitelist", response_model=WhitelistResponse, tags=["info", "pairlist"]) -def whitelist(rpc: RPC = Depends(get_rpc)): - return rpc._rpc_whitelist() - - -@router.get("/locks", response_model=Locks, tags=["info", "locks"]) -def locks(rpc: RPC = Depends(get_rpc)): - return rpc._rpc_locks() - - -@router.delete("/locks/{lockid}", response_model=Locks, tags=["info", "locks"]) -def delete_lock(lockid: int, rpc: RPC = Depends(get_rpc)): - return rpc._rpc_delete_lock(lockid=lockid) - - -@router.post("/locks/delete", response_model=Locks, tags=["info", "locks"]) -def delete_lock_pair(payload: DeleteLockRequest, rpc: RPC = Depends(get_rpc)): - return rpc._rpc_delete_lock(lockid=payload.lockid, pair=payload.pair) - - -@router.post("/locks", response_model=Locks, tags=["info", "locks"]) -def add_locks(payload: list[LocksPayload], rpc: RPC = Depends(get_rpc)): - for lock in payload: - rpc._rpc_add_lock(lock.pair, lock.until, lock.reason, lock.side) - return rpc._rpc_locks() - - -@router.get("/logs", response_model=Logs, tags=["info"]) +@router.get("/logs", response_model=Logs, tags=["Info"]) def logs(limit: int | None = None): return RPC._rpc_get_logs(limit) -@router.post("/start", response_model=StatusMsg, tags=["botcontrol"]) -def start(rpc: RPC = Depends(get_rpc)): - return rpc._rpc_start() - - -@router.post("/stop", response_model=StatusMsg, tags=["botcontrol"]) -def stop(rpc: RPC = Depends(get_rpc)): - return rpc._rpc_stop() - - -@router.post("/pause", response_model=StatusMsg, tags=["botcontrol"]) -@router.post("/stopentry", response_model=StatusMsg, tags=["botcontrol"]) -@router.post("/stopbuy", response_model=StatusMsg, tags=["botcontrol"]) -def pause(rpc: RPC = Depends(get_rpc)): - return rpc._rpc_pause() - - -@router.post("/reload_config", response_model=StatusMsg, tags=["botcontrol"]) -def reload_config(rpc: RPC = Depends(get_rpc)): - return rpc._rpc_reload_config() - - -@router.get("/pair_candles", response_model=PairHistory, tags=["candle data"]) -def pair_candles(pair: str, timeframe: str, limit: int | None = None, rpc: RPC = Depends(get_rpc)): - return rpc._rpc_analysed_dataframe(pair, timeframe, limit, None) - - -@router.post("/pair_candles", response_model=PairHistory, tags=["candle data"]) -def pair_candles_filtered(payload: PairCandlesRequest, rpc: RPC = Depends(get_rpc)): - # Advanced pair_candles endpoint with column filtering - return rpc._rpc_analysed_dataframe( - payload.pair, payload.timeframe, payload.limit, payload.columns - ) - - -@router.get("/plot_config", response_model=PlotConfig, tags=["candle data"]) +@router.get("/plot_config", response_model=PlotConfig, tags=["Candle data"]) def plot_config( strategy: str | None = None, config=Depends(get_config), @@ -434,119 +115,7 @@ def plot_config( raise HTTPException(status_code=502, detail=str(e)) -@router.get("/strategies", response_model=StrategyListResponse, tags=["strategy"]) -def list_strategies(config=Depends(get_config)): - from freqtrade.resolvers.strategy_resolver import StrategyResolver - - strategies = StrategyResolver.search_all_objects( - config, False, config.get("recursive_strategy_search", False) - ) - strategies = sorted(strategies, key=lambda x: x["name"]) - - return {"strategies": [x["name"] for x in strategies]} - - -@router.get("/strategy/{strategy}", response_model=StrategyResponse, tags=["strategy"]) -def get_strategy(strategy: str, config=Depends(get_config)): - if ":" in strategy: - raise HTTPException(status_code=500, detail="base64 encoded strategies are not allowed.") - - config_ = deepcopy(config) - from freqtrade.resolvers.strategy_resolver import StrategyResolver - - try: - strategy_obj = StrategyResolver._load_strategy( - strategy, config_, extra_dir=config_.get("strategy_path") - ) - except OperationalException: - raise HTTPException(status_code=404, detail="Strategy not found") - except Exception as e: - raise HTTPException(status_code=502, detail=str(e)) - return { - "strategy": strategy_obj.get_strategy_name(), - "code": strategy_obj.__source__, - "timeframe": getattr(strategy_obj, "timeframe", None), - } - - -@router.get("/exchanges", response_model=ExchangeListResponse, tags=[]) -def list_exchanges(config=Depends(get_config)): - from freqtrade.exchange import list_available_exchanges - - exchanges = list_available_exchanges(config) - return { - "exchanges": exchanges, - } - - -@router.get( - "/hyperoptloss", response_model=HyperoptLossListResponse, tags=["hyperopt", "webserver"] -) -def list_hyperoptloss( - config=Depends(get_config), -): - import textwrap - - from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver - - loss_functions = HyperOptLossResolver.search_all_objects(config, False) - loss_functions = sorted(loss_functions, key=lambda x: x["name"]) - - return { - "loss_functions": [ - { - "name": x["name"], - "description": textwrap.dedent((x["class"].__doc__ or "").strip()), - } - for x in loss_functions - ] - } - - -@router.get("/freqaimodels", response_model=FreqAIModelListResponse, tags=["freqai"]) -def list_freqaimodels(config=Depends(get_config)): - from freqtrade.resolvers.freqaimodel_resolver import FreqaiModelResolver - - models = FreqaiModelResolver.search_all_objects(config, False) - models = sorted(models, key=lambda x: x["name"]) - - return {"freqaimodels": [x["name"] for x in models]} - - -@router.get("/available_pairs", response_model=AvailablePairs, tags=["candle data"]) -def list_available_pairs( - timeframe: str | None = None, - stake_currency: str | None = None, - candletype: CandleType | None = None, - config=Depends(get_config), -): - dh = get_datahandler(config["datadir"], config.get("dataformat_ohlcv")) - trading_mode: TradingMode = config.get("trading_mode", TradingMode.SPOT) - pair_interval = dh.ohlcv_get_available_data(config["datadir"], trading_mode) - - if timeframe: - pair_interval = [pair for pair in pair_interval if pair[1] == timeframe] - if stake_currency: - pair_interval = [pair for pair in pair_interval if pair[0].endswith(stake_currency)] - if candletype: - pair_interval = [pair for pair in pair_interval if pair[2] == candletype] - else: - candle_type = CandleType.get_default(trading_mode) - pair_interval = [pair for pair in pair_interval if pair[2] == candle_type] - - pair_interval = sorted(pair_interval, key=lambda x: x[0]) - - pairs = list({x[0] for x in pair_interval}) - pairs.sort() - result = { - "length": len(pairs), - "pairs": pairs, - "pair_interval": pair_interval, - } - return result - - -@router.get("/markets", response_model=MarketResponse, tags=["candle data", "webserver"]) +@router.get("/markets", response_model=MarketResponse, tags=["Candle data"]) def markets( query: Annotated[MarketRequest, Query()], config=Depends(get_config), @@ -569,11 +138,11 @@ def markets( } -@router.get("/sysinfo", response_model=SysInfo, tags=["info"]) +@router.get("/sysinfo", response_model=SysInfo, tags=["Info"]) def sysinfo(): return RPC._rpc_sysinfo() -@router.get("/health", response_model=Health, tags=["info"]) +@router.get("/health", response_model=Health, tags=["Info"]) def health(rpc: RPC = Depends(get_rpc)): return rpc.health() diff --git a/freqtrade/rpc/api_server/api_webserver.py b/freqtrade/rpc/api_server/api_webserver.py new file mode 100644 index 000000000..7b2cce0fc --- /dev/null +++ b/freqtrade/rpc/api_server/api_webserver.py @@ -0,0 +1,136 @@ +import logging +from copy import deepcopy + +from fastapi import APIRouter, Depends +from fastapi.exceptions import HTTPException + +from freqtrade.data.history.datahandlers import get_datahandler +from freqtrade.enums import CandleType, TradingMode +from freqtrade.exceptions import OperationalException +from freqtrade.rpc.api_server.api_schemas import ( + AvailablePairs, + ExchangeListResponse, + FreqAIModelListResponse, + HyperoptLossListResponse, + StrategyListResponse, + StrategyResponse, +) +from freqtrade.rpc.api_server.deps import get_config + + +logger = logging.getLogger(__name__) + +# Private API, protected by authentication and webserver_mode dependency +router = APIRouter() + + +@router.get("/strategies", response_model=StrategyListResponse, tags=["Strategy"]) +def list_strategies(config=Depends(get_config)): + from freqtrade.resolvers.strategy_resolver import StrategyResolver + + strategies = StrategyResolver.search_all_objects( + config, False, config.get("recursive_strategy_search", False) + ) + strategies = sorted(strategies, key=lambda x: x["name"]) + + return {"strategies": [x["name"] for x in strategies]} + + +@router.get("/strategy/{strategy}", response_model=StrategyResponse, tags=["Strategy"]) +def get_strategy(strategy: str, config=Depends(get_config)): + if ":" in strategy: + raise HTTPException(status_code=500, detail="base64 encoded strategies are not allowed.") + + config_ = deepcopy(config) + from freqtrade.resolvers.strategy_resolver import StrategyResolver + + try: + strategy_obj = StrategyResolver._load_strategy( + strategy, config_, extra_dir=config_.get("strategy_path") + ) + except OperationalException: + raise HTTPException(status_code=404, detail="Strategy not found") + except Exception as e: + raise HTTPException(status_code=502, detail=str(e)) + return { + "strategy": strategy_obj.get_strategy_name(), + "code": strategy_obj.__source__, + "timeframe": getattr(strategy_obj, "timeframe", None), + } + + +@router.get("/exchanges", response_model=ExchangeListResponse, tags=[]) +def list_exchanges(config=Depends(get_config)): + from freqtrade.exchange import list_available_exchanges + + exchanges = list_available_exchanges(config) + return { + "exchanges": exchanges, + } + + +@router.get("/hyperoptloss", response_model=HyperoptLossListResponse, tags=["Hyperopt"]) +def list_hyperoptloss( + config=Depends(get_config), +): + import textwrap + + from freqtrade.resolvers.hyperopt_resolver import HyperOptLossResolver + + loss_functions = HyperOptLossResolver.search_all_objects(config, False) + loss_functions = sorted(loss_functions, key=lambda x: x["name"]) + + return { + "loss_functions": [ + { + "name": x["name"], + "description": textwrap.dedent((x["class"].__doc__ or "").strip()), + } + for x in loss_functions + ] + } + + +@router.get("/freqaimodels", response_model=FreqAIModelListResponse, tags=["FreqAI"]) +def list_freqaimodels(config=Depends(get_config)): + from freqtrade.resolvers.freqaimodel_resolver import FreqaiModelResolver + + models = FreqaiModelResolver.search_all_objects(config, False) + models = sorted(models, key=lambda x: x["name"]) + + return {"freqaimodels": [x["name"] for x in models]} + + +@router.get( + "/available_pairs", response_model=AvailablePairs, tags=["Candle data", "Download-data"] +) +def list_available_pairs( + timeframe: str | None = None, + stake_currency: str | None = None, + candletype: CandleType | None = None, + config=Depends(get_config), +): + dh = get_datahandler(config["datadir"], config.get("dataformat_ohlcv")) + trading_mode: TradingMode = config.get("trading_mode", TradingMode.SPOT) + pair_interval = dh.ohlcv_get_available_data(config["datadir"], trading_mode) + + if timeframe: + pair_interval = [pair for pair in pair_interval if pair[1] == timeframe] + if stake_currency: + pair_interval = [pair for pair in pair_interval if pair[0].endswith(stake_currency)] + if candletype: + pair_interval = [pair for pair in pair_interval if pair[2] == candletype] + else: + candle_type = CandleType.get_default(trading_mode) + pair_interval = [pair for pair in pair_interval if pair[2] == candle_type] + + pair_interval = sorted(pair_interval, key=lambda x: x[0]) + + pairs = list({x[0] for x in pair_interval}) + pairs.sort() + result = { + "length": len(pairs), + "pairs": pairs, + "pair_interval": pair_interval, + } + return result diff --git a/freqtrade/rpc/api_server/deps.py b/freqtrade/rpc/api_server/deps.py index 74ee82a8c..0991366ce 100644 --- a/freqtrade/rpc/api_server/deps.py +++ b/freqtrade/rpc/api_server/deps.py @@ -5,7 +5,7 @@ from uuid import uuid4 from fastapi import Depends, HTTPException from freqtrade.constants import Config -from freqtrade.enums import RunMode +from freqtrade.enums import TRADE_MODES, RunMode from freqtrade.persistence import Trade from freqtrade.persistence.models import _request_id_ctx_var from freqtrade.rpc.api_server.webserver_bgwork import ApiBG @@ -69,3 +69,9 @@ def is_webserver_mode(config=Depends(get_config)): if config["runmode"] != RunMode.WEBSERVER: raise HTTPException(status_code=503, detail="Bot is not in the correct state.") return None + + +def is_trading_mode(config=Depends(get_config)): + if config["runmode"] not in TRADE_MODES: + raise HTTPException(status_code=503, detail="Bot is not in the correct state.") + return None diff --git a/freqtrade/rpc/api_server/webserver.py b/freqtrade/rpc/api_server/webserver.py index aa81684fc..7d3d9543d 100644 --- a/freqtrade/rpc/api_server/webserver.py +++ b/freqtrade/rpc/api_server/webserver.py @@ -21,6 +21,74 @@ from freqtrade.rpc.rpc_types import RPCSendMsg logger = logging.getLogger(__name__) +_TRADE_MODE_ONLY = "*only available in trading mode*" +_WEBSERVER_MODE_ONLY = "*only available in webserver mode*" + +_OPENAPI_TAGS = [ + {"name": "Auth", "description": "Authentication endpoints."}, + { + "name": "Info", + "description": ("Information endpoints providing general information about the bot."), + }, + { + "name": "Bot-control", + "description": (f"Bot control endpoints to start/stop trading - {_TRADE_MODE_ONLY}."), + }, + { + "name": "Pairlist", + "description": f"Pairlist management - {_TRADE_MODE_ONLY}.", + }, + { + "name": "Locks", + "description": f"Pair lock management - {_TRADE_MODE_ONLY}.", + }, + { + "name": "Candle data", + "description": "Candle / OHLCV data.", + }, + { + "name": "Trading-info", + "description": f"Trading related information - {_TRADE_MODE_ONLY}.", + }, + { + "name": "Trades", + "description": f"Trade management - {_TRADE_MODE_ONLY}.", + }, + { + "name": "Strategy", + "description": f"List and retrieve strategies - {_WEBSERVER_MODE_ONLY}.", + }, + { + "name": "Hyperopt", + "description": f"Retrieve hyperopt loss functions - {_WEBSERVER_MODE_ONLY}.", + }, + { + "name": "FreqAI", + "description": f"FreqAI related endpoints - {_WEBSERVER_MODE_ONLY}.", + }, + { + "name": "Download-data", + "description": f"Download data endpoints - {_WEBSERVER_MODE_ONLY}.", + }, + { + "name": "Backtest", + "description": f"Backtest endpoints - {_WEBSERVER_MODE_ONLY}.", + }, + { + "name": "Pairlists", + "description": f"Pairlist endpoints - {_WEBSERVER_MODE_ONLY}.", + }, + { + "name": "Trading", + "description": f"Trading related endpoints - {_TRADE_MODE_ONLY}.", + }, + { + "name": "Webserver", + "description": (f"Webserver related endpoints - {_WEBSERVER_MODE_ONLY}."), + }, +] + + class FTJSONResponse(JSONResponse): media_type = "application/json" @@ -68,6 +136,7 @@ class ApiServer(RPCHandler): docs_url="/docs" if api_config.get("enable_openapi", False) else None, redoc_url=None, default_response_class=FTJSONResponse, + openapi_tags=_OPENAPI_TAGS, ) self.configure_app(self.app, self._config) self.start_api() @@ -122,28 +191,44 @@ class ApiServer(RPCHandler): from freqtrade.rpc.api_server.api_download_data import router as api_download_data from freqtrade.rpc.api_server.api_pair_history import router as api_pair_history from freqtrade.rpc.api_server.api_pairlists import router as api_pairlists + from freqtrade.rpc.api_server.api_trading import router as api_trading from freqtrade.rpc.api_server.api_v1 import router as api_v1 from freqtrade.rpc.api_server.api_v1 import router_public as api_v1_public + from freqtrade.rpc.api_server.api_webserver import router as api_webserver from freqtrade.rpc.api_server.api_ws import router as ws_router - from freqtrade.rpc.api_server.deps import is_webserver_mode + from freqtrade.rpc.api_server.deps import is_trading_mode, is_webserver_mode from freqtrade.rpc.api_server.web_ui import router_ui app.include_router(api_v1_public, prefix="/api/v1") - app.include_router(router_login, prefix="/api/v1", tags=["auth"]) + app.include_router(router_login, prefix="/api/v1", tags=["Auth"]) app.include_router( api_v1, prefix="/api/v1", dependencies=[Depends(http_basic_or_jwt_token)], ) + app.include_router( + api_trading, + prefix="/api/v1", + tags=["Trading"], + dependencies=[Depends(http_basic_or_jwt_token), Depends(is_trading_mode)], + ) + app.include_router( + api_webserver, + prefix="/api/v1", + tags=["Webserver"], + dependencies=[Depends(http_basic_or_jwt_token), Depends(is_webserver_mode)], + ) app.include_router( api_backtest, prefix="/api/v1", + tags=["Backtest"], dependencies=[Depends(http_basic_or_jwt_token), Depends(is_webserver_mode)], ) app.include_router( api_bg_tasks, prefix="/api/v1", + tags=["Webserver"], dependencies=[Depends(http_basic_or_jwt_token), Depends(is_webserver_mode)], ) app.include_router( @@ -154,11 +239,13 @@ class ApiServer(RPCHandler): app.include_router( api_pairlists, prefix="/api/v1", + tags=["Webserver", "Pairlists"], dependencies=[Depends(http_basic_or_jwt_token), Depends(is_webserver_mode)], ) app.include_router( api_download_data, prefix="/api/v1", + tags=["Download-data", "Webserver"], dependencies=[Depends(http_basic_or_jwt_token), Depends(is_webserver_mode)], ) app.include_router(ws_router, prefix="/api/v1") diff --git a/freqtrade/rpc/fiat_convert.py b/freqtrade/rpc/fiat_convert.py index 16d64dd50..4ae5842d5 100644 --- a/freqtrade/rpc/fiat_convert.py +++ b/freqtrade/rpc/fiat_convert.py @@ -5,7 +5,6 @@ e.g BTC to USD import logging from datetime import datetime -from typing import Any from requests.exceptions import RequestException @@ -13,6 +12,7 @@ from freqtrade.constants import SUPPORTED_FIAT, Config from freqtrade.mixins.logging_mixin import LoggingMixin from freqtrade.util import FtTTLCache from freqtrade.util.coin_gecko import FtCoinGeckoApi +from freqtrade.util.singleton import SingletonMeta logger = logging.getLogger(__name__) @@ -32,26 +32,16 @@ coingecko_mapping = { } -class CryptoToFiatConverter(LoggingMixin): +class CryptoToFiatConverter(LoggingMixin, metaclass=SingletonMeta): """ Main class to initiate Crypto to FIAT. This object contains a list of pair Crypto, FIAT This object is also a Singleton """ - __instance = None - _coinlistings: list[dict] = [] _backoff: float = 0.0 - def __new__(cls, *args: Any, **kwargs: Any) -> Any: - """ - Singleton pattern to ensure only one instance is created. - """ - if not cls.__instance: - cls.__instance = super().__new__(cls) - return cls.__instance - def __init__(self, config: Config) -> None: # Timeout: 6h self._pair_price: FtTTLCache = FtTTLCache(maxsize=500, ttl=6 * 60 * 60) diff --git a/freqtrade/rpc/rpc.py b/freqtrade/rpc/rpc.py index 81bb52ce5..38d4eb49f 100644 --- a/freqtrade/rpc/rpc.py +++ b/freqtrade/rpc/rpc.py @@ -19,7 +19,16 @@ from freqtrade import __version__ from freqtrade.configuration.timerange import TimeRange from freqtrade.constants import CANCEL_REASON, DEFAULT_DATAFRAME_COLUMNS, Config from freqtrade.data.history import load_data -from freqtrade.data.metrics import DrawDownResult, calculate_expectancy, calculate_max_drawdown +from freqtrade.data.metrics import ( + DrawDownResult, + calculate_cagr, + calculate_calmar, + calculate_expectancy, + calculate_max_drawdown, + calculate_sharpe, + calculate_sortino, + calculate_sqn, +) from freqtrade.enums import ( CandleType, ExitCheckTuple, @@ -689,6 +698,34 @@ class RPC: last_date = trades[-1].open_date_utc if trades else None num = float(len(durations) or 1) bot_start = KeyValueStore.get_datetime_value("bot_start_time") + + sharpe = calculate_sharpe( + trades=trades_df, + min_date=first_date, + max_date=last_date, + starting_balance=starting_balance, + ) + sortino = calculate_sortino( + trades=trades_df, + min_date=first_date, + max_date=last_date, + starting_balance=starting_balance, + ) + sqn = calculate_sqn(trades=trades_df, starting_balance=starting_balance) + calmar = calculate_calmar( + trades=trades_df, + min_date=first_date, + max_date=last_date, + starting_balance=starting_balance, + ) + current_balance = self._freqtrade.wallets.get_total_stake_amount() + days_passed = max(1, (last_date - first_date).days) if first_date and last_date else 1 + cagr = calculate_cagr( + starting_balance=starting_balance, + final_balance=current_balance, + days_passed=days_passed, + ) + return { "profit_closed_coin": profit_closed_coin_sum, "profit_closed_percent_mean": round(profit_closed_ratio_mean * 100, 2), @@ -725,6 +762,11 @@ class RPC: "winrate": winrate, "expectancy": expectancy, "expectancy_ratio": expectancy_ratio, + "sharpe": sharpe, + "sortino": sortino, + "sqn": sqn, + "calmar": calmar, + "cagr": cagr, "max_drawdown": drawdown.relative_account_drawdown, "max_drawdown_abs": drawdown.drawdown_abs, "max_drawdown_start": format_date(drawdown.high_date), @@ -804,12 +846,9 @@ class RPC: if is_stake_currency: trade_amount = self._freqtrade.wallets.get_available_stake_amount() - try: - est_stake, est_stake_bot = self.__balance_get_est_stake( - coin, stake_currency, trade_amount, balance - ) - except ValueError: - continue + est_stake, est_stake_bot = self.__balance_get_est_stake( + coin, stake_currency, trade_amount, balance + ) total += est_stake @@ -832,10 +871,33 @@ class RPC: } ) symbol: str - position: PositionWallet - for symbol, position in self._freqtrade.wallets.get_all_positions().items(): - total += position.collateral - total_bot += position.collateral + pos: PositionWallet + 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: + try: + rate = self._freqtrade.exchange.get_conversion_rate(pos_base, stake_currency) + if rate: + # For a leveraged position, equity (what we want as est_stake) is: + # equity = collateral + unlevered PnL + # For longs: unlevered PnL = current_value - open_value + # est_stake = rate * pos.position - pos.collateral * (pos.leverage - 1) + # For shorts: unlevered PnL = open_value - current_value + # est_stake = collateral + (open_value - current_value) + # = collateral + (pos.collateral * pos.leverage) + # - rate * pos.position + if pos.side == "long": + est_stake = rate * pos.position - pos.collateral * (pos.leverage - 1) + else: + est_stake = pos.collateral * (1 + pos.leverage) - rate * pos.position + except (ExchangeError, PricingError) as e: + logger.warning(f"Error {e} getting rate for futures {symbol} / {pos_base}") + pass + + # Add the estimated stake (collateral + unlevered PnL) to totals + total += est_stake + total_bot += est_stake currencies.append( { @@ -843,12 +905,12 @@ class RPC: "free": 0, "balance": 0, "used": 0, - "position": position.position, - "est_stake": position.collateral, - "est_stake_bot": position.collateral, + "position": pos.position, + "est_stake": est_stake, + "est_stake_bot": est_stake, "stake": stake_currency, - "side": position.side, - "is_bot_managed": True, + "side": pos.side, + "is_bot_managed": pos_base in open_assets, "is_position": True, } ) diff --git a/freqtrade/rpc/telegram.py b/freqtrade/rpc/telegram.py index 151d6daec..03486e3d3 100644 --- a/freqtrade/rpc/telegram.py +++ b/freqtrade/rpc/telegram.py @@ -1285,7 +1285,7 @@ class Telegram(RPCHandler): if curr["is_position"]: curr_output = ( f"*{curr['currency']}:*\n" - f"\t`{curr['side']}: {curr['position']:.8f}`\n" + f"\t`{curr['side']}: {round_value(curr['position'], 8)}`\n" f"\t`Est. {curr['stake']}: " f"{fmt_coin(curr['est_stake'], curr['stake'], False)}`\n" ) @@ -1296,10 +1296,10 @@ class Telegram(RPCHandler): curr_output = ( f"*{curr['currency']}:*\n" - f"\t`Available: {curr['free']:.8f}`\n" - f"\t`Balance: {curr['balance']:.8f}`\n" - f"\t`Pending: {curr['used']:.8f}`\n" - f"\t`Bot Owned: {curr['bot_owned']:.8f}`\n" + f"\t`Available: {fmt_coin(curr['free'], curr['currency'], False)}`\n" + f"\t`Balance: {fmt_coin(curr['balance'], curr['currency'], False)}`\n" + f"\t`Pending: {fmt_coin(curr['used'], curr['currency'], False)}`\n" + f"\t`Bot Owned: {fmt_coin(curr['bot_owned'], curr['currency'], False)}`\n" f"\t`Est. {curr['stake']}: {est_stake}`\n" ) diff --git a/freqtrade/strategy/strategyupdater.py b/freqtrade/strategy/strategyupdater.py index 5377865d7..965f6bcd6 100644 --- a/freqtrade/strategy/strategyupdater.py +++ b/freqtrade/strategy/strategyupdater.py @@ -66,8 +66,7 @@ class StrategyUpdater: target_file = Path.joinpath(strategies_backup_folder, strategy_obj["location_rel"]) # read the file - with Path(source_file).open("r") as f: - old_code = f.read() + old_code = Path(source_file).read_text(encoding="utf-8") if not strategies_backup_folder.is_dir(): Path(strategies_backup_folder).mkdir(parents=True, exist_ok=True) @@ -80,8 +79,7 @@ class StrategyUpdater: # update the code new_code = self.update_code(old_code) # write the modified code to the destination folder - with Path(source_file).open("w") as f: - f.write(new_code) + Path(source_file).write_text(new_code, encoding="utf-8") # define the function to update the code def update_code(self, code): diff --git a/freqtrade/util/migrations/__init__.py b/freqtrade/util/migrations/__init__.py index 50f7dff01..20aafb04b 100644 --- a/freqtrade/util/migrations/__init__.py +++ b/freqtrade/util/migrations/__init__.py @@ -1,8 +1,4 @@ from freqtrade.exchange import Exchange -from freqtrade.util.migrations.binance_mig import ( - migrate_binance_futures_data, - migrate_binance_futures_names, -) from freqtrade.util.migrations.funding_rate_mig import migrate_funding_fee_timeframe @@ -10,7 +6,6 @@ def migrate_data(config, exchange: Exchange | None = None) -> None: """ Migrate persisted data from old formats to new formats """ - migrate_binance_futures_data(config) migrate_funding_fee_timeframe(config, exchange) @@ -20,4 +15,5 @@ def migrate_live_content(config, exchange: Exchange | None = None) -> None: Migrate database content from old formats to new formats Used for dry/live mode. """ - migrate_binance_futures_names(config) + # Currently not used + pass diff --git a/freqtrade/util/migrations/binance_mig.py b/freqtrade/util/migrations/binance_mig.py deleted file mode 100644 index b85ee721d..000000000 --- a/freqtrade/util/migrations/binance_mig.py +++ /dev/null @@ -1,87 +0,0 @@ -import logging - -from packaging import version -from sqlalchemy import select - -from freqtrade.constants import DOCS_LINK, Config -from freqtrade.enums import TradingMode -from freqtrade.exceptions import OperationalException -from freqtrade.persistence import KeyValueStore, Trade -from freqtrade.persistence.pairlock import PairLock - - -logger = logging.getLogger(__name__) - - -def migrate_binance_futures_names(config: Config): - """ - Migrate binance futures names in both database and data files. - This is needed because ccxt naming changed from "BTC/USDT" to "BTC/USDT:USDT" - """ - if not ( - config.get("trading_mode", TradingMode.SPOT) == TradingMode.FUTURES - and config["exchange"]["name"] == "binance" - ): - # only act on new futures - return - if KeyValueStore.get_int_value("binance_migration"): - # already migrated - return - import ccxt - - if version.parse("2.6.26") > version.parse(ccxt.__version__): - raise OperationalException( - "Please follow the update instructions in the docs " - f"({DOCS_LINK}/updating/) to install a compatible ccxt version." - ) - _migrate_binance_futures_db(config) - migrate_binance_futures_data(config) - KeyValueStore.store_value("binance_migration", 1) - - -def _migrate_binance_futures_db(config: Config): - logger.info("Migrating binance futures pairs in database.") - trades = Trade.get_trades([Trade.exchange == "binance", Trade.trading_mode == "FUTURES"]).all() - for trade in trades: - if ":" in trade.pair: - # already migrated - continue - new_pair = f"{trade.pair}:{trade.stake_currency}" - trade.pair = new_pair - - for order in trade.orders: - order.ft_pair = new_pair - # Should symbol be migrated too? - # order.symbol = new_pair - Trade.commit() - pls = PairLock.session.scalars(select(PairLock).filter(PairLock.pair.notlike("%:%"))).all() - for pl in pls: - pl.pair = f"{pl.pair}:{config['stake_currency']}" - # print(pls) - # pls.update({'pair': concat(PairLock.pair,':USDT')}) - Trade.commit() - logger.info("Done migrating binance futures pairs in database.") - - -def migrate_binance_futures_data(config: Config): - if not ( - config.get("trading_mode", TradingMode.SPOT) == TradingMode.FUTURES - and config["exchange"]["name"] == "binance" - ): - # only act on new futures - return - - from freqtrade.data.history import get_datahandler - - dhc = get_datahandler(config["datadir"], config["dataformat_ohlcv"]) - - paircombs = dhc.ohlcv_get_available_data( - config["datadir"], config.get("trading_mode", TradingMode.SPOT) - ) - - for pair, timeframe, candle_type in paircombs: - if ":" in pair: - # already migrated - continue - new_pair = f"{pair}:{config['stake_currency']}" - dhc.rename_futures_data(pair, new_pair, timeframe, candle_type) diff --git a/freqtrade/util/singleton.py b/freqtrade/util/singleton.py new file mode 100644 index 000000000..91841cdec --- /dev/null +++ b/freqtrade/util/singleton.py @@ -0,0 +1,16 @@ +from typing import Any + + +class SingletonMeta(type): + """ + A thread-safe implementation of Singleton. + Use as metaclass to create singleton classes. + """ + + _instances: dict = {} + + def __call__(cls, *args: Any, **kwargs: Any) -> Any: + if cls not in cls._instances: + instance = super().__call__(*args, **kwargs) + cls._instances[cls] = instance + return cls._instances[cls] diff --git a/freqtrade/wallets.py b/freqtrade/wallets.py index 1630ac600..56c32adb6 100644 --- a/freqtrade/wallets.py +++ b/freqtrade/wallets.py @@ -52,6 +52,12 @@ class Wallets: self._last_wallet_refresh: datetime | None = None self.update() + def __repr__(self) -> str: + return ( + f"Wallets(stake_currency={self._stake_currency}, start_cap={self._start_cap}, " + f"wallets={len(self._wallets)}, positions={len(self._positions)})" + ) + def get_free(self, currency: str) -> float: balance = self._wallets.get(currency) if balance and balance.free: @@ -198,7 +204,10 @@ class Wallets: continue size = self._exchange._contracts_to_amount(symbol, position["contracts"]) collateral = safe_value_fallback(position, "initialMargin", "collateral", 0.0) - leverage = position.get("leverage") + leverage: float | None = position.get("leverage") + if not leverage: + trade = Trade.get_trades_proxy(is_open=True, pair=symbol) + leverage = trade[0].leverage if trade else None _parsed_positions[symbol] = PositionWallet( symbol, position=size, diff --git a/ft_client/freqtrade_client/__init__.py b/ft_client/freqtrade_client/__init__.py index b50cbb152..3277aeec2 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__ = "2025.12" +__version__ = "2026.1" if "dev" in __version__: from pathlib import Path diff --git a/mkdocs.yml b/mkdocs.yml index 681a892b2..cd658e54a 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -7,9 +7,7 @@ use_directory_urls: True nav: - Home: index.md - Quickstart with Docker: docker_quickstart.md - - Installation: - - Linux/MacOS/Raspberry: installation.md - - Windows: windows_installation.md + - Installation: installation.md - Freqtrade Basics: bot-basics.md - Configuration: configuration.md - Strategy Quickstart: strategy-101.md diff --git a/requirements-dev.txt b/requirements-dev.txt index 0c1eadce2..e12ee2fa5 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -6,7 +6,7 @@ -r requirements-freqai-rl.txt -r docs/requirements-docs.txt -ruff==0.14.9 +ruff==0.14.13 mypy==1.19.1 pre-commit==4.5.1 pytest==9.0.2 @@ -24,10 +24,10 @@ time-machine==3.2.0 nbconvert==7.16.6 # mypy types -scipy-stubs==1.16.3.3 # keep in sync with `scipy` in `requirements-hyperopt.txt` +scipy-stubs==1.17.0.1 # keep in sync with `scipy` in `requirements-hyperopt.txt` types-cachetools==6.2.0.20251022 types-filelock==3.2.7 -types-requests==2.32.4.20250913 +types-requests==2.32.4.20260107 types-tabulate==0.9.0.20241207 types-python-dateutil==2.9.0.20251115 pip-audit==2.10.0 diff --git a/requirements-freqai-rl.txt b/requirements-freqai-rl.txt index 83a6515cc..04a8afbf6 100644 --- a/requirements-freqai-rl.txt +++ b/requirements-freqai-rl.txt @@ -3,7 +3,7 @@ # Required for freqai-rl torch==2.9.1; sys_platform != 'darwin' or platform_machine != 'x86_64' -gymnasium==1.2.2 +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' diff --git a/requirements-freqai.txt b/requirements-freqai.txt index 1325c431a..80377ffee 100644 --- a/requirements-freqai.txt +++ b/requirements-freqai.txt @@ -6,6 +6,6 @@ scikit-learn==1.8.0 joblib==1.5.3 lightgbm==4.6.0 -xgboost==3.1.2 +xgboost==3.1.3 tensorboard==2.20.0 datasieve==0.1.9 diff --git a/requirements-hyperopt.txt b/requirements-hyperopt.txt index 4de1a4e34..b9b1ca2f8 100644 --- a/requirements-hyperopt.txt +++ b/requirements-hyperopt.txt @@ -2,8 +2,8 @@ -r requirements.txt # Required for hyperopt -scipy==1.16.3 +scipy==1.17.0 scikit-learn==1.8.0 -filelock==3.20.1 +filelock==3.20.3 optuna==4.6.0 cmaes==0.12.0 diff --git a/requirements-plot.txt b/requirements-plot.txt index 5c3da9386..2c91c7013 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.5.0 +plotly==6.5.2 diff --git a/requirements.txt b/requirements.txt index 6a788a198..24a782afd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,31 +1,31 @@ -numpy==2.3.5 +numpy==2.4.1 pandas==2.3.3 bottleneck==1.6.0 numexpr==2.14.1 # Indicator libraries ft-pandas-ta==0.3.16 ta-lib==0.6.8 -technical==1.5.3 +technical==1.5.4 -ccxt==4.5.29 +ccxt==4.5.34 cryptography==46.0.3 -aiohttp==3.13.2 +aiohttp==3.13.3 SQLAlchemy==2.0.45 python-telegram-bot==22.5 # can't be hard-pinned due to telegram-bot pinning httpx with ~ httpx>=0.24.1 -humanize==4.14.0 +humanize==4.15.0 cachetools==6.2.4 requests==2.32.5 -urllib3==2.6.2 -certifi==2025.11.12 -jsonschema==4.25.1 +urllib3==2.6.3 +certifi==2026.1.4 +jsonschema==4.26.0 tabulate==0.9.0 pycoingecko==3.2.0 jinja2==3.1.6 joblib==1.5.3 rich==14.2.0 -pyarrow==22.0.0; platform_machine != 'armv7l' +pyarrow==23.0.0; platform_machine != 'armv7l' # Load ticker files 30% faster @@ -37,12 +37,12 @@ orjson==3.11.5 sdnotify==0.3.2 # API Server -fastapi==0.125.0 +fastapi==0.128.0 pydantic==2.12.5 -uvicorn==0.38.0 +uvicorn==0.40.0 pyjwt==2.10.1 aiofiles==25.1.0 -psutil==7.1.3 +psutil==7.2.1 # Building config files interactively questionary==2.1.1 @@ -55,7 +55,7 @@ pytz==2025.2 schedule==1.2.2 #WS Messages -websockets==15.0.1 +websockets==16.0 janus==2.0.0 ast-comments==1.2.3 diff --git a/tests/commands/test_startup_time.py b/tests/commands/test_startup_time.py index 32b4f80fd..c0821ece8 100644 --- a/tests/commands/test_startup_time.py +++ b/tests/commands/test_startup_time.py @@ -1,10 +1,10 @@ import subprocess # noqa: S404, RUF100 import time -from tests.conftest import is_arm, is_mac +from tests.conftest import is_mac -MAXIMUM_STARTUP_TIME = 0.7 if is_mac() and not is_arm(True) else 0.5 +MAXIMUM_STARTUP_TIME = 0.7 if is_mac() else 0.5 def test_startup_time(): diff --git a/tests/conftest.py b/tests/conftest.py index 93144ffc4..1c0435ff3 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -3184,7 +3184,7 @@ def leverage_tiers(): }, { "minNotional": 5000000, - "maxNotional": 30000000, + "maxNotional": None, "maintenanceMarginRate": 0.5, "maxLeverage": 1, "maintAmt": 1527500.0, diff --git a/tests/data/test_datahandler.py b/tests/data/test_datahandler.py index 9d40bf5cb..9bab3e797 100644 --- a/tests/data/test_datahandler.py +++ b/tests/data/test_datahandler.py @@ -288,7 +288,7 @@ def test_jsondatahandler_trades_load(testdatadir, caplog): dh.trades_load("XRP/ETH", TradingMode.SPOT) assert not log_has(logmsg, caplog) - # Test conversation is happening + # Test conversion is happening dh.trades_load("XRP/OLD", TradingMode.SPOT) assert log_has(logmsg, caplog) diff --git a/tests/exchange/test_binance.py b/tests/exchange/test_binance.py index 59a6c41ae..bb2b49002 100644 --- a/tests/exchange/test_binance.py +++ b/tests/exchange/test_binance.py @@ -975,6 +975,18 @@ def test_get_historic_ohlcv_binance( archive_mock.assert_called_once() if api_called: api_mock.assert_called_once() + candle_mock.reset_mock() + api_mock.reset_mock() + archive_mock.reset_mock() + + # binanceus does not use archive mode! + exchange._can_use_data_download_fast = False + df = exchange.get_historic_ohlcv(pair, timeframe, since_ms, candle_type, is_new_pair, until_ms) + # Never uses archive + assert archive_mock.call_count == 0 + assert candle_mock.call_count == (0 if not candle_called else 1) + if api_called: + assert api_mock.call_count == 1 @pytest.mark.parametrize( diff --git a/tests/exchange/test_exchange.py b/tests/exchange/test_exchange.py index 94428cbbf..12c7be5d4 100644 --- a/tests/exchange/test_exchange.py +++ b/tests/exchange/test_exchange.py @@ -6182,6 +6182,10 @@ def test_get_max_leverage_futures(default_conf, mocker, leverage_tiers): assert exchange.get_max_leverage("BTC/USDT:USDT", 300000000) == 2.0 assert exchange.get_max_leverage("BTC/USDT:USDT", 600000000) == 1.0 # Last tier + # Test ADA - last maxNotional is None + assert exchange.get_max_leverage("ADA/USDT:USDT", 2500000) == 2.0 # Second last tier + assert exchange.get_max_leverage("ADA/USDT:USDT", 6000000) == 1.0 # Last tier, open maxNotional + assert exchange.get_max_leverage("SPONGE/USDT:USDT", 200) == 1.0 # Pair not in leverage_tiers assert exchange.get_max_leverage("BTC/USDT:USDT", 0.0) == 125.0 # No stake amount with pytest.raises( diff --git a/tests/exchange/test_exchange_utils.py b/tests/exchange/test_exchange_utils.py index 47ad7cccb..d0d35a9dd 100644 --- a/tests/exchange/test_exchange_utils.py +++ b/tests/exchange/test_exchange_utils.py @@ -1,6 +1,7 @@ # pragma pylint: disable=missing-docstring, protected-access, invalid-name from datetime import UTC, datetime, timedelta from math import isnan, nan +from unittest.mock import MagicMock import pytest from ccxt import ( @@ -28,6 +29,7 @@ from freqtrade.exchange import ( timeframe_to_seconds, ) from freqtrade.exchange.check_exchange import check_exchange +from freqtrade.exchange.exchange_utils import _exchange_has_helper from tests.conftest import log_has_re @@ -385,3 +387,42 @@ def test_amount_to_contract_precision_standalone( ): res = amount_to_contract_precision(amount, precision, precision_mode, contract_size) assert pytest.approx(res) == expected + + +def test_exchange__exchange_has_helper(): + e_mod = MagicMock() + e_mod.has = { + "fetchTicker": True, + "fetchOHLCV": False, + "fetchTrades": True, + "fetchMyTrades": False, + "fetchOrder": True, + } + required = { + "fetchOHLCV": [], + "fetchTicker": [], + "fetchMyTrades": ["fetchTrades"], + "fetchOrder": ["fetchOpenOrder", "fetchClosedOrder"], + } + missing = _exchange_has_helper(e_mod, required) + assert set(missing) == {"fetchOHLCV"} + + e_mod.has = { + "fetchTicker": True, + "fetchOHLCV": False, + "fetchTrades": False, + "fetchMyTrades": False, + "fetchOrder": True, + } + missing = _exchange_has_helper(e_mod, required) + assert set(missing) == {"fetchOHLCV", "fetchMyTrades"} + + e_mod.has = { + "fetchTicker": True, + "fetchOHLCV": False, + "fetchTrades": False, + "fetchMyTrades": False, + "fetchOrder": False, + } + missing = _exchange_has_helper(e_mod, required) + assert set(missing) == {"fetchOHLCV", "fetchMyTrades", "fetchOrder"} diff --git a/tests/exchange/test_hyperliquid.py b/tests/exchange/test_hyperliquid.py index c2bff9339..70a38f04c 100644 --- a/tests/exchange/test_hyperliquid.py +++ b/tests/exchange/test_hyperliquid.py @@ -1,21 +1,125 @@ +from copy import deepcopy from datetime import UTC, datetime from unittest.mock import MagicMock, PropertyMock import pytest -from tests.conftest import EXMS, get_mock_coro, get_patched_exchange +from freqtrade.exceptions import ConfigurationError +from tests.conftest import EXMS, get_mock_coro, get_patched_exchange, log_has_re + + +@pytest.fixture +def markets_hip3(): + markets = { + "BTC/USDC:USDC": { + "quote": "USDC", + "base": "BTC", + "type": "swap", + "swap": True, + "linear": True, + "limits": {"leverage": {"max": 50}}, + "info": {}, + }, + "ETH/USDC:USDC": { + "quote": "USDC", + "base": "ETH", + "type": "swap", + "swap": True, + "linear": True, + "limits": {"leverage": {"max": 50}}, + "info": {}, + }, + "SOL/USDC:USDC": { + "quote": "USDC", + "base": "SOL", + "type": "swap", + "swap": True, + "linear": True, + "limits": {"leverage": {"max": 20}}, + "info": {}, + }, + "DOGE/USDC:USDC": { + "quote": "USDC", + "base": "DOGE", + "type": "swap", + "swap": True, + "linear": True, + "limits": {"leverage": {"max": 20}}, + "info": {}, + }, + "XYZ-AAPL/USDC:USDC": { + "quote": "USDC", + "base": "XYZ-AAPL", + "type": "swap", + "swap": True, + "linear": True, + "limits": {"leverage": {"max": 10}}, + "info": {"hip3": True, "dex": "xyz"}, + }, + "XYZ-TSLA/USDC:USDC": { + "quote": "USDC", + "base": "XYZ-TSLA", + "type": "swap", + "swap": True, + "linear": True, + "limits": {"leverage": {"max": 10}}, + "info": {"hip3": True, "dex": "xyz"}, + }, + "XYZ-GOOGL/USDC:USDC": { + "quote": "USDC", + "base": "XYZ-GOOGL", + "type": "swap", + "swap": True, + "linear": True, + "limits": {"leverage": {"max": 10}}, + "info": {"hip3": True, "dex": "xyz"}, + }, + "XYZ-NVDA/USDC:USDC": { + "quote": "USDC", + "base": "XYZ-NVDA", + "type": "swap", + "swap": True, + "linear": True, + "limits": {"leverage": {"max": 10}}, + "info": {"hip3": True, "dex": "xyz"}, + }, + "VNTL-SPACEX/USDH:USDH": { + "quote": "USDH", + "base": "VNTL-SPACEX", + "type": "swap", + "swap": True, + "linear": True, + "limits": {"leverage": {"max": 3}}, + "info": {"hip3": True, "dex": "vntl"}, + }, + "VNTL-ANTHROPIC/USDH:USDH": { + "quote": "USDH", + "base": "VNTL-ANTHROPIC", + "type": "swap", + "swap": True, + "linear": True, + "limits": {"leverage": {"max": 3}}, + "info": {"hip3": True, "dex": "vntl"}, + }, + "FLX-TOKEN/USDC:USDC": { + "quote": "USDC", + "base": "FLX-TOKEN", + "type": "swap", + "swap": True, + "linear": True, + "limits": {"leverage": {"max": 3}}, + "info": {"hip3": True, "dex": "flx"}, + }, + } + + return markets @pytest.mark.parametrize("margin_mode", ["isolated", "cross"]) -def test_hyperliquid_dry_run_liquidation_price(default_conf, mocker, margin_mode): +def test_hyperliquid_dry_run_liquidation_price(default_conf, markets_hip3, mocker, margin_mode): # test if liq price calculated by dry_run_liquidation_price() is close to ccxt liq price # testing different pairs with large/small prices, different leverages, long, short - markets = { - "BTC/USDC:USDC": {"limits": {"leverage": {"max": 50}}}, - "ETH/USDC:USDC": {"limits": {"leverage": {"max": 50}}}, - "SOL/USDC:USDC": {"limits": {"leverage": {"max": 20}}}, - "DOGE/USDC:USDC": {"limits": {"leverage": {"max": 20}}}, - } + positions = [ { "symbol": "ETH/USDC:USDC", @@ -278,6 +382,33 @@ def test_hyperliquid_dry_run_liquidation_price(default_conf, mocker, margin_mode "leverage": 3.0, "liquidationPrice": 45236.52992613, }, + { + "symbol": "XYZ-AAPL/USDC:USDC", + "entryPrice": 250.0, + "side": "long", + "contracts": 0.5, + "collateral": 25.0, + "leverage": 5.0, + "liquidationPrice": 210.5263157894737, + }, + { + "symbol": "XYZ-GOOGL/USDC:USDC", + "entryPrice": 190.0, + "side": "short", + "contracts": 0.5, + "collateral": 9.5, + "leverage": 10.0, + "liquidationPrice": 199.04761904761904, + }, + { + "symbol": "XYZ-TSLA/USDC:USDC", + "entryPrice": 350.0, + "side": "long", + "contracts": 1.0, + "collateral": 50.0, + "leverage": 7.0, + "liquidationPrice": 315.7894736842105, + }, ] api_mock = MagicMock() @@ -285,7 +416,7 @@ def test_hyperliquid_dry_run_liquidation_price(default_conf, mocker, margin_mode default_conf["margin_mode"] = margin_mode default_conf["stake_currency"] = "USDC" api_mock.load_markets = get_mock_coro() - api_mock.markets = markets + api_mock.markets = markets_hip3 exchange = get_patched_exchange( mocker, default_conf, api_mock, exchange="hyperliquid", mock_markets=False ) @@ -331,41 +462,62 @@ def test_hyperliquid_get_funding_fees(default_conf, mocker): now = datetime.now(UTC) exchange = get_patched_exchange(mocker, default_conf, exchange="hyperliquid") exchange._fetch_and_calculate_funding_fees = MagicMock() + + # Spot mode - no funding fees exchange.get_funding_fees("BTC/USDC:USDC", 1, False, now) assert exchange._fetch_and_calculate_funding_fees.call_count == 0 default_conf["trading_mode"] = "futures" default_conf["margin_mode"] = "isolated" + default_conf["exchange"]["hip3_dexes"] = ["xyz", "vntl"] + + # Mock validate_config to skip validation + mocker.patch("freqtrade.exchange.hyperliquid.Hyperliquid.validate_config") + exchange = get_patched_exchange(mocker, default_conf, exchange="hyperliquid") exchange._fetch_and_calculate_funding_fees = MagicMock() - exchange.get_funding_fees("BTC/USDC:USDC", 1, False, now) + # Normal market + exchange.get_funding_fees("BTC/USDC:USDC", 1, False, now) + assert exchange._fetch_and_calculate_funding_fees.call_count == 1 + + # HIP-3 XYZ market + exchange._fetch_and_calculate_funding_fees.reset_mock() + exchange.get_funding_fees("XYZ-TSLA/USDC:USDC", 1, False, now) + assert exchange._fetch_and_calculate_funding_fees.call_count == 1 + + # HIP-3 VNTL market + exchange._fetch_and_calculate_funding_fees.reset_mock() + exchange.get_funding_fees("VNTL-SPACEX/USDH:USDH", 1, True, now) assert exchange._fetch_and_calculate_funding_fees.call_count == 1 -def test_hyperliquid_get_max_leverage(default_conf, mocker): - markets = { - "BTC/USDC:USDC": {"limits": {"leverage": {"max": 50}}}, - "ETH/USDC:USDC": {"limits": {"leverage": {"max": 50}}}, - "SOL/USDC:USDC": {"limits": {"leverage": {"max": 20}}}, - "DOGE/USDC:USDC": {"limits": {"leverage": {"max": 20}}}, - } +def test_hyperliquid_get_max_leverage(default_conf, mocker, markets_hip3): exchange = get_patched_exchange(mocker, default_conf, exchange="hyperliquid") assert exchange.get_max_leverage("BTC/USDC:USDC", 1) == 1.0 default_conf["trading_mode"] = "futures" default_conf["margin_mode"] = "isolated" - exchange = get_patched_exchange(mocker, default_conf, exchange="hyperliquid") - mocker.patch.multiple( - EXMS, - markets=PropertyMock(return_value=markets), - ) + default_conf["exchange"]["hip3_dexes"] = ["xyz", "vntl"] + # Mock validate_config to skip validation + mocker.patch("freqtrade.exchange.hyperliquid.Hyperliquid.validate_config") + + exchange = get_patched_exchange(mocker, default_conf, exchange="hyperliquid") + mocker.patch.multiple(EXMS, markets=PropertyMock(return_value=markets_hip3)) + + # Normal markets assert exchange.get_max_leverage("BTC/USDC:USDC", 1) == 50 assert exchange.get_max_leverage("ETH/USDC:USDC", 20) == 50 assert exchange.get_max_leverage("SOL/USDC:USDC", 50) == 20 assert exchange.get_max_leverage("DOGE/USDC:USDC", 3) == 20 + # HIP-3 markets + assert exchange.get_max_leverage("XYZ-TSLA/USDC:USDC", 1) == 10 + assert exchange.get_max_leverage("XYZ-NVDA/USDC:USDC", 5) == 10 + assert exchange.get_max_leverage("VNTL-SPACEX/USDH:USDH", 2) == 3 + assert exchange.get_max_leverage("VNTL-ANTHROPIC/USDH:USDH", 1) == 3 + def test_hyperliquid__lev_prep(default_conf, mocker): api_mock = MagicMock() @@ -382,25 +534,59 @@ def test_hyperliquid__lev_prep(default_conf, mocker): default_conf["trading_mode"] = "futures" default_conf["margin_mode"] = "isolated" + default_conf["exchange"]["hip3_dexes"] = ["xyz", "vntl"] + + # Mock validate_config to skip validation + mocker.patch("freqtrade.exchange.hyperliquid.Hyperliquid.validate_config") exchange = get_patched_exchange(mocker, default_conf, api_mock, exchange="hyperliquid") - exchange._lev_prep("BTC/USDC:USDC", 3.2, "buy") + # Normal market + exchange._lev_prep("BTC/USDC:USDC", 3.2, "buy") assert api_mock.set_margin_mode.call_count == 1 api_mock.set_margin_mode.assert_called_with("isolated", "BTC/USDC:USDC", {"leverage": 3}) api_mock.reset_mock() - exchange._lev_prep("BTC/USDC:USDC", 19.99, "sell") - assert api_mock.set_margin_mode.call_count == 1 api_mock.set_margin_mode.assert_called_with("isolated", "BTC/USDC:USDC", {"leverage": 19}) + # HIP-3 XYZ market + api_mock.reset_mock() + exchange._lev_prep("XYZ-TSLA/USDC:USDC", 5.7, "buy") + assert api_mock.set_margin_mode.call_count == 1 + api_mock.set_margin_mode.assert_called_with("isolated", "XYZ-TSLA/USDC:USDC", {"leverage": 5}) -def test_hyperliquid_fetch_order(default_conf_usdt, mocker): + api_mock.reset_mock() + exchange._lev_prep("XYZ-TSLA/USDC:USDC", 10.0, "sell") + assert api_mock.set_margin_mode.call_count == 1 + api_mock.set_margin_mode.assert_called_with("isolated", "XYZ-TSLA/USDC:USDC", {"leverage": 10}) + + # HIP-3 VNTL market + api_mock.reset_mock() + exchange._lev_prep("VNTL-SPACEX/USDH:USDH", 2.5, "buy") + assert api_mock.set_margin_mode.call_count == 1 + api_mock.set_margin_mode.assert_called_with( + "isolated", "VNTL-SPACEX/USDH:USDH", {"leverage": 2} + ) + + api_mock.reset_mock() + exchange._lev_prep("VNTL-ANTHROPIC/USDH:USDH", 3.0, "sell") + assert api_mock.set_margin_mode.call_count == 1 + api_mock.set_margin_mode.assert_called_with( + "isolated", "VNTL-ANTHROPIC/USDH:USDH", {"leverage": 3} + ) + + +def test_hyperliquid_fetch_order(default_conf_usdt, mocker, markets_hip3): default_conf_usdt["dry_run"] = False + default_conf_usdt["trading_mode"] = "futures" + default_conf_usdt["margin_mode"] = "isolated" + default_conf_usdt["exchange"]["hip3_dexes"] = ["xyz", "vntl"] api_mock = MagicMock() + + # Test with normal market api_mock.fetch_order = MagicMock( return_value={ "id": "12345", @@ -432,9 +618,293 @@ def test_hyperliquid_fetch_order(default_conf_usdt, mocker): }, ], ) - exchange = get_patched_exchange(mocker, default_conf_usdt, api_mock, exchange="hyperliquid") + mocker.patch("freqtrade.exchange.hyperliquid.Hyperliquid.validate_config") + exchange = get_patched_exchange( + mocker, default_conf_usdt, api_mock, exchange="hyperliquid", mock_markets=markets_hip3 + ) o = exchange.fetch_order("12345", "ETH/USDC:USDC") # Uses weighted average assert o["average"] == 1500 - assert gtfo_mock.call_count == 1 + + # Test with HIP-3 XYZ market + api_mock.fetch_order = MagicMock( + return_value={ + "id": "67890", + "symbol": "XYZ-TSLA/USDC:USDC", + "status": "closed", + "filled": 2.5, + "average": None, + "timestamp": 1630000100, + } + ) + gtfo_mock.reset_mock() + gtfo_mock.return_value = [ + { + "order_id": "67890", + "price": 250, + "amount": 1.5, + "filled": 1.5, + "remaining": 0, + }, + { + "order_id": "67890", + "price": 260, + "amount": 1.0, + "filled": 1.0, + "remaining": 0, + }, + ] + + o = exchange.fetch_order("67890", "XYZ-TSLA/USDC:USDC") + # Weighted average: (250*1.5 + 260*1.0) / 2.5 = 254 + assert o["average"] == 254 + assert gtfo_mock.call_count == 1 + + # Test with HIP-3 VNTL market + api_mock.fetch_order = MagicMock( + return_value={ + "id": "11111", + "symbol": "VNTL-SPACEX/USDH:USDH", + "status": "closed", + "filled": 5.0, + "average": None, + "timestamp": 1630000200, + } + ) + gtfo_mock.reset_mock() + gtfo_mock.return_value = [ + { + "order_id": "11111", + "price": 100, + "amount": 3.0, + "filled": 3.0, + "remaining": 0, + }, + { + "order_id": "11111", + "price": 105, + "amount": 2.0, + "filled": 2.0, + "remaining": 0, + }, + ] + + o = exchange.fetch_order("11111", "VNTL-SPACEX/USDH:USDH") + assert o["average"] == 102 + assert gtfo_mock.call_count == 1 + + +def test_hyperliquid_hip3_config_validation(default_conf_usdt, mocker, markets_hip3): + """Test HIP-3 DEX configuration validation.""" + + api_mock = MagicMock() + default_conf_usdt["stake_currency"] = "USDC" + + # Futures mode, no dex configured + default_conf_copy = deepcopy(default_conf_usdt) + default_conf_copy["trading_mode"] = "futures" + default_conf_copy["margin_mode"] = "isolated" + exchange = get_patched_exchange( + mocker, default_conf_copy, api_mock, exchange="hyperliquid", mock_markets=markets_hip3 + ) + exchange.validate_config(default_conf_copy) + + # Not in futures mode - no dex configured - no error + get_patched_exchange( + mocker, default_conf_usdt, api_mock, exchange="hyperliquid", mock_markets=markets_hip3 + ) + # Not in futures mode + default_conf_usdt["exchange"]["hip3_dexes"] = ["xyz"] + with pytest.raises( + ConfigurationError, match=r"HIP-3 DEXes are only supported in FUTURES trading mode\." + ): + get_patched_exchange( + mocker, default_conf_usdt, api_mock, exchange="hyperliquid", mock_markets=markets_hip3 + ) + # Valid single DEX + default_conf_usdt["trading_mode"] = "futures" + default_conf_usdt["margin_mode"] = "isolated" + default_conf_usdt["exchange"]["hip3_dexes"] = ["xyz"] + exchange = get_patched_exchange( + mocker, default_conf_usdt, api_mock, exchange="hyperliquid", mock_markets=markets_hip3 + ) + assert exchange._get_configured_hip3_dexes() == ["xyz"] + + # Invalid DEX + default_conf_usdt["exchange"]["hip3_dexes"] = ["invalid_dex"] + with pytest.raises(ConfigurationError, match="Invalid HIP-3 DEXes configured"): + exchange = get_patched_exchange( + mocker, default_conf_usdt, api_mock, exchange="hyperliquid", mock_markets=markets_hip3 + ) + exchange.validate_config(default_conf_usdt) + + # Mix of valid and invalid DEX + default_conf_usdt["exchange"]["hip3_dexes"] = ["xyz", "invalid_dex"] + with pytest.raises(ConfigurationError, match="Invalid HIP-3 DEXes configured"): + exchange = get_patched_exchange( + mocker, default_conf_usdt, api_mock, exchange="hyperliquid", mock_markets=markets_hip3 + ) + exchange.validate_config(default_conf_usdt) + + default_conf_usdt["margin_mode"] = "cross" + with pytest.raises(ConfigurationError, match="HIP-3 DEXes require 'isolated' margin mode"): + exchange = get_patched_exchange( + mocker, default_conf_usdt, api_mock, exchange="hyperliquid", mock_markets=markets_hip3 + ) + exchange.validate_config(default_conf_usdt) + + +def test_hyperliquid_get_balances_hip3(default_conf, mocker, caplog, markets_hip3): + """Test balance fetching from HIP-3 DEXes.""" + api_mock = MagicMock() + + api_mock.load_markets = get_mock_coro() + + # Mock balance responses + default_balance = {"USDC": {"free": 1000, "used": 0, "total": 1000}} + xyz_balance = {"USDC": {"free": 0, "used": 600, "total": 600}} + vntl_balance = {"USDH": {"free": 0, "used": 300, "total": 300}} + + def fetch_balance_side_effect(params=None): + if params and params.get("dex") == "xyz": + return xyz_balance + elif params and params.get("dex") == "vntl": + return vntl_balance + elif params and params.get("dex") == "flx": + raise Exception("FLX DEX error") + return default_balance + + api_mock.fetch_balance = MagicMock(side_effect=fetch_balance_side_effect) + + # Test with two HIP-3 DEXes + default_conf["exchange"]["hip3_dexes"] = ["xyz", "vntl", "flx"] + default_conf["trading_mode"] = "futures" + default_conf["margin_mode"] = "isolated" + mocker.patch("freqtrade.exchange.hyperliquid.Hyperliquid.validate_config") + exchange = get_patched_exchange( + mocker, default_conf, api_mock, exchange="hyperliquid", mock_markets=markets_hip3 + ) + + balances = exchange.get_balances() + + # Should have combined balances + assert balances["USDC"]["free"] == 1000 + assert balances["USDC"]["used"] == 600 + assert balances["USDC"]["total"] == 1600 + assert balances["USDH"]["free"] == 0 + assert balances["USDH"]["used"] == 300 + assert balances["USDH"]["total"] == 300 + + assert api_mock.fetch_balance.call_count == 4 + assert log_has_re("Could not fetch balance for HIP-3 DEX.*", caplog) + + +def test_hyperliquid_fetch_positions_hip3(default_conf, mocker, caplog, markets_hip3): + """Test position fetching from HIP-3 DEXes.""" + api_mock = MagicMock() + + # Mock position responses + default_positions = [{"symbol": "BTC/USDC:USDC", "contracts": 0.5}] + xyz_positions = [{"symbol": "XYZ-AAPL/USDC:USDC", "contracts": 10}] + vntl_positions = [{"symbol": "VNTL-SPACEX/USDH:USDH", "contracts": 5}] + + def fetch_positions_side_effect(symbols=None, params=None): + if params and params.get("dex") == "xyz": + return xyz_positions + elif params and params.get("dex") == "vntl": + return vntl_positions + elif params and params.get("dex") == "flx": + raise Exception("FLX DEX error") + return default_positions + + positions_mock = MagicMock(side_effect=fetch_positions_side_effect) + + default_conf["trading_mode"] = "futures" + default_conf["margin_mode"] = "isolated" + default_conf["exchange"]["hip3_dexes"] = ["xyz", "vntl", "flx"] + + mocker.patch("freqtrade.exchange.hyperliquid.Hyperliquid.validate_config") + exchange = get_patched_exchange( + mocker, default_conf, api_mock, exchange="hyperliquid", mock_markets=markets_hip3 + ) + + # Mock super().fetch_positions() to return default positions + mocker.patch(f"{EXMS}.fetch_positions", positions_mock) + + positions = exchange.fetch_positions() + + assert log_has_re("Could not fetch positions from HIP-3 .*", caplog) + + # Should have all positions combined (default + HIP-3) + assert len(positions) == 3 + assert any(p["symbol"] == "BTC/USDC:USDC" for p in positions) + assert any(p["symbol"] == "XYZ-AAPL/USDC:USDC" for p in positions) + assert any(p["symbol"] == "VNTL-SPACEX/USDH:USDH" for p in positions) + + # Verify API calls (xyz + vntl, default is mocked separately) + assert positions_mock.call_count == 4 + + +def test_hyperliquid_market_is_tradable(default_conf_usdt, mocker, markets_hip3): + """Test market_is_tradable filters HIP-3 markets correctly.""" + default_conf_usdt["stake_currency"] = "USDC" + default_conf_usdt["trading_mode"] = "futures" + default_conf_usdt["margin_mode"] = "isolated" + api_mock = MagicMock() + api_mock.load_markets = get_mock_coro(return_value=markets_hip3) + api_mock.markets = markets_hip3 + # Mock parent call - we only want to test hyperliquid specifics here. + mocker.patch(f"{EXMS}.market_is_tradable", return_value=True) + + # Test 1: No HIP-3 DEXes configured - only default markets tradable + default_conf_usdt["exchange"]["hip3_dexes"] = [] + exchange = get_patched_exchange( + mocker, default_conf_usdt, api_mock, exchange="hyperliquid", mock_markets=False + ) + + assert exchange.market_is_tradable(markets_hip3["BTC/USDC:USDC"]) is True + assert exchange.market_is_tradable(markets_hip3["ETH/USDC:USDC"]) is True + assert exchange.market_is_tradable(markets_hip3["XYZ-AAPL/USDC:USDC"]) is False + assert exchange.market_is_tradable(markets_hip3["XYZ-TSLA/USDC:USDC"]) is False + assert exchange.market_is_tradable(markets_hip3["VNTL-SPACEX/USDH:USDH"]) is False + assert exchange.market_is_tradable(markets_hip3["FLX-TOKEN/USDC:USDC"]) is False + + # Test 2: Only 'xyz' configured - default + xyz markets tradable + default_conf_usdt["exchange"]["hip3_dexes"] = ["xyz"] + exchange = get_patched_exchange( + mocker, default_conf_usdt, api_mock, exchange="hyperliquid", mock_markets=False + ) + + assert exchange.market_is_tradable(markets_hip3["BTC/USDC:USDC"]) is True + assert exchange.market_is_tradable(markets_hip3["ETH/USDC:USDC"]) is True + assert exchange.market_is_tradable(markets_hip3["XYZ-AAPL/USDC:USDC"]) is True + assert exchange.market_is_tradable(markets_hip3["XYZ-TSLA/USDC:USDC"]) is True + assert exchange.market_is_tradable(markets_hip3["VNTL-SPACEX/USDH:USDH"]) is False + assert exchange.market_is_tradable(markets_hip3["FLX-TOKEN/USDC:USDC"]) is False + + # Test 3: 'xyz' and 'vntl' configured - default + xyz + vntl markets tradable + default_conf_usdt["exchange"]["hip3_dexes"] = ["xyz", "flx"] + exchange = get_patched_exchange( + mocker, default_conf_usdt, api_mock, exchange="hyperliquid", mock_markets=False + ) + + assert exchange.market_is_tradable(markets_hip3["BTC/USDC:USDC"]) is True + assert exchange.market_is_tradable(markets_hip3["ETH/USDC:USDC"]) is True + assert exchange.market_is_tradable(markets_hip3["XYZ-AAPL/USDC:USDC"]) is True + assert exchange.market_is_tradable(markets_hip3["XYZ-TSLA/USDC:USDC"]) is True + assert exchange.market_is_tradable(markets_hip3["VNTL-SPACEX/USDH:USDH"]) is False + assert exchange.market_is_tradable(markets_hip3["FLX-TOKEN/USDC:USDC"]) is True + + # Use USDH stake currency to enable VNTL markets + default_conf_usdt["exchange"]["hip3_dexes"] = ["vntl"] + default_conf_usdt["stake_currency"] = "USDH" + exchange = get_patched_exchange( + mocker, default_conf_usdt, api_mock, exchange="hyperliquid", mock_markets=False + ) + assert exchange.market_is_tradable(markets_hip3["BTC/USDC:USDC"]) is True + assert exchange.market_is_tradable(markets_hip3["ETH/USDC:USDC"]) is True + assert exchange.market_is_tradable(markets_hip3["XYZ-AAPL/USDC:USDC"]) is False + assert exchange.market_is_tradable(markets_hip3["XYZ-TSLA/USDC:USDC"]) is False + assert exchange.market_is_tradable(markets_hip3["VNTL-SPACEX/USDH:USDH"]) is True + assert exchange.market_is_tradable(markets_hip3["FLX-TOKEN/USDC:USDC"]) is False diff --git a/tests/exchange_online/conftest.py b/tests/exchange_online/conftest.py index 531fec307..89e280a6e 100644 --- a/tests/exchange_online/conftest.py +++ b/tests/exchange_online/conftest.py @@ -435,14 +435,13 @@ EXCHANGES = { "candle_count": 1000, "orderbook_max_entries": 50, }, - # TODO: re-enable htx once certificates work again - # "htx": { - # "pair": "ETH/BTC", - # "stake_currency": "BTC", - # "hasQuoteVolume": True, - # "timeframe": "1h", - # "candle_count": 1000, - # }, + "htx": { + "pair": "ETH/BTC", + "stake_currency": "BTC", + "hasQuoteVolume": True, + "timeframe": "1h", + "candle_count": 1000, + }, "bitvavo": { "pair": "BTC/EUR", "stake_currency": "EUR", @@ -523,6 +522,8 @@ EXCHANGES = { "candle_count": 5000, "orderbook_max_entries": 20, "futures_pair": "BTC/USDC:USDC", + # Assert that HIP3 pairs are fetched as part of load_markets + "futures_alt_pairs": ["XYZ-NVDA/USDC:USDC", "VNTL-ANTHROPIC/USDH:USDH"], "hasQuoteVolumeFutures": True, "leverage_tiers_public": False, "leverage_in_spot_market": False, diff --git a/tests/exchange_online/test_ccxt_compat.py b/tests/exchange_online/test_ccxt_compat.py index d0dc715b9..b0161cbf2 100644 --- a/tests/exchange_online/test_ccxt_compat.py +++ b/tests/exchange_online/test_ccxt_compat.py @@ -67,12 +67,14 @@ class TestCCXTExchange: def test_load_markets_futures(self, exchange_futures: EXCHANGE_FIXTURE_TYPE): exchange, exchangename = exchange_futures pair = EXCHANGES[exchangename]["pair"] - pair = EXCHANGES[exchangename].get("futures_pair", pair) + pair1 = EXCHANGES[exchangename].get("futures_pair", pair) + alternative_pairs = EXCHANGES[exchangename].get("futures_alt_pairs", []) markets = exchange.markets - assert pair in markets - assert isinstance(markets[pair], dict) + for pair in [pair1] + alternative_pairs: + assert pair in markets, f"Futures pair {pair} not found in markets" + assert isinstance(markets[pair], dict) - assert exchange.market_is_future(markets[pair]) + assert exchange.market_is_future(markets[pair]) def test_ccxt_order_parse(self, exchange: EXCHANGE_FIXTURE_TYPE): exch, exchange_name = exchange @@ -515,12 +517,13 @@ class TestCCXTExchange: for tier in pair_tiers: for key in ["maintenanceMarginRate", "minNotional", "maxNotional", "maxLeverage"]: assert key in tier - assert tier[key] >= 0.0 - assert tier["maxNotional"] > tier["minNotional"] + # maxNotional can be None (no limit) + assert tier[key] is None or tier[key] >= 0.0 + assert tier["maxNotional"] is None or tier["maxNotional"] > tier["minNotional"] assert tier["maxLeverage"] <= oldLeverage assert tier["maintenanceMarginRate"] >= oldMaintenanceMarginRate assert tier["minNotional"] > oldminNotional - assert tier["maxNotional"] > oldmaxNotional + assert tier["maxNotional"] is None or tier["maxNotional"] > oldmaxNotional oldLeverage = tier["maxLeverage"] oldMaintenanceMarginRate = tier["maintenanceMarginRate"] oldminNotional = tier["minNotional"] diff --git a/tests/persistence/test_db_context.py b/tests/persistence/test_db_context.py index 164095d5d..b879ccfd9 100644 --- a/tests/persistence/test_db_context.py +++ b/tests/persistence/test_db_context.py @@ -6,19 +6,19 @@ from freqtrade.persistence import FtNoDBContext, PairLocks, Trade @pytest.mark.parametrize("timeframe", ["", "5m", "1d"]) def test_FtNoDBContext(timeframe): PairLocks.timeframe = "" - assert Trade.use_db is True - assert PairLocks.use_db is True + assert Trade.use_db + assert PairLocks.use_db assert PairLocks.timeframe == "" with FtNoDBContext(timeframe): - assert Trade.use_db is False - assert PairLocks.use_db is False + assert not Trade.use_db + assert not PairLocks.use_db assert PairLocks.timeframe == timeframe with FtNoDBContext(): - assert Trade.use_db is False - assert PairLocks.use_db is False + assert not Trade.use_db + assert not PairLocks.use_db assert PairLocks.timeframe == "" - assert Trade.use_db is True - assert PairLocks.use_db is True + assert Trade.use_db + assert PairLocks.use_db diff --git a/tests/plugins/test_pairlist.py b/tests/plugins/test_pairlist.py index fb42f22ea..e3b63bdb3 100644 --- a/tests/plugins/test_pairlist.py +++ b/tests/plugins/test_pairlist.py @@ -1396,7 +1396,7 @@ def test_gen_pair_whitelist_not_supported(mocker, default_conf, tickers) -> None ) with pytest.raises( - OperationalException, match=r"Exchange does not support dynamic whitelist.*" + OperationalException, match=r"Exchange .* does not support dynamic whitelist.*" ): get_patched_freqtradebot(mocker, default_conf) @@ -1410,7 +1410,9 @@ def test_pair_whitelist_not_supported_Spread(mocker, default_conf, tickers) -> N exchange_has=MagicMock(return_value=False), ) - with pytest.raises(OperationalException, match=r"Exchange does not support fetchTickers, .*"): + with pytest.raises( + OperationalException, match=r"Exchange .* does not support fetchTickers, .*" + ): get_patched_freqtradebot(mocker, default_conf) mocker.patch(f"{EXMS}.exchange_has", MagicMock(return_value=True)) diff --git a/tests/plugins/test_percentchangepairlist.py b/tests/plugins/test_percentchangepairlist.py index 7bbf92e5d..bf362d4fb 100644 --- a/tests/plugins/test_percentchangepairlist.py +++ b/tests/plugins/test_percentchangepairlist.py @@ -43,9 +43,9 @@ def test_volume_change_pair_list_init_exchange_support(mocker, rpl_config): with pytest.raises( OperationalException, - match=r"Exchange does not support dynamic whitelist in this configuration. " + match=r"Exchange .* does not support dynamic whitelist in this configuration. " r"Please edit your config and either remove PercentChangePairList, " - r"or switch to using candles. and restart the bot.", + r"or switch to using candles and restart the bot.", ): get_patched_freqtradebot(mocker, rpl_config) diff --git a/tests/rpc/test_fiat_convert.py b/tests/rpc/test_fiat_convert.py index 4ae7441c8..8d58113f0 100644 --- a/tests/rpc/test_fiat_convert.py +++ b/tests/rpc/test_fiat_convert.py @@ -12,6 +12,14 @@ from freqtrade.util.coin_gecko import FtCoinGeckoApi from tests.conftest import log_has, log_has_re +@pytest.fixture(autouse=True) +def reset_singleton_instance(): + # Reset the singleton instance before each test + CryptoToFiatConverter._instances = {} + yield + CryptoToFiatConverter._instances = {} + + def test_fiat_convert_is_singleton(): fiat_convert = CryptoToFiatConverter({"a": 22}) fiat_convert2 = CryptoToFiatConverter({}) diff --git a/tests/rpc/test_rpc.py b/tests/rpc/test_rpc.py index f288cd0ba..fe82e9904 100644 --- a/tests/rpc/test_rpc.py +++ b/tests/rpc/test_rpc.py @@ -17,6 +17,7 @@ from tests.conftest import ( create_mock_trades, create_mock_trades_usdt, get_patched_freqtradebot, + log_has_re, patch_get_signal, ) @@ -230,11 +231,7 @@ def test_rpc_trade_status(default_conf, ticker, fee, mocker) -> None: def test_rpc_status_table(default_conf, ticker, fee, mocker, time_machine) -> None: time_machine.move_to("2024-05-10 11:15:00 +00:00", tick=False) - mocker.patch.multiple( - "freqtrade.rpc.fiat_convert.FtCoinGeckoApi", - get_price=MagicMock(return_value={"bitcoin": {"usd": 15000.0}}), - ) - mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=15000.0) + mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock()) mocker.patch.multiple( EXMS, @@ -279,6 +276,8 @@ def test_rpc_status_table(default_conf, ticker, fee, mocker, time_machine) -> No # Test with fiat convert rpc._config["fiat_display_currency"] = "USD" rpc._fiat_converter = CryptoToFiatConverter({}) + mocker.patch.object(rpc._fiat_converter, "get_price", return_value=15000.0) + result, headers, fiat_profit_sum, total_sum = rpc._rpc_status_table( default_conf["stake_currency"], "USD" ) @@ -447,7 +446,6 @@ def test_rpc_delete_trade(mocker, default_conf, fee, markets, caplog, is_short): def test_rpc_trade_statistics(default_conf_usdt, ticker, fee, mocker) -> None: - mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=1.1) mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock()) mocker.patch.multiple( EXMS, @@ -461,6 +459,7 @@ def test_rpc_trade_statistics(default_conf_usdt, ticker, fee, mocker) -> None: rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter({}) + mocker.patch.object(rpc._fiat_converter, "get_price", return_value=1.1) res = rpc._rpc_trade_statistics(stake_currency, fiat_display_currency) assert res["trade_count"] == 0 @@ -505,7 +504,7 @@ def test_rpc_trade_statistics(default_conf_usdt, ticker, fee, mocker) -> None: assert isnan(stats["profit_all_coin"]) -def test_rpc_balance_handle_error(default_conf, mocker): +def test_rpc_balance_handle_error(default_conf, mocker, caplog): mock_balance = { "BTC": { "free": 10.0, @@ -519,35 +518,73 @@ def test_rpc_balance_handle_error(default_conf, mocker): }, } # ETH will be skipped due to mocked Error below + mock_pos = [ + { + "symbol": "ADA/USDT:USDT", + "timestamp": None, + "datetime": None, + "initialMargin": 20, + "initialMarginPercentage": None, + "maintenanceMargin": 0.0, + "maintenanceMarginPercentage": 0.005, + "entryPrice": 0.0, + "notional": 10.0, + "leverage": 5.0, + "unrealizedPnl": 0.0, + "contracts": 1.0, + "contractSize": 1, + "marginRatio": None, + "liquidationPrice": 0.0, + "markPrice": 2896.41, + # Collateral is in USDT - and can be higher than position size in cross mode + "collateral": 50, + "marginType": "cross", + "side": "short", + "percentage": None, + } + ] - mocker.patch.multiple( - "freqtrade.rpc.fiat_convert.FtCoinGeckoApi", - get_price=MagicMock(return_value={"bitcoin": {"usd": 15000.0}}), - ) - mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=15000.0) mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock()) mocker.patch.multiple( EXMS, get_balances=MagicMock(return_value=mock_balance), + fetch_positions=MagicMock(return_value=mock_pos), get_tickers=MagicMock(side_effect=TemporaryError("Could not load ticker due to xxx")), ) - + default_conf["trading_mode"] = "futures" + default_conf["margin_mode"] = "isolated" + default_conf["dry_run"] = False freqtradebot = get_patched_freqtradebot(mocker, default_conf) patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter({}) + mocker.patch.object(rpc._fiat_converter, "get_price", return_value=15000.0) res = rpc._rpc_balance(default_conf["stake_currency"], default_conf["fiat_display_currency"]) assert res["stake"] == "BTC" - assert len(res["currencies"]) == 1 + assert len(res["currencies"]) == 3 assert res["currencies"][0]["currency"] == "BTC" - # ETH has not been converted. - assert all(currency["currency"] != "ETH" for currency in res["currencies"]) + curr_ETH = next(currency for currency in res["currencies"] if currency["currency"] == "ETH") + # coins are part of the result, but were not converted + assert curr_ETH is not None + assert curr_ETH["currency"] == "ETH" + assert curr_ETH["est_stake"] == 0 + curr_ADA = next( + currency for currency in res["currencies"] if currency["currency"] == "ADA/USDT:USDT" + ) + assert curr_ADA is not None + assert curr_ADA["currency"] == "ADA/USDT:USDT" + # Fall back to collateral value when rate not available + assert curr_ADA["est_stake"] == 20 + + assert log_has_re(r"Error .* getting rate for futures ADA.*", caplog) + assert log_has_re(r"Error .* getting rate for ETH.*", caplog) @pytest.mark.parametrize("proxy_coin", [None, "BNFCR"]) @pytest.mark.parametrize("margin_mode", ["isolated", "cross"]) -def test_rpc_balance_handle(default_conf_usdt, mocker, tickers, proxy_coin, margin_mode): +@pytest.mark.parametrize("is_short", [True, False]) +def test_rpc_balance_handle(default_conf_usdt, mocker, tickers, proxy_coin, margin_mode, is_short): mock_balance = { "BTC": { "free": 0.01, @@ -572,6 +609,8 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers, proxy_coin, marg "used": 5.0, }, } + # Fake ADA response + tickers.return_value["ADA/USDT"] = tickers.return_value["ETH/USDT"] if proxy_coin: default_conf_usdt["proxy_coin"] = proxy_coin mock_balance[proxy_coin] = { @@ -580,12 +619,13 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers, proxy_coin, marg "used": 0.0, } + # Current ADA price based on Tickers is 530.21 USDT mock_pos = [ { - "symbol": "ETH/USDT:USDT", + "symbol": "ADA/USDT:USDT", "timestamp": None, "datetime": None, - "initialMargin": 20, + "initialMargin": 50, "initialMarginPercentage": None, "maintenanceMargin": 0.0, "maintenanceMarginPercentage": 0.005, @@ -593,24 +633,19 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers, proxy_coin, marg "notional": 10.0, "leverage": 5.0, "unrealizedPnl": 0.0, - "contracts": 1.0, + "contracts": 0.48, "contractSize": 1, "marginRatio": None, "liquidationPrice": 0.0, - "markPrice": 2896.41, + "markPrice": 520, # Entry price ... # Collateral is in USDT - and can be higher than position size in cross mode - "collateral": 50, + "collateral": 100, "marginType": "cross", - "side": "short", + "side": "short" if is_short else "long", "percentage": None, } ] - mocker.patch.multiple( - "freqtrade.rpc.fiat_convert.FtCoinGeckoApi", - get_price=MagicMock(return_value={"bitcoin": {"usd": 1.2}}), - ) - mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=1.2) mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock()) mocker.patch.multiple( EXMS, @@ -621,6 +656,7 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers, proxy_coin, marg get_valid_pair_combination=MagicMock( side_effect=lambda a, b: [f"{b}/{a}" if a == "USDT" else f"{a}/{b}"] ), + _contracts_to_amount=MagicMock(side_effect=lambda c, cs: cs), ) default_conf_usdt["dry_run"] = False default_conf_usdt["trading_mode"] = "futures" @@ -629,15 +665,21 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers, proxy_coin, marg patch_get_signal(freqtradebot) rpc = RPC(freqtradebot) rpc._fiat_converter = CryptoToFiatConverter({}) - + mocker.patch.object(rpc._fiat_converter, "get_price", return_value=1.2) + mocker.patch( + "freqtrade.persistence.trade_model.Trade.get_open_trades", + return_value=[ + MagicMock(pair="ADA/USDT:USDT", safe_base_currency="ADA"), + ], + ) result = rpc._rpc_balance( default_conf_usdt["stake_currency"], default_conf_usdt["fiat_display_currency"] ) - assert tickers.call_count == 4 if not proxy_coin else 6 + assert tickers.call_count == (7 if proxy_coin and margin_mode != "cross" else 5) assert tickers.call_args_list[0][1]["cached"] is True # Testing futures - so we should get spot tickers - assert tickers.call_args_list[-1][1]["market_type"] == "spot" + tickers.assert_any_call(symbols=None, cached=True, market_type=TradingMode.SPOT) assert "USD" == result["symbol"] expected_curr = [ { @@ -697,15 +739,15 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers, proxy_coin, marg "is_position": False, }, { - "currency": "ETH/USDT:USDT", + "currency": "ADA/USDT:USDT", "free": 0, "balance": 0, "used": 0, - "position": 10.0, - "est_stake": 20, - "est_stake_bot": 20, + "position": 0.48, + "est_stake": pytest.approx(45.4992 if is_short else 54.5008), + "est_stake_bot": pytest.approx(45.4992 if is_short else 54.5008), "stake": "USDT", - "side": "short", + "side": "short" if is_short else "long", "is_bot_managed": True, "is_position": True, }, @@ -765,18 +807,105 @@ def test_rpc_balance_handle(default_conf_usdt, mocker, tickers, proxy_coin, marg assert result["currencies"] == expected_curr if proxy_coin and margin_mode == "cross": - assert pytest.approx(result["total_bot"]) == 1505.0 - assert pytest.approx(result["total"]) == 2186.6972 # ETH stake is missing. + # only USDT and ADA position are bot-managed + assert pytest.approx(result["total_bot"]) == (1530.4992 if is_short else 1539.5008) + assert pytest.approx(result["total"]) == (2212.19640 if is_short else 2221.198) assert result["starting_capital"] == 1500 * default_conf_usdt["tradable_balance_ratio"] - assert result["starting_capital_ratio"] == pytest.approx(0.013468013468013407) + assert result["starting_capital_ratio"] == pytest.approx( + 0.03063919 if is_short else 0.03670087 + ) else: - assert pytest.approx(result["total_bot"]) == 69.5 - assert pytest.approx(result["total"]) == 686.6972 # ETH stake is missing. + # only USDT and ADA position are bot-managed + assert pytest.approx(result["total_bot"]) == (94.9992 if is_short else 104.0008) + assert pytest.approx(result["total"]) == (712.1964 if is_short else 721.1980) assert result["starting_capital"] == 50 * default_conf_usdt["tradable_balance_ratio"] - assert result["starting_capital_ratio"] == pytest.approx(0.4040404) + assert result["starting_capital_ratio"] == pytest.approx(0.919175 if is_short else 1.101026) assert pytest.approx(result["value"]) == result["total"] * 1.2 +def test_rpc_balance_futures(default_conf_usdt, mocker): + """Validate est_stake (equity) calculation for both short and long positions. + + Short scenario: + - collateral = 100, leverage = 2, position = 2, rate = 50 + - open_value = 200, current_value = 100 -> unlevered PnL = 100 + - equity = collateral + PnL = 200 + + Long scenario: + - collateral = 150, leverage = 3, position = 3, rate = 200 + - open_value = 450, current_value = 600 -> unlevered PnL = 150 + - equity = collateral + PnL = 300 + """ + from freqtrade.wallets import PositionWallet, Wallet + + mock_balance = {"USDT": {"free": 1000.0, "total": 1000.0, "used": 0.0}} + + # Patch exchange and wallets with different rates per base currency + def _rate(base, stake): + if base == "FOO": + return 50.0 + if base == "BAR": + return 200.0 + return None + + mocker.patch.multiple( + EXMS, + validate_trading_mode_and_margin_mode=MagicMock(), + get_balances=MagicMock(return_value=mock_balance), + get_tickers=MagicMock(return_value={}), + get_conversion_rate=MagicMock(side_effect=_rate), + get_pair_base_currency=MagicMock(side_effect=lambda pair: pair.split("/")[0]), + ) + + default_conf_usdt["dry_run"] = False + default_conf_usdt["trading_mode"] = "futures" + default_conf_usdt["margin_mode"] = "isolated" + + freqtradebot = get_patched_freqtradebot(mocker, default_conf_usdt) + + # Create a short and a long position wallet directly to avoid depending on position parsing + short_pos = PositionWallet( + symbol="FOO/USDT:USDT", + position=2.0, + leverage=2.0, + collateral=100.0, + side="short", + ) + long_pos = PositionWallet( + symbol="BAR/USDT:USDT", + position=3.0, + leverage=3.0, + collateral=150.0, + side="long", + ) + + mocker.patch.multiple( + freqtradebot.wallets, + get_all_positions=MagicMock( + return_value={short_pos.symbol: short_pos, long_pos.symbol: long_pos} + ), + get_all_balances=MagicMock(return_value={"USDT": Wallet("USDT", 1000.0, 1000.0, 0.0)}), + ) + + rpc = RPC(freqtradebot) + result = rpc._rpc_balance( + default_conf_usdt["stake_currency"], default_conf_usdt["fiat_display_currency"] + ) + + pos_short = next(c for c in result["currencies"] if c["currency"] == short_pos.symbol) + pos_long = next(c for c in result["currencies"] if c["currency"] == long_pos.symbol) + + assert pos_short["est_stake"] == 200.0 + assert pos_long["est_stake"] == 300.0 + assert result["total"] == 1500.0 + assert result["total_bot"] == 1490.0 + assert result["value_bot"] == 0 # No fiat conversion + stake_pos = result["currencies"][0] + assert stake_pos["currency"] == "USDT" + assert stake_pos["est_stake_bot"] == 990.0 + assert stake_pos["bot_owned"] == 990.0 + + def test_rpc_start(mocker, default_conf) -> None: mocker.patch("freqtrade.rpc.telegram.Telegram", MagicMock()) mocker.patch.multiple(EXMS, fetch_ticker=MagicMock()) diff --git a/tests/rpc/test_rpc_apiserver.py b/tests/rpc/test_rpc_apiserver.py index 768858aac..57831935b 100644 --- a/tests/rpc/test_rpc_apiserver.py +++ b/tests/rpc/test_rpc_apiserver.py @@ -1199,6 +1199,11 @@ def test_api_logs(botclient): "winrate": 0.0, "expectancy": -0.0033695635, "expectancy_ratio": -1.0, + "cagr": -0.0024567404889381805, + "calmar": -1910.497317469542, + "sharpe": -58.138247358830355, + "sortino": -58.138247358830355, + "sqn": -1.5215, "trading_volume": 75.945, }, ), @@ -1231,6 +1236,11 @@ def test_api_logs(botclient): "winrate": 1.0, "expectancy": 0.0003695635, "expectancy_ratio": 100, + "cagr": 0.0002698167695580622, + "calmar": -100.0, + "sharpe": 65.81269184917424, + "sortino": -100.0, + "sqn": 1.7224, "trading_volume": 75.945, }, ), @@ -1263,6 +1273,11 @@ def test_api_logs(botclient): "winrate": 0.5, "expectancy": -0.0027145635000000003, "expectancy_ratio": -0.48612137582114445, + "cagr": -0.0019796559404918757, + "calmar": -1857.4671689202785, + "sharpe": -36.14602907243071, + "sortino": -100.0, + "sqn": -0.946, "trading_volume": 75.945, }, ), @@ -1326,6 +1341,11 @@ def test_api_profit(botclient, mocker, ticker, fee, markets, is_short, expected) "winrate": expected["winrate"], "expectancy": expected["expectancy"], "expectancy_ratio": expected["expectancy_ratio"], + "sharpe": expected["sharpe"], + "sortino": expected["sortino"], + "sqn": expected["sqn"], + "calmar": expected["calmar"], + "cagr": expected["cagr"], "max_drawdown": ANY, "max_drawdown_abs": ANY, "max_drawdown_start": ANY, @@ -1911,16 +1931,34 @@ def gen_annotation_params(): "width": 2, "line_style": "dashed", } + point_annotation = { + "type": "point", + "x": "2024-01-01 15:30:00", + "y": 97000, + "color": "", + "label": "some label", + "size": 10, + "shape": "circle", + } line_wrong = deepcopy(line_annotation) line_wrong["line_style"] = "dashed2222" + point_wrong = deepcopy(point_annotation) + point_wrong["shape"] = "circle2222" + # annotations / expected return [ ([area_annotation], [area_annotation]), # Only area ([line_annotation], [line_annotation]), # Only line - ([area_annotation, line_annotation], [area_annotation, line_annotation]), # Both together + ([point_annotation], [point_annotation]), # Only point + ([area_annotation, line_annotation], [area_annotation, line_annotation]), # mark and line + ( + [area_annotation, line_annotation, point_annotation], + [area_annotation, line_annotation, point_annotation], + ), # all together ([], []), # Empty ([line_wrong], []), # Invalid line ([area_annotation, line_wrong], [area_annotation]), # Invalid line + ([point_wrong], []), # Invalid point ] @@ -2488,6 +2526,7 @@ def test_api_plot_config(botclient, mocker, tmp_path): def test_api_strategies(botclient, tmp_path): ftbot, client = botclient ftbot.config["user_data_dir"] = tmp_path + ftbot.config["runmode"] = RunMode.WEBSERVER rc = client_get(client, f"{BASE_URI}/strategies") @@ -2513,15 +2552,18 @@ def test_api_strategies(botclient, tmp_path): def test_api_strategy(botclient, tmp_path, mocker): - _ftbot, client = botclient - _ftbot.config["user_data_dir"] = tmp_path + ftbot, client = botclient + ftbot.config["user_data_dir"] = tmp_path + ftbot.config["runmode"] = RunMode.WEBSERVER rc = client_get(client, f"{BASE_URI}/strategy/{CURRENT_TEST_STRATEGY}") assert_response(rc) assert rc.json()["strategy"] == CURRENT_TEST_STRATEGY - data = (Path(__file__).parents[1] / "strategy/strats/strategy_test_v3.py").read_text() + data = (Path(__file__).parents[1] / "strategy/strats/strategy_test_v3.py").read_text( + encoding="utf-8" + ) assert rc.json()["code"] == data rc = client_get(client, f"{BASE_URI}/strategy/NoStrat") @@ -2541,6 +2583,7 @@ def test_api_strategy(botclient, tmp_path, mocker): def test_api_exchanges(botclient): _ftbot, client = botclient + _ftbot.config["runmode"] = RunMode.WEBSERVER rc = client_get(client, f"{BASE_URI}/exchanges") assert_response(rc) @@ -2554,6 +2597,7 @@ def test_api_exchanges(botclient): "valid": True, "supported": True, "comment": "", + "comment_futures": ANY, "dex": False, "is_alias": False, "alias_for": None, @@ -2571,6 +2615,7 @@ def test_api_exchanges(botclient): "supported": False, "dex": False, "comment": "", + "comment_futures": ANY, "is_alias": False, "alias_for": None, "trade_modes": [{"trading_mode": "spot", "margin_mode": ""}], @@ -2583,6 +2628,7 @@ def test_api_exchanges(botclient): "supported": False, "dex": True, "comment": ANY, + "comment_futures": ANY, "is_alias": False, "alias_for": None, "trade_modes": [{"trading_mode": "spot", "margin_mode": ""}], @@ -2592,6 +2638,7 @@ def test_api_exchanges(botclient): def test_list_hyperoptloss(botclient, tmp_path): ftbot, client = botclient ftbot.config["user_data_dir"] = tmp_path + ftbot.config["runmode"] = RunMode.WEBSERVER rc = client_get(client, f"{BASE_URI}/hyperoptloss") assert_response(rc) @@ -2608,6 +2655,8 @@ def test_list_hyperoptloss(botclient, tmp_path): def test_api_freqaimodels(botclient, tmp_path, mocker): ftbot, client = botclient ftbot.config["user_data_dir"] = tmp_path + ftbot.config["runmode"] = RunMode.WEBSERVER + mocker.patch( "freqtrade.resolvers.freqaimodel_resolver.FreqaiModelResolver.search_all_objects", return_value=[ @@ -2779,6 +2828,7 @@ def test_api_pairlists_evaluate(botclient, tmp_path, mocker): def test_list_available_pairs(botclient): ftbot, client = botclient + ftbot.config["runmode"] = RunMode.WEBSERVER rc = client_get(client, f"{BASE_URI}/available_pairs") diff --git a/tests/rpc/test_rpc_telegram.py b/tests/rpc/test_rpc_telegram.py index 49245230b..b84497593 100644 --- a/tests/rpc/test_rpc_telegram.py +++ b/tests/rpc/test_rpc_telegram.py @@ -119,7 +119,7 @@ class DummyCls(Telegram): raise Exception("test") -def get_telegram_testobject(mocker, default_conf, mock=True, ftbot=None): +def get_telegram_testobject(mocker, default_conf, mock=True, ftbot=None, mock_fiat=True): msg_mock = AsyncMock() if mock: mocker.patch.multiple( @@ -131,6 +131,9 @@ def get_telegram_testobject(mocker, default_conf, mock=True, ftbot=None): if not ftbot: ftbot = get_patched_freqtradebot(mocker, default_conf) rpc = RPC(ftbot) + if rpc._fiat_converter is not None and mock_fiat: + mocker.patch.object(rpc._fiat_converter, "get_price", return_value=1.1) + telegram = Telegram(rpc, default_conf) telegram._loop = MagicMock() patch_eventloop_threading(telegram) @@ -667,7 +670,6 @@ async def test_status_table_handle(default_conf, update, ticker, fee, mocker) -> async def test_daily_handle(default_conf_usdt, update, ticker, fee, mocker, time_machine) -> None: - mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=1.1) mocker.patch.multiple( EXMS, fetch_ticker=ticker, @@ -750,7 +752,6 @@ async def test_daily_wrong_input(default_conf, update, ticker, mocker) -> None: async def test_weekly_handle(default_conf_usdt, update, ticker, fee, mocker, time_machine) -> None: default_conf_usdt["max_open_trades"] = 1 - mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=1.1) mocker.patch.multiple( EXMS, fetch_ticker=ticker, @@ -821,7 +822,6 @@ async def test_weekly_handle(default_conf_usdt, update, ticker, fee, mocker, tim async def test_monthly_handle(default_conf_usdt, update, ticker, fee, mocker, time_machine) -> None: default_conf_usdt["max_open_trades"] = 1 - mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=1.1) mocker.patch.multiple( EXMS, fetch_ticker=ticker, @@ -903,7 +903,6 @@ async def test_monthly_handle(default_conf_usdt, update, ticker, fee, mocker, ti async def test_telegram_profit_handle( default_conf_usdt, update, ticker_usdt, ticker_sell_up, fee, limit_sell_order_usdt, mocker ) -> None: - mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=1.1) mocker.patch.multiple( EXMS, fetch_ticker=ticker_usdt, @@ -983,7 +982,6 @@ async def test_telegram_profit_long_short_handle( is consistent with /profit, covering both no trades and trades present cases. """ - mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=1.1) mocker.patch.multiple(EXMS, fetch_ticker=ticker_usdt, get_fee=fee) telegram, _freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf_usdt) @@ -1062,7 +1060,6 @@ async def test_telegram_profit_long_short_handle( @pytest.mark.parametrize("is_short", [True, False]) async def test_telegram_stats(default_conf, update, ticker, fee, mocker, is_short) -> None: - mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=15000.0) mocker.patch.multiple( EXMS, fetch_ticker=ticker, @@ -1158,7 +1155,7 @@ async def test_telegram_balance_handle_futures( "percentage": None, }, { - "symbol": "XRP/USDT:USDT", + "symbol": "ADA/USDT:USDT", "timestamp": None, "datetime": None, "initialMargin": 0.0, @@ -1184,9 +1181,17 @@ async def test_telegram_balance_handle_futures( mocker.patch(f"{EXMS}.fetch_positions", return_value=mock_pos) mocker.patch(f"{EXMS}.get_tickers", tickers) mocker.patch(f"{EXMS}.get_valid_pair_combination", side_effect=lambda a, b: [f"{a}/{b}"]) + mocker.patch(f"{EXMS}.get_conversion_rate", return_value=3200) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) patch_get_signal(freqtradebot) + mocker.patch( + "freqtrade.persistence.trade_model.Trade.get_open_trades", + return_value=[ + MagicMock(pair="ETH/USDT:USDT", safe_base_currency="ETH"), + MagicMock(pair="ADA/USDT:USDT", safe_base_currency="ADA"), + ], + ) await telegram._balance(update=update, context=MagicMock()) result = msg_mock.call_args_list[0][0][0] @@ -1194,7 +1199,7 @@ async def test_telegram_balance_handle_futures( assert "ETH/USDT:USDT" in result assert "`short: 10" in result - assert "XRP/USDT:USDT" in result + assert "ADA/USDT:USDT" in result async def test_balance_handle_empty_response(default_conf, update, mocker) -> None: @@ -1341,7 +1346,6 @@ async def test_reload_config_handle(default_conf, update, mocker) -> None: async def test_telegram_forceexit_handle( default_conf, update, ticker, fee, ticker_sell_up, mocker ) -> None: - mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=15000.0) msg_mock = mocker.patch("freqtrade.rpc.telegram.Telegram.send_msg", MagicMock()) mocker.patch("freqtrade.rpc.telegram.Telegram._init", MagicMock()) patch_exchange(mocker) @@ -1411,9 +1415,6 @@ async def test_telegram_forceexit_handle( async def test_telegram_force_exit_down_handle( default_conf, update, ticker, fee, ticker_sell_down, mocker ) -> None: - mocker.patch( - "freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price", return_value=15000.0 - ) msg_mock = mocker.patch("freqtrade.rpc.telegram.Telegram.send_msg", MagicMock()) mocker.patch("freqtrade.rpc.telegram.Telegram._init", MagicMock()) patch_exchange(mocker) @@ -1484,9 +1485,6 @@ async def test_telegram_force_exit_down_handle( async def test_forceexit_all_handle(default_conf, update, ticker, fee, mocker) -> None: patch_exchange(mocker) - mocker.patch( - "freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price", return_value=15000.0 - ) msg_mock = mocker.patch("freqtrade.rpc.telegram.Telegram.send_msg", MagicMock()) mocker.patch("freqtrade.rpc.telegram.Telegram._init", MagicMock()) patch_whitelist(mocker, default_conf) @@ -1549,10 +1547,6 @@ async def test_forceexit_all_handle(default_conf, update, ticker, fee, mocker) - async def test_forceexit_handle_invalid(default_conf, update, mocker) -> None: - mocker.patch( - "freqtrade.rpc.fiat_convert.CryptoToFiatConverter._find_price", return_value=15000.0 - ) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) patch_get_signal(freqtradebot) @@ -1630,8 +1624,6 @@ async def test_force_exit_no_pair(default_conf, update, ticker, fee, mocker) -> async def test_force_enter_handle(default_conf, update, mocker) -> None: - mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=15000.0) - fbuy_mock = MagicMock(return_value=None) mocker.patch("freqtrade.rpc.rpc.RPC._rpc_force_entry", fbuy_mock) @@ -1663,8 +1655,6 @@ async def test_force_enter_handle(default_conf, update, mocker) -> None: async def test_force_enter_handle_exception(default_conf, update, mocker) -> None: - mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=15000.0) - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) patch_get_signal(freqtradebot) @@ -1675,10 +1665,7 @@ async def test_force_enter_handle_exception(default_conf, update, mocker) -> Non async def test_force_enter_no_pair(default_conf, update, mocker) -> None: - mocker.patch("freqtrade.rpc.rpc.CryptoToFiatConverter._find_price", return_value=15000.0) - - fbuy_mock = MagicMock(return_value=None) - mocker.patch("freqtrade.rpc.rpc.RPC._rpc_force_entry", fbuy_mock) + fbuy_mock = mocker.patch("freqtrade.rpc.rpc.RPC._rpc_force_entry", return_value=None) telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) @@ -2241,7 +2228,9 @@ def test_send_msg_enter_notification( "analyzed_candle": {"open": 1.1, "high": 2.2, "low": 1.0, "close": 1.5}, "open_date": dt_now() + timedelta(hours=-1), } - telegram, freqtradebot, msg_mock = get_telegram_testobject(mocker, default_conf) + telegram, freqtradebot, msg_mock = get_telegram_testobject( + mocker, default_conf, mock_fiat=False + ) telegram.send_msg(msg) leverage_text = f" ({leverage:.3g}x)" if leverage and leverage != 1.0 else "" @@ -2347,7 +2336,7 @@ def test_send_msg_entry_fill_notification( default_conf, mocker, message_type, entered, enter_signal, leverage ) -> None: default_conf["telegram"]["notification_settings"]["entry_fill"] = "on" - telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf) + telegram, _, msg_mock = get_telegram_testobject(mocker, default_conf, mock_fiat=False) telegram.send_msg( { diff --git a/tests/strategy/strats/strategy_test_v2.py b/tests/strategy/strats/strategy_test_v2.py index 58473e9c0..04d218bc6 100644 --- a/tests/strategy/strats/strategy_test_v2.py +++ b/tests/strategy/strats/strategy_test_v2.py @@ -14,6 +14,16 @@ class StrategyTestV2(IStrategy): Please look at the SampleStrategy in the user_data/strategy directory or strategy repository https://github.com/freqtrade/freqtrade-strategies for samples and inspiration. + + --- + + Some test asian characters. + Ensures that unicode characters are handled correctly when reading strategy files. + Otherwise this may break on windows systems. + All roughly translate to "hello world". + chinese string: "δ½ ε₯½δΈ–η•Œ" + korean string: "μ•ˆλ…•ν•˜μ„Έμš”,세계" + japanese string: "γ“γ‚“γ«γ‘γ―γ€δΈ–η•Œ" """ INTERFACE_VERSION = 2 diff --git a/tests/strategy/strats/strategy_test_v3.py b/tests/strategy/strats/strategy_test_v3.py index e87810111..993b04d4b 100644 --- a/tests/strategy/strats/strategy_test_v3.py +++ b/tests/strategy/strats/strategy_test_v3.py @@ -23,6 +23,16 @@ class StrategyTestV3(IStrategy): Please look at the SampleStrategy in the user_data/strategy directory or strategy repository https://github.com/freqtrade/freqtrade-strategies for samples and inspiration. + + --- + + Some test asian characters. + Ensures that unicode characters are handled correctly when reading strategy files. + Otherwise this may break on windows systems. + All roughly translate to "hello world". + chinese string: "δ½ ε₯½δΈ–η•Œ" + korean string: "μ•ˆλ…•ν•˜μ„Έμš”,세계" + japanese string: "γ“γ‚“γ«γ‘γ―γ€δΈ–η•Œ" """ INTERFACE_VERSION = 3 diff --git a/tests/strategy/test_interface.py b/tests/strategy/test_interface.py index 1cad0e2b4..5eb933f44 100644 --- a/tests/strategy/test_interface.py +++ b/tests/strategy/test_interface.py @@ -1047,7 +1047,7 @@ def test_pandas_warning_direct(ohlcv_history, function, raises, recwarn): # Fixed in 2.2.x getattr(_STRATEGY, function)(df, {"pair": "ETH/BTC"}) else: - assert len(recwarn) == 0 + assert len(recwarn) == 0, f"warnings: {', '.join(recwarn.list)}" getattr(_STRATEGY, function)(df, {"pair": "ETH/BTC"}) @@ -1055,4 +1055,4 @@ def test_pandas_warning_direct(ohlcv_history, function, raises, recwarn): def test_pandas_warning_through_analyze_pair(ohlcv_history, mocker, recwarn): mocker.patch.object(_STRATEGY.dp, "ohlcv", return_value=ohlcv_history) _STRATEGY.analyze_pair("ETH/BTC") - assert len(recwarn) == 0 + assert len(recwarn) == 0, f"warnings: {', '.join(recwarn.list)}" diff --git a/tests/test_pip_audit.py b/tests/test_pip_audit.py index ab9017b89..7c135c2c9 100644 --- a/tests/test_pip_audit.py +++ b/tests/test_pip_audit.py @@ -4,6 +4,7 @@ Original Idea and base for this implementation by Michael Kennedy's blog: https://mkennedy.codes/posts/python-supply-chain-security-made-easy/ """ +import os import subprocess import sys from pathlib import Path @@ -11,6 +12,12 @@ from pathlib import Path import pytest +IN_GITHUB_ACTIONS = os.getenv("GITHUB_ACTIONS") == "true" + + +# Skip this test in github actions - github issues a security warning on it's own. +# This is to detect local transient dependencies. +@pytest.mark.skipif(IN_GITHUB_ACTIONS, reason="Skip pip-audit in GitHub Actions") def test_pip_audit_no_vulnerabilities(): """ Run pip-audit to check for known security vulnerabilities. diff --git a/tests/test_strategy_updater.py b/tests/test_strategy_updater.py index 48f1d27d7..3c53f425e 100644 --- a/tests/test_strategy_updater.py +++ b/tests/test_strategy_updater.py @@ -15,7 +15,7 @@ def test_strategy_updater_start(user_dir, capsys) -> None: tmpdirp = Path(user_dir) / "strategies" tmpdirp.mkdir(parents=True, exist_ok=True) shutil.copy(teststrats / "strategy_test_v2.py", tmpdirp) - old_code = (teststrats / "strategy_test_v2.py").read_text() + old_code = (teststrats / "strategy_test_v2.py").read_text(encoding="utf-8") args = ["strategy-updater", "--userdir", str(user_dir), "--strategy-list", "StrategyTestV2"] pargs = get_args(args) @@ -29,7 +29,7 @@ def test_strategy_updater_start(user_dir, capsys) -> None: # updated file exists new_file = tmpdirp / "strategy_test_v2.py" assert new_file.exists() - new_code = new_file.read_text() + new_code = new_file.read_text(encoding="utf-8") assert "INTERFACE_VERSION = 3" in new_code assert "INTERFACE_VERSION = 2" in old_code captured = capsys.readouterr() diff --git a/tests/util/test_binance_mig.py b/tests/util/test_binance_mig.py deleted file mode 100644 index db3d8b282..000000000 --- a/tests/util/test_binance_mig.py +++ /dev/null @@ -1,65 +0,0 @@ -import shutil - -import pytest - -from freqtrade.persistence import Trade -from freqtrade.util.migrations import migrate_binance_futures_data, migrate_data -from freqtrade.util.migrations.binance_mig import migrate_binance_futures_names -from tests.conftest import create_mock_trades_usdt, log_has - - -def test_binance_mig_data_conversion(default_conf_usdt, tmp_path, testdatadir): - # call doing nothing (spot mode) - migrate_binance_futures_data(default_conf_usdt) - default_conf_usdt["trading_mode"] = "futures" - pair_old = "XRP_USDT" - pair_unified = "XRP_USDT_USDT" - futures_src = testdatadir / "futures" - futures_dst = tmp_path / "futures" - futures_dst.mkdir() - files = [ - "-1h-mark.feather", - "-1h-futures.feather", - "-1h-funding_rate.feather", - "-1h-mark.feather", - ] - - # Copy files to tmpdir and rename to old naming - for file in files: - fn_after = futures_dst / f"{pair_old}{file}" - shutil.copy(futures_src / f"{pair_unified}{file}", fn_after) - - default_conf_usdt["datadir"] = tmp_path - # Migrate files to unified namings - migrate_binance_futures_data(default_conf_usdt) - - for file in files: - fn_after = futures_dst / f"{pair_unified}{file}" - assert fn_after.exists() - - -@pytest.mark.usefixtures("init_persistence") -def test_binance_mig_db_conversion(default_conf_usdt, fee, caplog): - # Does nothing in spot mode - migrate_binance_futures_names(default_conf_usdt) - - create_mock_trades_usdt(fee, None) - - for t in Trade.get_trades(): - t.trading_mode = "FUTURES" - t.exchange = "binance" - Trade.commit() - - default_conf_usdt["trading_mode"] = "futures" - migrate_binance_futures_names(default_conf_usdt) - assert log_has("Migrating binance futures pairs in database.", caplog) - - -def test_migration_wrapper(default_conf_usdt, mocker): - default_conf_usdt["trading_mode"] = "futures" - binmock = mocker.patch("freqtrade.util.migrations.migrate_binance_futures_data") - funding_mock = mocker.patch("freqtrade.util.migrations.migrate_funding_fee_timeframe") - migrate_data(default_conf_usdt) - - assert binmock.call_count == 1 - assert funding_mock.call_count == 1 diff --git a/tests/util/test_datetime_helpers.py b/tests/util/test_datetime_helpers.py index 8ccb8ba20..9b4f98809 100644 --- a/tests/util/test_datetime_helpers.py +++ b/tests/util/test_datetime_helpers.py @@ -82,7 +82,7 @@ def test_dt_humanize() -> None: assert dt_humanize_delta(dt_now() - timedelta(minutes=50)) == "50 minutes ago" assert dt_humanize_delta(dt_now() - timedelta(hours=16)) == "16 hours ago" assert dt_humanize_delta(dt_now() - timedelta(hours=16, minutes=30)) == "16 hours ago" - assert dt_humanize_delta(dt_now() - timedelta(days=16, hours=10, minutes=25)) == "16 days ago" + assert dt_humanize_delta(dt_now() - timedelta(days=16, hours=10, minutes=25)) == "a month ago" assert dt_humanize_delta(dt_now() - timedelta(minutes=50)) == "50 minutes ago"